前情导航:WPF 自实现Arc控件和Pie控件

看过上一篇文章的应该知道,Arc和Pie的实现方式区别不过是一个把Path路径的终点与起点相连,一个没有相连,于是本人就索性把二者合二为一了。决定是画Arc还是画Pie,完全根据Stroke和Fill两个属性来判断。

废话不多说,终极代码奉上:

public class Circle : Shape
{#region 成员变量private Geometry m_Data;private Geometry Data{get { return m_Data; }set{if (m_Data != value){m_Data = value;this.InvalidateVisual();}}}private CircleStatus m_Status;#endregion#region 重写方法/// <summary>/// 监听StrokeThickness变化/// </summary>/// <param name="e"></param>protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e){base.OnPropertyChanged(e);if (e.Property == StrokeThicknessProperty){propertyChangedCallback(this, new DependencyPropertyChangedEventArgs());}else if (e.Property == FillProperty){m_Status = e.NewValue == null ? CircleStatus.Ring : CircleStatus.Pie;propertyChangedCallback(this, new DependencyPropertyChangedEventArgs());}else if (e.Property == WidthProperty || e.Property == HeightProperty || e.Property == ActualWidthProperty || e.Property == ActualHeightProperty){propertyChangedCallback(this, new DependencyPropertyChangedEventArgs());}}/// <summary>/// 重写此方法实现图形绘制/// </summary>protected override Geometry DefiningGeometry { get { return Data; } }#endregion#region 依赖属性/// <summary>/// 起始角度/// </summary>public double StartAngle{get { return (double)GetValue(StartAngleProperty); }set { SetValue(StartAngleProperty, value); }}// Using a DependencyProperty as the backing store for StartAngle.  This enables animation, styling, binding, etc...public static readonly DependencyProperty StartAngleProperty =DependencyProperty.Register("StartAngle", typeof(double), typeof(Circle), new PropertyMetadata(0.0, propertyChangedCallback));/// <summary>/// 终止角度/// </summary>public double EndAngle{get { return (double)GetValue(EndAngleProperty); }set { SetValue(EndAngleProperty, value); }}// Using a DependencyProperty as the backing store for EndAngle.  This enables animation, styling, binding, etc...public static readonly DependencyProperty EndAngleProperty =DependencyProperty.Register("EndAngle", typeof(double), typeof(Circle), new PropertyMetadata(0.0, propertyChangedCallback));/// <summary>/// 内边距/// </summary>public double Padding{get { return (double)GetValue(PaddingProperty); }set { SetValue(PaddingProperty, value); }}// Using a DependencyProperty as the backing store for Padding.  This enables animation, styling, binding, etc...public static readonly DependencyProperty PaddingProperty =DependencyProperty.Register("Padding", typeof(double), typeof(Circle), new PropertyMetadata(0.0, propertyChangedCallback));#endregion#region 图形语句生成private static void propertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e){if (d is Circle circle){// 计算内边距var padding = circle.m_Status == CircleStatus.Ring ? circle.StrokeThickness / 2 : 0;padding += circle.Padding;// 判断绘制的圆弧是否可见,以提高性能double width = circle.Width;double height = circle.Height;if (double.IsNaN(width) || double.IsNaN(height)){if (double.IsNaN(circle.DesiredSize.Width) || double.IsNaN(circle.DesiredSize.Height) || circle.DesiredSize == new Size(0, 0)){width = circle.ActualWidth;height = circle.ActualHeight;}else{width = circle.DesiredSize.Width;height = circle.DesiredSize.Height;}}if (width > 0 && height > 0 && circle.StartAngle != circle.EndAngle){double ellipseA = width / 2 - padding;double ellipseB = height / 2 - padding;// 起止角度间隔大于等于360°,直接画圆if (Math.Abs(circle.StartAngle - circle.EndAngle) >= 360){string data = $"M{padding + ellipseA * 2},{padding + ellipseB + 0.001} A{ellipseA},{ellipseB} 0,1,1 {padding + ellipseA * 2},{padding + ellipseB}z";circle.Data = (Geometry)Geometry.Parse(data);return;}// 椭圆公式:X²/a²+Y²/b²=1// Rect与椭圆各参数的对应关系:// Rect.X与Rect.Y分别是椭圆外接矩形相对Circle区域的左上角的偏移量;// Rect.Width与Rect.Height分别是椭圆外接矩形的宽和高// 以下根据StartAngle和EndAngle算出圆弧在矩形函数曲线中的起始点和终止点// 判断绘制方向bool clockWise = circle.EndAngle > circle.StartAngle;// 判断优弧/劣弧bool majorArc = Math.Abs(circle.StartAngle - circle.EndAngle) % 360 >= 180;// 将起始点和终止点转化为椭圆上的坐标double rad = 0;Point startPoint = new Point();if ((90 - circle.StartAngle) % 360 == 0){startPoint.X = 0;startPoint.Y = ellipseB;}else if ((270 - circle.StartAngle) % 360 == 0){startPoint.X = 0;startPoint.Y = -ellipseB;}else{rad = GetRadian(circle.StartAngle);startPoint.X = ellipseA * ellipseB / Math.Sqrt(Math.Pow(ellipseB, 2) + Math.Pow(ellipseA * Math.Tan(rad), 2));startPoint.X *= Math.Cos(rad) > 0 ? 1 : -1;startPoint.Y = startPoint.X * Math.Tan(rad);}Point endPoint = new Point();if ((90 - circle.EndAngle) % 360 == 0){endPoint.X = 0;endPoint.Y = ellipseB;}else if ((270 - circle.EndAngle) % 360 == 0){endPoint.X = 0;endPoint.Y = -ellipseB;}else{rad = GetRadian(circle.EndAngle);endPoint.X = ellipseA * ellipseB / Math.Sqrt(Math.Pow(ellipseB, 2) + Math.Pow(ellipseA * Math.Tan(rad), 2));endPoint.X *= Math.Cos(rad) > 0 ? 1 : -1;endPoint.Y = endPoint.X * Math.Tan(rad);}string pathData = $"M";if (circle.m_Status == CircleStatus.Pie){pathData += $"{ellipseA + padding},{ellipseB + padding} ";}pathData += $"{startPoint.X + ellipseA + padding},{startPoint.Y + ellipseB + padding} ";pathData += $"A{ellipseA},{ellipseB} ";pathData += $"0,{(majorArc ? "1" : "0")},{(clockWise ? "1" : "0")} ";pathData += $"{endPoint.X + ellipseA + padding},{endPoint.Y + ellipseB + padding}";circle.Data = (Geometry)Geometry.Parse(pathData);}else{circle.Data = (Geometry)Geometry.Parse("");}}}/// <summary>/// 将角度转化为弧度/// </summary>/// <param name="angle"></param>/// <returns></returns>private static double GetRadian(double angle){return angle / 180.0 * Math.PI;}#endregion
}

进阶玩法:

只需两个Circle即可构成环形进度条

<Grid Width="70" Height="70"><local:Circle StartAngle="0" EndAngle="360" Stroke="LightGray" StrokeThickness="15"/><local:Circle StartAngle="-90" EndAngle="180" Stroke="DodgerBlue" StrokeThickness="15"/><TextBlock Text="75%" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

WPF 实现Circle控件(原Arc控件和Pie控件的合并版)相关推荐

  1. WPF数据可视化控件(一) LED风格数字控件

    原帖地址:https://blog.csdn.net/zhuo_wp/article/details/81561190 资源源码:https://download.csdn.net/download/ ...

  2. ESP32 开发笔记(四)littleVGL LVGL 控件学习 Arc 弧形控件

    先看效果,创建了两个Arc 弧形控件,一个可触摸拖动的默认风格,一个动画控制的自定义风格的弧形控件 ESP-IDF版本:V4.2 littleVGL版本:V7.10.0 开发板购买链接https:// ...

  3. WPF 滚动条控件ScrollViewer的使用及自定义滚动条控件(一)

    WPF 滚动条控件ScrollViewer的使用及自定义滚动条控件(一) 首先看一下两种空间的运行效果: 左边是自定义滑条控件,右边是自带的滑条控件: **滑条使用方法:**我们在ScrollView ...

  4. slider(滑动条)控件模版,样式--用图片定义控件模版

    初接触wpf,想自己用图片做一个个性slider滑块控件,网上找了很久无果.后来一些机缘巧合看别人源码的时候找到了类似的,然后自己再查了一下msdn,大概弄懂了. 我们先来看看slider控件组成元素 ...

  5. 海思一颗料暴涨7倍,全球芯片缺货真相:原厂38颗MCU掌控汽车产业链

    origin: https://new.qq.com/omn/20210309/20210309A031G200.html 2020年一场前所未有的疫情灾难席卷全球,作为全球化分工最明确的半导体产业首 ...

  6. Maps模块管理地图控件,用于在web页面中显示地图控件,提供各种接口操作地图控件,如添加标点、路线等。通过plus.maps可获取地图管理对象

    方法: openSysMap: 调用系统第三方程序进行导航 create: 创建Map对象 getMapById: 查找已经创建的Map对象 对象: Map: 地图控件对象 MapStyles: 地图 ...

  7. 判断鼠标不在控件上_基础设施:一套基本控件

    类可以把需要重复使用的代码封装到一起多次使用, 但这样的代码仍不能被不同的程序使用. 把自己常用的代码编到一起,打包成库的话,就能制成一套自用工具库作为基础设施. 这里常用的部分,首先就是界面控件. ...

  8. android控件常用的属性,android?常用的控件属性

    1.Android RelativeLayout 属性 // 相对于给定ID控件 android:layout_above 将该控件的底部置于给定ID的控件之上; android:layout_bel ...

  9. [ASP.NET 控件实作 Day14] 继承 CompositeControl 实作 Toolbar 控件

    之前我们简单介绍过继承 CompositeControl  来实作复合控件,在本文我们将以 Toolbar 控件为例,以复合控件的作法(继承 CompositeControl )来实作 Toolbar ...

最新文章

  1. Github配置(git+vscode+python+jupyter)
  2. C++ IPv4与IPv6的兼容编码(转,出自http://blog.csdn.net/ligt0610/article/details/18667595)...
  3. 基于 Spring Boot 的 Restful 风格实现增删改查
  4. 报名 | “智见AI”SpringCamp:物体检测与深度神经网络模型设计
  5. android的logcat详细用法
  6. UIView中常用的方法
  7. 杯子 + Kronican
  8. 推荐两款工具给爱做实验的人
  9. nginx 配置反向代理和负载均衡
  10. 使用WebService进行异步通信
  11. python+selenium自动化driver.switch_to.frame用法
  12. 71计算机组装与维修期中,《计算机维修》期中考试卷
  13. 金融行业的JAVA软件开发
  14. deap dataset的不同分类模型的实现(2)-认识数据
  15. 异步赠书:10月Python畅销书升级
  16. docker下安装wekan看板工具
  17. 如何删除搜索框的搜索记录(谷歌浏览器)
  18. Vue - 路由导航守卫控制访问权限,设置 localStorage 过期时间
  19. Linux(一)之相关介绍与安装
  20. java 获取当年法定假日以及公休日-接口坞

热门文章

  1. Python3零基础学习笔记二
  2. 120D02S 调试与维修(三)
  3. 【毕业设计】 基于单片机的wifi智能远程开关控制
  4. 大厂实践|Apache Pulsar 消息队列在拉卡拉的应用
  5. 多级缓存分析篇(二) 常用分布式锁分析
  6. 学生用计算机怎么爱心形,excel表格怎么绘制爱心? excel公式画爱心的教程
  7. 小米集团的喜和忧:业绩下滑明显,市值再“腰斩”,造车是未来?
  8. gps修改国内服务器,gps服务器修改
  9. Android添加文件打开方式,将你的app加入打开方式
  10. CTR预估之FMs系列模型:FM/FFM/FwFM/FEFM