[深入浅出Windows 10]实现饼图控件
13.2 实现饼图控件
上一小节讲解了动态生成折线图和区域图,对于简单的图形这样通过C#代码来生成的方式是很方便的,但是当我们的图表要实现更加复杂的逻辑的时候,这种动态生成的方式就显得力不从心了,那就需要利用控件封装的方式来实现更加强大的图表控件功能。这一小节将来讲解怎样去用封装控件的方式去实现图表,用一个饼图控件作为例子进行分析讲解。
13.2.1 自定义饼图片形形状
饼图其实就是把一个圆形分成若干块,每一块代表着一个类别的数据,可以把这每一块的图形看作是饼图片形形状。要实现一个饼图控件,首先需要做的就是要实现饼图片形形状,在第4章里面讲解了实现如何实现自定义的形状,饼图片形形状也可以通过这种方式来实现。饼图片形形状有一些重要的属性,如饼图半径Radius,内圆半径InnerRadius,旋转角度RotationAngle,片形角度WedgeAngle,点innerArcStartPoint,点innerArcEndPoint,点outerArcStartPoint和点outerArcEndPoint等,这些属性的含义如图13.5所示。要绘制出这个饼图片形形状需要计算出4个点的坐标(点innerArcStartPoint,点innerArcEndPoint,点outerArcStartPoint和点outerArcEndPoint),这4的点的坐标需要通过半径和角度相关的属性计算出来。计算出这4个点的坐标的坐标之后,然后通过这4个点创建一个Path图形,这个Path图形由两条直线和两条弧线组成,形成了一个饼图片形形状。通过这种方式不仅仅把这个饼图片形形状创建好了,连这个图形在整个饼图的位置也设置好了。代码如下所示。
代码清单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(, );
} private double lastWidth = ;
private double lastHeight = ;
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 > )
{
Point offset = ComputeCartesianCoordinate(RotationAngle + WedgeAngle / , 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 - );
double x = radius * Math.Cos(angleRad);
double y = radius * Math.Sin(angleRad);
return new Point(x, y);
}
}
}
13.2.2 封装饼图控件
创建好了PiePiece形状之后,下面就要开始创建利用PiePiece形状来创建饼图控件了。创建饼图控件是通过UserControl控件来实现,UserControl控件的XAML代码里面只有一个Grid面板,是用来加载PiePiece形状来组成饼图。XAML代码如下所示:
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代码如下:
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 / ;
// 内圆半径大小
double innerRadius = halfWidth * HoleSize;
// 计算图表数据的总和
double total = ;
foreach (PieDataItem item in myCollectionView)
{
total += item.Value;
}
// 通过PiePiece构建饼图
LayoutRoot.Children.Clear();
piePieces.Clear();
double accumulativeAngle = ;
foreach (PieDataItem item in myCollectionView)
{
bool selectedItem = item == CurrentItem;
double wedgeAngle = item.Value * / total;
// 根据数据来创建饼图的每一块图形
PiePiece piece = new PiePiece()
{
Radius = halfWidth,
InnerRadius = innerRadius,
CentreX = halfWidth,
CentreY = halfWidth,
PushOut = (selectedItem ? 10.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上调用饼图控件。
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方法初始化饼图。代码如下:
MainPage.xaml.cs文件主要代码
------------------------------------------------------------------------------------------------------------------
public MainPage()
{
InitializeComponent();
List<PieDataItem> datas=new List<PieDataItem>();
datas.Add(new PieDataItem{ Value=, Brush = new SolidColorBrush(Colors.Red)});
datas.Add(new PieDataItem { Value = , Brush = new SolidColorBrush(Colors.Orange) });
datas.Add(new PieDataItem { Value = , Brush = new SolidColorBrush(Colors.Blue) });
datas.Add(new PieDataItem { Value = , Brush = new SolidColorBrush(Colors.LightGray) });
datas.Add(new PieDataItem { Value = , Brush = new SolidColorBrush(Colors.Purple) });
datas.Add(new PieDataItem { Value = , Brush = new SolidColorBrush(Colors.Green) });
piePlotter.DataContext = datas;
piePlotter.ShowPie();
}
源代码下载:http://vdisk.weibo.com/u/2186322691
目录:http://www.cnblogs.com/linzheng/p/5021428.html
欢迎关注我的微博@WP林政 微信公众号:wp开发(号:wpkaifa)
Windows10/WP技术交流群:284783431
[深入浅出Windows 10]实现饼图控件的更多相关文章
- [深入浅出Windows 10]分屏控件(SplitView)
4.18 分屏控件(SplitView) 分屏控件(SplitView)是Windows 10新增的控件类型,也是Windows 10通用应用程序主推的交互控件,通常和一个汉堡按钮搭配作为一种抽屉式菜 ...
- [深入浅出Windows 10]QuickCharts图表控件库解析
13.4 QuickCharts图表控件库解析 QuickCharts图表控件是Amcharts公司提供的一个开源的图表控件库,这个控件库支持WPF.Silverlight.和Windows等 ...
- 《深入浅出Windows 10通用应用开发》
<深入浅出Windows 10通用应用开发>采用Windows 10的SDK进行重新改版,整合了<深入浅出Windows Phone 8.1应用开发>和<深入解析 ...
- Windows高DPI系列控件(一) - 饼图
目录 一.醉一醉 二.效果展示 三.高DPI适配 1.高DPI框架运作 2.适配高DPI 3.适配饼图 四.相关文章 原文链接:Windos高DPI系列控件(一) - 饼图 一.醉一醉 眨眼功夫,20 ...
- [深入浅出Windows 10]布局原理
5.2 布局原理 很多时候在编写程序界面的时候都会忽略了应用布局的重要性,仅仅只是把布局看作是对UI元素的排列,只要能实现布局的效果就可以了,但是在实际的产品开发中这是远远不够的,你可能面临要实现的布 ...
- [深入浅出Windows 10]不同平台设备的适配
2.3 不同平台设备的适配 Windows 10通用应用程序针对特定的平台还会有一个子API的集合,当我们要使用到某个平台的特定API的时候(比如手机相机硬件按钮触发事件),这时候就需要调用特定平台的 ...
- [深入浅出Windows 10]应用实战:Bing在线壁纸
本章介绍一个使用Bing搜索引擎背景图接口实现的一个应用——Bing在线壁纸,讲解如何使用网络的接口来实现一个壁纸下载,壁纸列表展示和网络请求封装的内容.通过该例子我们可以学习到如何使用网络编程的知识 ...
- Windows高DPI系列控件(二) - 柱状图
目录 一.QCP 二.效果展示 三.高DPI适配 1.自定义柱状图 2.新的柱状图 3.测试代码 四.相关文章 原文链接:Windows高DPI系列控件(二) - 柱状图 一.QCP QCP全称QCu ...
- 深入Windows窗体原理及控件重绘技巧
之前有学MFC的同学告诉我觉得Windows的控件重绘难以理解,就算重绘成功了还是有些地方不明白,我觉得可能很多人都有这样的问题,在这里我从Windows窗体的最基本原理来讲解,如果你有类似的疑惑希望 ...
随机推荐
- Redis笔记(六)Redis的消息通知
Redis的消息通知可以使用List类型的LPUSH和RPOP(左进右出),当然更方便的是直接使用Redis的Pub/Sub(发布/订阅)模式. >>使用List实现队列 使用列表类型的L ...
- Android中Thread和Service的区别zz
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位.可以用 Thread 来执行一些异步的操作. 2). Service:Service 是android的一种机制,当 ...
- Linux(CentOS)常用操作指令(一)
基本指令集合 1.查看CentOS版本信息 cat /proc/version cat /etc/redhat-release 2.查看安全日志文件信息 tail -f /var/log/secure ...
- <转>ORA-06413 连接未打开错误
ORA-06413 Connection not open.Cause: Unable to establish connection.Action: Use diagnostic procedure ...
- oracle限制ip訪問
我這oracle版本為11.2.0.4,裝的GRID,所以在grid用戶下編輯sqlnet.ora 1.cd /grid/product/11.2.0/network/admin 2.編輯sqlne ...
- 2-01SQL的概述
SQL: Structured Query Languaage:结构化查询语言. 美国国家标准局ANSI. 国际标准化组织. T-SQL: Transact-SQL. T-SQL是SQL的扩展集. 对 ...
- 解决因为I_JOB_NEXT问题导致job执行不正常,不停生成trace文件问题
今天同事说有个项目生产环境的目录老是满.查看了一下bdump目录,发现确实是平均1分钟生成一个8M左右的trace文件.查询了一下alert日志,发现是个job的报错引起的.具体查看了一下trace文 ...
- Git提交基本流程
在无其他分支,大家都向同一分支master分支提交代码的情况下: 1.查看本地对代码的修改情况,即可以被提交的修改记录 git status 其中被修改过的文件标识为modified,删除的文件del ...
- JQuery初探
[TOC] jquery 通过jQuery,您可以选取(查询,query)HTML元素,并对它们执行"操作"(actions). jQuery 使用的语法是 XPath 与 CSS ...
- github 使用网址
[Github教程]史上最全github使用方法:github入门到精通 http://blog.csdn.net/hcbbt/article/details/11651229 githup的使用 h ...