ItemsControl (виртуализация информации о футбольном драйве)

Я пытаюсь эмулировать диаграмму движения НФЛ, чтобы визуализировать некоторые данные о движении из игры. Пример того, чему я примерно пытаюсь подражать, можно увидеть здесь, любезно предоставленный nfl.com. Я пытаюсь добиться следующего:

  • Показать игровое поле
  • Нарисуйте данные поверх игрового поля
  • Когда я навожу что-то, я хочу запустить событие (например, всплывающую подсказку)

http://www.nfl.com/gamecenter/2011100909/2011/REG5/jets@patriots#menu=drivechart

Вот где я сейчас нахожусь, прежде всего xaml:

<StackPanel x:Name="myStackPanel" Orientation="Vertical">
  <StackPanel.Background>
    <ImageBrush ImageSource="{StaticResource PlayingField}" Stretch="Fill" />
  </StackPanel.Background>
  <ItemsControl Background="AliceBlue" Opacity="0.5" 
                ItemsSource="{Binding Path=DriveDetails}"           
                ItemTemplate="{StaticResource myDataTemplate}" 
                ItemsPanel="{StaticResource myItemsPanelTemplate}" />
</StackPanel>

Результатом чего является:

(Извините, поскольку я новичок, я не могу добавить скриншот!)

Я сделаю все возможное, чтобы описать то, что я вижу, я уверен, что большинство из вас поймут это после прочтения xaml. В основном я вижу футбольное поле на заднем плане, а в левом верхнем углу — элемент управления ItemsControl.

StackPanel не требует пояснений, я теперь, когда у меня позже возникнут проблемы с изображением с изменением размера, поскольку один ярд не представляет один пиксель, но я оставлю это на другой день, моя главная забота - как я могу визуализировать данные. Кроме того, визуализация диска не должна начинаться в верхнем левом углу :D Итак, имея дело исключительно с ItemsControl, у меня остается следующее:

(Извините, поскольку я новичок, я не могу добавить скриншот!)

Еще раз попытаюсь описать изображение. Это крупный план вышеупомянутого элемента управления ItemsControl без фонового изображения (футбольное поле).

Примечание. Фон ItemsControl был установлен на AliceBlue, чтобы его было легче увидеть, он станет прозрачным, как только я заработаю.

ItemsSource привязан к списку класса Play:

public class Play
{
  public int Quarter { get; set; }
  public int Result { get; set; }
  public PlayType TypeOfPlay { get; set; }
  public double WidthOfPlay { get; set; }
}

PlayType — это перечисление:

public enum PlayType
{
  Run,
  Pass,
  Penalty,
  Punt,
  FieldGoal
}

Список получает некоторые данные… (пример данных, я знаю, что 1-й даун был получен, лол)

List<Play> SamplePlays = new List<Play>()
{
  new Play { Quarter = 1, Result = 5, TypeOfPlay = PlayType.Penalty, WidthOfPlay = 30 },
  new Play { Quarter = 1, Result = 5, TypeOfPlay = PlayType.Run, WidthOfPlay = 30 },
  new Play { Quarter = 1, Result = 5, TypeOfPlay = PlayType.Pass, WidthOfPlay = 30 },
  new Play { Quarter = 1, Result = 40, TypeOfPlay = PlayType.Punt, WidthOfPlay = 240 }
};

… и передается с помощью класса параметра передачи во ViewModel…

ucHomeInstance = ucDriveChartInstance;
((ucDriveChart)ucHomeInstance).setViewModel(new ucDriveChartVM(new ucDriveChartTP() 
  { Plays = SamplePlays }));

… где он назначен свойству DriveDetails

public class ucDriveChartVM : ViewModelBase
{
    public List<Play> DriveDetails { get; set; }

    public ucDriveChartVM(ucDriveChartTP transferParameter)
    {
        this.DriveDetails = transferParameter.Plays;
    }
}

Вот xaml для ItemsPanel:

<ItemsPanelTemplate x:Key="myItemsPanelTemplate">
  <StackPanel Orientation="Horizontal" IsItemsHost="True" />
</ItemsPanelTemplate>

Вот xaml для ItemTemplate — обратите внимание, что я привязываюсь к WidthOfPlay, так как один ярд составляет примерно 6 единиц на фоновой графике, как только я разберусь с графикой и изменением размера, я привяжусь к фактическому свойству Result:

<DataTemplate x:Key="myDataTemplate">
  <StackPanel Orientation="Horizontal" Width="{Binding WidthOfPlay}"
              SnapsToDevicePixels="True" Style="{StaticResource onMouseOver}">
    <Border Padding="0,5,0,5">
      <Grid>
        <StackPanel Style="{StaticResource onMouseOver}">
          <Rectangle Width="{Binding WidthOfPlay}" Height="10" 
                     Fill="{Binding TypeOfPlay,
                       Converter={StaticResource PlayTypeToColourConverter}}" />
        </StackPanel>
      </Grid>
    </Border>
  </StackPanel>
</DataTemplate>

Я преобразовываю перечисление PlayType в кисть, поэтому с примерами данных вы можете увидеть отображаемые цвета.

<converter:PlayToColour x:Key="PlayTypeToColourConverter"
  ColourRun="Red" ColourPass="Blue" ColourPenalty="Yellow" ColourSpecial="Purple" />

Таким образом, все это работает нормально, но, очевидно, представление не такое, как хотелось бы. Если посмотреть на пример вверху, диск состоит из множества люфтов, которые расположены рядом друг с другом, но поскольку люфт может привести к отрицательному значению в ярдах, они также располагаются друг под другом, смещенные в конце последней игры!

Итак, я думаю, это мой вопрос - как я могу представить данные в такой форме, чтобы каждое воспроизведение диска располагалось под предыдущим воспроизведением этого диска, но также начиналось в конце этого последнего диска (как-то смещено? )

Поскольку игра состоит из более чем одного драйва, на более позднем этапе я передам список «Драйвов», состоящий из начальной позиции драйва и списка ходов для этого драйва (вместо простого списка ходов в этом пример).

Это означает, что все это также должно быть прокручиваемым!

Я думаю, что я на правильном пути, используя элемент управления ItemsControl вместе с ItemTemplate и DataTemplate, но я уверен, что мог бы воспользоваться советом экспертов о том, как этого добиться.

Если вы дочитали до этого места, спасибо за потраченное время - ПОПРОБУЙТЕ ;)

[править]

Что ж, благодаря AngelWPF (виртуализация) и Rachel (всему!) мне действительно удалось заставить его работать, по крайней мере, для списка воспроизведений, которые я предоставлял ItemsControl. Рэйчел, у тебя отличный блог. Запись AttachedProperties была действительно очень полезной, спасибо, что поделились своими знаниями!

На каком-то более позднем этапе я надеюсь, что смогу добавить скриншот или два, которые лучше демонстрируют то, о чем я болтаю ;)

Как было предложено, я изменил класс Play (среди прочих свойств) PlayIndex для позиционирования сетки.

public class Play
{
  public int PlayIndex { get; set; }
  public int Quarter { get; set; }
  public int Down { get; set; }
  public int YardsToGo { get; set; }
  public int Position { get; set; }
  public PlayType TypeOfPlay { get; set; }
  public PlayDetail DetailsOfPlay { get; set; }
  public PlayResult ResultOfPlay { get; set; }
  public int YardsOfPlay { get; set; }
  public double PlayLength { get; set; }
  public string Player1 { get; set; }
  public string Player2 { get; set; }
}

и чтобы использовать этот PlayIndex, ItemContainerStyle был добавлен в ItemsControl.

<ItemsControl.ItemContainerStyle>
  <Style>
    <Setter Property="Grid.Column" Value="{Binding PlayIndex}" />
    <Setter Property="Grid.Row" Value="{Binding PlayIndex}" />
  </Style>
</ItemsControl.ItemContainerStyle>

Изменение ItemsPanelTemplate из StackPanel в Grid также помогло в этом :) Теперь каждый Play отображается рядом друг с другом, а также друг под другом с соответствующим цветом. Добавление всплывающей подсказки к ItemsControl (с использованием других свойств в классе Play) отполировало его.

Как я уже говорил, теперь это прекрасно работает со списком элементов Play (сокращено для простоты):

List<Play> SamplePlays = new List<Play>()
{
  new Play { PlayIndex = 0, Position = 30, YardsOfPlay = 5, PlayLength = 25 },
  new Play { PlayIndex = 1, Position = 35, YardsOfPlay = 15, PlayLength = 75 },
  new Play { PlayIndex = 2, Position = 50, YardsOfPlay = 0, PlayLength = 0 },
  new Play { PlayIndex = 3, Position = 50,  YardsOfPlay = 8, PlayLength = 40 },
  new Play { PlayIndex = 4, Position = 58,  YardsOfPlay = 1, PlayLength = 5 },
  new Play { PlayIndex = 5, Position = 59,  YardsOfPlay = 30, PlayLength = 150 }
};

Но как мне связать то, что я написал в конце исходного поста, относительно передачи списка «Дисков»?

Обратите внимание: поскольку я написал это в исходном сообщении, я надеюсь, что это «позволит» мне отредактировать это сообщение с дополнительной информацией, а не задавать совершенно новый вопрос!

Учитывая следующий класс «Drive»:

public class Drive
{
  public int DriveIndex { get; set; }
  public int StartPos { get; set; }
  public int EndPos { get; set; }
  public double DriveLength { get; set; }
  public DriveResult Result { get; set; }
  public List<Play> DrivePlays { get; set; }
}

Заполнение списка данными (с использованием тех же воспроизведений, что и выше)

List<Drive> SampleDrives = new List<Drive>() 
{
  new Drive { DriveIndex = 0, StartPos = 30, EndPos = 49, DriveLength = 19,
              DrivePlays = new List<Play>() 
              {
                // create plays here
              }},
  new Drive { DriveIndex = 1, StartPos = 30, EndPos = 49, DriveLength = 19,
              DrivePlays = new List<Play>() 
              {
                // create plays here
              }}
};

и ViewModel теперь выглядит так:

public class ucDriveChartVM : ViewModelBase
{
  public List<Play> DriveDetails { get; set; }
  public List<Drive> Drives { get; set; }

  public ucDriveChartVM(ucDriveChartTP transferParameter)
  {
    this.DriveDetails = transferParameter.Plays;
    this.Drives = transferParameter.Drives;
  }
}

Мне, очевидно, нужно предоставить этому ItemsControl (содержащему диски) другой DataTemplate, но этот DataTemplate должен содержать воспроизведения в этом списке дисков. Что я быстро понял, так это то, что вы не можете размещать контент в прямоугольнике, поэтому мне нужно подумать об этом :/

Немного поэкспериментировав, я привязался к свойству DriveLength, чтобы графически увидеть длину данного диска, и придумал DataGridCell, чтобы я мог размещать туда контент (например, StackPanel).

(Я знаю, что не привязываюсь ни к чему, кроме длины диска, прямоугольник в этом примере просто показывает что-то при тестировании!)

<DataTemplate x:Key="myDataTemplateDrives">
  <StackPanel Orientation="Horizontal" Width="{Binding DriveLength}" 
              SnapsToDevicePixels="True" Margin="0,0,1,0">
    <Border>
      <Grid>
        <StackPanel>
          <DataGridCell>
            <StackPanel Width="{Binding Path=DriveLength}">
              <Rectangle Height="10" Fill="Gray" />
            </StackPanel>
          </DataGridCell>
        </StackPanel>
      </Grid>
    </Border>
  </StackPanel>
</DataTemplate>

Но я уже выпил четвертую чашку кофе и у меня возникли проблемы с привязкой к списку в объекте списка? Что-то вроде Drives.DrivePlays?!

Итак, я исхожу из простого (спасибо вам обоим)

List<Play>

привязка к

List<Drive>

переплет, который содержит

List<Play>!

называется "Драйвплейс" :D

Я пропустил что-то очевидное или мне нужно больше кофе?

изменить

Я объяснил последнюю часть проблемы в отдельном вопросе

Связанный вопрос относительно последней части исходного вопроса


person Dave    schedule 11.10.2011    source источник


Ответы (2)


Позвольте мне начать с того, что я ничего не знаю о футболе, поэтому, если я вас неправильно понял, простите меня.

Начнем с того, что ItemsControl фактически оборачивает каждый элемент в ContentPresenter, поэтому не получится настроить позиционирование ваших элементов из вашего DataTemplate. Если вы хотите изменить расположение своих элементов, вам нужно использовать ItemContainerStyle (например, см. здесь)

Из-за того, как вы хотите расположить свои данные, я бы попытался добавить свойства Grid.Row/Grid.Column в ваш класс Play и выяснить, что они должны быть в коде позади. Затем вы можете нарисовать свой ItemsControl как сетку со столбцами/строками на основе количества строк/столбцов в вашем списке Plays и поместить каждый элемент в соответствующую ячейку. (У меня есть пример использования вложенных свойств для определения Grid.Rows/Columns здесь, хотя я считаю, что им нужны небольшие изменения, чтобы связать значения... нужно изменить Grid на GridHelpers и изменить Register на RegisterAttached)

Для прокрутки ItemsControl вы можете обернуть его в ScrollViewer. Если вы также хотите виртуализировать ItemsControl, проверьте этот вопрос

person Rachel    schedule 11.10.2011
comment
Привет, Рэйчел! Каждый раз, когда я считаю, что узнал что-то вроде WPF, появляется что-то новое — спасибо! ItemContainerStyle выглядит интересно, и добавление предлагаемых свойств в класс не представляет проблемы. Единственное, что меня интересует, это xaml и количество столбцов, поскольку диск теоретически может иметь неограниченное количество воспроизведений (реально максимум от 15 до 20), поэтому я немного не уверен, как я должен перейти к созданию количества столбцов в сетке. - person Dave; 11.10.2011
comment
... Вы тоже дали мне много на что посмотреть, а также пару отличных ссылок, так что я повнимательнее рассмотрю завтра, если позволит время. Что касается вашего отсутствия знаний о футболе, я должен быть тем, у кого нет знаний, так как я родился в Англии, а сейчас работаю в Германии :D Еще раз большое спасибо за ваши комментарии! - person Dave; 11.10.2011
comment
@Dave Рад, что это помогает вам :) После того, как вы настроили свойства Grid.Row/Grid.Column, вы, вероятно, можете написать что-то, что получает максимальное значение каждого, и привязать это к свойствам RowCount/ColumnCount вашей сетки ItemsPanel. - person Rachel; 11.10.2011

Я предлагаю для выравнивания макета элементов данных и их виртуализации вам необходимо проанализировать несколько параметров, предоставляемых всеми элементами управления элементами в WPF...

  1. ItemsControl с UniformGrid как ItemsPanel.
  2. ListBox с прозрачной кистью выбора (чтобы она выглядела как элемент управления) обеспечивает встроенную виртуализацию.
  3. ListView обеспечивает как макет выравнивания GridView, так и виртуализацию.
  4. WPF DataGrid можно использовать для улучшения параметров, таких как виртуализация столбцов/строк, сортировка и т. д.
  5. Однако вы можете реализовать виртуализированные ItemsControl в версии до 4.0, http://omniscientist.net:8081/efficientlargedatasets
person WPF-it    schedule 11.10.2011
comment
Здравствуйте, AngelWPF, спасибо за ваши комментарии. Я считаю, что UniformGrid не подойдет, так как пьесы могут быть разной длины. Я думаю, что, используя ItemsControl, я на самом деле делаю то же самое, что и с элементом управления ListBox и прозрачной кистью, хотя мне придется больше изучать виртуализацию. Ссылка, которую вы мне предоставили, разбила мой Firefox (позор вам, лол), мне нужно многое прочитать, так что еще раз, спасибо! - person Dave; 11.10.2011
comment
Вот почему иногда стоит доверять IE ;-) - person WPF-it; 11.10.2011
comment
Политика компании Боюсь, я посмотрю сегодня вечером из дома :D - person Dave; 12.10.2011