WPF数据可视化控件(一) LED风格数字控件
原帖地址: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风格数字控件相关推荐
- WPF自定义LED风格数字显示控件
WPF自定义LED风格数字显示控件 原文:WPF自定义LED风格数字显示控件 版权声明:本文为博主原创文章,转载请注明作者和出处 https://blog.csdn.net/ZZZWWWPPP1119 ...
- 体验Dundas Dashboard数据可视化控件
概述:Dundas公司开发的Dundas Dashboard是集所有需求于一体的商业智能仪表盘,这款仪表盘控件提供大量完全可定制的交互式图表.仪表.地图和记分卡等等,开启即可使用,这意味着您总能快捷方 ...
- 精通 WPF UI Virtualization (提升 OEA 框架中 TreeGrid 控件的性能)
精通 WPF UI Virtualization (提升 OEA 框架中 TreeGrid 控件的性能) 原文:精通 WPF UI Virtualization (提升 OEA 框架中 TreeGri ...
- WPF显示经常使用的几个显示文字控件TextBox, TextBlock, Lable
WPF显示经常使用的几个显示文字控件TextBox, TextBlock, Lable TextBox, TextBlock. Lable 当中TextBox 和Lable均继承了Control类 能 ...
- [转载]潜移默化学会WPF(技巧篇)--具有Items元素的控件子项获取(一)
潜移默化学会WPF(技巧篇)--具有Items元素的控件子项获取(一) 1. treeview的Item获取 var g = this.tree.ItemContainerGenerator;Tree ...
- WPF 获取鼠标屏幕位置、窗口位置、控件位置
原文:WPF 获取鼠标屏幕位置.窗口位置.控件位置 public struct POINT{public int X;public int Y;public POINT(int x, int y){t ...
- WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法
WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法 原文:WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法 问题描 ...
- Winform(C#) 国内开源美化控件主题库2:花木兰控件库
Winform(C#) 国内开源美化控件主题库2:花木兰控件库 地址 博客:https://www.cnblogs.com/tlmbem/控件的介绍. gitee:https://gitee.com/ ...
- wpf中使用控件时,最好给控件取一个好的名字
wpf中使用控件时,最好给控件取一个好的名字, 比如按钮A可以 取名为 btnA 这样使用会是编程清晰.
最新文章
- dispatch js实现_详解vuex中action何时完成以及如何正确调用dispatch的思考
- UCSC hg19.ensembl.gtf
- lable标签的用途
- My SQL出错代码及出错信息对照
- 树莓派开机运行python脚本_【树莓派】开机自启动脚本方法之一(.Desktop文件)...
- HDU - 3341 Lost's revenge(AC自动机+状压dp)
- T-SQL语句之创建、修改、删除数据库
- 单身暴击!程序员用 Python 给女朋友写了个翻译软件
- python 进程间通信效率_(1)进程间几种通信方式
- SQL Server中char与nchar区别
- MATLAB矩阵计算大全
- 豆瓣8.0高分电影~渣男人格之《剧场》追剧后感
- 如何在ASP.NET网络应用实现数据可视化图表
- 《领导沟通艺术与真实影响力》感想二
- 好程序员Java培训分享如何快速入门Java编程
- 【tensorflow】制作自己的数据集
- 我与梅西粉丝们的世界杯观球日常
- 【在线工具】在线视频压缩工具
- 小白求,用RE文件管理器移植移远EC20 4G模块驱动。 有重谢。请加w jiao1998524
- chrome提示不安全