13.4 QuickCharts圖表控件庫解析
QuickCharts圖表控件是Amcharts公司提供的一個開源的圖表控件庫,這個控件庫支持WPF、Silverlight、和Windows等平台,源代碼可以從Github網站上下載到(https://github.com/ailon/amCharts-Quick-Charts)。目前從Github上下載到的QuickCharts圖表控件的源代碼並不能直接在Windows 10上使用,但是由於都是基於XAML來創建的,所以很方便進行移植到Windows 10平台,移植的代碼請參考本書的配套源代碼。QuickCharts圖表控件封裝了一些常用的圖表控件如餅圖、柱形圖、折線圖、區域圖等,可以直接在項目中進行其提供的圖表控件來創建圖表。使用QuickCharts圖表控件來創建圖表控件是比較簡單的,通過設置相關的屬性就可以實現一個完整的圖表,這一小節主要是根據QuickCharts的源碼來講解QuickCharts項目的結構和圖表控件實現的原理,同時這也是一種實現圖表的很好的思路,可以給實現其他的圖表控件作為一個參考。
13.4.1 QuickCharts項目結構分析
打開QuickCharts的項目可以看到QuickCharts的項目結構如圖13.8所示,Themes文件夾下面存放的是圖表控件和控件相關模塊的XAML樣式文件,Generic.xaml文件是控件的樣式入口文件,所有的控件都是默認從這里來讀取樣式的,所以在Generic.xaml里面會通過資源字典(ResourceDictionary)的方式把其他的樣式文件都加載進來。在QuickCharts項目中每個樣式文件都與一個相關的控件對應起來,如PieChart.xaml樣式文件對應了餅圖PieChart類。控件的初始化的時候會自動到Themes/Generic.xaml這個路徑下去搜素控件關聯的樣式的 這是一種典型的自定義控件的方式。
QuickCharts控件庫里面包含了兩類圖表,一種是餅圖圖表PieChart,另外一種是連續圖表SerialChart。連續圖表包含了線形、柱形、區域圖這些圖形,因為這些圖形有很多共同的特點如坐標軸,網格等,所以QuickCharts控件庫把這些圖形進行了統一的封裝處理。
QuickCharts項目類圖如圖13.9和圖13.10所示,主要的類和接口的說明如下:
ILegendItem接口:定義了圖例基本屬性。
SerialGraph抽象類:定義了連續圖表圖形的基本方法和屬性,繼承Control控件和ILegendItem接口。
AreaGraph類:實現了區域圖的邏輯,繼承SerialGraph類。
LineGraph類:實現了線性圖的邏輯,繼承SerialGraph類。
ColumnGraph類:實現了柱形圖的邏輯,繼承SerialGraph類。
SerialChart類:表示是連續圖形圖表,可以看作是AreaGraph類、LineGraph類和ColumnGraph類的容器,繼承Control類。
LegendItem類:表示單條的圖例記錄,繼承DependencyObject類和ILegendItem接口。
Legend類:表示圖表的圖例,繼承ItemsControl類,是一個列表控件,LegendItem為其列表項的類型。
Indicator類:表示連續圖表圖形的標示,繼承Control類。
Balloon類:表示圖表彈出的數據指示框提示,繼承Control類。
CategoryAxis類:表示連續圖形圖表的分類軸,通常為X軸,繼承Control類。
ValueAxis類:表示連續圖形圖表的數值軸,通常為Y軸,繼承Control類。
ValueGrid類:表示連續圖形圖表的網格,繼承Control類。
Slice類:表示一塊片形餅圖,繼承Control控件和ILegendItem接口。
PieChart類:表示餅圖圖表,繼承Control類。
13.4.2 餅圖圖表PieChart的實現邏輯
在QuickCharts控件庫里面餅圖PieChart是由多個餅圖切片Slice控件,一個圖例Legend控件和一個標注Balloon控件組成。下面來看一下PieChart是怎么把這些模塊組合起來實現一個餅圖圖表的。
(1)Slice控件的實現
文件Slice.xaml里面定義了Slice控件的樣式,使用Path圖形來繪圖,Slice控件的形狀如圖所示。在Slice類里面封裝了初始化的方法RenderSlice()方法,RenderSlice方法里面會根據當前的Slice圖形所占的比例來實現Slice圖形,如果小於1則是餅圖里面的一個切片,否則則是一個完整的圓。代碼如下所示:
代碼清單5-4:QuickCharts控件(源代碼:第5章\Examples_5_4)
Slice.cs文件主要代碼 ------------------------------------------------------------------------------------------------------------------ // 初始化圖形 private void RenderSlice() { if (_sliceVisual != null) { _sliceVisual.Fill = Brush; if (_percentage < 1) { RenderRegularSlice(); } else { RenderSingleSlice(); } } } // 只有一個數據的情況,直接創建一個圓形 private void RenderSingleSlice() { EllipseGeometry ellipse = new EllipseGeometry() { Center = new Point(0, 0), RadiusX = _radius, RadiusY = _radius }; _sliceVisual.Data = ellipse; } // 創建扇形圖形 private void RenderRegularSlice() { PathGeometry geometry = new PathGeometry(); PathFigure figure = new PathFigure(); geometry.Figures.Add(figure); _sliceVisual.Data = geometry; // 根據比例計算角度 double endAngleRad = _percentage * 360 * Math.PI / 180; Point endPoint = new Point(_radius * Math.Cos(endAngleRad), _radius * Math.Sin(endAngleRad)); // 添加直線 figure.Segments.Add(new LineSegment() { Point = new Point(_radius, 0) }); // 添加弧線 figure.Segments.Add(new ArcSegment() { Size = new Size(_radius, _radius), Point = endPoint, SweepDirection = SweepDirection.Clockwise, IsLargeArc = _percentage > 0.5 }); // 添加直線 figure.Segments.Add(new LineSegment() { Point = new Point(0, 0) }); }
(2)圖例Legend控件的實現
圖例Legend控件是通過繼承ItemsControl類實現了一個列表控件,列表項是由LegendItem類組成的,在樣式文件Legend.xaml上可以找到這個列表控件所實現的綁定的邏輯。LegendItem類有兩個屬性一個是Brush表示圖形的顏色畫刷,一個是Title表示圖形的標題,它們跟Legend控件綁定的ItemTemplate的XAML代碼如下所示:
Legend.xaml文件主要代碼 ------------------------------------------------------------------------------------------------------------------ <DataTemplate> <StackPanel Orientation="Horizontal"> <Rectangle Fill="{Binding Brush}" Height="10" Width="10" Margin="5" /> <TextBlock Text="{Binding Title}" VerticalAlignment="Center" /> </StackPanel> </DataTemplate>
(3)標注Balloon控件的實現
Balloon控件是由Border控件和TextBlock控件組成的,用來顯示圖形的標注,Balloon類的Text屬性則是表示標示的文本內容,XAML的語法如下:
Balloon.xaml文件主要代碼 ------------------------------------------------------------------------------------------------------------------ <ControlTemplate TargetType="amq:Balloon"> <Border Background="#20000000" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5" Padding="5"> <TextBlock Text="{TemplateBinding Text}" /> </Border> </ControlTemplate>
(4)PieChart控件把Slice控件、Legend控件和Balloon控件組成餅圖圖表
在上面已經講解了Slice控件、Legend控件和Balloon控件的實現方式,可以把這三個控件看作是餅圖圖表的三大模塊,接下來PieChart控件要做的事情就是把這三者結合起來形成一個完整的餅圖圖表。先來看一下PieChart控件的XAML樣式,分析它的UI布局。打開PieChart.xaml樣式文件,可以看到如下的代碼:
PieChart.xaml文件主要代碼 ------------------------------------------------------------------------------------------------------------------ <ControlTemplate TargetType="amq:PieChart"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> </Grid.ColumnDefinitions> <Border x:Name="PART_SliceCanvasDecorator" Background="Transparent"> <Canvas x:Name="PART_SliceCanvas" /> </Border> <amq:Legend x:Name="PART_Legend" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="10,0,0,0" Visibility="{TemplateBinding LegendVisibility}"/> <Canvas> <amq:Balloon x:Name="PART_Balloon" BorderBrush="{TemplateBinding Foreground}" BorderThickness="2" Visibility="Collapsed"/> </Canvas> </Grid> </Border> </ControlTemplate>
命名為PART_SliceCanvas的Canvas對象是餅圖的圖形面板,在該面板上會添加多個餅圖切片Slice控件形成一個完成的圓形,組成一個餅圖,當然如果只有一個數據的時候就只有一個Slice控件,這時候Slice控件是一個完整的圓。PieChart類里面定義的Legend對象對應樣式里面的命名為PART_Legend的Legend控件,表示餅圖的圖例,顯示在餅圖圖表的右上角上。PieChart類里面定義的Balloon對象對應樣式里面的命名為PART_Balloon的Balloon控件,表示餅圖的標示,這個Balloon控件是在一個Canvas面板的里面的,它的位置會根據用戶點擊的Slice控件的位置而進行改變,改變的原理是通過設置Balloon控件的Canvas.LeftProperty和Canvas.TopProperty屬性來實現。
在PieChart控件里面最核心的邏輯就是對Slice控件初始化的過程了,這個過程是通過調用ProcessData()方法來初始化餅圖里面所有的Slice控件,在ProcessData()方法里面先后調用了三個封裝好的方法,SetData()方法、ReallocateSlices()方法和RenderSlices()方法。SetData()方法設置餅圖數據屬性的綁定,餅圖的數據包含了標題和數值兩個屬性,標題是用於表示Slice控件的含義,數值是用來計算Slice控件的大小。ReallocateSlices()方法創建和初始化餅圖圖表里面所有的Slice控件,把Slice控件添加到PART_SliceCanvas面板上。RenderSlices()方法用於設置Slice控件在PART_SliceCanvas面板上的位置和隱藏Balloon控件,因為Balloon控件要點擊了Slice控件才顯示出來。
(5)使用PieChart控件
PieChart控件把相關的圖形創建初始化等邏輯都封裝好了,使用PieChart控件創建餅圖圖表只需要設置好TitleMemberPath屬性和ValueMemberPath屬性和數據源數據屬性的對應關系,就可以把餅圖圖表顯示出來。
XAML代碼如下所示:
PieChart.xaml文件主要代碼 ------------------------------------------------------------------------------------------------------------------ <amq:PieChart x:Name="pie1" TitleMemberPath="title" ValueMemberPath="value"></amq:PieChart>
后台CS代碼如下所示:
PieChart.xaml.cs文件主要代碼 ------------------------------------------------------------------------------------------------------------------ public ObservableCollection<PData> Data = new ObservableCollection<PData>() { new PData() { title = "slice #1", value = 30 }, new PData() { title = "slice #2", value = 60 }, new PData() { title = "slice #3", value = 40 }, new PData() { title = "slice #4", value = 10 }, }; private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { // 通過DataSource屬性把數據集合傳遞給餅圖圖表 pie1.DataSource = Data; } ……省略若干代碼 // 餅圖綁定的數據集合的數據類型表示餅圖的一塊 public class PData { public string title { get; set; } public double value { get; set; } }
13.4.3 連續圖形圖表SerialChart的實現邏輯
在QuickCharts控件庫里面連續圖形圖表SerialChart實現了三種圖形,線性圖LineGraph、柱形圖ColumnGraph和區域圖AreaGraph。可以在SerialChart圖表里面顯示其中一種或多種圖形,因為這三種圖形實現的原理是類似,都是在坐標軸上連續性地展示相關的數據,只是圖形的形狀不一樣,所以在QuickCharts控件庫里面這三種圖形統一使用SerialChart控件來封裝起來。先打開SerialChart控件的樣式文件分析SerialChart控件的樣式結構,SerialChart控件的樣式文件SerialChart.xaml的主要代碼如下所示:
SerialChart.xaml文件主要代碼 ------------------------------------------------------------------------------------------------------------------ <ControlTemplate TargetType="amq:SerialChart"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <!--軸線 X軸 Y軸--> <amq:ValueAxis x:Name="PART_ValueAxis" Grid.Row="0" Margin="0,0,0,-2" Canvas.ZIndex="100" Foreground="{TemplateBinding AxisForeground}" HorizontalAlignment="Right" /> <!--軸線上的類別--> <amq:CategoryAxis x:Name="PART_CategoryAxis" Grid.Row="1" Foreground="{TemplateBinding AxisForeground}" /> <!--圖表網格--> <Border Grid.Row="0" Background="{TemplateBinding PlotAreaBackground}"> <amq:ValueGrid x:Name="PART_ValueGrid" Foreground="{TemplateBinding GridStroke}" /> </Border> <!--圖形面板--> <Border x:Name="PART_GraphCanvasDecorator" Grid.Row="0"> <Canvas x:Name="PART_GraphCanvas" Background="Transparent" /> </Border> <!--圖例--> <amq:Legend x:Name="PART_Legend" Grid.Row="0" Margin="10,0,0,0" Visibility="{TemplateBinding LegendVisibility}" VerticalAlignment="Top" HorizontalAlignment="Left" /> <!--標注--> <Canvas Grid.Row="0"> <amq:Balloon x:Name="PART_Balloon" BorderBrush="{TemplateBinding AxisForeground}" BorderThickness="2" Visibility="Collapsed" /> </Canvas> </Grid> </Border> </ControlTemplate>
從SerialChart控件的樣式里面可以看到SerialChart控件是由數值軸控件/Y軸控件(ValueAxis)、類別軸控件/X軸控件(CategoryAxis)、圖表網格控件(ValueGrid)、圖形面板(Canvas面板上添加LineGraph、ColumnGraph和AreaGraph控件)、圖例控件(Legend)和標注控件(Balloon)組成的。其中圖例和標注控件在上一小節已經講解了,下面看一下其他的控件的實現原理以及如何使用SerialChart控件。
(1)數值軸控件/Y軸控件(ValueAxis)和類別軸控件/X軸控件(CategoryAxis)
ValueAxis和CategoryAxis控件的實現原理是基本一樣的,打開它們的樣式文件可以看到,軸控件是由兩個Canvas面板和一個Rectangle控件組成,命名為PART_ValuesPanel的Canvas面板是用於顯示軸上的數字,命名為PART_TickPanel的Canvas面板是用於顯示軸上的刻度(數值和軸之間的小線段),Rectangle控件則是用於表示軸線。Y軸(ValueAxis)采用Grid面板的ColumnDefinitions來排列,X軸(CategoryAxis)則是采用RowDefinition。
(2)圖表網格控件(ValueGrid)
ValueGrid控件是由一個Canvas面板組成,在使用ValueGrid控件的時候需要通過SetLocations方法來把圖表的坐標數值傳遞進來,然后ValueGrid控件再根據坐標的數值在Canvas面板上來創建Line線段繪制成網格。
(3)LineGraph、ColumnGraph和AreaGraph控件
LineGraph、ColumnGraph和AreaGraph控件是SerialChart圖表里面最核心的圖形,這三個控件的XAML樣式文件都是只有一個Canvas面板,三個控件類都繼承SerialGraph抽象類,SerialGraph類封裝了三個控件共性的一些屬性,如Locations(圖表數據的點集合)、XStep(X軸的兩個值的間距)等。LineGraph控件表示線性圖,實現的原理是通過圖表的數據點集合來創建一個Polyline圖形添加到Canvas面板上。ColumnGraph控件則是使用Path來繪制柱形的形狀。AreaGraph控件使用Polygon圖形來繪制區域圖。
(4)SerialChart控件
SerialChart控件把各大模塊組成連續圖形圖表的原理和PieChart控件是一樣的流程,只是在PieChart控件里面初始化的是Slice控件,而在SerialChart控件里面初始化的是LineGraph、ColumnGraph和AreaGraph控件。
(5)使用SerialChart控件
因為SerialChart控件是可以加載LineGraph、ColumnGraph和AreaGraph三種控件的,所以提供了一個Graphs屬性,可以通過Graphs屬性來添加多個圖形。代碼如下所示:
SerialChart.xaml文件主要代碼 ------------------------------------------------------------------------------------------------------------------ <amq:SerialChart x:Name="chart1" DataSource="{Binding Data}" CategoryValueMemberPath="cat1" AxisForeground="White" PlotAreaBackground="Black" GridStroke="DarkGray"> <amq:SerialChart.Graphs> <amq:LineGraph ValueMemberPath="val1" Title="Line #1" Brush="Blue" /> <amq:ColumnGraph ValueMemberPath="val2" Title="Column #2" Brush="#8000FF00" ColumnWidthAllocation="0.4" /> <amq:AreaGraph ValueMemberPath="val3" Title="Area #1" Brush="#80FF0000" /> </amq:SerialChart.Graphs> </amq:SerialChart>
SerialChart.xaml.cs文件主要代碼 ------------------------------------------------------------------------------------------------------------------ // 圖表數據實體類 public class TestDataItem { // cat1表示X軸的分類 public string cat1 { get; set; } // 用來作為LineGraph圖形的展示數據 public double val1 { get; set; } // 用來作為ColumnGraph圖形的展示數據 public double val2 { get; set; } // 用來作為AreaGraph圖形的展示數據 public decimal val3 { get; set; } } private ObservableCollection<TestDataItem> _data = new ObservableCollection<TestDataItem>() { new TestDataItem() { cat1 = "cat1", val1=5, val2=15, val3=12}, new TestDataItem() { cat1 = "cat2", val1=13.2, val2=1.5, val3=2.1M}, new TestDataItem() { cat1 = "cat3", val1=25, val2=5, val3=2}, new TestDataItem() { cat1 = "cat4", val1=8.1, val2=1, val3=8}, new TestDataItem() { cat1 = "cat5", val1=8.1, val2=1, val3=4}, new TestDataItem() { cat1 = "cat6", val1=8.1, val2=1, val3=10}, }; // 綁定的數據集合屬性 public ObservableCollection<TestDataItem> Data { get { return _data; } } private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { this.DataContext = this; }
源代碼下載:http://vdisk.weibo.com/u/2186322691
目錄:http://www.cnblogs.com/linzheng/p/5021428.html
歡迎關注我的微博@WP林政 微信公眾號:wp開發(號:wpkaifa)
Windows10/WP技術交流群:284783431