上一小節講解了動態生成折線圖和區域圖,對於簡單的圖形這樣經過C#代碼來生成的方式是很方便的,可是當咱們的圖表要實現更加複雜的邏輯的時候,這種動態生成的方式就顯得力不從心了,那就須要利用控件封裝的方式來實現更增強大的圖表控件功能。這一小節未來講解怎樣去用封裝控件的方式去實現圖表,用一個餅圖控件做爲例子進行分析講解。html
餅圖其實就是把一個圓形分紅若干塊,每一塊表明着一個類別的數據,能夠把這每一塊的圖形看做是餅圖片形形狀。要實現一個餅圖控件,首先須要作的就是要實現餅圖片形形狀,在第4章裏面講解了實現如何實現自定義的形狀,餅圖片形形狀也能夠經過這種方式來實現。餅圖片形形狀有一些重要的屬性,如餅圖半徑Radius,內圓半徑InnerRadius,旋轉角度RotationAngle,片形角度WedgeAngle,點innerArcStartPoint,點innerArcEndPoint,點outerArcStartPoint和點outerArcEndPoint等,這些屬性的含義如圖13.5所示。要繪製出這個餅圖片形形狀須要計算出4個點的座標(點innerArcStartPoint,點innerArcEndPoint,點outerArcStartPoint和點outerArcEndPoint),這4的點的座標須要經過半徑和角度相關的屬性計算出來。計算出這4個點的座標的座標以後,而後經過這4個點建立一個Path圖形,這個Path圖形由兩條直線和兩條弧線組成,造成了一個餅圖片形形狀。經過這種方式不單單把這個餅圖片形形狀建立好了,連這個圖形在整個餅圖的位置也設置好了。代碼以下所示。express
代碼清單5-2:餅圖圖表(源代碼:第5章\Examples_5_2)微信
PiePiece.cs文件代碼:自定義的餅圖片形形狀 ------------------------------------------------------------------------------------------------------------------ using System; using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Shapes; namespace PieChartDemo { /// <summary> /// 自定義的餅圖片形形狀 /// </summary> class PiePiece : Path { #region 依賴屬性 // 註冊半徑屬性 public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("RadiusProperty", typeof(double), typeof(PiePiece), new PropertyMetadata(0.0)); // 餅圖半徑 public double Radius { get { return (double)GetValue(RadiusProperty); } set { SetValue(RadiusProperty, value); } } // 註冊餅圖片形點擊後推出的距離 public static readonly DependencyProperty PushOutProperty = DependencyProperty.Register("PushOutProperty", typeof(double), typeof(PiePiece), new PropertyMetadata(0.0)); // 距離餅圖中心的距離 public double PushOut { get { return (double)GetValue(PushOutProperty); } set { SetValue(PushOutProperty, value); } } // 註冊餅圖內圓半徑屬性 public static readonly DependencyProperty InnerRadiusProperty = DependencyProperty.Register("InnerRadiusProperty", typeof(double), typeof(PiePiece), new PropertyMetadata(0.0)); // 餅圖內圓半徑 public double InnerRadius { get { return (double)GetValue(InnerRadiusProperty); } set { SetValue(InnerRadiusProperty, value); } } // 註冊餅圖片形的角度屬性 public static readonly DependencyProperty WedgeAngleProperty = DependencyProperty.Register("WedgeAngleProperty", typeof(double), typeof(PiePiece), new PropertyMetadata(0.0)); // 餅圖片形的角度 public double WedgeAngle { get { return (double)GetValue(WedgeAngleProperty); } set { SetValue(WedgeAngleProperty, value); this.Percentage = (value / 360.0); } } // 註冊餅圖片形旋轉角度的屬性 public static readonly DependencyProperty RotationAngleProperty = DependencyProperty.Register("RotationAngleProperty", typeof(double), typeof(PiePiece), new PropertyMetadata(0.0)); // 旋轉的角度 public double RotationAngle { get { return (double)GetValue(RotationAngleProperty); } set { SetValue(RotationAngleProperty, value); } } // 註冊中心點的X座標屬性 public static readonly DependencyProperty CentreXProperty = DependencyProperty.Register("CentreXProperty", typeof(double), typeof(PiePiece), new PropertyMetadata(0.0)); // 中心點的X座標 public double CentreX { get { return (double)GetValue(CentreXProperty); } set { SetValue(CentreXProperty, value); } } // 註冊中心點的Y座標屬性 public static readonly DependencyProperty CentreYProperty = DependencyProperty.Register("CentreYProperty", typeof(double), typeof(PiePiece), new PropertyMetadata(0.0)); // 中心點的Y座標 public double CentreY { get { return (double)GetValue(CentreYProperty); } set { SetValue(CentreYProperty, value); } } // 註冊該餅圖片形所佔餅圖的百分比屬性 public static readonly DependencyProperty PercentageProperty = DependencyProperty.Register("PercentageProperty", typeof(double), typeof(PiePiece), new PropertyMetadata(0.0)); // 餅圖片形所佔餅圖的百分比 public double Percentage { get { return (double)GetValue(PercentageProperty); } private set { SetValue(PercentageProperty, value); } } // 註冊該餅圖片形所表明的數值屬性 public static readonly DependencyProperty PieceValueProperty = DependencyProperty.Register("PieceValueProperty", typeof(double), typeof(PiePiece), new PropertyMetadata(0.0)); // 該餅圖片形所表明的數值 public double PieceValue { get { return (double)GetValue(PieceValueProperty); } set { SetValue(PieceValueProperty, value); } } #endregion public PiePiece() { CreatePathData(0, 0); } private double lastWidth = 0; private double lastHeight = 0; private PathFigure figure; // 在圖形中添加一個點 private void AddPoint(double x, double y) { LineSegment segment = new LineSegment(); segment.Point = new Point(x + 0.5 * StrokeThickness, y + 0.5 * StrokeThickness); figure.Segments.Add(segment); } // 在圖形中添加一條線段 private void AddLine(Point point) { LineSegment segment = new LineSegment(); segment.Point = point; figure.Segments.Add(segment); } // 在圖形中添加一個圓弧 private void AddArc(Point point, Size size, bool largeArc, SweepDirection sweepDirection) { ArcSegment segment = new ArcSegment(); segment.Point = point; segment.Size = size; segment.IsLargeArc = largeArc; segment.SweepDirection = sweepDirection; figure.Segments.Add(segment); } private void CreatePathData(double width, double height) { // 用於退出佈局的循環邏輯 if (lastWidth == width && lastHeight == height) return; lastWidth = width; lastHeight = height; Point startPoint = new Point(CentreX, CentreY); // 計算餅圖片形內圓弧的開始點 Point innerArcStartPoint = ComputeCartesianCoordinate(RotationAngle, InnerRadius); // 根據中心點來校訂座標的位置 innerArcStartPoint = Offset(innerArcStartPoint,CentreX, CentreY); // 計算餅圖片形內圓弧的結束點 Point innerArcEndPoint = ComputeCartesianCoordinate(RotationAngle + WedgeAngle, InnerRadius); innerArcEndPoint = Offset(innerArcEndPoint, CentreX, CentreY); // 計算餅圖片形外圓弧的開始點 Point outerArcStartPoint = ComputeCartesianCoordinate(RotationAngle, Radius); outerArcStartPoint = Offset(outerArcStartPoint, CentreX, CentreY); // 計算餅圖片形外圓弧的結束點 Point outerArcEndPoint = ComputeCartesianCoordinate(RotationAngle + WedgeAngle, Radius); outerArcEndPoint = Offset(outerArcEndPoint, CentreX, CentreY); // 判斷餅圖片形的角度是否大於180度 bool largeArc = WedgeAngle > 180.0; // 把扇面餅圖往偏離中心點推出一部分 if (PushOut > 0) { Point offset = ComputeCartesianCoordinate(RotationAngle + WedgeAngle / 2, PushOut); // 根據偏移量來從新設置圓弧的座標 innerArcStartPoint = Offset(innerArcStartPoint,offset.X, offset.Y); innerArcEndPoint = Offset(innerArcEndPoint,offset.X, offset.Y); outerArcStartPoint = Offset(outerArcStartPoint,offset.X, offset.Y); outerArcEndPoint = Offset(outerArcEndPoint,offset.X, offset.Y); } // 外圓的大小 Size outerArcSize = new Size(Radius, Radius); // 內圓的大小 Size innerArcSize = new Size(InnerRadius, InnerRadius); var geometry = new PathGeometry(); figure = new PathFigure(); // 從內圓開始座標開始畫一個閉合的扇形圖形 figure.StartPoint = innerArcStartPoint; AddLine(outerArcStartPoint); AddArc(outerArcEndPoint, outerArcSize, largeArc, SweepDirection.Clockwise); AddLine(innerArcEndPoint); AddArc(innerArcStartPoint, innerArcSize, largeArc, SweepDirection.Counterclockwise); figure.IsClosed = true; geometry.Figures.Add(figure); this.Data = geometry; } protected override Size MeasureOverride(Size availableSize) { return availableSize; } protected override Size ArrangeOverride(Size finalSize) { CreatePathData(finalSize.Width, finalSize.Height); return finalSize; } //把點進行偏移轉換 private Point Offset(Point point, double offsetX, double offsetY) { point.X += offsetX; point.Y += offsetY; return point; } /// <summary> /// 根據角度和半徑來計算出圓弧上的點的座標 /// </summary> /// <param name="angle">角度</param> /// <param name="radius">半徑</param> /// <returns>圓弧上的點座標</returns> private Point ComputeCartesianCoordinate(double angle, double radius) { // 轉換成弧度單位 double angleRad = (Math.PI / 180.0) * (angle - 90); double x = radius * Math.Cos(angleRad); double y = radius * Math.Sin(angleRad); return new Point(x, y); } } }
建立好了PiePiece形狀以後,下面就要開始建立利用PiePiece形狀來建立餅圖控件了。建立餅圖控件是經過UserControl控件來實現,UserControl控件的XAML代碼裏面只有一個Grid面板,是用來加載PiePiece形狀來組成餅圖。XAML代碼以下所示:app
PiePlotter.xaml文件代碼 ------------------------------------------------------------------------------------------------------------------ <UserControl x:Class="PieChartDemo.PiePlotter" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" d:DesignHeight="480" d:DesignWidth="480" > <Grid x:Name="LayoutRoot"></Grid> </UserControl>
在實現餅圖以前,須要知道餅圖裏面的數據集合的,還須要用一個實體類PieDataItem來表示餅圖的數據項,有兩個屬性一個是表示圖形的數值Value屬性,另一個是表示餅圖片形塊的顏色Brush屬性。PieDataItem代碼以下:ide
PieDataItem.cs文件代碼 ------------------------------------------------------------------------------------------------------------------ using Windows.UI.Xaml.Media; namespace PieChartDemo { /// <summary> /// 餅圖數據實體 /// </summary> public class PieDataItem { public double Value { get; set; } public SolidColorBrush Brush { get; set; } } }
下面來實現餅圖控件加載的邏輯,在餅圖控件裏面還須要自定義一些相關的屬性,用來傳遞相關的參數。屬性HoleSize表示餅圖內圓的大小,按照比例來計算;屬性PieWidth表示餅圖的寬度。餅圖的數據集合是經過控件的數據上下文屬性DataContext屬性來傳遞,在初始化餅圖的時候須要把DataContext的數據讀取出來而後再建立PiePiece圖形。每一個PiePiece圖形都添加了Tap事件,用來實現當用戶點擊餅圖的時候,相應的某一塊回往外推出去。代碼以下所示:佈局
PiePlotter.xaml.cs文件代碼 ------------------------------------------------------------------------------------------------------------------ using System.Collections.Generic; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; namespace PieChartDemo { /// <summary> /// 餅圖控件 /// </summary> public partial class PiePlotter : UserControl { #region dependency properties // 註冊內圓大小屬性 public static readonly DependencyProperty HoleSizeProperty = DependencyProperty.Register("HoleSize", typeof(double), typeof(PiePlotter), new PropertyMetadata(0.0)); // 內圓的大小,按照比例來計算 public double HoleSize { get { return (double)GetValue(HoleSizeProperty); } set { SetValue(HoleSizeProperty, value); } } // 註冊餅圖寬度屬性 public static readonly DependencyProperty PieWidthProperty = DependencyProperty.Register("PieWidth", typeof(double), typeof(PiePlotter), new PropertyMetadata(0.0)); // 餅圖寬度 public double PieWidth { get { return (double)GetValue(PieWidthProperty); } set { SetValue(PieWidthProperty, value); } } #endregion // 餅圖的片形PiePiece的集合 private List<PiePiece> piePieces = new List<PiePiece>(); // 選中的當前餅圖的數據項 private PieDataItem CurrentItem; public PiePlotter() { InitializeComponent(); } // 初始化展現餅圖的方法 public void ShowPie() { // 獲取控件的數據上下文,轉化成數據集合 List<PieDataItem> myCollectionView = (List<PieDataItem>)this.DataContext; if (myCollectionView == null) return; // 半徑的大小 double halfWidth = PieWidth / 2; // 內圓半徑大小 double innerRadius = halfWidth * HoleSize; // 計算圖表數據的總和 double total = 0; foreach (PieDataItem item in myCollectionView) { total += item.Value; } // 經過PiePiece構建餅圖 LayoutRoot.Children.Clear(); piePieces.Clear(); double accumulativeAngle = 0; foreach (PieDataItem item in myCollectionView) { bool selectedItem = item == CurrentItem; double wedgeAngle = item.Value * 360 / total; // 根據數據來建立餅圖的每一塊圖形 PiePiece piece = new PiePiece() { Radius = halfWidth, InnerRadius = innerRadius, CentreX = halfWidth, CentreY = halfWidth, PushOut = (selectedItem ? 10.0 : 0), WedgeAngle = wedgeAngle, PieceValue = item.Value, RotationAngle = accumulativeAngle, Fill = item.Brush, Tag = item }; // 添加餅圖片形的點擊事件 piece.Tapped += piece_Tapped; piePieces.Add(piece); LayoutRoot.Children.Add(piece); accumulativeAngle += wedgeAngle; } } void piece_Tapped(object sender, TappedRoutedEventArgs e) { PiePiece piePiece = sender as PiePiece; CurrentItem = piePiece.Tag as PieDataItem; ShowPie(); } } }
在調用餅圖控件時須要引用控件所屬的空間,而後在XAML上調用餅圖控件。this
MainPage.xaml文件主要代碼 ------------------------------------------------------------------------------------------------------------------ ……省略若干代碼 xmlns:local="using:PieChartDemo" ……省略若干代碼 <local:PiePlotter x:Name="piePlotter" Width="400" Height="400" PieWidth="400" HoleSize="0.2"></local:PiePlotter>
在C#代碼裏面,對餅圖的DataContext屬性進行賦值餅圖的數據集合,而後調用ShowPie方法初始化餅圖。代碼以下:spa
MainPage.xaml.cs文件主要代碼 ------------------------------------------------------------------------------------------------------------------ public MainPage() { InitializeComponent(); List<PieDataItem> datas=new List<PieDataItem>(); datas.Add(new PieDataItem{ Value=30, Brush = new SolidColorBrush(Colors.Red)}); datas.Add(new PieDataItem { Value = 40, Brush = new SolidColorBrush(Colors.Orange) }); datas.Add(new PieDataItem { Value = 50, Brush = new SolidColorBrush(Colors.Blue) }); datas.Add(new PieDataItem { Value = 30, Brush = new SolidColorBrush(Colors.LightGray) }); datas.Add(new PieDataItem { Value = 20, Brush = new SolidColorBrush(Colors.Purple) }); datas.Add(new PieDataItem { Value = 40, Brush = new SolidColorBrush(Colors.Green) }); piePlotter.DataContext = datas; piePlotter.ShowPie(); }
本文來源於《深刻淺出Windows 10通用應用開發》code
源代碼下載:http://vdisk.weibo.com/u/2186322691orm
目錄:http://www.cnblogs.com/linzheng/p/5021428.html
歡迎關注個人微博@WP林政 微信公衆號:wp開發(號:wpkaifa)
Windows10/WP技術交流羣:284783431