0

我正在尝试模拟 NFL 驱动器图表以可视化游戏中的一些驱动器数据。可以在这里看到我大致试图模仿的一个例子,由 nfl.com 提供。我正在努力实现以下目标:

  • 显示比赛场地
  • 在比赛场地顶部绘制数据
  • 当我将鼠标悬停在某些东西上时,我想触发一个事件(例如 ToolTip)

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
}

该列表获得了一些数据……(示例数据,我知道已经获得了第一个倒数,哈哈)

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…</p>

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;
    }
}

这是 ItemsPanel 的 xaml:

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

这是 ItemTemplate 的 xaml - 请注意,我绑定到 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(虚拟化)和Rachel(一切!),我实际上设法让它工作,至少对于我提供 ItemsControl 的播放列表。瑞秋,你的博客很棒。AttachedProperties 条目确实非常有用,感谢您分享您的知识!

在稍后的某个阶段,我希望能够添加一两个屏幕截图,以更好地展示我在胡扯的内容;)

正如建议的那样,我使用(以及其他属性) PlayIndex 修改了 Play 类以进行网格定位

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 都以各自的颜色彼此相邻以及在彼此下方显示。将 ToolTip 添加到 ItemsControl(使用 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)。

(我知道除了驱动器的长度之外我没有绑定任何东西,这个例子中的 Rectangle 只是为了在我测试时显示一些东西!)

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

但是我已经喝完第四杯咖啡了,我在绑定到 List 对象中的 List 时遇到问题?Drives.DrivePlays 之类的东西?!

所以我从一个简单的开始(谢谢你们)

List<Play>

绑定,到

List<Drive>

绑定,其中包含一个

List<Play>!

称为“DrivePlays”:D

我是否遗漏了一些明显的东西或者我需要更多的咖啡?

编辑

我已经在一个单独的问题中解释了问题的最后一部分

关于原始问题最后一部分的相关问题

4

2 回答 2

0

我建议数据项的布局对齐及其虚拟化,您必须分析 WPF 下所有项目控件提供的重要选项...

  1. ItemsControlUniformGridas ItemsPanel
  2. ListBox使用透明选择画笔(使其看起来像一个项目控件)提供了固有的虚拟化。
  3. ListView提供对齐布局GridView和虚拟化。
  4. WPFDataGrid可用于更好的选项,例如列/行虚拟化、排序等...
  5. ItemsControl但是,您可以在 4.0 之前的版本中实现虚拟化, http ://omniscientist.net:8081/efficientlargedatasets
于 2011-10-11T11:37:43.893 回答
0

首先让我说我对足球一无所知,所以如果我理解错误,请原谅我。

首先,anItemsControl实际上将每个项目包装在 ContentPresenter 中,因此无法从DataTemplate. 如果你想改变你的物品的位置,你需要使用ItemContainerStyle(例如,看这里

由于您希望定位数据的方式,我会考虑尝试将 Grid.Row/Grid.Column 属性添加到您的Play类中,并找出这些应该在后面的代码中。然后,您可以根据Plays列表中的行/列数将 ItemsControl 绘制为具有列/行的网格,并将每个项目放在适当的单元格中。(我有一个使用附加属性在这里定义 Grid.Rows/Columns 的示例,尽管我认为它们需要一些小的修改才能绑定值......需要更改GridGridHelpers并更改RegisterRegisterAttached

要滚动 ItemsControl,您可以将其包装在ScrollViewer. 如果您也希望虚拟化 ItemsControl,请查看此问题

于 2011-10-11T13:26:18.563 回答