WPF 实现带明细的环形图表

本文经原作者授权以原创方式二次分享,欢迎转载、分享。

原文作者:普通的地球人

原文地址:https://www.cnblogs.com/tsliwei/p/7155616.html

Github地址:https://github.com/WPFDevelopersOrg/WPFDevelopers.Charts

大体思路

  • 图表使用Arc+Popup实现;

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

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

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

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

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

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

f14728989b704f5003085b1c62bae3e8.png

圆弧部分

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

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

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

Popup明细部分

明细部分分为四种,见图;

1aa8b21f1afcf62f680bd7acddc81985.png

椭圆

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

  • 也就是代码的这部分;

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

折线

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

private Polyline GetPopupPolyline(double middleAngle)
{Polyline pLine = new Polyline() { Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0)), StrokeDashArray = new DoubleCollection(new double[] { 5, 2 }) };double x1 = 0, y1 = 0;double x2 = 0, y2 = 0;double x3 = 0, y3 = 0;if (middleAngle > 0 && middleAngle <= 90){x1 = 0;y1 = _popupHeight;x2 = _popupWidth / 2;y2 = _popupHeight;x3 = _popupWidth * 3 / 4;y3 = _popupHeight / 2;}if (middleAngle > 90 && middleAngle <= 180){x1 = 0;y1 = 0;x2 = _popupWidth / 2;y2 = 0;x3 = _popupWidth * 3 / 4;y3 = _popupHeight / 2;}if (middleAngle > 180 && middleAngle <= 270){x1 = _popupWidth;y1 = 0;x2 = _popupWidth / 2;y2 = 0;x3 = _popupWidth / 4;y3 = _popupHeight / 2;}if (middleAngle > 270 && middleAngle <= 360){x1 = _popupWidth;y1 = _popupHeight;x2 = _popupWidth / 2;y2 = _popupHeight;x3 = _popupWidth / 4;y3 = _popupHeight / 2;}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度为例,说明一些基本的东西,见图;

2a0ad0e205b7628ae95551c4deba9a3f.png
  • 首先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的值已知了,那么就可以求出ab的长度,然后就可以去移动Popup了;

1)0-90

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

34135a4462613075eaa5b31bebd73c94.png
  • Y轴
    1、向上移动二分之一个容器的height
    2、向上移动一个Popupheight
    3、向上移动一个a的距离;

5c048ef4cfe5eafd2457d7d398c17044.png

2)90-180

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

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

0a9d5cc0a5977adb15b499cde8d83b41.png

3)180-270

  • X轴
    1、向左移动一个b的距离;

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

41b28bb5f901d011af184e5b93103c29.png

4)270-360

  • X轴
    1、向左移动一个a的距离;

b05d721c2ec6d90c023535e17acd3987.png
  • Y轴
    1、向上移动二分之一个容器的height
    2、向上移动一个Popupheight
    3、向上移动一个b的距离;

b2cbf23cd0606edd8234391c20935017.png
  • 代码如下;

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 / 2 - _arcThickness / 2;double offsetX = 0, offsetY = 0;if (middleAngle > 0 && middleAngle <= 90){double sinA = Math.Sin(Math.PI * (90 - middleAngle) / 180);double a = r * sinA;double c = r;double b = Math.Sqrt(c * c - a * a);offsetX = _chartSize / 2 + b;offsetY = -(_chartSize / 2 + _popupHeight + a);}if (middleAngle > 90 && middleAngle <= 180){double sinA = Math.Sin(Math.PI * (180 - middleAngle) / 180);double a = r * sinA;double c = r;double b = Math.Sqrt(c * c - a * a);offsetX = _chartSize / 2 + a;offsetY = -(_arcThickness / 2 + (r - b));}if (middleAngle > 180 && middleAngle <= 270){double sinA = Math.Sin(Math.PI * (270 - middleAngle) / 180);double a = r * sinA;double c = r;double b = Math.Sqrt(c * c - a * a);offsetX = -_popupWidth + (r - b) + _arcThickness / 2;offsetY = -(_arcThickness / 2 + (r - a));}if (middleAngle > 270 && middleAngle <= 360){double sinA = Math.Sin(Math.PI * (360 - middleAngle) / 180);double a = r * sinA;double c = r;double b = Math.Sqrt(c * c - a * a);offsetX = -_popupWidth + (r - a) + _arcThickness / 2;offsetY = -(_chartSize / 2 + _popupHeight + b);}popup.HorizontalOffset = offsetX;popup.VerticalOffset = offsetY;return popup;
}

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

e85968af144e76fc5ffad3a1231ffef8.gif

源码1[1]Gtihub[2]Gitee[3]

参考资料

[1]

源码: https://files.cnblogs.com/files/tsliwei/ArcChart.zip

[2]

Gtihub: https://github.com/WPFDevelopersOrg/WPFDevelopers.Charts

[3]

gitee: https://gitee.com/WPFDevelopersOrg/WPFDevelopers.Charts

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/286386.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

几行代码搞定树形文本转XML和JSON

由于需要将百度脑图的内容导出为xml或者json格式&#xff0c;发现百度脑图只能导出为树形文本&#xff0c;所以就写了个小应用给编辑用。/// <summary>/// 树形文本转xml/// </summary>/// <param name"txt"></param>/// <returns><…

《iVX 高仿美团APP制作移动端完整项目》01 标题需求分析思路及制作流程

点击整个专栏查看其它系列文章 &#xff08;系列文章更新中…&#xff09;&#xff1a;《iVX 高仿美团APP制作移动端完整项目》 项目界面预览&#xff1a; 一、创建项目 首先打开在线编辑器地址&#xff1a;https://editor.ivx.cn/ 随后登录帐号后创建一个相对应用项目&…

zepto源码研究 - ajax.js($.ajaxJSONP 的分析)

简要&#xff1a;jsonp是一种服务器和客户端信息传递方式&#xff0c;一般是利用script元素赋值src来发起请求。一般凡是带有src属性的元素发起的请求都是可以跨域的。 那么jsonp是如何获取服务器的数据的呢&#xff1f; jsonp先将指定的一个函数名作为url后面的参数传递到服务…

创建 overlay 网络 - 每天5分钟玩转 Docker 容器技术(50)

上一节我们搭建好实验环境&#xff0c;配置并运行了consul&#xff0c;今天开始创建 overlay 网络。 在 host1 中创建 overlay 网络 ov_net1&#xff1a; -d overlay 指定 driver 为 overaly。 docker network ls 查看当前网络&#xff1a; 注意到 ov_net1 的 SCOPE 为 global&…

Js+Css 控制iframe内容自动缩放

竖屏横屏效果<div class"h5box"> <iframe src"http://player.youku.com/embed/XMTI4MjU5OTA3Mg" frameborder"0" width"1280px" height"720px"></iframe> </div><script type"tex…

西北冬日的校园很静谧,却不失韵味,因为有我们美好的青春!

冬日的校园&#xff0c;从枯黄的落叶开始。。。 落叶与栅栏情深。 冬日的篮球场上&#xff0c;不乏挥汗如雨的你&#xff0c;因为你是梦想与自由的追逐者&#xff0c;你可以战胜自己的懒惰。 我们的测量实训场&#xff0c;英雄的用武之地。 测桩&#xff1a;测量的控制点&#…

ps、top 、free查看用户资源信息

查看root用户的进程信息。 运行命令&#xff1a; ps -u root 查看oracle用户的进程信息。 运行命令&#xff1a; ps -u oracle 若查看现在的资源占用情况&#xff0c;如何呢&#xff1f; 运行命令&#xff1a; top 可以很详细的查看各个进程的运行情况。 若查看内存使用情…

《iVX 高仿美团APP制作移动端完整项目》02 搜索、搜索提示及类别需求分析思路及制作流程

点击整个专栏查看其它系列文章 &#xff08;系列文章更新中…&#xff09;&#xff1a;《iVX 高仿美团APP制作移动端完整项目》 项目界面预览&#xff1a; 一、搜索制作 在上一节中我们完成了标题头的制作&#xff0c;接下来我们查看如何制作搜索栏以及分类区制作。 首先我…

10.2.0.5启动enterprise manager

10.2.0.5启动enterprise manager OEM作为一个实用工具&#xff0c;随着10g和11g的普及&#xff0c;OEM功能越来越强大&#xff0c;oem也应用越来越广泛。但是如果是10.2.0.5的版本&#xff0c;并且安装时间在2010年1月之后&#xff0c;可能会遇到OEM无法启动的情况&#xff0c;…

【数据结构与算法】多种语言(VB、C、C#、JavaScript)系列数据结构算法经典案例教程合集目录

文章目录1. 专栏简介2. 专栏地址3. 专栏目录1. 专栏简介 2. 专栏地址 「 刘一哥与GIS的故事 」之《数据结构与算法》 3. 专栏目录 【经典回放】多种语言系列数据结构算法&#xff1a;二叉树&#xff08;JavaScript版&#xff09;【经典回放】多种语言系列数据结构算法&#…

《iVX 高仿美团APP制作移动端完整项目》03 推介信息及推荐商家分析及制作

点击整个专栏查看其它系列文章 &#xff08;系列文章更新中…&#xff09;&#xff1a;《iVX 高仿美团APP制作移动端完整项目》 项目界面预览&#xff1a; 一、推荐信息制作 推荐信息与之前的标题下推荐信息制作类似&#xff1a; 此时依旧创建一个行&#xff0c;设置其上下…

利用百度云盘API上传文件至百度云盘

一、获取Access Token示例 1. 请您将以下HTTP请求直接粘贴到浏览器地址栏内&#xff0c;并按下回车键。 https://openapi.baidu.com/oauth/2.0/authorize?response_typetoken&client_idL6g70tBRRIXLsY0Z3HwKqlRE&redirect_urioob&scopenetdisk 2、执行后&#x…

Docker 容器抓包

背景介绍程序在运行期间出现问题时&#xff0c;通常会通过抓包的方式来分析、定位问题。非容器应用一般可以通过 fiddler、wireshark 等工具进行抓包&#xff0c;那么&#xff0c;运行在容器的应用一般通过什么方式进行抓包呢&#xff1f;容器应用一般可以通过 tcpdump、ngrep …

服务发现与健康监测框架Consul-DNS转发的应用

关于Consul Consul是一个提供服务注册与发现&#xff0c;健康监测&#xff0c;Key/Value存储以及多数据中心存储的分布式框架。官网地址是https://www.consul.io/&#xff0c;公司初步应用后我们老大觉得这东西有点意思&#xff0c;随即有了翻译文档的想法&#xff0c;由于精力…

【ArcGIS风暴】ArcGIS10.6图斑椭球面积计算原理与方法

文章目录 1. 椭球面积计算原理2. ArcGIS计算图斑椭球面积3. ArcGIS计算图斑投影平面面积1. 椭球面积计算原理 <

实践 Neutron 前的两个准备工作 - 每天5分钟玩转 OpenStack(78)

上一节配置了 linux-bridge mechanism driver&#xff0c;本节再做两个准备工作&#xff1a; 1. 检视初始的网络状态。2. 了解 linux bridge 环境中的各种网络设备。 初始网络状态 我们首先考察实验环境最初始的网络状态。随着学习的深入&#xff0c;我们会对网络不断进行新的配…

《iVX 高仿美团APP制作移动端完整项目》04 美食页 标题、搜索、商家标题制作

点击整个专栏查看其它系列文章 &#xff08;系列文章更新中…&#xff09;&#xff1a;《iVX 高仿美团APP制作移动端完整项目》 项目界面预览&#xff1a; 一、美食页顶部商家页制作 1.1 页面主格调确认 该美食页为首页中美食按钮点击后进入的页面。该页面分为顶部的标题、搜…

利用浏览器调试功能 计算 百度网盘 文件数量

“百度网盘”程序做的比较烂&#xff0c;以百度的技术实力按说不应该如此。真正试了就知道真的不怎么样。为了统计百度网盘的文件写了以下脚本&#xff0c;仅供参考&#xff1a; var root "";//指定目录&#xff0c;空取当前目录 var totalCount 0; var startTime …

就在刚刚,HTTP/3 正式发布了!

经过了多年的努力&#xff0c;在 6 月 6 号&#xff0c;IETF &#xff08;互联网工程任务小组&#xff09; 正式发布了 HTTP/3 的 RFC&#xff0c; 这是超文本传输协议&#xff08;HTTP&#xff09;的第三个主要版本&#xff0c;完整的 RFC 超过了 20000 字&#xff0c;非常详细…

通渭县义岗川镇之行(2020年11月19日)

2020年11月18日&#xff0c;应邀前往通渭县义岗川镇老家&#xff0c;经过了3个小时的自驾车程&#xff0c;夜幕降临时刻到达了美丽的义岗川小镇。 义岗川镇&#xff0c;隶属甘肃省定西市通渭县&#xff0c;地处通渭县城最北部&#xff0c;东南与寺子川乡毗连&#xff0c;南邻北…