前一篇文章裏面講到了實現一個直方圖控件所要注意的問題,既然是一個控件,那麼需要先將給用戶調用的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裏面硬編碼的數據用實際程序中變化的數據代替,未完待續……