[深刻淺出Windows 10]QuickCharts圖表控件庫解析

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項目的結構和圖表控件實現的原理,同時這也是一種實現圖表的很好的思路,能夠給實現其餘的圖表控件做爲一個參考。html

13.4.1 QuickCharts項目結構分析

    打開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類。

13.4.2 餅圖圖表PieChart的實現邏輯

    在QuickCharts控件庫裏面餅圖PieChart是由多個餅圖切片Slice控件,一個圖例Legend控件和一個標註Balloon控件組成。下面來看一下PieChart是怎麼把這些模塊組合起來實現一個餅圖圖表的。

    (1)Slice控件的實現

    文件Slice.xaml裏面定義了Slice控件的樣式,使用Path圖形來繪圖,Slice控件的形狀如圖所示。在Slice類裏面封裝了初始化的方法RenderSlice()方法,RenderSlice方法裏面會根據當前的Slice圖形所佔的比例來實現Slice圖形,若是小於1則是餅圖裏面的一個切片,不然則是一個完整的圓。代碼以下所示:

代碼清單5-4QuickCharts控件(源代碼:第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;
    }

本文來源於《深刻淺出Windows 10通用應用開發》

源代碼下載:http://vdisk.weibo.com/u/2186322691

目錄:http://www.cnblogs.com/linzheng/p/5021428.html

歡迎關注個人微博@WP林政   微信公衆號:wp開發(號:wpkaifa)

Windows10/WP技術交流羣:284783431

相關文章
相關標籤/搜索