QuickCharts圖表控件是Amcharts公司提供的一個開源的圖表控件庫,這個控件庫支持WPF、Silverlight、和Windows等平臺,源代碼能夠從Github網站上下載到(https://github.com/ailon/amCharts-Quick-Charts)。目前從Github上下載到的QuickCharts圖表控件的源代碼並不能直接在Windows 10上使用,可是因爲都是基於XAML來建立的,因此很方便進行移植到Windows 10平臺,移植的代碼請參考本書的配套源代碼。QuickCharts圖表控件封裝了一些經常使用的圖表控件如餅圖、柱形圖、折線圖、區域圖等,能夠直接在項目中進行其提供的圖表控件來建立圖表。使用QuickCharts圖表控件來建立圖表控件是比較簡單的,經過設置相關的屬性就能夠實現一個完整的圖表,這一小節主要是根據QuickCharts的源碼來說解QuickCharts項目的結構和圖表控件實現的原理,同時這也是一種實現圖表的很好的思路,能夠給實現其餘的圖表控件做爲一個參考。html
打開QuickCharts的項目能夠看到QuickCharts的項目結構如圖13.8所示,Themes文件夾下面存放的是圖表控件和控件相關模塊的XAML樣式文件,Generic.xaml文件是控件的樣式入口文件,全部的控件都是默認從這裏來讀取樣式的,因此在Generic.xaml裏面會經過資源字典(ResourceDictionary)的方式把其餘的樣式文件都加載進來。在QuickCharts項目中每一個樣式文件都與一個相關的控件對應起來,如PieChart.xaml樣式文件對應了餅圖PieChart類。控件的初始化的時候會自動到Themes/Generic.xaml這個路徑下去搜素控件關聯的樣式的 這是一種典型的自定義控件的方式。git
QuickCharts控件庫裏面包含了兩類圖表,一種是餅圖圖表PieChart,另一種是連續圖表SerialChart。連續圖表包含了線形、柱形、區域圖這些圖形,由於這些圖形有不少共同的特色如座標軸,網格等,因此QuickCharts控件庫把這些圖形進行了統一的封裝處理。github
QuickCharts項目類圖如圖13.9和圖13.10所示,主要的類和接口的說明以下:微信
ILegendItem接口:定義了圖例基本屬性。佈局
SerialGraph抽象類:定義了連續圖表圖形的基本方法和屬性,繼承Control控件和ILegendItem接口。網站
AreaGraph類:實現了區域圖的邏輯,繼承SerialGraph類。ui
LineGraph類:實現了線性圖的邏輯,繼承SerialGraph類。this
ColumnGraph類:實現了柱形圖的邏輯,繼承SerialGraph類。spa
SerialChart類:表示是連續圖形圖表,能夠看做是AreaGraph類、LineGraph類和ColumnGraph類的容器,繼承Control類。code
LegendItem類:表示單條的圖例記錄,繼承DependencyObject類和ILegendItem接口。
Legend類:表示圖表的圖例,繼承ItemsControl類,是一個列表控件,LegendItem爲其列表項的類型。
Indicator類:表示連續圖表圖形的標示,繼承Control類。
Balloon類:表示圖表彈出的數據指示框提示,繼承Control類。
CategoryAxis類:表示連續圖形圖表的分類軸,一般爲X軸,繼承Control類。
ValueAxis類:表示連續圖形圖表的數值軸,一般爲Y軸,繼承Control類。
ValueGrid類:表示連續圖形圖表的網格,繼承Control類。
Slice類:表示一塊片形餅圖,繼承Control控件和ILegendItem接口。
PieChart類:表示餅圖圖表,繼承Control類。
在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; } }
在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