效果

明细用Popup实现的,录gif时,Popup显示不出来,不知道为什么,所以静态图凑合看吧

大体思路

图表使用Arc+Popup实现

图表分为两部分,一是环形部分,一是标注的明细部分.

环形部分使用Arc图形表示.需要注意这个Arc是Blend里的图形.用Blend建项目的话可以直接用,使用VS建项目需要添加引用 Microsoft.Expression.Drawing 在引用管理器=>程序集=>扩展 下(前提是已经安装了Blend)

明细部分使用Popup控件,IsOpen属性绑定到Arc的IsMouseOver,也就是鼠标进入圆弧的时候,Popup就打开显示.

Popup内部一个椭圆控件当作背景,一个文字显示,一个折线虚线化当作指针

然后就是把Popup定位到对应圆弧合适的位置去显示(这里取的是圆弧的中间)

比较抱歉的是样式比较丑陋,忽略吧,重点看定位.

圆弧部分

Arc有两个重要的属性:StartAngle起始角度和EndAngle终结角度.这两个属性决定了圆弧占所在圆环的比例.

每一个数据项就对应一个圆弧,把所有圆弧都放到一个容器里,首尾相连

数据项的总和为100,那么所有圆弧也就组成一个完整的圆环.

Popup明细部分

明细部分分为四种,见图

椭圆

从图可知,作为背景的椭圆分为两种情况,小于180度,椭圆靠容器的右边对齐,大于180度,靠容器的左边对齐

也就是代码的这部分:

Ellipse ell = new Ellipse() { Fill = brush };
//中间点角度小于180 明细靠右显示 否则靠左显示
Grid detailGrid = new Grid() { Width = _popupHeight, HorizontalAlignment = HorizontalAlignment.Right };
if (middleAngle > )
{
detailGrid.HorizontalAlignment = HorizontalAlignment.Left;
}
折线

折线是分为四种,每一个角度区间都对应一种

private Polyline GetPopupPolyline(double middleAngle)
{
Polyline pLine = new Polyline() { Stroke = new SolidColorBrush(Color.FromRgb(, , )), StrokeDashArray = new DoubleCollection(new double[] { , }) };
double x1 = , y1 = ;
double x2 = , y2 = ;
double x3 = , y3 = ;
if (middleAngle > && middleAngle <= )
{
x1 = ; y1 = _popupHeight;
x2 = _popupWidth / ; y2 = _popupHeight;
x3 = _popupWidth * / ; y3 = _popupHeight / ;
}
if (middleAngle > && middleAngle <= )
{
x1 = ; y1 = ;
x2 = _popupWidth / ; y2 = ;
x3 = _popupWidth * / ; y3 = _popupHeight / ;
}
if (middleAngle > && middleAngle <= )
{
x1 = _popupWidth; y1 = ;
x2 = _popupWidth / ; y2 = ;
x3 = _popupWidth / ; y3 = _popupHeight / ;
}
if (middleAngle > && middleAngle <= )
{
x1 = _popupWidth; y1 = _popupHeight;
x2 = _popupWidth / ; y2 = _popupHeight;
x3 = _popupWidth / ; y3 = _popupHeight / ;
}
pLine.Points.Add(new Point(x1, y1));
pLine.Points.Add(new Point(x2, y2));
pLine.Points.Add(new Point(x3, y3));
return pLine;
}
Popup的定位

首先以0-90度为例,说明一些基本的东西,见图

首先Popup默认的位置,都是在它容器的左下方的,Popup的左上角和容器的左下角重合.

现在要做的是Popup标记为红点的位置,和圆环上标记为红点的位置重合.

先来回顾一下小时候学过的公式:

1.直角三角形 a=r*sinA

2.勾股定理 c^2=a^2+b^2 b=Sqrt(c^2-a^2)

上图的直角三角形,角A的对边为a,临边为b,斜边为c.显然c边于圆的半径r相等.注意:因为圆弧是有厚度的,所以取r的时候要减去二分之一的圆弧厚度.

角A是可以通过90度减去圆弧的对应的角度求出来的,也就是sinA的值已知了,那么就可以求出a和b的长度,然后就可以去移动Popup了

一.0-90度

X轴:1.向右移动二分之一个容器的width 2.向右移动一个b的距离

Y轴:1.向上移动二分之一个容器的height 2.向上移动一个Popup的height 3.向上移动一个a的距离

二.90-180度

X轴:1.向右移动二分之一个容器的width 2.向右移动一个a的距离

Y轴:1.上移二分之一个圆弧的Thickness,以保证标记的起点在圆弧的中央 2.上移一个(r-b)的距离

三.180-270度

X轴:1.向左移动一个b的距离

Y轴:1.上移二分之一个圆弧的Thickness,以保证标记的起点在圆弧的中央 2.上移一个(r-a)的距离

四.270-360度

X轴:1.向左移动一个a的距离

Y轴:1.向上移动二分之一个容器的height 2.向上移动一个Popup的height 3.向上移动一个b的距离

代码部分

private Popup GetPopup(double middleAngle)
{
/*
* 生成popup
* 设置popup的offset 让标记线的起点 对应到圆弧的中间点
*/
Popup popup = new Popup() { Width = _popupWidth, Height = _popupHeight, AllowsTransparency = true, IsHitTestVisible = false };
//直角三角形 a=r*sinA 勾股定理 c^2=a^2+b^2 b=Sqrt(c^2-a^2)
double r = _chartSize / - _arcThickness / ;
double offsetX = , offsetY = ;
if (middleAngle > && middleAngle <= )
{
double sinA = Math.Sin(Math.PI * ( - middleAngle) / );
double a = r * sinA;
double c = r;
double b = Math.Sqrt(c * c - a * a);
offsetX = _chartSize / + b;
offsetY = -(_chartSize / + _popupHeight + a);
}
if (middleAngle > && middleAngle <= )
{
double sinA = Math.Sin(Math.PI * ( - middleAngle) / );
double a = r * sinA;
double c = r;
double b = Math.Sqrt(c * c - a * a);
offsetX = _chartSize / + a;
offsetY = -(_arcThickness / + (r - b));
}
if (middleAngle > && middleAngle <= )
{
double sinA = Math.Sin(Math.PI * ( - middleAngle) / );
double a = r * sinA;
double c = r;
double b = Math.Sqrt(c * c - a * a);
offsetX = -_popupWidth + (r - b) + _arcThickness / ;
offsetY = -(_arcThickness / + (r - a));
}
if (middleAngle > && middleAngle <= )
{
double sinA = Math.Sin(Math.PI * ( - middleAngle) / );
double a = r * sinA;
double c = r;
double b = Math.Sqrt(c * c - a * a);
offsetX = -_popupWidth + (r - a) + _arcThickness / ;
offsetY = -(_chartSize / + _popupHeight + b);
}
popup.HorizontalOffset = offsetX;
popup.VerticalOffset = offsetY; return popup;
}

差不多主要的就是这些了.到这.画图有点累.

源码下载:ArcChart.zip

2017-07-13更新:

昨天刚发了博客,今天就发现了bug,真尴尬.180-270度和270-360度的算法有问题,由于例子选用尺寸的问题,早时没有发现.

正确的算法:

180-270度:

X轴:1.向左移动一个Popup的Width 2.向右移动一个(r-b)的距离 3.向右移动二分之一个ArcThickness的距离

Y轴不变

270-360度:

X轴:1.向左移动一个Popup的Width 2.向右移动一个(r-a)的距离 3.向右移动二分之一个ArcThickness的距离

Y轴不变

源码已更新,欢迎重新下载

WPF制作带明细的环形图表的更多相关文章

  1. WPF制作带明细的环形图标

    效果 明细用Popup实现的,录gif时,Popup显示不出来,不知道为什么,所以静态图凑合看吧 大体思路 图表使用Arc+Popup实现 图表分为两部分,一是环形部分,一是标注的明细部分. 环形部分 ...

  2. WPF原生环形图表

    原文:WPF原生环形图表 版权声明:欢迎转载.转载请注明出处,谢谢 https://blog.csdn.net/wzcool273509239/article/details/56480963 主要利 ...

  3. WPF中制作带中国农历的万年历

    原文:WPF中制作带中国农历的万年历 本例应用.net 2.0中的ChineseLunisolarCalendar类,制作出带中国农历的万年历.  先看看效果图片(已缩小,原始图片为:http://p ...

  4. 在线报表设计实战系列 – 制作多Y轴组合图表(8)

    葡萄城报表是一套强大的报表开发和系统搭建工具,既能与您开发的报表软件项目紧密集成,也可独立部署运行,支持多数据源,具有无编码.灵活.稳定等特性,可以帮您快速搭建专业的报表软件系统,实现各类报表的设计. ...

  5. WPF 自带Datagrid编辑后无法更新数据源的问题

    原文  WPF 自带Datagrid编辑后无法更新数据源的问题 解决办法: 在列的绑定属性里加上UpdateSourceTrigger,示例XAML如下 <DataGrid Grid.Row=& ...

  6. WPF制作的小型笔记本

    WPF制作的小型笔记本-仿有道云笔记 楼主所在的公司不允许下载外部资源, 不允许私自安装应用程序, 平时记录东西都是用记事本,时间久了很难找到以前记的东西. 平时在家都用有道笔记, 因此就模仿着做了一 ...

  7. WPF制作表示透明区域的马赛克画刷

    最近在用WPF制作一款软件,需要像ps一样表示透明区域,于是制作了一个马赛克背景的style.实现比较简单,那么过程和思路就不表了,直接上代码 <DrawingBrush TileMode=&q ...

  8. WPF制作的小时钟

    原文:WPF制作的小时钟 周末无事, 看到WEB QQ上的小时钟挺可爱的, 于是寻思着用WPF模仿着做一个. 先看下WEB QQ的图: 打开VS, 开始动工. 建立好项目后, 面对一个空荡荡的页面, ...

  9. [Swift通天遁地]三、手势与图表-(13)制作美观简介的滚动图表:折线图表、面积图表、柱形图表、散点图表

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

随机推荐

  1. EF Core扩展工具记录

    Microsoft.EntityFrameworkCore.AutoHistory Microsoft.EntityFrameworkCore 的一个插件,支持自动记录数据更改历史记录. GitHub ...

  2. iframe无刷新跨域上传文件并获得返回值

    原文:http://geeksun.iteye.com/blog/1070607 需求:从S平台上传文件到R平台,上传成功后R平台返回给S平台一个值,S平台是在一个页面弹出的浮窗里上传文件,所以不能用 ...

  3. 西安80投影坐标系转WGS84地理坐标系如何求七参数

    需求:西安80投影坐标系(平面坐标)转为WGS84地理坐标系(球面坐标) 这其中涉及的问题主要有以下两点: 1.一个是投影坐标系,一个是地理坐标系,而七参数指的是两个地理坐标系之间的转换,因此需要把投 ...

  4. 5、爬虫之scrapy框架

    一 scrapy框架简介 1 介绍 Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速.简单.可扩展的方式从网站中提取所需的数据.但目前Sc ...

  5. Swift中的反射

    原文:http://www.cocoachina.com/applenews/devnews/2014/0623/8923.html Swift 事实上是支持反射的.只是功能略弱. 本文介绍主要的反射 ...

  6. XGBOOST应用及调参示例

    该示例所用的数据可从该链接下载,提取码为3y90,数据说明可参考该网页.该示例的“模型调参”这一部分引用了这篇博客的步骤. 数据前处理 导入数据 import pandas as pd import ...

  7. 从Excel表导入数据到Table

    步骤: 1.写第一行SQL,(本sql对应的是oracle数据库) ="INSERT INTO TD_PROMOTION_RATE VALUES("&A3&&quo ...

  8. 深入浅出的webpack4构建工具--webpack4+vue+vuex+mock模拟后台数据(十九)

    mock的官网文档 mock官网 关于mockjs的优点,官网这样描述它:1)可以前后端分离.2)增加单元测试的真实性(通过随机数据,模拟各种场景).3)开发无侵入(不需要修改既有代码,就可以拦截 A ...

  9. http协议以及get和post请求

    HTTP协议是网络传输信息的一种规范. 就好比两个人之间的交流,甲只会讲英语,乙只会说汉语,结果是他们必然无法开怀畅谈. HTTP协议也类   GET 请求获取 Request-URI 所标识的资源 ...

  10. 【ZOJ 3200】Police and Thief

    ZOJ 3200 首先我写了个高斯消元,但是消出来了一些奇怪的东西,我就放弃了... 然后只好考虑dp:\(dp[i][j][k]\)表示走到了第i步,到了\((j,k)\)这个节点的概率. 那么答案 ...