Scope
I am trying to have my application arrange lines of some complex controls and split those lines into pages. Something like this:
Much like a word processor would do, only there are complex controls instead of words. Note that the requirement of splitting into pages is essential.
Also note that the obvious solution of inheriting from Panel and implementing MeasureOverride/ArrangeOverride wouldn't do, because I would like very much to have pages as separate framework elements - this way, I would be able to do some nice things with them, like dragging them around, rotating, etc. I am thinking of this solution (inheriting from Panel) as of last resort, but I'm sure there should be a better way.
Adopted strategy
This seems like something relatively simple, provided I know sizes of those controls. I use vertical-oriented stackpanels for pages, and horizontal-oriented ones for lines. I pack controls into line one by one until they exceed the line's width, and then create stackpanel for the next line and continue. When the next line doesn't fit into current page, I create next page, and the process goes on.
I expect the order of several hundred controls at once, so I figured this approach should do.
The problem
The problem is the very one I would expect from WinForms: I do not know the size of those controls! Turns out, if the control doesn't have Width and Height set explicitly, it will have DesiredSize of zero right after it is created.
This problem can be solved by calling .Measure( Size.Empty ). After that, DesiredSize is calculated (somewhat) correctly.
I say "somewhat" - because it is still not entirely correct, unfortunately. The latest problem I've found is with bindings to relative source. Consider the following XAML:
<ItemsControl x:Class="MyClass" xmlns=...>
<ItemsControl.ItemTemplate>
<Ellipse Width={DataBinding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyClass}}, Path=EllipseWidth} />
</ItemsContro.ItemTemplate>
</ItemsControl>
Where "EllipseWidth" is a property on MyClass, which is my custom ItemsControl descendant.
Turns out, a call to Measure() causes the ItemsControl to create its items, but the binding doesn't get resolved. Check out the trace:
52 : Created BindingExpression (hash=15030582) for Binding (hash=15006601)
54 : Path: 'EllipseWidth'
56 : BindingExpression (hash=15030582): Default mode resolved to OneWay
57 : BindingExpression (hash=15030582): Default update trigger resolved to PropertyChanged
58 : BindingExpression (hash=15030582): Attach to Ellipse.Width (hash=12274123)
62 : BindingExpression (hash=15030582): RelativeSource (FindAncestor) requires tree context
61 : BindingExpression (hash=15030582): Resolve source deferred
Specifically, note the last two lines. What is a "tree context" and how do I provide it? I tried to add the control to a "live" panel before calling Measure(), but it didn't help much.
I must say that, according to the trace, the source does get resolved eventually, but that's too late: I need to know the control size now.
Generalizing the problem
Even if I resolve this binding problem, I cannot be sure that there won't be any others. So the ultimate question is: I need a "clean" (as in "supported") way to know when a control's initialization is completely done, and the to find the size of a that control
Or (another option) maybe I'm taking the wrong approach, and there is another, cleaner, way to do what I'm trying to do?