博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WPF自定义LED风格数字显示控件
阅读量:6269 次
发布时间:2019-06-22

本文共 15913 字,大约阅读时间需要 53 分钟。

原文:

版权声明:本文为博主原创文章,转载请注明作者和出处 https://blog.csdn.net/ZZZWWWPPP11199988899/article/details/52895167

       前段时间做过一个WPF的项目,需要用到这样一个控件,在网上找了一下没找到特别合适的,要么有的不是WPF的控件(无法使用WPF的绑定功能),要么有的是WPF的但是效果不太好,或者不满足需求(比如没有小数点.或者冒号:)。所以就参考着别人的东西自己做了一个。

       本文大部分属于原创内容,但是在图像绘制的时候参考(其实是照搬)了这个文章里的内容:

     

      一 整个控件库的目录结构:

      

       其中DigitalControl.cs为单个LED数字控件,DigitalPanelControl.cs为多个DigitalControl的组合控件,Segment目录下的类为数字的7条边和冒号以及小数点的类。Data目录下的DigitalData类存放一个DigitalControl的Segment数据,而DigitalParam存放单个DigitalControl的基本属性,包括宽,高,线条粗细以及间距等。

       控件构造的基本思路为:通过单个控件的基本属性参数(宽、高、粗细等)——也就是DigitalParam类中的数据——去构造组成单个数字的10个Segment,然后通过组合Segment得到DigitalControl,再通过组合DigitControl得到DigitalPanelControl,普通的控件使用者主要是使用DigitalPanelControl。

       二 Segment类 

      

        ===========================

      

       Segment类为数字的7条边(如上图)以及冒号(:)的两个点和小数点,一共10个子类,他们继承共同的父类Segments,由一个或多个点构成的Path组成,每个Segment的画法如上图所示。

       (注:上图照搬自  )

       Segment类为一个抽象父类,类中定义了一个List<Point>列表,用来存放构成Segment的点,然后声明了一个GetPoints的虚方法,在其子类中复写了此方法,主要用于通过给定的DigitalParam数据得到Segment所需要的点(填充_points)。

       下面是Segment父类和其中一个子类TopSegment的代码。

public abstract class Segment    {        private List
_points = new List
(); public List
Points { get { return _points; } set { _points = value; } } public abstract void GetPoints(DigitalParam dp); }

public class TopSegment : Segment    {        public TopSegment(DigitalParam dp)        {            GetPoints(dp);        }        public override void GetPoints(DigitalParam dp)        {            Points.Add(new Point(dp.BevelWidth * 2 + dp.SegmentInterval, 0));            Points.Add(new Point(dp.DigitalWidth - dp.BevelWidth * 2 - dp.SegmentInterval, 0));            Points.Add(new Point(dp.DigitalWidth - dp.BevelWidth - dp.SegmentInterval, dp.BevelWidth));            Points.Add(new Point(dp.DigitalWidth - dp.SegmentInterval - dp.SegmentThickness, dp.SegmentThickness));            Points.Add(new Point(dp.SegmentThickness + dp.SegmentInterval, dp.SegmentThickness));            Points.Add(new Point(dp.BevelWidth + dp.SegmentInterval, dp.BevelWidth));        }    }

      

       三 DigitalData类

       该类为单个数字控件的数据类,主要包括10个支持数字显示的Segment的字段和属性,以及两个控制数字显示内容的方法。其中DisplayDigital根据给定的数字字符去调用LightDigitalSegments方法,通过LightDigitalSegments方法设置不同Segment的显示或隐藏来显示具体的数字。

public class DigitalData    {        #region Fields        private Path topSegment;        private Path upLeftSegment;        private Path upRightSegment;        private Path middleSegment;        private Path downLeftSegment;        private Path downRightSegment;        private Path bottomSegment;        private Path dotSegment;        private Path colonUpSegment;        private Path colonDownSegment;        #endregion        #region Properties        public Path TopSegment        {            get            {                return topSegment;            }            set            {                topSegment = value;            }        }        public Path UpLeftSegment        {            get            {                return upLeftSegment;            }            set            {                upLeftSegment = value;            }        }        public Path UpRightSegment        {            get            {                return upRightSegment;            }            set            {                upRightSegment = value;            }        }        public Path MiddleSegment        {            get            {                return middleSegment;            }            set            {                middleSegment = value;            }        }        public Path DownLeftSegment        {            get            {                return downLeftSegment;            }            set            {                downLeftSegment = value;            }        }        public Path DownRightSegment        {            get            {                return downRightSegment;            }            set            {                downRightSegment = value;            }        }        public Path BottomSegment        {            get            {                return bottomSegment;            }            set            {                bottomSegment = value;            }        }        public Path DotSegment        {            get            {                return dotSegment;            }            set            {                dotSegment = value;            }        }        public Path UpColonSegment        {            get            {                return colonUpSegment;            }            set            {                colonUpSegment = value;            }        }        public Path DownColonSegment        {            get            {                return colonDownSegment;            }            set            {                colonDownSegment = value;            }        }        #endregion        #region Methods        ///         /// 设置segment的状态        ///         ///         ///         ///         ///         ///         ///         ///         ///         ///         private void LightDigitalSegments(bool top, bool upRight, bool downRight, bool bottom,            bool downLeft, bool upLeft, bool middle, bool dot, bool colon)        {            double ON = 1;            double OFF = 0.05;            TopSegment.Opacity = top ? ON : OFF;            UpRightSegment.Opacity = upRight ? ON : OFF;            DownRightSegment.Opacity = downRight ? ON : OFF;            BottomSegment.Opacity = bottom ? ON : OFF;            DownLeftSegment.Opacity = downLeft ? ON : OFF;            UpLeftSegment.Opacity = upLeft ? ON : OFF;            MiddleSegment.Opacity = middle ? ON : OFF;            DotSegment.Opacity = dot ? ON : OFF;            UpColonSegment.Opacity = DownColonSegment.Opacity = colon ? ON : OFF;                   }        ///         /// 根据输入字符显示相应的字符        ///         ///         public void DisplayDigital(string digital)        {            switch (digital)            {                case null:                case " ":                    LightDigitalSegments(false, false, false, false, false, false, false, false, false);                    break;                case "0":                    LightDigitalSegments(true, true, true, true, true, true, false, false, false);                    break;                case "1":                    LightDigitalSegments(false, true, true, false, false, false, false, false, false);                    break;                case "2":                    LightDigitalSegments(true, true, false, true, true, false, true, false, false);                    break;                case "3":                    LightDigitalSegments(true, true, true, true, false, false, true, false, false);                    break;                case "4":                    LightDigitalSegments(false, true, true, false, false, true, true, false, false);                    break;                case "5":                    LightDigitalSegments(true, false, true, true, false, true, true, false, false);                    break;                case "6":                    LightDigitalSegments(true, false, true, true, true, true, true, false, false);                    break;                case "7":                    LightDigitalSegments(true, true, true, false, false, false, false, false, false);                    break;                case "8":                    LightDigitalSegments(true, true, true, true, true, true, true, false, false);                    break;                case "9":                    LightDigitalSegments(true, true, true, true, false, true, true, false, false);                    break;                case "0.":                    LightDigitalSegments(true, true, true, true, true, true, false, true, false);                    break;                case "1.":                    LightDigitalSegments(false, true, true, false, false, false, false, true, false);                    break;                case "2.":                    LightDigitalSegments(true, true, false, true, true, false, true, true, false);                    break;                case "3.":                    LightDigitalSegments(true, true, true, true, false, false, true, true, false);                    break;                case "4.":                    LightDigitalSegments(false, true, true, false, false, true, true, true, false);                    break;                case "5.":                    LightDigitalSegments(true, false, true, true, false, true, true, true, false);                    break;                case "6.":                    LightDigitalSegments(true, false, true, true, true, true, true, true, false);                    break;                case "7.":                    LightDigitalSegments(true, true, true, false, false, false, false, true, false);                    break;                case "8.":                    LightDigitalSegments(true, true, true, true, true, true, true, true, false);                    break;                case "9.":                    LightDigitalSegments(true, true, true, true, false, true, true, true, false);                    break;                case ":":                case ":":                    LightDigitalSegments(false, false, false, false, false, false, false, false, true);                    break;                case "-":                    LightDigitalSegments(false, false, false, false, false, false, true, false, false);                    break;                default:                    throw new Exception("输入字符错误!");            }        }        #endregion    }

       四 DigitalControl类

      

       单个数字控件的类,继承自ContentControl

public class DigitalControl : ContentControl

       类里面定义了一些和控件显示相关的依赖属性,如控制颜色的LEDColorProperty,控制segment粗细的LEDThicknessProperty,控制显示内容的ValueProperty等等,并为这些依赖属性定义了属性包装器和属性变化回调方法。

       控件在显示前会调用OnApplyTemplate方法,会进行控件的初始化,包括调用SetSegmentsData方法得到所有Segment的点集,然后调用DrwaSegments绘制所有的Segment,然后将这些Segment都添加到控件的根容器中,最终设置其显示的值。

public override void OnApplyTemplate()    {        base.OnApplyTemplate();        //获取根布局        rootGrid = GetTemplateChild("gdRoot") as Grid;        //初始化Segments的点集digitalSegmentDict        SetSegmentsData();        //画数字        dd = DrawSegments(digitalSegmentDict, LEDColor);        //将线段添加到容器        AddSegmentsToPanel(dd);        dd.DisplayDigital(Value);    }

       SetSegmentsData方法首先初始化DigitalParam数据,然后根据DigitalParam通过Segment子类实例化时调用各自的GetPoints方法得到各自的点集合,并存储到字典里面。

///     /// 初始化Segments的点集    ///     private void SetSegmentsData()    {        dp = new DigitalParam();        dp.BevelWidth = BevelWidth;        dp.SegmentInterval = SegmentInterval;        dp.SegmentThickness = LEDThickness;        dp.DigitalHeight = LEDHeight;        dp.DigitalWidth = LEDWidth;
        digitalSegmentDict["TopSegment"] = new TopSegment(dp);        digitalSegmentDict["UpRightSegment"] = new UpRightSegment(dp);        digitalSegmentDict["DownRightSegment"] = new DownRightSegment(dp);        digitalSegmentDict["BottomSegment"] = new BottomSegment(dp);        digitalSegmentDict["DownLeftSegment"] = new DownLeftSegment(dp);        digitalSegmentDict["UpLeftSegment"] = new UpLeftSegment(dp);        digitalSegmentDict["MiddleSegment"] = new MiddleSegment(dp);        digitalSegmentDict["UpColonSegment"] = new UpColonSegment(dp);        digitalSegmentDict["DownColonSegment"] = new DownColonSegment(dp);        digitalSegmentDict["DotSegment"] = new DotSegment(dp);    }
 

       DrawSegments会根据参数的Point个数调用DrawLine和DrawEllipse方法来绘制图像得到DigitalData。

///     /// 画直线段    ///     ///     ///     /// 
private static Path DrawLine(List
points, Color clr) { PathSegmentCollection segments = new PathSegmentCollection(); for (int i = 1; i < points.Count; i++) { segments.Add(new LineSegment(points[i], true)); } Path segment = new Path() { StrokeLineJoin = PenLineJoin.Round, Stroke = new SolidColorBrush(clr), Fill = new SolidColorBrush(clr), Opacity = 0.05, StrokeThickness = 0.25, Data = new PathGeometry() { Figures = new PathFigureCollection() { new PathFigure(){IsClosed = true, IsFilled = true, StartPoint = points[0], Segments = segments} } } }; return segment; } ///
/// 画圆点 /// ///
///
///
///
private Path DrawEllipse(Point p, double radius, Color clr) { Color strokecolor; if (clr == Colors.Transparent) { strokecolor = clr; } else { strokecolor = Colors.White; } Path segment = new Path() { StrokeLineJoin = PenLineJoin.Round, Stroke = new SolidColorBrush(strokecolor), Fill = new SolidColorBrush(clr), Opacity = 0.05, StrokeThickness = 0.25, Data = new EllipseGeometry(p, radius, radius) }; return segment; }

       五 DigitalPanelControl类

       先在Generic.xaml中设置控件风格,病将背景颜色、宽、高绑定为TemplatedParent的数据,

       同样在该类中定义了一系列的依赖属性用于控件的数据绑定,包括Digital的个数,控件的宽、高,背景色、前景色等等,同时定义了一系列属性改变回调方法和属性包装器。
        在OnApplyTemplate方法中,会根据当前的DigitalCount数据添加相应个数的DigitalContral到根容器,然后调用DisplayData方法显示数据
///         /// 调用模板时的方法        ///         public override void OnApplyTemplate()        {            base.OnApplyTemplate();            //获取根布局            rootPanel = GetTemplateChild("LayoutRoot") as StackPanel;            //添加Led            DrawDigitals(DigitalCount);            //将Digitals 加入到rootPanel中            foreach(DigitalControl digital in digitalsList)            {                rootPanel.Children.Add(digital);            }            DisplayData(Value);        }

       DisplayData方法会先将所要显示的字符串转换成单个DigitalControl可显示的字符列表,然后根据DigitalPanelControl的DigitalControl的个数和所要显示的数据的长度,来决定显示的方式,数据过长则截断尾数,数据少于digital的个数,则填充左边digital

///         /// 显示值        ///         ///         private void DisplayData(string value )        {            if (value == null)                return;            //准备字符            List
showStringList = ConvertStringToSingleDigitalCharList(value); //显示文字 if (digitalsList.Count < showStringList.Count)//要显示的字符个数大于显示器的个数,截断尾数 { for (int i = 0; i < digitalsList.Count; i++) { digitalsList[i].Value = showStringList[i]; } } else//否则从显示器的低位开始填充 { for(int i = 0; i < digitalsList.Count - showStringList.Count; i++) { digitalsList[i].Value = null; } for(int i = digitalsList.Count - showStringList.Count; i < digitalsList.Count; i++) { digitalsList[i].Value = showStringList[i - digitalsList.Count + showStringList.Count]; } } } ///
/// 将字符串转换成可显示的单个字符的集合 /// ///
///
private static List
ConvertStringToSingleDigitalCharList(string value) { char[] charArray = value.ToCharArray(); List
showStringList = new List
(); for (int i = 0; i < charArray.Length; i++) { if (i == charArray.Length - 1)//最后一位数字,直接复制 { showStringList.Add(charArray[i].ToString()); break; } if (charArray[i] >= '0' && charArray[i] <= '9' && charArray[i + 1] == '.')//带小数点数字,将小数点与数字放到同一个字符串里 { showStringList.Add(charArray[i] + "."); i++; } else { showStringList.Add(charArray[i].ToString()); } } return showStringList; }
       最终的效果图:

      

你可能感兴趣的文章
asp.net core 2.0 web api基于JWT自定义策略授权
查看>>
Skype for Business Server 2015-04-前端服务器-3-安装-管理工具
查看>>
第12章代码《跟老男孩学习Linux运维:Shell编程实战》
查看>>
我们为什么从Python转到go?
查看>>
5.Azure负载均衡(上)
查看>>
轻松精通awk数组企业问题案例
查看>>
26.Azure备份服务器(下)
查看>>
从“网上说的能信么”说开去---学习的思考
查看>>
DHCP 日志分析
查看>>
.NET Micro Framework动态调用C/C++底层代码(原理篇)
查看>>
Windows Server 2012正式版RDS系列⒃
查看>>
Shell脚本之awk篇
查看>>
微软发布Azure Stack硬件需求
查看>>
python socket编程详细介绍
查看>>
Windows Server 2016第三个技术预览版新技术
查看>>
Everything 本地磁盘文件搜索工具下载!
查看>>
Python dict(字典) 详细总结
查看>>
RPF(Reverse Path Forwarding 反向路径转发)技术
查看>>
2016年收到的第一件礼物,被评上微软全球最有价值专家MVP(一)
查看>>
2016中国VR开发者论坛第一期
查看>>