Как я могу расположить элементы WPF в горизонтальной панели обертки, чтобы они выстраивались на основе произвольной вертикальной точки в каждом элементе? - PullRequest
0 голосов
/ 22 мая 2010

Я пытаюсь создать представление в WPF и с трудом выясняю, как его настроить.Вот что я пытаюсь построить:

  • Мой ViewModel предоставляет свойство IEnumerable с именем Items
  • Каждый элемент является событием на временной шкале, и каждый реализует ITimelineItem
  • ViewModel для каждого элемента имеет собственный DataTemplate для отображения
  • Я хочу отобразить все элементы на временной шкале, соединенные линией.Я думаю, что для этого хорошо подойдет WrapPanel внутри ListView.
  • Однако высота каждого элемента будет варьироваться в зависимости от отображаемой информации.Некоторые элементы будут иметь графические объекты прямо на линии (например, круг или ромб, или что-то еще), а некоторые имеют аннотации выше или ниже линии.

Так что мне это кажется сложным.Кажется, что каждый элемент на временной шкале должен отображать свой собственный сегмент линии.Все в порядке.Но расстояние между верхней частью элемента и линией (и нижней частью элемента и линией) может варьироваться.Если один элемент имеет линию на 50 пикселей сверху вниз, а следующий элемент - на 100 пикселей сверху вниз, то для первого элемента требуется отступ 50 пикселей, чтобы сегменты линий суммировались.

IЯ думаю, что я мог бы решить эту проблему, однако нам нужно добавить заполнение, только если эти два элемента находятся на одной строке в WrapPanel!Допустим, есть 5 элементов и только место на экране для 3-х по ширине ... WrapPanel поместит два других на следующей строке.Это нормально, но это означает, что только первые 3 должны дополнять друг друга, а последние 2 должны дополнять друг друга.

Это то, что вызывает у меня головную боль.Есть ли другой подход, на который я мог бы обратить внимание?

EDIT : Я склоняюсь к замене WrapPanel на пользовательскую панель, которая наследует от Canvas и переопределяет методы MeasureOverride и ArrangeOverride.

Ответы [ 2 ]

2 голосов
/ 22 мая 2010

Идеальным решением для этого, вероятно, было бы использование Adorners. На AdornerLayer у вас будет собственный Adorner для каждого элемента на панели (вы определяете его в шаблоне данных). Вы сможете восстановить размеры позиции каждого предмета (Ограничительная коробка Adorner). Также на AdornerLayer вы бы рисовали линии между этими ограничивающими прямоугольниками. Используя это решение, вы можете расположить элементы по своему усмотрению (используя любую панель), и элементы будут по-прежнему связаны линиями.

Давным-давно я использовал этот подход для визуализации графа. Узлы, где расположены произвольные элементы UIE, которые могут быть расположены любым способом. Предметы, связанные с линиями.

Чтобы все элементы выровнялись идеально, вы можете использовать Canvas в качестве корневой панели вашего ItemTemplate. Холст может быть размером 0x0 единиц. Затем вы разместите свои элементы вокруг этого холста. Холст становится вашей точкой отсчета. Все в верхней части строки получит отрицательные значения Canvas.Top. Другой подход заключается в использовании отрицательных полей, которые привязаны к высоте верхнего и нижнего элементов управления, окружающих линию. Используйте IValueConverter (Высота * -1), чтобы инвертировать Высоту.

1 голос
/ 23 мая 2010

Это не решит вашу проблему, но я уверен, что это упростит ее:

Попробуйте определить DataTemplate, который выглядит примерно так:

<Grid>
   <Grid.RowDefinitions>
      <RowDefinition Height="Auto" SharedSizeGroup="Top"/>
      <RowDefinition Height="10"/>
      <RowDefinition Height="Auto" SharedSizeGroup="Bottom"/>
   </Grid.RowDefinitions>
   <ContentControl Grid.Row="0" Content="{Binding TopAnnotations}/>
   <Rectangle Fill="Black" Grid.Row="1" Margin="0,4,0,4"/>
   <ContentControl Grid.Row="1" Height="10" Content="{Binding GraphicObject}"/>
   <ContentControl Grid.Row="2" Content="{Binding BottomAnnotations}"/>
</Grid>  

Теперь создайте ItemsControl, ItemsPanelTemplate которого является горизонтальным WrapPanel, со свойством Grid.IsSharedSizeScope, установленным в True. Вам, конечно, также нужно будет определить шаблоны данных для того, что содержат свойства аннотации и графического объекта.

У вас все еще есть проблема, заключающаяся в том, что когда элементы в WrapPanel переносятся, объекты над и под временной шкалой имеют одинаковый отступ, независимо от того, сколько им фактически нужно разместить в текущей строке WrapPanel. Таким образом, чтобы получить эффект, который, я думаю, вы ищете, вам все равно придется поэкспериментировать с мерой и аранжировкой - в основном, вместо создания одного WrapPanel, вы создадите что-то, что поместит эти элементы в StackPanel в каждой «строке», каждая из которых является собственной областью общего размера. Но на самом деле измерение и расположение высот элементов шкалы времени - это не то, о чем вам нужно беспокоиться. Вам нужны только их ширины (чтобы ваша логика могла определить, когда обернуть). Grid позаботится о расчете высоты для вас.

...