diff --git a/QuestPDF.Examples/DynamicExamples.cs b/QuestPDF.Examples/DynamicExamples.cs index 7353198..7e1a6f3 100644 --- a/QuestPDF.Examples/DynamicExamples.cs +++ b/QuestPDF.Examples/DynamicExamples.cs @@ -15,25 +15,29 @@ namespace QuestPDF.Examples public int Price { get; set; } = Placeholders.Random.Next(1, 11) * 10; public int Count { get; set; } = Placeholders.Random.Next(1, 11); } + + public struct OrdersTableState + { + public int ShownItemsCount { get; set; } + } - public class OrdersTable : IDynamicComponent + public class OrdersTable : IDynamicComponent { private ICollection Items { get; } - private ICollection ItemsLeft { get; set; } - + public OrdersTableState State { get; set; } + public OrdersTable(ICollection items) { Items = items; + + State = new OrdersTableState + { + ShownItemsCount = 0 + }; } public void Compose(DynamicContext context, IDynamicContainer container) { - if (context.Operation == DynamicLayoutOperation.Reset) - { - ItemsLeft = new List(Items); - return; - } - var header = ComposeHeader(context); var sampleFooter = ComposeFooter(context, Enumerable.Empty()); var decorationHeight = header.Size.Height + sampleFooter.Size.Height; @@ -41,12 +45,9 @@ namespace QuestPDF.Examples var rows = GetItemsForPage(context, decorationHeight).ToList(); var footer = ComposeFooter(context, rows.Select(x => x.Item)); - if (ItemsLeft.Count > rows.Count) + if (State.ShownItemsCount + rows.Count < Items.Count) container.HasMoreContent(); - if (context.Operation == DynamicLayoutOperation.Draw) - ItemsLeft = ItemsLeft.Skip(rows.Count).ToList(); - container.MinimalBox().Decoration(decoration => { decoration.Header().Element(header); @@ -59,6 +60,11 @@ namespace QuestPDF.Examples decoration.Footer().Element(footer); }); + + State = new OrdersTableState + { + ShownItemsCount = State.ShownItemsCount + rows.Count + }; } private IDynamicElement ComposeHeader(DynamicContext context) @@ -98,10 +104,11 @@ namespace QuestPDF.Examples private IEnumerable<(OrderItem Item, IDynamicElement Element)> GetItemsForPage(DynamicContext context, float decorationHeight) { var totalHeight = decorationHeight; - var counter = Items.Count - ItemsLeft.Count + 1; - - foreach (var orderItem in ItemsLeft) + + foreach (var index in Enumerable.Range(State.ShownItemsCount, Items.Count - State.ShownItemsCount)) { + var item = Items.ElementAt(index); + var element = context.CreateElement(content => { content @@ -110,11 +117,11 @@ namespace QuestPDF.Examples .Padding(5) .Row(row => { - row.ConstantItem(30).Text(counter++); - row.RelativeItem().Text(orderItem.ItemName); - row.ConstantItem(50).AlignRight().Text(orderItem.Count); - row.ConstantItem(50).AlignRight().Text($"{orderItem.Price}$"); - row.ConstantItem(50).AlignRight().Text($"{orderItem.Count*orderItem.Price}$"); + row.ConstantItem(30).Text(index + 1); + row.RelativeItem().Text(item.ItemName); + row.ConstantItem(50).AlignRight().Text(item.Count); + row.ConstantItem(50).AlignRight().Text($"{item.Price}$"); + row.ConstantItem(50).AlignRight().Text($"{item.Count*item.Price}$"); }); }); @@ -124,7 +131,7 @@ namespace QuestPDF.Examples break; totalHeight += elementHeight; - yield return (orderItem, element); + yield return (item, element); } } } diff --git a/QuestPDF/Elements/Dynamic.cs b/QuestPDF/Elements/Dynamic.cs index c744430..dc526c1 100644 --- a/QuestPDF/Elements/Dynamic.cs +++ b/QuestPDF/Elements/Dynamic.cs @@ -8,22 +8,26 @@ namespace QuestPDF.Elements { internal class DynamicHost : Element, IStateResettable { - private IDynamicComponent Child { get; } + private DynamicComponentProxy Child { get; } + private object InitialComponentState { get; set; } + internal TextStyle TextStyle { get; } = new(); - public DynamicHost(IDynamicComponent child) + public DynamicHost(DynamicComponentProxy child) { Child = child; + + InitialComponentState = Child.GetState(); } public void ResetState() { - GetContent(Size.Zero, DynamicLayoutOperation.Reset); + Child.SetState(InitialComponentState); } internal override SpacePlan Measure(Size availableSpace) { - var content = GetContent(availableSpace, DynamicLayoutOperation.Measure); + var content = GetContent(availableSpace, acceptNewState: false); var measurement = content.Measure(availableSpace); if (measurement.Type != SpacePlanType.FullRender) @@ -36,11 +40,13 @@ namespace QuestPDF.Elements internal override void Draw(Size availableSpace) { - GetContent(availableSpace, DynamicLayoutOperation.Draw).Draw(availableSpace); + GetContent(availableSpace, acceptNewState: true).Draw(availableSpace); } - DynamicContainer GetContent(Size availableSize, DynamicLayoutOperation operation) + private DynamicContainer GetContent(Size availableSize, bool acceptNewState) { + var componentState = Child.GetState(); + var context = new DynamicContext { PageNumber = PageContext.CurrentPage, @@ -48,13 +54,15 @@ namespace QuestPDF.Elements Canvas = Canvas, TextStyle = TextStyle, - AvailableSize = availableSize, - Operation = operation + AvailableSize = availableSize }; var container = new DynamicContainer(); Child.Compose(context, container); - + + if (!acceptNewState) + Child.SetState(componentState); + container.VisitChildren(x => x?.Initialize(PageContext, Canvas)); container.VisitChildren(x => (x as IStateResettable)?.ResetState()); @@ -62,23 +70,15 @@ namespace QuestPDF.Elements } } - public enum DynamicLayoutOperation - { - Reset, - Measure, - Draw - } - public class DynamicContext { internal IPageContext PageContext { get; set; } internal ICanvas Canvas { get; set; } internal TextStyle TextStyle { get; set; } - + public int PageNumber { get; internal set; } public Size AvailableSize { get; internal set; } - public DynamicLayoutOperation Operation { get; internal set; } - + public IDynamicElement CreateElement(Action content) { var container = new DynamicElement(); diff --git a/QuestPDF/Fluent/DynamicComponentExtensions.cs b/QuestPDF/Fluent/DynamicComponentExtensions.cs index c5335ca..5fe013b 100644 --- a/QuestPDF/Fluent/DynamicComponentExtensions.cs +++ b/QuestPDF/Fluent/DynamicComponentExtensions.cs @@ -5,14 +5,10 @@ namespace QuestPDF.Fluent { public static class DynamicComponentExtensions { - public static void Dynamic(this IContainer element) where TDynamic : IDynamicComponent, new() + public static void Dynamic(this IContainer element, IDynamicComponent dynamicElement) where TState : struct { - element.Dynamic(new TDynamic()); - } - - public static void Dynamic(this IContainer element, IDynamicComponent dynamicElement) - { - element.Element(new DynamicHost(dynamicElement)); + var componentProxy = DynamicComponentProxy.CreateFrom(dynamicElement); + element.Element(new DynamicHost(componentProxy)); } } } \ No newline at end of file diff --git a/QuestPDF/Infrastructure/IDynamicComponent.cs b/QuestPDF/Infrastructure/IDynamicComponent.cs index d31cb9f..e401362 100644 --- a/QuestPDF/Infrastructure/IDynamicComponent.cs +++ b/QuestPDF/Infrastructure/IDynamicComponent.cs @@ -1,9 +1,29 @@ -using QuestPDF.Elements; +using System; +using QuestPDF.Elements; +using QuestPDF.Helpers; namespace QuestPDF.Infrastructure { - public interface IDynamicComponent + internal class DynamicComponentProxy { - void Compose(DynamicContext context, IDynamicContainer container); + internal Action SetState { get; private set; } + internal Func GetState { get; private set; } + internal Action Compose { get; private set; } + + internal static DynamicComponentProxy CreateFrom(IDynamicComponent component) where TState : struct + { + return new DynamicComponentProxy + { + GetState = () => component.State, + SetState = x => component.State = (TState)x, + Compose = component.Compose + }; + } + } + + public interface IDynamicComponent where TState : struct + { + TState State { get; set; } + void Compose(DynamicContext context, IDynamicContainer dynamicContainer); } } \ No newline at end of file