Область
Я пытаюсь заставить мое приложение упорядочивать строки некоторых сложных элементов управления и разбивать эти строки на страницы. Что-то вроде этого:
альтернативный текст http://apreleva.com/vm/controls-in-pages.png
Так же, как и текстовый процессор, только слова имеют сложные элементы управления вместо слов. Обратите внимание, что требование разделения на страницы является существенным.
Также обратите внимание, что очевидное решение наследования от Panel и реализации MeasureOverride / ArrangeOverride не сработает, потому что я очень хотел бы, чтобы страницы были отдельными элементами каркаса - таким образом, я мог бы сделать некоторые хорошие вещи с их, как перетаскивание их, вращение и т. д. Я думаю об этом решении (унаследованном от Panel) в крайнем случае, но я уверен, что должен быть лучший способ.
Принятая стратегия
Это кажется чем-то относительно простым, если я знаю размеры этих элементов управления. Я использую вертикальные стековые панели для страниц и горизонтальные для линий. Я упаковываю элементы управления в строку один за другим, пока они не превысят ширину линии, а затем создаю панель стека для следующей строки и продолжаю. Когда следующая строка не помещается на текущей странице, я создаю следующую страницу, и процесс продолжается.
Я ожидаю порядка нескольких сотен элементов управления одновременно, поэтому я решил, что такой подход должен подойти.
Проблема
Проблема та самая, которую я ожидал от WinForms: я не знаю размера этих элементов управления! Оказывается, если элемент управления не имеет явно установленных ширины и высоты, он будет иметь нулевой DesiredSize сразу после его создания.
Эту проблему можно решить, вызвав .Measure (Size.Empty). После этого DesiredSize рассчитывается (несколько) правильно.
Я говорю «несколько» - потому что, к сожалению, это все еще не совсем правильно. Последняя проблема, которую я обнаружил, связана с привязками к относительному источнику. Рассмотрим следующий XAML:
<ItemsControl x:Class="MyClass" xmlns=...>
<ItemsControl.ItemTemplate>
<Ellipse Width={DataBinding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyClass}}, Path=EllipseWidth} />
</ItemsContro.ItemTemplate>
</ItemsControl>
Где EllipseWidth - это свойство в MyClass, которое является моим потомком ItemsControl.
Оказывается, вызов Measure () заставляет ItemsControl создавать свои элементы, но привязка не разрешается. Проверьте след:
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
В частности, обратите внимание на последние две строки. Что такое «древовидный контекст» и как мне его предоставить? Я пытался добавить элемент управления на «живую» панель перед вызовом Measure (), но это не сильно помогло.
Я должен сказать, что, согласно трассировке, источник в конечном итоге разрешается, но уже слишком поздно: мне нужно знать размер элемента управления.
Обобщая проблему
Даже если я решу эту обязательную проблему, я не могу быть уверен, что других не будет. Итак, главный вопрос: Мне нужен «чистый» (как в «поддерживаемом») способ узнать, когда инициализация элемента управления полностью завершена, и найти размер этого элемента управления
Или (другой вариант) может быть, я выбрал неправильный подход, и есть другой, более чистый, способ сделать то, что я пытаюсь сделать?