Я пытаюсь эмулировать диаграмму движения НФЛ, чтобы визуализировать некоторые данные о движении из игры. Пример того, чему я примерно пытаюсь подражать, можно увидеть здесь, любезно предоставленный 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
Я пропустил что-то очевидное или мне нужно больше кофе?
изменить
Я объяснил последнюю часть проблемы в отдельном вопросе
Связанный вопрос относительно последней части исходного вопроса