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 ...
ItemsControl
сUniformGrid
какItemsPanel
,ListBox
с прозрачной кистью выделения (чтобы она выглядела как элемент управления) обеспечивает встроенную виртуализацию.ListView
обеспечивает как макет выравниванияGridView
и виртуализация.- WPF
DataGrid
может быть использован для лучших вариантов, таких как виртуализация столбцов / строк, сортировка и т. д. - Однако вы можете реализовать виртуализацию
ItemsControl
в версии до 4.0 http://omniscientist.net:8081/efficientlargedatasets