你的位置:首页 > ASP.net教程

[ASP.net教程]WPF实现的简单饼图


简介

前段时间帮一个同事的忙,利用WPF实现的一个简单饼图,仅能看饼图的比例,无文字查看功能。效果图如下:

Snap1Snap2

Snap3Snap4

用法:

var sectorParts = new List<SectorPart>();      sectorParts.Add(new SectorPart(90, Brushes.Red));      sectorParts.Add(new SectorPart(30, Brushes.Green));      sectorParts.Add(new SectorPart(120, Brushes.GreenYellow));      sectorParts.Add(new SectorPart(70, Brushes.HotPink));      sectorParts.Add(new SectorPart(50, Brushes.Yellow));      var ringParts = new List<RingPart>();      ringParts.Add(new RingPart(40, 20, 40, 20, Brushes.White));      var shapes = PieChartDrawer.GetEllipsePieChartShapes(midPoint, 180, 90, 30, sectorParts, ringParts);      foreach (var shape in shapes)      {        GrdPie.Children.Add(shape);      }

设置好饼图相关的信息,获取其各个组成部分,再将其添加到容器中。

原理

可以看出简介中的图由一系列中扇形和环组成,计算出扇形和环的形状就可以完成饼图的绘制了。

扇形

一个扇形由两边和一条弧组成,扇形的关键就在已知圆周的圆形和半径,以及扇形的边绕Y轴正向旋转的角度,如何求出扇形在圆周上的点。

在才开始我走了不少弯路,利用矩阵做了许多运算,结果都不对。后面灵机一动,发现不用那么麻烦。直接把圆平移至原点,计算出相应扇形在圆周上的点,再将其平移回来即可。代码如下:

/// <summary>    /// 获取圆周上指定角度的点坐标    /// </summary>    /// <param name="center">圆心</param>    /// <param name="radius">半径</param>    /// <param name="angle">角度,从0到360度,以正北方向为0度,顺时针旋转角度增加</param>    /// <returns>在圆周上旋转角度后的坐标</returns>    public static Point GetCirclePoint(this Point center, double radius, double angle)    {      // 圆心平移到原点后0度所对应的向量      var zeroAngleVector = new Vector(0, radius);      // 旋转角度所对应的矩阵      var rotateMatrix = new Matrix();      rotateMatrix.Rotate(180 + angle);      // 因旋转的中心点在原点,最后需要平移到实际坐标上      return (zeroAngleVector * rotateMatrix) + center;    }

有了圆的计算方法,椭圆的也就水到渠成了,因为椭圆相当于圆的拉伸或者收缩。

/// <summary>    /// 获取椭圆上指定角度的点坐标    /// </summary>    /// <param name="center">椭圆两焦点的中点</param>    /// <param name="radiusX">长轴</param>    /// <param name="radiusY">短轴</param>    /// <param name="angle">角度,从0到360度,以正北方向为0度,顺时针旋转角度增加</param>    /// <returns>在椭圆上旋转角度后的坐标</returns>    public static Point GetEllipsePoint(this Point center, double radiusX, double radiusY, double angle)    {      // 半径为X半轴的圆圆心平移到原点后0度所对应的向量      var circleZeroAnglePoint = new Vector(0, radiusX);      // 旋转角度所对应的矩阵      var rotateMatrix = new Matrix();      rotateMatrix.Rotate(180 + angle);      // 圆旋转角度后的坐标      var circlePoint = circleZeroAnglePoint * rotateMatrix;      // 将圆拉伸椭圆后的坐标      var ellpseOrigin = new Point(circlePoint.X, circlePoint.Y * radiusY / radiusX);      // 将坐标平移至实际坐标      return (Vector)ellpseOrigin + center;    }

环可由两个椭圆计算出来。较简单,就不多说了。

代码

扇形的定义

namespace PieChartTest{  using System.Windows.Media;  /// <summary>  /// 扇形  /// </summary>  public class SectorPart  {    /// <summary>    /// 构造函数    /// </summary>    /// <param name="spanAngle">跨越角度</param>    /// <param name="fillBrush">填充画刷</param>    public SectorPart(double spanAngle, Brush fillBrush)    {      this.SpanAngle = spanAngle;      this.FillBrush = fillBrush;    }    /// <summary>    /// 跨越角度,单位为角度,取值范围为0到360    /// </summary>    public double SpanAngle { get; set; }    /// <summary>    /// 填充画刷    /// </summary>    public Brush FillBrush { get; set; }  }}

环的定义

namespace PieChartTest{  using System.Windows.Media;  /// <summary>  /// 环  /// </summary>  public class RingPart  {    /// <summary>    /// 构造函数,构造里外均为圆的圆环    /// </summary>    /// <param name="radius">里圆半径</param>    /// <param name="spanRadius">里外圆半径差</param>    /// <param name="fillBrush">填充画刷</param>    public RingPart(double radius, double spanRadius, Brush fillBrush)    {      this.RadiusX = radius;      this.RadiusY = radius;      this.SpanRadiusX = spanRadius;      this.SpanRadiusY = spanRadius;      this.FillBrush = fillBrush;    }    /// <summary>    /// 构造函数    /// </summary>    /// <param name="radiusX">里边椭圆的长轴</param>    /// <param name="radiusY">里边椭圆的短轴</param>    /// <param name="spanRadiusX">里外椭圆的长轴差</param>    /// <param name="spanRadiusY">里外椭圆的短轴差</param>    /// <param name="fillBrush">填充画刷</param>    public RingPart(double radiusX, double radiusY, double spanRadiusX, double spanRadiusY, Brush fillBrush)    {      this.RadiusX = radiusX;      this.RadiusY = radiusY;      this.SpanRadiusX = spanRadiusX;      this.SpanRadiusY = spanRadiusY;      this.FillBrush = fillBrush;    }    /// <summary>    /// 长轴    /// </summary>    public double RadiusX { get; set; }    /// <summary>    /// 短轴    /// </summary>    public double RadiusY { get; set; }    /// <summary>    /// 长轴跨越的距离    /// </summary>    public double SpanRadiusX { get; set; }    /// <summary>    /// 短轴跨越的距离    /// </summary>    public double SpanRadiusY { get; set; }    /// <summary>    /// 填充画刷    /// </summary>    public Brush FillBrush { get; set; }  }}

饼图的绘制类

namespace PieChartTest{  using System.Collections.Generic;  using System.Windows;  using System.Windows.Media;  using System.Windows.Shapes;  /// <summary>  /// 饼图的绘制类  /// </summary>  public static class PieChartDrawer  {    /// <summary>    /// 获取饼图的形状列表    /// </summary>    /// <param name="center">圆心</param>    /// <param name="radius">圆的半径</param>    /// <param name="offsetAngle">偏移角度,即第一个扇形开始的角度</param>    /// <param name="sectorParts">扇形列表,扇形列表的SpanAngle之和应为360度</param>    /// <param name="ringParts">环列表</param>    /// <returns>构成饼图的形状列表</returns>    public static IEnumerable<Shape> GetPieChartShapes(Point center, double radius, double offsetAngle, IEnumerable<SectorPart> sectorParts, IEnumerable<RingPart> ringParts)    {      return GetEllipsePieChartShapes(center, radius, radius, offsetAngle, sectorParts, ringParts);    }    /// <summary>    /// 获取椭圆形状的饼图的形状列表    /// </summary>    /// <param name="center">椭圆两个焦点的中点</param>    /// <param name="radiusX">椭圆的长轴</param>    /// <param name="radiusY">椭圆的短轴</param>    /// <param name="offsetAngle">偏移角度,即第一个扇形开始的角度</param>    /// <param name="sectorParts">扇形列表,扇形列表的SpanAngle之和应为360度</param>    /// <param name="ringParts">环列表</param>    /// <returns>构成饼图的形状列表</returns>    public static IEnumerable<Shape> GetEllipsePieChartShapes(Point center, double radiusX, double radiusY, double offsetAngle, IEnumerable<SectorPart> sectorParts, IEnumerable<RingPart> ringParts)    {      var shapes = new List<Shape>();      double startAngle = offsetAngle;      foreach (var sectorPart in sectorParts)      {        // 扇形顺时针方向在椭圆上的第一个点        var firstPoint = center.GetEllipsePoint(radiusX, radiusY, startAngle);        startAngle += sectorPart.SpanAngle;        // 扇形顺时针方向在椭圆上的第二个点        var secondPoint = center.GetEllipsePoint(radiusX, radiusY, startAngle);        var sectorFigure = new PathFigure { StartPoint = center };        // 添加中点到第一个点的弦        sectorFigure.Segments.Add(new LineSegment(firstPoint, false));        // 添加第一个点和第二个点之间的弧        sectorFigure.Segments.Add(          new ArcSegment(secondPoint, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, false));        var sectorGeometry = new PathGeometry();        sectorGeometry.Figures.Add(sectorFigure);        var sectorPath = new Path { Data = sectorGeometry, Fill = sectorPart.FillBrush };        shapes.Add(sectorPath);      }      var ringShapes = GetRingShapes(center, ringParts);      shapes.AddRange(ringShapes);      return shapes;    }    /// <summary>    /// 获取环的形状列表    /// </summary>    /// <param name="center">中心点,为圆表示圆形,为椭圆表示椭圆两个焦点的中点</param>    /// <param name="ringParts">环列表</param>    /// <returns>环的形状列表</returns>    private static IEnumerable<Shape> GetRingShapes(Point center, IEnumerable<RingPart> ringParts)    {      var shapes = new List<Shape>();      foreach (var ringPart in ringParts)      {        var innerEllipse = new EllipseGeometry(center, ringPart.RadiusX, ringPart.RadiusY);        var outterEllipse = new EllipseGeometry(center, ringPart.RadiusX + ringPart.SpanRadiusX, ringPart.RadiusY + ringPart.SpanRadiusY);        // 根据里外椭圆求出圆环的形状        var ringGeometry = new CombinedGeometry(GeometryCombineMode.Xor, innerEllipse, outterEllipse);        var ringPath = new Path        {          Data = ringGeometry,          Fill = ringPart.FillBrush        };        shapes.Add(ringPath);      }      return shapes;    }  }}




深圳去日本旅游报价办理日本旅游签证跟团日本旅游需要多少钱几月份去日本旅游最好什么时候去日本旅游最便宜洛阳白云山祥云宾馆价格?白云山祥云宾馆住一晚多少钱? 洛阳白云山农家宾馆推荐?白云山农家宾馆一晚多少钱? 洛阳白云山景区宾馆介绍?白云山景区宾馆多少钱? 洛阳白云山宾馆哪家好?白云山风景区住宿哪里好? 澳门玫瑰圣母堂好玩吗?怎么去? 香港哪里有影视城吗?具体地址在那里? 去澳门那里买戒指好?澳门有哪些珠宝店? 在澳门葡京娱乐场要门票吗?要的话多少钱? 马尔代夫维斯瑞岛Viceroy Maldives Resort 的房间设施如何? 马尔代夫最近天气怎么样? 泰国哪个时候最适合旅游?什么时候是最佳的旅游季节? 马尔代夫维斯瑞岛Viceroy Maldives Resort 是最新开发的岛屿吗? 春色满园关不住三里洋渡的油菜花 深圳小梅沙三八节有什么活动?三八妇女节小梅沙海洋世界活动介绍? 2015三八节小梅沙海洋世界女士免费吗?深圳小梅沙三八妇女节门票价格? 2015三水荷花世界什么时候去最好?3月佛山三水荷花世界有什么花开了? TAJA336K004RNJ Datasheet TAJA336K004RNJ Datasheet 08055C153KAZ2A Datasheet 08055C153KAZ2A Datasheet TAJA336K006RNJ Datasheet TAJA336K006RNJ Datasheet 贵州省旅游景点 贵州省旅游景点 贵州省旅游景点 鼓浪屿 攻略 鼓浪屿 攻略 鼓浪屿 攻略 台湾土特产 台湾土特产 台湾土特产