原帖地址:https://blog.csdn.net/zhuo_wp/article/details/81561190

资源源码:https://download.csdn.net/download/zhuo_wp/10597840

在做数据可视化显示的时候,有时需要LED风格的数字显示。WPF的原生控件中并没有这样的控件,因此需要自定义这样的控件。本博文便是描述一种LED风格数字控件的实现方式。

本控件图像的绘制参考了文章:A Fine-looking Segmented LED Control 。

一 定义参数模型

由上图可知,影响数字控件外观的因子由:控件的宽/高、控件各部分的间隔、数字控件的粗细以及截断长度。可以将这些参数封装成一个参数模型类,方便对参数进行统一管理。代码如下:

    public class DigitalParameter{public double DigitalWidth { get; set; }public double DigitalHeight { get; set; }public double SegmentThickness { get; set; }public double SegmentInterval { get; set; }public double BevelWidth { get; set; }}

二 定义控件各部件关键点获取的类

根据前文连接中给出的这个绘制数字的方式,统一由一个类给出绘制数字的每一个组成部件所需要的关键点。

public class BaseLedDigitalSegmentCreator : ILedDigitalSegmentCreator{private DigitalParameter _parameter = null;public BaseLedDigitalSegmentCreator(DigitalParameter parameter){_parameter = parameter;}public List<Point> GetBottomSegment(){List<Point> points = new List<Point>();points.Add(new Point(_parameter.SegmentThickness + _parameter.SegmentInterval, _parameter.DigitalHeight - _parameter.SegmentThickness));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentThickness - _parameter.SegmentInterval, _parameter.DigitalHeight - _parameter.SegmentThickness));points.Add(new Point(_parameter.DigitalWidth - _parameter.BevelWidth - _parameter.SegmentInterval, _parameter.DigitalHeight - _parameter.BevelWidth));points.Add(new Point(_parameter.DigitalWidth - _parameter.BevelWidth * 2 - _parameter.SegmentInterval, _parameter.DigitalHeight));points.Add(new Point(_parameter.BevelWidth * 2 + _parameter.SegmentInterval, _parameter.DigitalHeight));points.Add(new Point(_parameter.BevelWidth + _parameter.SegmentInterval, _parameter.DigitalHeight - _parameter.BevelWidth));return points;}public List<Point> GetDotSegment(){List<Point> points = new List<Point>();points.Add(new Point(_parameter.DigitalWidth + _parameter.SegmentThickness / 2, _parameter.DigitalHeight - _parameter.SegmentThickness / 2));return points;}public List<Point> GetDownColonSegment(){List<Point> points = new List<Point>();points.Add(new Point(_parameter.DigitalWidth / 2, 3 * _parameter.DigitalHeight / 4 - _parameter.SegmentThickness / 4));return points;}public List<Point> GetDownLeftSegment(){List<Point> points = new List<Point>();points.Add(new Point(0, _parameter.DigitalHeight - _parameter.BevelWidth * 2 - _parameter.SegmentInterval));points.Add(new Point(0, _parameter.DigitalHeight / 2 + _parameter.SegmentInterval + _parameter.SegmentThickness / 2));points.Add(new Point(_parameter.SegmentThickness / 2, _parameter.DigitalHeight / 2 + _parameter.SegmentInterval));points.Add(new Point(_parameter.SegmentThickness, _parameter.DigitalHeight / 2 + _parameter.SegmentThickness / 2 + _parameter.SegmentInterval));points.Add(new Point(_parameter.SegmentThickness, _parameter.DigitalHeight - _parameter.SegmentThickness - _parameter.SegmentInterval));points.Add(new Point(_parameter.BevelWidth, _parameter.DigitalHeight - _parameter.BevelWidth - _parameter.SegmentInterval));return points;}public List<Point> GetDownRightSegment(){List<Point> points = new List<Point>();points.Add(new Point(_parameter.DigitalWidth, _parameter.DigitalHeight - _parameter.BevelWidth * 2 - _parameter.SegmentInterval));points.Add(new Point(_parameter.DigitalWidth, _parameter.DigitalHeight / 2 + _parameter.SegmentInterval + _parameter.SegmentThickness / 2));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentThickness / 2, _parameter.DigitalHeight / 2 + _parameter.SegmentInterval));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentThickness, _parameter.DigitalHeight / 2 + _parameter.SegmentThickness / 2 + _parameter.SegmentInterval));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentThickness, _parameter.DigitalHeight - _parameter.SegmentThickness - _parameter.SegmentInterval));points.Add(new Point(_parameter.DigitalWidth - _parameter.BevelWidth, _parameter.DigitalHeight - _parameter.BevelWidth - _parameter.SegmentInterval));return points;}public List<Point> GetMiddleSegment(){List<Point> points = new List<Point>();points.Add(new Point(_parameter.SegmentThickness + _parameter.SegmentInterval, _parameter.DigitalHeight / 2 - _parameter.SegmentThickness / 2));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentThickness - _parameter.SegmentInterval, _parameter.DigitalHeight / 2 - _parameter.SegmentThickness / 2));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentInterval - _parameter.SegmentThickness / 2, _parameter.DigitalHeight / 2));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentThickness - _parameter.SegmentInterval, _parameter.DigitalHeight / 2 + _parameter.SegmentThickness / 2));points.Add(new Point(_parameter.SegmentThickness + _parameter.SegmentInterval, _parameter.DigitalHeight / 2 + _parameter.SegmentThickness / 2));points.Add(new Point(_parameter.SegmentThickness / 2 + _parameter.SegmentInterval, _parameter.DigitalHeight / 2));return points;}public List<Point> GetTopSegment(){List<Point> points = new List<Point>();points.Add(new Point(_parameter.BevelWidth * 2 + _parameter.SegmentInterval, 0));points.Add(new Point(_parameter.DigitalWidth - _parameter.BevelWidth * 2 - _parameter.SegmentInterval, 0));points.Add(new Point(_parameter.DigitalWidth - _parameter.BevelWidth - _parameter.SegmentInterval, _parameter.BevelWidth));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentInterval - _parameter.SegmentThickness, _parameter.SegmentThickness));points.Add(new Point(_parameter.SegmentThickness + _parameter.SegmentInterval, _parameter.SegmentThickness));points.Add(new Point(_parameter.BevelWidth + _parameter.SegmentInterval, _parameter.BevelWidth));return points;}public List<Point> GetUpColonSegment(){List<Point> points = new List<Point>();points.Add(new Point(_parameter.DigitalWidth / 2, _parameter.DigitalHeight / 4 + _parameter.SegmentThickness / 4));return points;}public List<Point> GetUpLeftSegment(){List<Point> points = new List<Point>();points.Add(new Point(0, _parameter.BevelWidth * 2 + _parameter.SegmentInterval));points.Add(new Point(0, _parameter.DigitalHeight / 2 - _parameter.SegmentInterval - _parameter.SegmentThickness / 2));points.Add(new Point(_parameter.SegmentThickness / 2, _parameter.DigitalHeight / 2 - _parameter.SegmentInterval));points.Add(new Point(_parameter.SegmentThickness, _parameter.DigitalHeight / 2 - _parameter.SegmentThickness / 2 - _parameter.SegmentInterval));points.Add(new Point(_parameter.SegmentThickness, _parameter.SegmentThickness + _parameter.SegmentInterval));points.Add(new Point(_parameter.BevelWidth, _parameter.BevelWidth + _parameter.SegmentInterval));return points;}public List<Point> GetUpRightSegment(){List<Point> points = new List<Point>();points.Add(new Point(_parameter.DigitalWidth, _parameter.BevelWidth * 2 + _parameter.SegmentInterval));points.Add(new Point(_parameter.DigitalWidth, _parameter.DigitalHeight / 2 - _parameter.SegmentThickness / 2 - _parameter.SegmentInterval));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentThickness / 2, _parameter.DigitalHeight / 2 - _parameter.SegmentInterval));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentThickness, _parameter.DigitalHeight / 2 - _parameter.SegmentThickness / 2 - _parameter.SegmentInterval));points.Add(new Point(_parameter.DigitalWidth - _parameter.SegmentThickness, _parameter.SegmentThickness + _parameter.SegmentInterval));points.Add(new Point(_parameter.DigitalWidth - _parameter.BevelWidth, _parameter.BevelWidth + _parameter.SegmentInterval));return points;}}

需要注意的两点:

1 上面的类BaseLedDigitalSegmentCreator实现了接口ILedDigitalSegmentCreator,此举是为方便以后修改数字控件的绘制方式做的预留接口。如有需要新增一种数字绘制方式,只需要重新定义一个实现了该接口的类。

    public interface ILedDigitalSegmentCreator{List<Point> GetBottomSegment();List<Point> GetDotSegment();List<Point> GetDownColonSegment();List<Point> GetDownLeftSegment();List<Point> GetDownRightSegment();List<Point> GetMiddleSegment();List<Point> GetTopSegment();List<Point> GetUpColonSegment();List<Point> GetUpLeftSegment();List<Point> GetUpRightSegment();}

2 上述代码多了2个部件,一个时小数点,为了以一种更好的方式显示小数点;另外一个时冒号“:”,在显示数字的时候有可能需要用到这个字符,如显示时间的时候。

三 单个数字控件

单个数字控件定义了单个数字控件绘制所需要的参数的依赖属性,绘制了单个控件的图形,给出了对于不同数字值的显示逻辑。

public class LedDigital : Control{#region Fieldsprivate Panel _rootPanel;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;private ILedDigitalSegmentCreator _segementCreator = null;#endregion#region Dependency properties/// <summary>/// Dependency property to Get/Set the current value /// </summary>public static readonly DependencyProperty ValueProperty =DependencyProperty.Register("Value", typeof(string), typeof(LedDigital), new PropertyMetadata(null, new PropertyChangedCallback(OnValuePropertyChanged)));/// <summary>/// 依赖属性-LED显示颜色/// </summary>public static readonly DependencyProperty DigitalBrushProperty =DependencyProperty.Register("DigitalBrush", typeof(Brush), typeof(LedDigital), new PropertyMetadata(new SolidColorBrush(Colors.Red), new PropertyChangedCallback(OnDigitalBrushPropertyChanged)));/// <summary>/// LED高度/// </summary>public static readonly DependencyProperty DigitalHeightProperty =DependencyProperty.Register("DigitalHeight", typeof(double), typeof(LedDigital), new PropertyMetadata(40.0, new PropertyChangedCallback(OnSizePropertyChanged)));/// <summary>/// LED的宽度/// </summary>public static readonly DependencyProperty DigitalWidthProperty =DependencyProperty.Register("DigitalWidth", typeof(double), typeof(LedDigital), new PropertyMetadata(20.0, new PropertyChangedCallback(OnSizePropertyChanged)));/// <summary>/// LED字体的粗细/// </summary>public static readonly DependencyProperty DigitalThicknessProperty =DependencyProperty.Register("DigitalThickness", typeof(double), typeof(LedDigital), new PropertyMetadata(5.0, new PropertyChangedCallback(OnSizePropertyChanged)));/// <summary>/// Segment间的距离/// </summary>public static readonly DependencyProperty SegmentIntervalProperty =DependencyProperty.Register("SegmentInterval", typeof(double), typeof(LedDigital), new PropertyMetadata(2.0, new PropertyChangedCallback(OnSizePropertyChanged)));/// <summary>/// segment两端的截断长度/// </summary>public static readonly DependencyProperty BevelWidthProperty =DependencyProperty.Register("BevelWidth", typeof(double), typeof(LedDigital), new PropertyMetadata(2.0, new PropertyChangedCallback(OnSizePropertyChanged)));/// <summary>/// 数字片段非高亮状态下的透明度/// </summary>public static readonly DependencyProperty DigitalDimOpacityProperty =DependencyProperty.Register("DigitalDimOpacity", typeof(double), typeof(LedDigital), new PropertyMetadata(0.05, OnDigitalBrushPropertyChanged));/// <summary>/// 数字片段非高亮状态下的颜色/// </summary>public static readonly DependencyProperty DigitalDimBrushProperty =DependencyProperty.Register("DigitalDimBrush", typeof(Brush), typeof(LedDigital), new PropertyMetadata(new SolidColorBrush(Colors.Red), OnDigitalBrushPropertyChanged));#endregion#region Dependency Property Wrappers/// <summary>/// Gets/Sets the current value/// </summary>public string Value{get{return (string)GetValue(ValueProperty);}set{SetValue(ValueProperty, value);}}/// <summary>/// Gets/Sets the digital brush/// </summary>public Brush DigitalBrush{get{return (Brush)GetValue(DigitalBrushProperty);}set{SetValue(DigitalBrushProperty, value);}}/// <summary>/// 获取或设置LED高度/// </summary>public double DigitalHeight{get{return (double)GetValue(DigitalHeightProperty);}set{SetValue(DigitalHeightProperty, value);}}/// <summary>/// 获取或设置LED宽度/// </summary>public double DigitalWidth{get{return (double)GetValue(DigitalWidthProperty);}set{SetValue(DigitalWidthProperty, value);}}/// <summary>/// 获取或设置LED字体粗细/// </summary>public double DigitalThickness{get{return (double)GetValue(DigitalThicknessProperty);}set{SetValue(DigitalThicknessProperty, value);}}/// <summary>/// 获取或设置LED segment间距/// </summary>public double SegmentInterval{get{return (double)GetValue(SegmentIntervalProperty);}set{SetValue(SegmentIntervalProperty, value);}}/// <summary>/// 获取或设置LED截断宽度/// </summary>public double BevelWidth{get{return (double)GetValue(BevelWidthProperty);}set{SetValue(BevelWidthProperty, value);}}public double DigitalDimOpacity{get { return (double)GetValue(DigitalDimOpacityProperty); }set { SetValue(DigitalDimOpacityProperty, value); }}public Brush DigitalDimBrush{get { return (Brush)GetValue(DigitalDimBrushProperty); }set { SetValue(DigitalDimBrushProperty, value); }}#endregion#region Constructorstatic LedDigital(){DefaultStyleKeyProperty.OverrideMetadata(typeof(LedDigital), new FrameworkPropertyMetadata(typeof(LedDigital)));}#endregion#region Property Changed Callbacks/// <summary>/// 控件属性值发生变化时调用的方法/// </summary>/// <param name="e"></param>protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e){base.OnPropertyChanged(e);if (e.Property.Name == "Height" || e.Property.Name == "ActualHeight"){DigitalHeight = (double)e.NewValue;}else if (e.Property.Name == "Width" || e.Property.Name == "ActualWidth"){DigitalWidth = (double)e.NewValue - DigitalThickness;}}/// <summary>/// 当前值发生变化时候调用的方法/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigital led = d as LedDigital;if (led != null){led.SetDisplayDigitalValue(led.Value);}}/// <summary>/// LED颜色发生变化时调用的方法/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnDigitalBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigital led = d as LedDigital;if (led != null){led.UpdateAllSegmentsBrush();}}/// <summary>/// 当led形状参数发生变化时调用的方法/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigital led = d as LedDigital;if (led == null || led._rootPanel == null){return;}led._rootPanel.Children.Clear();//初始化数字片段生成器DigitalParameter dp = led.GetDigitalParameter();led._segementCreator = new BaseLedDigitalSegmentCreator(dp);//初始化数字led.InitAllDigitalSegments();//设置初始值led.SetDisplayDigitalValue(led.Value);}#endregion#region Method/// <summary>/// 调用模板时的方法/// </summary>public override void OnApplyTemplate(){base.OnApplyTemplate();//获取根容器_rootPanel = GetTemplateChild("PART_Root") as Panel;//初始化数字片段生成器DigitalParameter dp = GetDigitalParameter();_segementCreator = new BaseLedDigitalSegmentCreator(dp);//初始化数字InitAllDigitalSegments();//设置初始值SetDisplayDigitalValue(Value);}private DigitalParameter GetDigitalParameter(){DigitalParameter dp = new DigitalParameter();dp.BevelWidth = BevelWidth;dp.SegmentInterval = SegmentInterval;dp.SegmentThickness = DigitalThickness;dp.DigitalHeight = DigitalHeight;dp.DigitalWidth = DigitalWidth;return dp;}/// <summary>/// 初始化Segments的点集/// </summary>private void InitAllDigitalSegments(){if (_segementCreator == null){throw new Exception("The segment creator is null.");}if (_rootPanel == null){throw new Exception("The root panel is null.");}InitDigitalSegment(_segementCreator.GetTopSegment(), ref _topSegment);InitDigitalSegment(_segementCreator.GetUpLeftSegment(), ref _upLeftSegment);InitDigitalSegment(_segementCreator.GetUpRightSegment(), ref _upRightSegment);InitDigitalSegment(_segementCreator.GetMiddleSegment(), ref _middleSegment);InitDigitalSegment(_segementCreator.GetDownLeftSegment(), ref _downLeftSegment);InitDigitalSegment(_segementCreator.GetDownRightSegment(), ref _downRightSegment);InitDigitalSegment(_segementCreator.GetBottomSegment(), ref _bottomSegment);InitDigitalSegment(_segementCreator.GetDotSegment(), ref _dotSegment);InitDigitalSegment(_segementCreator.GetUpColonSegment(), ref _colonUpSegment);InitDigitalSegment(_segementCreator.GetDownColonSegment(), ref _colonDownSegment);}/// <summary>/// 将数字片段添加到显示容器/// </summary>/// <param name="dd"></param>private void InitDigitalSegment(List<Point> points, ref Path path){Path segment = null;if (points.Count > 1){segment = GraphicsPloter.DrawLine(points, DigitalBrush);}else if (points.Count == 1){segment = GraphicsPloter.DrawEllipse(points[0], DigitalThickness / 2, DigitalBrush);}path = segment;_rootPanel.Children.Add(segment);}/// <summary>/// 设置segment的状态/// </summary>/// <param name="top"></param>/// <param name="upRight"></param>/// <param name="downRight"></param>/// <param name="bottom"></param>/// <param name="downLeft"></param>/// <param name="upLeft"></param>/// <param name="middle"></param>/// <param name="dot"></param>/// <param name="colon"></param>private void HighLightAllDigitalSegments(bool top, bool upRight, bool downRight, bool bottom, bool downLeft, bool upLeft, bool middle, bool dot, bool colon){LightSingleSegment(_topSegment, top);LightSingleSegment(_upLeftSegment, upLeft);LightSingleSegment(_upRightSegment, upRight);LightSingleSegment(_middleSegment, middle);LightSingleSegment(_downLeftSegment, downLeft);LightSingleSegment(_downRightSegment, downRight);LightSingleSegment(_bottomSegment, bottom);LightSingleSegment(_dotSegment, dot);LightSingleSegment(_colonUpSegment, colon);LightSingleSegment(_colonDownSegment, colon);}private void LightSingleSegment(Path segment, bool isHighLight){if (segment == null){return;}if (isHighLight){segment.Fill = DigitalBrush;segment.Opacity = 1;}else{segment.Fill = DigitalDimBrush;segment.Opacity = 0.05;}}/// <summary>/// 根据输入字符显示相应的字符/// </summary>/// <param name="digital"></param>private void SetDisplayDigitalValue(string digital){switch (digital){case null:case "":case " ":HighLightAllDigitalSegments(false, false, false, false, false, false, false, false, false);break;case "0":HighLightAllDigitalSegments(true, true, true, true, true, true, false, false, false);break;case "1":HighLightAllDigitalSegments(false, true, true, false, false, false, false, false, false);break;case "2":HighLightAllDigitalSegments(true, true, false, true, true, false, true, false, false);break;case "3":HighLightAllDigitalSegments(true, true, true, true, false, false, true, false, false);break;case "4":HighLightAllDigitalSegments(false, true, true, false, false, true, true, false, false);break;case "5":HighLightAllDigitalSegments(true, false, true, true, false, true, true, false, false);break;case "6":HighLightAllDigitalSegments(true, false, true, true, true, true, true, false, false);break;case "7":HighLightAllDigitalSegments(true, true, true, false, false, false, false, false, false);break;case "8":HighLightAllDigitalSegments(true, true, true, true, true, true, true, false, false);break;case "9":HighLightAllDigitalSegments(true, true, true, true, false, true, true, false, false);break;case "0.":HighLightAllDigitalSegments(true, true, true, true, true, true, false, true, false);break;case "1.":HighLightAllDigitalSegments(false, true, true, false, false, false, false, true, false);break;case "2.":HighLightAllDigitalSegments(true, true, false, true, true, false, true, true, false);break;case "3.":HighLightAllDigitalSegments(true, true, true, true, false, false, true, true, false);break;case "4.":HighLightAllDigitalSegments(false, true, true, false, false, true, true, true, false);break;case "5.":HighLightAllDigitalSegments(true, false, true, true, false, true, true, true, false);break;case "6.":HighLightAllDigitalSegments(true, false, true, true, true, true, true, true, false);break;case "7.":HighLightAllDigitalSegments(true, true, true, false, false, false, false, true, false);break;case "8.":HighLightAllDigitalSegments(true, true, true, true, true, true, true, true, false);break;case "9.":HighLightAllDigitalSegments(true, true, true, true, false, true, true, true, false);break;case ":":case ":":HighLightAllDigitalSegments(false, false, false, false, false, false, false, false, true);break;case "-":HighLightAllDigitalSegments(false, false, false, false, false, false, true, false, false);break;default:throw new Exception("输入字符错误!");}}private void UpdateAllSegmentsBrush(){UpdateSegmentBrush(_topSegment);UpdateSegmentBrush(_upLeftSegment);UpdateSegmentBrush(_upRightSegment);UpdateSegmentBrush(_middleSegment);UpdateSegmentBrush(_downLeftSegment);UpdateSegmentBrush(_downRightSegment);UpdateSegmentBrush(_bottomSegment);UpdateSegmentBrush(_dotSegment);UpdateSegmentBrush(_colonUpSegment);UpdateSegmentBrush(_colonDownSegment);}private void UpdateSegmentBrush(Path segment){if (segment == null){return;}if (segment.Opacity == 1){segment.Fill = DigitalBrush;}else{segment.Fill = DigitalDimBrush;}}#endregion}

四 多数字控件的组合控件

在实际使用时,我们往往不会只显示一个数字,而是对一个由多个数字组成的值进行显示。

多数字控件对多个单个数字控件进行组合,并通过定义的依赖属性来决定数字的个数,数值显示逻辑等等。

public class LedDigitalPanel : Control{#region Fieldprivate Panel rootPanel;private List<LedDigital> digitalsList = new List<LedDigital>();#endregion#region Dependency properties/// <summary>/// 容器中LED Digital的个数/// </summary>public static readonly DependencyProperty DigitalCountProperty =DependencyProperty.Register("DigitalCount", typeof(int), typeof(LedDigitalPanel),new PropertyMetadata(4, new PropertyChangedCallback(OnDigitalCountPropertyChanged)));/// <summary>/// 依赖属性-获取或设置当前显示的值/// </summary>public static readonly DependencyProperty ValueProperty =DependencyProperty.Register("Value", typeof(string), typeof(LedDigitalPanel),new PropertyMetadata(null, new PropertyChangedCallback(OnValuePropertyChanged)));/// <summary>/// 依赖属性-LED Digital显示颜色/// </summary>public static readonly DependencyProperty DigitalBrushProperty =DependencyProperty.Register("DigitalBrush", typeof(Brush), typeof(LedDigitalPanel),new PropertyMetadata(new SolidColorBrush(Colors.Red), new PropertyChangedCallback(OnDigitalBrushPropertyChange)));public static readonly DependencyProperty DigitalDimBrushProperty= DependencyProperty.Register("DigitalDimBrush", typeof(Brush), typeof(LedDigitalPanel), new PropertyMetadata(new SolidColorBrush(Colors.Red), OnDigitalDimBrushPropertyChanged));public static readonly DependencyProperty DigitalDimOpacityProperty= DependencyProperty.Register("DigitalDimOpacity", typeof(double), typeof(LedDigitalPanel), new PropertyMetadata(0.05, OnDigitalDimOpacityPropertyChanged));/// <summary>/// LED Digital字体的粗细/// </summary>public static readonly DependencyProperty DigitalThicknessProperty =DependencyProperty.Register("DigitalThickness", typeof(double), typeof(LedDigitalPanel),new PropertyMetadata(5.0, new PropertyChangedCallback(OnThicknessPropertyChanged)));/// <summary>/// Segment间的距离/// </summary>public static readonly DependencyProperty SegmentIntervalProperty =DependencyProperty.Register("SegmentInterval", typeof(double), typeof(LedDigitalPanel),new PropertyMetadata(2.0, new PropertyChangedCallback(OnSegmentIntervalPropertyChanged)));/// <summary>/// segment两端的截断长度/// </summary>public static readonly DependencyProperty BevelWidthProperty =DependencyProperty.Register("BevelWidth", typeof(double), typeof(LedDigitalPanel),new PropertyMetadata(2.0, new PropertyChangedCallback(OnBevelWidthPropertyChanged)));#endregion#region Wrapper properties/// <summary>/// 获取或设置容器中LED的个数/// </summary>public int DigitalCount{get{return (int)GetValue(DigitalCountProperty);}set{SetValue(DigitalCountProperty, value);}}/// <summary>/// 获取或设置显示的值/// </summary>public string Value{get{return (string)GetValue(ValueProperty);}set{SetValue(ValueProperty, value);}}/// <summary>/// 获取或设置LED颜色/// </summary>public Brush DigitalBrush{get{return (Brush)GetValue(DigitalBrushProperty);}set{SetValue(DigitalBrushProperty, value);}}public Brush DigitalDimBrush{get{return (Brush)GetValue(DigitalDimBrushProperty);}set{SetValue(DigitalDimBrushProperty, value);}}public double DigitalDimOpacity{get{return (double)GetValue(DigitalDimOpacityProperty);}set{SetValue(DigitalDimOpacityProperty, value);}}/// <summary>/// 获取或设置字体粗细/// </summary>public double DigitalThickness{get{return (double)GetValue(DigitalThicknessProperty);}set{SetValue(DigitalThicknessProperty, value);}}/// <summary>/// 获取或设置间距/// </summary>public double SegmentInterval{get{return (double)GetValue(SegmentIntervalProperty);}set{SetValue(SegmentIntervalProperty, value);}}/// <summary>/// 获取或设置截断长度/// </summary>public double BevelWidth{get{return (double)GetValue(BevelWidthProperty);}set{SetValue(BevelWidthProperty, value);}}#endregion#region Constructorstatic LedDigitalPanel(){DefaultStyleKeyProperty.OverrideMetadata(typeof(LedDigitalPanel), new FrameworkPropertyMetadata(typeof(LedDigitalPanel)));}#endregion#region Dependency Property Changed Callback/// <summary>/// 控件属性值发生变化时调用的方法/// </summary>/// <param name="e"></param>protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e){base.OnPropertyChanged(e);if (e.Property.Name == "Height" || e.Property.Name == "ActualHeight"){for (int i = 0; i < digitalsList.Count; i++){digitalsList[i].Height = (double)e.NewValue;}}else if (e.Property.Name == "Width" || e.Property.Name == "ActualWidth"){double ledWidth = (double)e.NewValue / DigitalCount;for (int i = 0; i < digitalsList.Count; i++){digitalsList[i].Width = ledWidth;}}}/// <summary>/// 当Led数量发生变化时调用的方法/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnDigitalCountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigitalPanel leds = d as LedDigitalPanel;leds.digitalsList.Clear();if (leds.rootPanel != null){leds.rootPanel.Children.Clear();leds.InitDigitals((int)e.NewValue);//将Digitals 加入到rootPanel中foreach (LedDigital digital in leds.digitalsList){leds.rootPanel.Children.Add(digital);}//显示值leds.DisplayData(leds.Value);}}/// <summary>/// 控件的值发生变化时调用的方法/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigitalPanel leds = d as LedDigitalPanel;string newValue = (string)e.NewValue;leds.DisplayData(newValue);}/// <summary>/// LED颜色发生变化时调用的方法/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnDigitalBrushPropertyChange(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigitalPanel leds = d as LedDigitalPanel;for (int i = 0; i < leds.digitalsList.Count; i++){leds.digitalsList[i].DigitalBrush = (Brush)e.NewValue;}}private static void OnDigitalDimBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigitalPanel leds = d as LedDigitalPanel;for (int i = 0; i < leds.digitalsList.Count; i++){leds.digitalsList[i].DigitalDimBrush = (Brush)e.NewValue;}}private static void OnDigitalDimOpacityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigitalPanel leds = d as LedDigitalPanel;for (int i = 0; i < leds.digitalsList.Count; i++){leds.digitalsList[i].DigitalDimOpacity = (double)e.NewValue;}}/// <summary>/// 字体粗细变化时调用的方法/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnThicknessPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigitalPanel leds = d as LedDigitalPanel;for (int i = 0; i < leds.digitalsList.Count; i++){leds.digitalsList[i].DigitalThickness = (double)e.NewValue;}}/// <summary>/// 间距变化时调用的方法/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnSegmentIntervalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigitalPanel leds = d as LedDigitalPanel;for (int i = 0; i < leds.digitalsList.Count; i++){leds.digitalsList[i].SegmentInterval = (double)e.NewValue;}}/// <summary>/// 截断长度变化时调用的方法/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnBevelWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){LedDigitalPanel leds = d as LedDigitalPanel;for (int i = 0; i < leds.digitalsList.Count; i++){leds.digitalsList[i].BevelWidth = (double)e.NewValue;}}#endregion#region Methods/// <summary>/// 调用模板时的方法/// </summary>public override void OnApplyTemplate(){base.OnApplyTemplate();//添加LedInitDigitals(DigitalCount);//获取根布局rootPanel = GetTemplateChild("PART_Root") as Panel;if (rootPanel != null){//将Digitals 加入到rootPanel中foreach (LedDigital digital in digitalsList){rootPanel.Children.Add(digital);}}DisplayData(Value);}/// <summary>/// 添加Led Digitals/// </summary>/// <param name="digitalCount"></param>private void InitDigitals(int digitalCount){digitalsList.Clear();double ledControlWidth = Width / digitalCount;double ledControlHeight = Height;for (int i = 0; i < digitalCount; i++){LedDigital led = new LedDigital();led.Width = ledControlWidth;led.Height = ledControlHeight;led.SegmentInterval = SegmentInterval;led.BevelWidth = BevelWidth;led.DigitalThickness = DigitalThickness;led.DigitalBrush = DigitalBrush;led.DigitalDimBrush = DigitalDimBrush;led.DigitalDimOpacity = DigitalDimOpacity;digitalsList.Add(led);}}/// <summary>/// 显示值/// </summary>/// <param name="value"></param>private void DisplayData(string value){if (value == null){return;}//准备字符List<string> 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];}}}/// <summary>/// 将字符串转换成可显示的单个字符的集合/// </summary>/// <param name="value"></param>/// <returns></returns>private static List<string> ConvertStringToSingleDigitalCharList(string value){char[] charArray = value.ToCharArray();List<string> showStringList = new List<string>();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;}#endregion}

五 默认样式。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:LedDigital.Controls"><Style TargetType="{x:Type local:LedDigitalPanel}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:LedDigitalPanel}"><Border Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"><StackPanel x:Name="PART_Root" Orientation="Horizontal"Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/></Border></ControlTemplate></Setter.Value></Setter></Style><Style TargetType="{x:Type local:LedDigital}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:LedDigital}"><Border Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"><Grid x:Name="PART_Root"></Grid></Border></ControlTemplate></Setter.Value></Setter></Style>
</ResourceDictionary>

六 控件的使用

因为和控件相关的自定义属性全部时依赖属性,因此可以通过绑定的方式来实现控件数据的更新。

<UserControl x:Class="LedDigitalDemo.View.LedDigitalView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:LedDigitalDemo.View"xmlns:dvd="clr-namespace:LedDigital.Controls;assembly=LedDigital"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"><Grid><Grid.RowDefinitions><RowDefinition Height="60"/><RowDefinition/></Grid.RowDefinitions><dvd:LedDigital x:Name="singleDigital" Value="{Binding SingleDigitalValue}" DigitalDimBrush="Blue" DigitalBrush="OrangeRed" Height="60" Width="30" HorizontalAlignment="Left"/><dvd:LedDigitalPanel x:Name="multiDigital" Grid.Row="1" DigitalCount ="19" Margin="20" Height="60" Width="600" HorizontalAlignment="Left"Value="{Binding MultiDigitalValue}" DigitalBrush="{Binding MultiDigitalBrush}" Background="{Binding MultiDigitalBackgroundBrush}"/></Grid>
</UserControl>
    /// LedDigitalView.xaml 的交互逻辑/// </summary>public partial class LedDigitalView : UserControl{public LedDigitalView(){InitializeComponent();DataContext = new LedDigitalViewModel();}}
    public class LedDigitalViewModel : ViewModelBase{#region Fieldsprivate string _singleDigitalValue = "";private string _multiDigitalValue = "";private Brush _multiDigitalBrush = null;private Brush _multiDigitalBackgroundBrush = null;#endregion#region Propertiespublic string SingleDigitalValue{get { return _singleDigitalValue; }set { _singleDigitalValue = value; RaisePropertyChanged("SingleDigitalValue"); }}public string MultiDigitalValue{get{return _multiDigitalValue;}set{_multiDigitalValue = value; RaisePropertyChanged("MultiDigitalValue");}}public Brush MultiDigitalBrush{get{return _multiDigitalBrush;}set{_multiDigitalBrush = value; RaisePropertyChanged("MultiDigitalBrush");}}public Brush MultiDigitalBackgroundBrush{get{return _multiDigitalBackgroundBrush;}set{_multiDigitalBackgroundBrush = value; RaisePropertyChanged("MultiDigitalBackgroundBrush");}}#endregion#region Constructorspublic LedDigitalViewModel(){System.Timers.Timer timer = new System.Timers.Timer();timer.Interval = 1000;timer.Elapsed += Timer_Elapsed;timer.Start();}#endregion#region Private Methodsprivate void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e){Color mutiDitigalForegroundColor = ColorHelper.GetRandomColor();Thread.Sleep(100);Color mutiDigitalBackgroundColor = ColorHelper.GetRandomColor();DispatcherHelper.UIDispatcher.Invoke(() =>{Random r = new Random();SingleDigitalValue = r.Next(0, 9).ToString();MultiDigitalValue = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");MultiDigitalBrush = new SolidColorBrush(mutiDitigalForegroundColor);MultiDigitalBackgroundBrush = new LinearGradientBrush(new GradientStopCollection(){new GradientStop(){ Offset = 0, Color = Colors.White},new GradientStop(){ Offset = 0.5, Color = mutiDigitalBackgroundColor},new GradientStop(){ Offset = 1, Color = Colors.White}}){StartPoint = new Point(0, 0),EndPoint = new Point(0, 1),Opacity = 0.3};});}#endregion#region Public Methods#endregion}

效果图:

WPF数据可视化控件(一) LED风格数字控件相关推荐

  1. WPF自定义LED风格数字显示控件

    WPF自定义LED风格数字显示控件 原文:WPF自定义LED风格数字显示控件 版权声明:本文为博主原创文章,转载请注明作者和出处 https://blog.csdn.net/ZZZWWWPPP1119 ...

  2. 体验Dundas Dashboard数据可视化控件

    概述:Dundas公司开发的Dundas Dashboard是集所有需求于一体的商业智能仪表盘,这款仪表盘控件提供大量完全可定制的交互式图表.仪表.地图和记分卡等等,开启即可使用,这意味着您总能快捷方 ...

  3. 精通 WPF UI Virtualization (提升 OEA 框架中 TreeGrid 控件的性能)

    精通 WPF UI Virtualization (提升 OEA 框架中 TreeGrid 控件的性能) 原文:精通 WPF UI Virtualization (提升 OEA 框架中 TreeGri ...

  4. WPF显示经常使用的几个显示文字控件TextBox, TextBlock, Lable

    WPF显示经常使用的几个显示文字控件TextBox, TextBlock, Lable TextBox, TextBlock. Lable 当中TextBox 和Lable均继承了Control类 能 ...

  5. [转载]潜移默化学会WPF(技巧篇)--具有Items元素的控件子项获取(一)

    潜移默化学会WPF(技巧篇)--具有Items元素的控件子项获取(一) 1. treeview的Item获取 var g = this.tree.ItemContainerGenerator;Tree ...

  6. WPF 获取鼠标屏幕位置、窗口位置、控件位置

    原文:WPF 获取鼠标屏幕位置.窗口位置.控件位置 public struct POINT{public int X;public int Y;public POINT(int x, int y){t ...

  7. WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法

    WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法 原文:WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法 问题描 ...

  8. Winform(C#) 国内开源美化控件主题库2:花木兰控件库

    Winform(C#) 国内开源美化控件主题库2:花木兰控件库 地址 博客:https://www.cnblogs.com/tlmbem/控件的介绍. gitee:https://gitee.com/ ...

  9. wpf中使用控件时,最好给控件取一个好的名字

    wpf中使用控件时,最好给控件取一个好的名字, 比如按钮A可以  取名为 btnA 这样使用会是编程清晰.

最新文章

  1. dispatch js实现_详解vuex中action何时完成以及如何正确调用dispatch的思考
  2. UCSC hg19.ensembl.gtf
  3. lable标签的用途
  4. My SQL出错代码及出错信息对照
  5. 树莓派开机运行python脚本_【树莓派】开机自启动脚本方法之一(.Desktop文件)...
  6. HDU - 3341 Lost's revenge(AC自动机+状压dp)
  7. T-SQL语句之创建、修改、删除数据库
  8. 单身暴击!程序员用 Python 给女朋友写了个翻译软件
  9. python 进程间通信效率_(1)进程间几种通信方式
  10. SQL Server中char与nchar区别
  11. MATLAB矩阵计算大全
  12. 豆瓣8.0高分电影~渣男人格之《剧场》追剧后感
  13. 如何在ASP.NET网络应用实现数据可视化图表
  14. 《领导沟通艺术与真实影响力》感想二
  15. 好程序员Java培训分享如何快速入门Java编程
  16. 【tensorflow】制作自己的数据集
  17. 我与梅西粉丝们的世界杯观球日常
  18. 【在线工具】在线视频压缩工具
  19. 小白求,用RE文件管理器移植移远EC20 4G模块驱动。 有重谢。请加w jiao1998524
  20. chrome提示不安全

热门文章

  1. linux查看目录被进程占用空间,linux lsof命令详解 (查看目录被哪些进程占用)
  2. jupyter notebook打不开浏览器 或出现端口被占用该如何解决
  3. 10k-15k java面试题
  4. 解决Safari浏览器input输入框在disabled不显示文字
  5. foobar2000专辑封面混乱解决方法
  6. 笔记本蓝屏,开不了机的处理记录
  7. vue父子组件动态传值的几种方式
  8. JasperReports ——打印PDF(项目工具)
  9. Java 电影票在线订购系统(93E778)
  10. 华为云函数调用百度ai sdk实现语音合成