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

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

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

http://www.nfl.com/gamecenter/2011100909/2011/REG5/jets@patriots

Вот где я сейчас, прежде всего, 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 в Brush, поэтому с примерами данных вы можете видеть отображаемые цвета.

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

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

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

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

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

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

Если вы читали это далеко, спасибо за ваше время - GO PATS;)

[редактировать]

Что ж, благодаря AngelWPF (виртуализация) и Рэйчел (все!) Мне действительно удалось заставить его работать, по крайней мере, для списка игр, которыми я предоставлял 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:) Теперь каждая игра отображается рядом друг с другом, а также под другим цветом. Добавление всплывающей подсказки к 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 }
};

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

Пожалуйста, обратите внимание - я написал это в оригинальном сообщении, и я надеюсь, что это "позволит" мне отредактировать этот пост с дополнительной информацией и не задавать совершенно новый вопрос!

Учитывая следующий класс "Драйв":

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>!

называется "DrivePlays":D

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

редактировать

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

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

2 ответа

Решение

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

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

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

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

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

  1. ItemsControl с UniformGrid как ItemsPanel,
  2. ListBox с прозрачной кистью выделения (чтобы она выглядела как элемент управления) обеспечивает встроенную виртуализацию.
  3. ListView обеспечивает как макет выравнивания GridView и виртуализация.
  4. WPF DataGrid может быть использован для лучших вариантов, таких как виртуализация столбцов / строк, сортировка и т. д.
  5. Однако вы можете реализовать виртуализацию ItemsControl в версии до 4.0 http://omniscientist.net:8081/efficientlargedatasets
Другие вопросы по тегам