Как организовать элементы управления в зависимости от их размера? (без наследования от Panel) - PullRequest
2 голосов
/ 11 ноября 2009

Область

Я пытаюсь заставить мое приложение упорядочивать строки некоторых сложных элементов управления и разбивать эти строки на страницы. Что-то вроде этого: альтернативный текст 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 (), но это не сильно помогло.

Я должен сказать, что, согласно трассировке, источник в конечном итоге разрешается, но уже слишком поздно: мне нужно знать размер элемента управления.

Обобщая проблему

Даже если я решу эту обязательную проблему, я не могу быть уверен, что других не будет. Итак, главный вопрос: Мне нужен «чистый» (как в «поддерживаемом») способ узнать, когда инициализация элемента управления полностью завершена, и найти размер этого элемента управления

Или (другой вариант) может быть, я выбрал неправильный подход, и есть другой, более чистый, способ сделать то, что я пытаюсь сделать?

1 Ответ

0 голосов
/ 11 ноября 2009

Не думаю, что вы сможете достичь того, чего хотите, просто используя контрольную композицию. Я думаю, вам определенно нужно будет принять участие в Measure / Arrange и предоставить свой собственный алгоритм. Вам не нужно наследовать от Panel, чтобы сделать это, но обычно это имеет смысл. Что касается страниц, ваша пользовательская панель может создавать поддержку для каждой логической группировки. Наконец, ваша настраиваемая панель может быть сконфигурирована с размерами логической группировки, чтобы вы могли поддерживать разные размеры (представьте, что поддерживает Letter против A4 или что-то в этом роде).

Так что, в конце концов, я думаю, что вы должны получить:

  1. Пользовательская Panel реализация, которая поддерживает раскладку и определение логических группировок на основе измерений, настроенных с помощью различных свойств.
  2. Предоставьте свойство, называемое чем-то вроде ItemGroupTemplate, которое при генерации логических групп используется для отображения в качестве «фона» логической группировки, эффективно создавая «страницу», на которой они находятся.
  3. Свяжите ваши фактические данные предмета с ItemsControl
  4. Установите свой пользовательский Panel как ItemControl::ItemsPanel

Наконец, имейте в виду, что вы, вероятно, захотите использовать алгоритм виртуализации на своей панели, если у вас будет столько элементов для размещения. Это означает, что вы должны наследовать от VirtualizingPanel. Подробнее о создании пользовательской VirtualizingPanel можно узнать в удивительной серии Дэна Кревье по теме .

.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...