原文:WPF自定义LED风格数字显示控件

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

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

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

     

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

      

       其中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<Point> _points = new List<Point>(); public List<Point> 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 /// <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 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;
} /// <summary>
/// 根据输入字符显示相应的字符
/// </summary>
/// <param name="digital"></param>
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方法得到各自的点集合,并存储到字典里面。

    /// <summary>
/// 初始化Segments的点集
/// </summary>
private void SetSegmentsData()
{
dp = new DigitalParam();
dp.BevelWidth = BevelWidth;
dp.SegmentInterval = SegmentInterval;
dp.SegmentThickness = LEDThickness;
dp.DigitalHeight = LEDHeight;
dp.DigitalWidth = LEDWidth;<pre class="csharp" name="code"> 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。

    /// <summary>
/// 画直线段
/// </summary>
/// <param name="points"></param>
/// <param name="clr"></param>
/// <returns></returns>
private static Path DrawLine(List<Point> 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;
} /// <summary>
/// 画圆点
/// </summary>
/// <param name="p"></param>
/// <param name="radius"></param>
/// <param name="clr"></param>
/// <returns></returns>
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的数据,

    <Style TargetType="{x:Type local:DigitalPanelControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DigitalPanelControl}">
<StackPanel x:Name="LayoutRoot" Orientation="Horizontal"
Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

       同样在该类中定义了一系列的依赖属性用于控件的数据绑定,包括Digital的个数,控件的宽、高,背景色、前景色等等,同时定义了一系列属性改变回调方法和属性包装器。

        在OnApplyTemplate方法中,会根据当前的DigitalCount数据添加相应个数的DigitalContral到根容器,然后调用DisplayData方法显示数据

        /// <summary>
/// 调用模板时的方法
/// </summary>
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

        /// <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;
}


       最终的效果图:

      

源代码

WPF自定义LED风格数字显示控件的更多相关文章

  1. 【C#】wpf自定义calendar日期选择控件的样式

    原文:[C#]wpf自定义calendar日期选择控件的样式 首先上图看下样式 原理 总览 ItemsControl内容的生成 实现 界面的实现 后台ViewModel的实现 首先上图,看下样式 原理 ...

  2. WPF 自定义ItemsControl/ListBox/ListView控件样式

    一.前言 ItemsControl.ListBox.ListView这三种控件在WPF中都可作为列表信息展示控件.我们可以通过修改这三个控件的样式来展示我们的列表信息. 既然都是展示列表信息的控件,那 ...

  3. WPF 自定义TextBox带水印控件,可设置圆角

    一.简单设置水印TextBox控件,废话不多说看代码: <TextBox TextWrapping="Wrap" Margin="10" Height=& ...

  4. 使WPF程序应用预置的控件风格, 如Aero, Luna, Royale, Classic等

    原文:使WPF程序应用预置的控件风格, 如Aero, Luna, Royale, Classic等      WPF预设有Aero, Classic, Luna, Royale主题, WPF程序会根据 ...

  5. WPF自定义控件第一 - 进度条控件

    本文主要针对WPF新手,高手可以直接忽略,更希望高手们能给出一些更好的实现思路. 前期一个小任务需要实现一个类似含步骤进度条的控件.虽然对于XAML的了解还不是足够深入,还是摸索着做了一个.这篇文章介 ...

  6. Matlab安装记录 - LED Control Activex控件安装

    Matlab安装记录-LED Control Activex控件安装 2013-12-01  22:06:36 最近在研究Matlab GUI技术,准备用于制作上位机程序:在Matlab GUI的技术 ...

  7. WPF Timeline简易时间轴控件的实现

    原文:WPF Timeline简易时间轴控件的实现 效果图: 由于整个控件是实现之后才写的教程,因此这里记录的代码是最终实现后的,前后会引用到其他的一些依赖属性或者代码,需要阅读整篇文章. 1.确定T ...

  8. 获取 AlertDialog自定义的布局 的控件

    AlertDialog自定义的布局 效果图: 创建dialog方法的代码如下: 1 LayoutInflater inflater = getLayoutInflater(); 2 View layo ...

  9. 使用VideoView自定义一个播放器控件

    介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了.在写VideoView播放视频时候定义控制的代码全写在Actv ...

随机推荐

  1. URL validation failed. The error could have been caused through the use of the browser&#39;s navigation

    URL validation failed. The error could have been caused through the use of the browser's navigation ...

  2. C++实践參考——二进制文件浏览器

    [项目-二进制文件浏览器] (1)做一个相似BinaryViewer的查看二进制文件的程序.输入文件名称后,能够以16进制和ASCII对比的方式列出该文件的内容.能够參考下图: 提示:循环中,一次读入 ...

  3. VS2008的C++TR1库已经支持正则表达式

    作者:朱金灿 来源:http://blog.csdn.net/clever101 发现VS2008的C++ TR1库已经支持正则表达式了(注意装了VS 2008sp1采用TR1库的).下面简单做个测试 ...

  4. Android 圆角ListView

    方法一:定义 <?xml version ="1.0" encoding ="UTF-8" ?> <shape xmlns:android = ...

  5. 小梦词典WP8.1应用发布

    这几天一直在做这款应用,今天终于发布了! 小梦词典简介: 小梦词典是一款永久免费无广告的网络词典. 支持英汉单词查询: 支持中,英,法,韩,德,俄,日七国语言翻译,多语言极致体验: 支持生词本记忆,查 ...

  6. 利用QPainter绘制各种图形(Shape, Pen 宽带,颜色,风格,Cap,Join,刷子)

    利用QPainter绘制各种图形 Qt的二维图形引擎是基于QPainter类的.QPainter既可以绘制几何形状(点.线.矩形.椭圆.弧形.弦形.饼状图.多边形和贝塞尔曲线),也可以绘制像素映射.图 ...

  7. 概念的理解 —— 奇点(singularity point)、第一性原理(first principle)

    奇点(singularity point)一词出现在不同的环境里,对应着不同的含义: wikipedia:Singularity 文艺作品: 未来学(Futurology):比如雷·库兹韦尔的< ...

  8. iOS中js与objective-c的简单交互

    1.首先是objective-c调用js中的代码,可以用UIWebview中的一个方法 stringByEvaluatingJavaScriptFromString:后面接的是js中的方法名.这个函数 ...

  9. Database Vault注冊

    默认情况下,在安装好Database Vault组件的Oracle HOme下创建的数据库是没有注冊Database Vault的. 1)停EM.监听.数据库 2)启用Database Vault 在 ...

  10. Cocos2d-x V3.2+Cocos Studio1.6 实现一个简单的uibutton点击功能

    好久没写博客了 这几天在学习cocos studio,这个软件可以很方便的设计游戏的一些界面,并导入到cocos2dx中,今天就用button来做个样例 首先我们打开Cocos Studio1.6,选 ...