前一篇文章里面讲到了实现一个直方图控件所要注意的问题,既然是一个控件,那么需要先将给用户调用的API调用出来。如果读者有使用Office Excel的经验的话,就会发现,制作一个直方图,实际上只需要显示直方图的数据就可以了,如下图所示:
上图,再分解一下,可以看到每一个Series是一个系列的数据(比如一个数组);而每一个Category可以看成是用来识别一个数据的标识(例如数组的下标);当然最后剩下的就是数据啦。因此直方图控件给用户的定义的使用方法应该是:
Xaml代码:
<Window x:Class="CodeLibrary.Test.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CodeLibrary.Charts"
Loaded="Window_Loaded" Title="Window1" Height="600" Width="800">
<local:Histogram x:Name="TestHistogram"
Width="8000"/>
</Window>
C#代码:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
double[] test = new double[256];
double[] test2 = new double[256];
Random random = new Random(1000);
for (int i = 0; i < test.Length; ++i)
{
test[i] = random.Next();
test2[i] = random.Next();
}
TestHistogram.HistogramData = test;
TestHistogram.AddHistogramData(test2, "Series 2");
}
这样一来,控件的最基本的API已经定义完毕,至于直方图其它的属性,可以等到整个直方图完善了以后再慢慢添加进去。对比WPF的控件实现方式,使用UserControl会比从已有的一个控件继承下来更方便一些,因为编辑UserControl的Xaml会相对直观一些。在写代码之前,先讲一下思路,为什么可以使用ItemsControl(ListBox是从ItemsControl继承下来的)来绘制一个直方图,先看看一个最普通的ItemsControl的代码:
默认情况,ListBox是以垂直排列的方式来排列每一个列表项的,而ListBox中的ItemsPanel属性允许你替换掉默认的排列列表项的面板,ListBox使用的默认面板是VirtualizingStackPanel,在数据绑定的情况下,VirtualizingStackPanel这个面板只会从绑定的数据里面创建在电脑屏幕中显示的列表项,这样最大程度优化了界面的性能—好东西。默认情况下,VirtualizingStackPanel是垂直排列放在里面的控件的—这就是为什么ListBox是垂直排列列表项的原因。
那么现在尝试一下将ListBox默认的排列行为改成横向排列,请看下图:
上面的代码,将VirtualizingStackPanel的布局方式从垂直排版改到水平排版,ListBox的列表项的布局方式也改变了。有了这个基础以后,我们来看如何实现直方图,其实我们可以把直方图看成一个大“竹简”,“竹简”里面的每一块显示直方图 里面的一个矩形和X轴上面的刻度,Y轴我们先不考虑,因此直方图可以以类似下面的角度来看:
这样一来,借助重载ItemsControl的ItemTemplate和ItemsPanel属性,比如下面的代码可以得到一个最简单的直方图的式样了:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:collection="clr-namespace:System.Collections;assembly=mscorlib">
<Page.Resources>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="data">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="HorizontalAlignment" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Page.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource data}}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="50">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Fill="Gray" Height="100" />
<Label HorizontalAlignment="Center" Content="{Binding}" Padding="0"Margin="0" Grid.Row="1" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Page>
下图就是最终得到的效果:
好了,接下来的问题就是如何把前面Xaml里面硬编码的数据用实际程序中变化的数据代替,未完待续……