基础内容

使用 DataTemplate 绘图

Charles Petzold

下载示例代码

WPF 的程序员的有关该数据模板的强大功能的一个主要发现附带如何 XAML (的图 1) 的一个小可以将业务对象条形图 (图 2) 转换的演示。

在查看图表的呈现在 exhilaration,以下技术遗憾的是似乎"隔栏出。增强简单的条形图将成为麻烦,并使用其他类型的常见的图表,--如饼形图和折线图--数据模板看起来几乎不可能。这是因为在这种方式中使用数据模板只是太强大一个技术 unadorned 条形图将不太不正确。

在本文中,我将介绍几个技术,以绕过明显的限制。将以帮助出 XAML 不能由其自身,管理的详细信息涉及一些代码,但代码通常是通用足够用于其他应用程序。目标始终是足够的可视化设计使在 XAML 中,以便更改和试验很容易。

当然,该数据模板并不是一个通用的解决方案的图表。您可能会发现一个更适合您的需要的实际图表程序包。您还应该注意从 ItemsControl 或有上千个数据项的列表框和一个数据模板的多个数据绑定可能会导致性能问题。我检查了相应的文章"书写其他高效 ItemsControls"中的问题的解决方案在三月份 2009年月刊的 MSDN Magazine 》 (msdn.microsoft.com/en-us/magazine/dd483292.aspx)。

基本概念

可下载本文的代码包含了一个名为 ChartingWithDataTemplates,包含一个名为 ChartingLib 和九个 WPF 应用程序项目的 DLL 项目的 Visual Studio 解决方案。的图 1 所示的该文件是从 SimpleBarChart 项目 Window1.xaml 文件。

一些类 ChartingLib DLL 中采用了业务对象提供的示例程序的数据的形式。此第一个示例中,该 Doodad 类实现 INotifyPropertyChanged,并包括 ModelName 和 BaseCost 属性。DoodadCollection 类只从 ObservableCollection < Doodad > 派生并包含 Doodad 对象的集合。DoodadPresenter 类实例化为 XAML 资源在图 1 定义 DoodadCollection 属性、 创建所有随机数据,并包括一个计时器,随着时间的推移,动态更改数据。

图 1 XAML 以显示图表工具栏

<Window x:Class="SimpleBarChart.Window1"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:charts="clr-namespace:ChartingLib;assembly=ChartingLib"
Title="Simple Bar Chart">
<Window.Resources>
<charts:DoodadPresenter x:Key="doodadPresenter" />
</Window.Resources>
<ItemsControl
ItemsSource="{Binding
Source={StaticResource doodadPresenter},
Path=DoodadCollection}"
VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Height="{Binding BaseCost}"
VerticalAlignment="Bottom"
Fill="Blue"
Margin="3">
<Rectangle.ToolTip>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ModelName}" />
<TextBlock
Text="{Binding BaseCost,
StringFormat=’: {0:C0}’}" />
</StackPanel>
</Rectangle.ToolTip>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Window>

很重要的业务对象用于在 INotifyPropertyChanged 接口,这样的基础数据中的更改将直观地反映在图表中该项目实现。集合对象应实现 ICollectionChanged,这样的集合本身--如添加或删除集合--从项目的更改将反映在 ItemsControl。ObservableCollection 类实现 ICollectionChanged,一个非常受欢迎的选择为此目的。


图 2 SimpleBarChart 项目通过显示的栏图表

本文的示例程序使用一个 ItemsControl 显示项目。如果您需要提供用户选择某一种方法,您或者可以使用列表框。每个项目将显示基于该数据模板,将设置为在 ItemsControl 的模板属性。在数据模板包含绑定到存储在本示例 Doodad 在集合中对象的属性。

请注意,该工具提示还包含绑定到 Doodad 类的属性。工具提示一定是最简单方法在图表中包含有关每个项的文本信息。

除了在数据模板,您还需要将该 ItemsControl 的 ItemsPanel 属性设置为一个 ItemsPanelTemplate 包含一个相应的面板。对于水平排列的条形图可以使用一个 UniformGrid 将行属性显式为 1 或一个 StackPanel 方向属性设置为水平。

在单行 UniformGrid 和水平的 StackPanel 看起来类似,但它们实际上完全不同的布局行为。在单行 UniformGrid 大小及其子级,以便它们都具有相同的宽度并适应面板的尺寸。水平的 StackPanel 使自己的大小基于复合其子项的宽度。使用 UniformGrid 如果您希望维护常量不管多少项显示在图表的宽度。如果您要维护一个常量的大小,您将需要显式设置的每个栏上,使用 StackPanel。使用一个 StackPanel 其 ItemsPanel 的图表将更改取决于项目,个数的大小,因此您可能希望在 ItemsControl 置于一个 ScrollViewer 以防止图表占用太多的空间在屏幕上。

实用程序和美感

SimpleBarChart 程序将显示指示该属性值的 BaseCost Doodad 类所定义的栏。Doodad 类还定义 AdditionalCost 和 ExtraCost 属性,因此一个可能的增强功能正在堆积的条形图来显示这些的属性的三个 StackedBarChart 程序中所示。图 3 和中的图 4 中显示该数据模板。

请注意,一个 StackPanel 包含三个矩形元素 ;它是此 StackPanel VerticalAlignment 和边距属性指定的。我已消除只是为了简化 XAML ; 工具提示如果您添加它的它应附加到该 StackPanel,而不是该矩形的任何。

图 3 数据模板的堆积的条形图

<DataTemplate>
<StackPanel VerticalAlignment="Bottom"
Margin="3">
<Rectangle Height="{Binding ExtraCost}"
Fill="Red" />
<Rectangle Height="{Binding AdditionalCost}"
Fill="Green" />
<Rectangle Height="{Binding BaseCost}"
Fill="Blue" />
</StackPanel>
</DataTemplate>

主颜色有点 garish,但您可以轻松地语气它们向下通过该 StackPanel 则包含另一个矩形将覆盖其他三个的单个单元格网格中。此第四个矩形是使用一个与各种度的透明白色 LinearGradientBrush 彩色的。StackedBarChartWithGradientBrush 项目使用此技术在 图 5 中,视觉效果的。

导致在 XAML 中定义该条形图的视觉效果的大好处之一就是图形的易于栏替换为其他类型,或颜色的图像画笔矩形的图形画笔。假设在 Doodad 是实际上是一个玩具机器人您想要在条形图表中使用的玩具图形化表示形式。图 6 显示一个数据模板,定义坐标介于 0 和 1 之间的一个简单图形具有包含一个路径。一个 ScaleTransform 提供这些数字的每个统一宽度。水平缩放比例是绑定到 BaseCost 属性。结果如图 7 所示。

是否可以在条形图中使用不同的颜色吗?绝对。Doodad 类包含 ProductType 名为从 0 到 3 的值的整数属性。可以通过提供一个简单绑定转换器将整数转换为一个画笔的此 ProductType 基础栏的颜色。在 IndexToBrushConverter 定义了一个名为画笔数组类型的画笔的公用属性:

public Brush[] Brushes { get; set; }

若要将通用该转换器不需要数组具有固定的数量的画笔。 而是,一个求模操作应用于传入的整数值基于数组的大小:

return Brushes[(int)value % Brushes.Length];


图 4 的 StackedBarChart 程序显示

该 XAML 文件中,IndexToBrushConverter 是作为资源进行实例化,初始化数组的画笔:

<charts:IndexToBrushConverter x:Key="brushConv">
<charts:IndexToBrushConverter.Brushes>
<x:Array Type="Brush">
<x:Static Member="Brushes.Orange" />
<x:Static Member="Brushes.Purple" />
<x:Static Member="Brushes.SkyBlue" />
<x:Static Member="Brushes.Pink" />
</x:Array>
</charts:IndexToBrushConverter.Brushes>
</charts:IndexToBrushConverter>

矩形元素上的 Fill 属性是绑定到的 Doodad ProductType 属性,但这些画笔的转换:

Fill="{Binding ProductType,
Converter={StaticResource brushConv}}"

您可能需要一系列重复的颜色--不指示任何有关项,但只是有点不同。渚嬪的方式  可以使用橙色为第一项,在第五,ninth 和等。

首次这似乎不可能,因为该数据模板需要访问该集合中特定数据项的索引。不过,它很容易在业务对象中包含此信息。渚嬪的方式  Doodad 类可以定义一个名为 ChildIndex 的属性,并且重 DoodadCollection 属性会负责为其成员的每个设置该属性。

另一个选项需要派生可视树中的子索引和要在 IndexToBrushConverter 传递的。此作业需要一些了解构成该 ItemsControl 可视化树。一个 ItemsControl 开头边框,其中包含一个 ItemsPresenter 其中包含在指定该 ItemsPanelTemplate 设置该 ItemsControl ItemsPanel 属性面板。此面板包含的子项数,等于数据集合中 ; 中的项的数量这些子级是类型的的 ContentPresenter,Content 属性设置为位置在可视树,已定义数据模板集,ItemsControl 的模板属性。


图 5 的 StackedBarChartWithGradientBrush 显示

ChartingLib DLL 包含一个名为 ChildIndexProvider,修饰派生自定义了一个名为索引,的依赖项属性的类,并安装为 LayoutUpdated 事件处理程序。此处理程序检查是否父级是一个 ContentPresenter,如果该 ContentPresenter 的父面板。如果是这样,然后查找该 ContentPresenter 控制面板中的子索引。

图 6 使用路径数据模板

<DataTemplate>
<Path VerticalAlignment="Bottom"
Margin="2"
Stroke="Black"
Fill="Silver">
<Path.Data>
<PathGeometry
Figures="M 0 0 L 1 0, 1 0.2, 0.6 0.2, 0.6 0.25,
1 0.25, 1 0.6, 0.9 0.6, 0.9 0.3, 0.8 0.3,
0.8 0.95, 1 0.95, 1 1, 0.55 1, 0.55 0.5,
0.45 0.5, 0.45 1, 0 1, 0 0.95, 0.2 0.95,
0.2 0.3, 0.1 0.3, 0.1 0.6, 0 0.6, 0 0.25,
0.4 0.25, 0.4 0.2, 0 0.2 Z
M 0.2 0.05 L 0.4 0.05, 0.4 0.1, 0.2 0.1 Z
M 0.6 0.05 L 0.8 0.05, 0.8 0.1, 0.6 0.1 Z
M 0.2 0.15 L 0.8 0.15">
<PathGeometry.Transform>
<ScaleTransform ScaleX="25"
ScaleY="{Binding BaseCost}" />
</PathGeometry.Transform>
</PathGeometry>
</Path.Data>
</Path>
</DataTemplate>

在 ChildIndexPresenter 被用于根元素的一个数据模板,BarChartWithRepeatingColors 项目所示:

<DataTemplate>
<charts:ChildIndexProvider Name="indexProv">
...
</charts:ChildIndexProvider>
</DataTemplate>

矩形的 Fill 属性然后绑定到使用该 IndexToBrushConverter 此元素的索引属性:

Fill="{Binding ElementName=indexProv,
Path=Index,
Converter={StaticResource brushConv}}"

一个虚设的三维效果

非常有趣的条形图,可能要使用一些三维效果,视觉效果。则实际上可能会为该数据模板具有一个根元素的类型 Viewport3D 和表示的整个三维场景的每个数据项。但是,这看起来有点 extravagant 我,并可能不完全对所需的。

您真正需要使用单个照相机和照明,一个三维场景的是其中的每个数据项是一个 ModelVisual3D 中的场景。但需要在 ItemsControl 和其模板的某些主要 re-architecting 此工艺路线。一个更好的解决方案是伪造三维的视觉效果,如图 8 所示的倾斜转换中使用几个其他的矩形。


图 7 的 BarChartWithFigure 显示


图 8 的 Fake3DBarChart 显示

条形图在图表中的每个三个矩形中前面,,一充晶和包含之上,一个使总体效果类似于纯色块。重叠的项目面板中元素的顺序被控制。若要在图 8 创建效果,您不能只是 cobble 一起多个的矩形因为包含这些块将布置这些基于每个块的整个宽度,它们将不重叠。

有基本上两种方法您可以诱使识别布局系统的边界之外显示元素的方法。第一个最明显是将应用一个 RenderTransform。正如名称所示,RenderTransform 更改可视对象的呈现,而不影响其布局中的解释的方式。

但 RenderTransform 本身有时是麻烦。在此特定示例中,untransformed 矩形,在右端,因此不将超过前的矩形的宽度必须定义它顶部。

persuading 布局系统将忽略某些元素的另一个方法是使它们画布的子级。除非画布提供一个明确的高度和宽度,它将始终报告零维度布局系统。渚嬪的方式  如果在单个单元格网格中将放置矩形和画布然后该画布的任何子将有效地忽略通过布局系统。

图 9 显示 Fake3DBarChart 程序在数据模板。一个高度基于 BaseCost Doodad 对象的属性和单个单元格网格给出一个明确的宽度。该网格包含一个矩形的块的前面和画布。画布包含两个附加的单个单元格网格右侧和顶部。这些网格 RenderTransforms 倾斜效果应用于但通过布局系统,甚至 untransformed 的网格将被忽略。

请注意颜色是如何显示为灰色:基本颜色是资源在该数据模板,名为"画笔"。矩形前面直接设置获取该颜色。实际上有两个矩形内单个单元格网格右侧。使用"画笔"颜色是第一个资源 ;第二个位于顶部的第一个上,颜色是用部分透明黑色画笔。这会导致整体颜色将被 darkened。使用"画笔"一个矩形颜色的顶端,介绍使用部分透明白色画笔,使总体颜色变浅。这是一个极好技术创建特定的固定的颜色或获取数据绑定通过一种颜色的不同阴影。

缩放闂  

已被了到目前为止显示的所有示例都基于 Doodad 类非常方便地低两个图中具有销售数据,因此几乎适合直接到矩形的高度 (或其他元素),它在条形图中的绑定。

值的实际数据通常不是这样符合标准,并且确实需要某种方式将数据值转换为适当的显示尺寸。您将可能还希望图表中显示实际值相对应的条形图高度的一侧显示一个小的比例。此外,如果数据项的某些集合的最大值 (例如) 是 76,543,您可能需要去向上舍入值如 100,000 小数位数。

这些问题通常会导致程序员图表和使用自定义图表控件放弃该数据模板的方法。但还有为 hasty 因此没有理由。有几个缩放图表中的误差线的解决方案。如果您事先知道您将图表的值的大致范围,您可以应用一个 ScaleTransform 在 XAML 中。渚嬪的方式  如果值需要图形范围从 0 到 10,您可能将一个 ScaleTransform 适用一个 ScaleY 的属性,以创建 150 单位高达栏设置为 15。

我将在此处说明) 的另一种方法需要其他属性添加到业务对象。在代码中,执行缩放计算,但是缩放的值可以访问在 XAML 中。

在 ChartingLib Gizmo 类名称属性和一个收入属性但它还包含名为缩放比例和 ScaledRevenues 的属性。每当收入或 ScalingFactor 更改,类将只是将这两个值相乘,并将结果设置 ScaledRevenues。(ScaledRevenues 实际上是一个独立于设备的单位的高度,但 Gizmo 确实不必费神本身与一个详细的)。

图 9 的 Fake3DBarChart 数据模板

<DataTemplate>
<DataTemplate.Resources>
<SolidColorBrush x:Key="brush" Color="#4080FF" />
</DataTemplate.Resources>
<Grid VerticalAlignment="Bottom"
Margin="4"
Width="20"
Height="{Binding BaseCost}">
<!-- Front -->
<Rectangle Fill="{StaticResource brush}" />
<Canvas>
<!-- Right Side -->
<Grid Canvas.Left="20"
Height="{Binding BaseCost}"
Width="25"
VerticalAlignment="Bottom">
<Grid.RenderTransform>
<SkewTransform AngleY="-30" />
</Grid.RenderTransform>
<Rectangle Fill="{StaticResource brush}" />
<Rectangle Fill="#40000000" />
</Grid>
<!-- Top: 14.4 = 25 * tan(30) -->
<Grid Canvas.Top="-14.4"
Height="14.4"
Width="20">
<Grid.RenderTransform>
<SkewTransform CenterY="14.4" AngleX="-60" />
</Grid.RenderTransform>
<Rectangle Fill="{StaticResource brush}" />
<Rectangle Fill="#40FFFFFF" />
</Grid>
</Canvas>
</Grid>
</DataTemplate>

有人有跟踪的最大收入所有 Gizmo 对象的属性的值。 可能此逻辑的最佳点是 GizmoCollection 类从 ObservableCollection < Gizmo > 派生的。 因为 Gizmo 本身实现 INotifyPropertyChanged 接口,GizmoCollection 可以安装在集合中的所有 Gizmo 项目的 PropertyChanged 事件处理程序。 如果在将更改收入属性在任何 Gizmo 对象上的 GizmoCollection 计算新它存储在名为 MaximumRevenues 属性中的最大值。 GizmoCollection 还定义了一个 MaximumRevenuesChanged 触发事件的它 MaximumRevenues 更改时。

在 GizmoPresenter 类中处理逻辑的其余部分。 GizmoPresenter 类定义一个名为 Gizmos ObservableCollection < Gizmo > 类型的属性、 创建该集合中,并安装为 MaximumRevenuesChanged 事件处理程序。
GizmoPresenter 定义一个名为 DisplayHeight,将在 XAML 中设置为该条形图中条形图的最大的所需高度的属性。 它还定义了两个只读属性名为 RevenuesBase 和 HalfRevenuesBase,用于的帮助中创建一个可视比例条形图。

触发 MaximumRevenuesChanged 事件时, 该 GizmoPresenter 将从 GizmoCollection 对象获取新 MaximumRevenues 值,并计算 RevenuesBase 值。 RevenuesBase 基本上是 MaximumRevenues 舍入为零的很多的值。 这是对应于 DisplayHeight 的收入值。 但您可能不希望限制 RevenuesBase 10 的幂 (渚嬪的方式  10、 100、 1000,等等) 因为如果 MaximumRevenues 是 1,001,RevenuesBase 将是 10,000,和最高的条形图只是关于十分之一图表的高度。 最好使用几何序列,如 1-2-5 的数据系列从而的收入基将等于从系列 1,2,5,10,20,50,100,200,500,值等。

ChartingLibHelper 类有一个名为 GetRoundedMaximum 帮助此计算出的静态方法。 (是相当是简单,但涉及对数)。您只需在计算最大值和一个有序的系列值之间传递 1 和 10,渚嬪的方式  2 和 5。 GizmoPresenter 使用 RevenuesBase 该值,并在 DisplayHeight 设置中的每个 Gizmo 对象 ScalingFactor 值。 ScaledBarChart 项目演示此方法。

不需要的 Gizmo 收入属性,但要 ScaledRevenues 属性绑定每个栏的高度。 鍙 ﹀ 的方式  GizmoPresenter 构造简单完全在 XAML 中左侧标尺从提供了足够的信息。 结果如图 10 所示。

如果运行此程序等待随机数字生成器使一个收入值大于 100,000 您将看到刻度移位显示最多 200,000,并且所有条形图减少高度的一半。


图 10 的 ScaledBarChart 显示

处理饼图

首次饼图看起来非常 unsuited 以实现由一个 ItemsControl,但它确实不太不正确。 您甚至不需要一个自定义面板,但您将需要一个自定义的元素将显示饼图的扇区。

在 ChartingLib PieSlice 类派生自使用我在我的专栏"矢量图形和 WPF Shape 类"中所述的技术的形状在 2008 年 3 月月刊的 MSDN Magazine 》 (msdn.microsoft.com/en-us/magazine/cc337899.aspx)。 它具有类型点的中心属性、 双,类型的半径属性和 StartAngle 和 SweepAngle,还类型的属性双引号。 在 StartAngle 是相对于正将按顺时针方向的角度与从中心,扩展向上垂直行中。

PieSlice 还重写 MeasureOverride 方法如果切片包含整个圆报告大小并不只是它的一个部分。 这样,将被放入单个单元格网格的所有扇区。

我将会显示饼图使用一个数据模板显示类型产品的七个对象。 这些被收集在名为 ProductLineup ObservableCollection < 产品 > 从派生类中。 产品类具有名称和销售属性,以及帮助显示出的两个属性:百分比属性: 这是一个 AccumulatedPercentage,销售总额的百分比即前此项集合中项的销售额累计的百分比。 设置这些属性的集合类。 显然,AccumulatedPercentage 是绑定到该饼图扇区的该 StartAngle 并百分比绑定到该扫描角度在的图 11 所示的数据模板的部分中所示。

图 11 数据模板的饼图图表

<DataTemplate>
<charts:ChildIndexProvider Name="indexProvider">
<Grid>
<charts:PieSlice
Name="pieslice"
StartAngle="{Binding Accumulated,
Converter={StaticResource angleConv}}"
SweepAngle="{Binding Percentage,
Converter={StaticResource angleConv}}"
Fill="{Binding ElementName=indexProvider,
Path=Index,
Converter={StaticResource brushConv}}"
Stroke="Black"
StrokeThickness="1"
Center="200 200"
Radius="200">
</charts:PieSlice>
...
</Grid>
</charts:ChildIndexProvider>
</DataTemplate>

"angleConv"资源键引用,只需将值乘以 360 的 PercentageToAngleConverter。 在 ItemsPanelTemplate 是单个单元格网格。

没有在此处定义的工具提示。 而是,我希望实际的标签。 此功能,我中名为 CenterAngle PieSlice 添加了一个只读属性。 其值被计算为在 StartAngle 加上该半 SweepAngle。 在模板中添加一个行元素和一个 TextBlock,CenterAngle 度,旋转它们相当容易,但您将获得一些颠倒的文本而不很好。 确实希望文本是一致的水平。
我决定另一个代码段的顺序,称为 CircleShifter 旨在排列子元素周围的圆形将修饰阶导数。 CircleShifter 具有名为 BasePoint 的点的类型和另一个命名的 RotateTransform RotateTransform 类型的属性。 CircleShifter 定位其子在 BasePoint RotateTransform--通过旋转但不同的角度属性在 RotateTransform 是否介于 0 到 180 度 (饼图的右边) 之间 180 和 360 度 (饼图的左侧) 之间。 图 12 显示其他的 XAML 和图 13 显示结果。

如果扇区太小,还有没有逻辑,以防止运行到两个标签。 但是,CircleShifter 可能会提高,以使更多居中向顶部和底部的饼图的标签。

图 12 数据模板的饼图图表标签

<DataTemplate>
<charts:ChildIndexProvider Name="indexProvider">
<Grid>
...
<Canvas>
<Line X1="200" Y1="50" X2="200" Y2="-25" Stroke="Black">
<Line.RenderTransform>
<RotateTransform
Angle="{Binding ElementName=pieslice,
Path=CenterAngle}"
CenterX="200" CenterY="200"/>
</Line.RenderTransform>
</Line>
<charts:CircleShifter BasePoint="200 -25">
<charts:CircleShifter.RotateTransform>
<RotateTransform
Angle="{Binding ElementName=pieslice,
Path=CenterAngle}"
CenterX="200" CenterY="200"/>
</charts:CircleShifter.RotateTransform>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Sales,
StringFormat=’: {0:C}’}" />
</StackPanel>
</charts:CircleShifter>
</Canvas>
</Grid>
</charts:ChildIndexProvider>
</DataTemplate>

很诱人试图派生以使某些转换的"分离"从中心位置存在一个或多个扇区的饼图图表。此功能可能最适合处理 PieSlice 类本身中。其他属性将指示一个偏移量 (为了 MeasureOverride 方法),中心并布尔属性将控制是否实际的偏移量应用于呈现切片。

图 13 的饼图的标签显示为

三维饼图图表可以是需要,但它提供了一些挑战。以可视方式,最佳结果将从单个的 Viewport3D,放置所有饼图扇区获得,但正如我前面提到的那样的不允许您使用一个数据模板的 ItemsControl。该数据模板本身可以是一个 Viewport3D 包含一个 GeometryModel3D 定义一个三维饼图扇区,但这些多个 Viewport3D 元素将从背景堆积到前景色为在网格中的子索引顺序相同。若要避免奇数重叠效果,只是万无一失方法是分解足够从中心扇区,以便它们不重叠在所有。

统一的边和顶端和统一的从右向左的重叠形状已简化栏图表的使用虚设三维效果。可能在 PieSlice 类中实现一个虚设的三维效果,但每个切片将需要略有不同,具体取决于它的位置,并在重叠将仍是一个问题。

连接点

在实现行图表时它将真正成为可疑是否 ItemsControl 和数据模板的方式转。大问题是水平和垂直两个数据必须进行缩放,折线图需要处理两个的变量。此外,通常很希望在点与线条或涉及到从所有在 DataTemplates 中获取信息的每个数据模板的外部内容的平滑的曲线。仍然,完全在 XAML 中定义,折线图的可视元素的优点使工作量值得。

下一步中,我将探讨将一个数据行的图表模板在现实情况所需的代码支持。

Charles Petzold是一个为 MSDN Magazine 》 的 longtime 构成编辑器。他最新简介册是"的 Annotated 生产:史生产的历史的纸张 Computability 和生产计算机上通过一个指导教程"(Wiley,2008年)。他的网站是 charespetzold.com