WPF自定义LED风格数字显示控件
前段时间做过一个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>
/// <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风格数字显示控件的更多相关文章
- 【C#】wpf自定义calendar日期选择控件的样式
原文:[C#]wpf自定义calendar日期选择控件的样式 首先上图看下样式 原理 总览 ItemsControl内容的生成 实现 界面的实现 后台ViewModel的实现 首先上图,看下样式 原理 ...
- WPF 自定义ItemsControl/ListBox/ListView控件样式
一.前言 ItemsControl.ListBox.ListView这三种控件在WPF中都可作为列表信息展示控件.我们可以通过修改这三个控件的样式来展示我们的列表信息. 既然都是展示列表信息的控件,那 ...
- WPF 自定义TextBox带水印控件,可设置圆角
一.简单设置水印TextBox控件,废话不多说看代码: <TextBox TextWrapping="Wrap" Margin="10" Height=& ...
- 使WPF程序应用预置的控件风格, 如Aero, Luna, Royale, Classic等
原文:使WPF程序应用预置的控件风格, 如Aero, Luna, Royale, Classic等 WPF预设有Aero, Classic, Luna, Royale主题, WPF程序会根据 ...
- WPF自定义控件第一 - 进度条控件
本文主要针对WPF新手,高手可以直接忽略,更希望高手们能给出一些更好的实现思路. 前期一个小任务需要实现一个类似含步骤进度条的控件.虽然对于XAML的了解还不是足够深入,还是摸索着做了一个.这篇文章介 ...
- Matlab安装记录 - LED Control Activex控件安装
Matlab安装记录-LED Control Activex控件安装 2013-12-01 22:06:36 最近在研究Matlab GUI技术,准备用于制作上位机程序:在Matlab GUI的技术 ...
- WPF Timeline简易时间轴控件的实现
原文:WPF Timeline简易时间轴控件的实现 效果图: 由于整个控件是实现之后才写的教程,因此这里记录的代码是最终实现后的,前后会引用到其他的一些依赖属性或者代码,需要阅读整篇文章. 1.确定T ...
- 获取 AlertDialog自定义的布局 的控件
AlertDialog自定义的布局 效果图: 创建dialog方法的代码如下: 1 LayoutInflater inflater = getLayoutInflater(); 2 View layo ...
- 使用VideoView自定义一个播放器控件
介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了.在写VideoView播放视频时候定义控制的代码全写在Actv ...
随机推荐
- URL validation failed. The error could have been caused through the use of the browser's navigation
URL validation failed. The error could have been caused through the use of the browser's navigation ...
- C++实践參考——二进制文件浏览器
[项目-二进制文件浏览器] (1)做一个相似BinaryViewer的查看二进制文件的程序.输入文件名称后,能够以16进制和ASCII对比的方式列出该文件的内容.能够參考下图: 提示:循环中,一次读入 ...
- VS2008的C++TR1库已经支持正则表达式
作者:朱金灿 来源:http://blog.csdn.net/clever101 发现VS2008的C++ TR1库已经支持正则表达式了(注意装了VS 2008sp1采用TR1库的).下面简单做个测试 ...
- Android 圆角ListView
方法一:定义 <?xml version ="1.0" encoding ="UTF-8" ?> <shape xmlns:android = ...
- 小梦词典WP8.1应用发布
这几天一直在做这款应用,今天终于发布了! 小梦词典简介: 小梦词典是一款永久免费无广告的网络词典. 支持英汉单词查询: 支持中,英,法,韩,德,俄,日七国语言翻译,多语言极致体验: 支持生词本记忆,查 ...
- 利用QPainter绘制各种图形(Shape, Pen 宽带,颜色,风格,Cap,Join,刷子)
利用QPainter绘制各种图形 Qt的二维图形引擎是基于QPainter类的.QPainter既可以绘制几何形状(点.线.矩形.椭圆.弧形.弦形.饼状图.多边形和贝塞尔曲线),也可以绘制像素映射.图 ...
- 概念的理解 —— 奇点(singularity point)、第一性原理(first principle)
奇点(singularity point)一词出现在不同的环境里,对应着不同的含义: wikipedia:Singularity 文艺作品: 未来学(Futurology):比如雷·库兹韦尔的< ...
- iOS中js与objective-c的简单交互
1.首先是objective-c调用js中的代码,可以用UIWebview中的一个方法 stringByEvaluatingJavaScriptFromString:后面接的是js中的方法名.这个函数 ...
- Database Vault注冊
默认情况下,在安装好Database Vault组件的Oracle HOme下创建的数据库是没有注冊Database Vault的. 1)停EM.监听.数据库 2)启用Database Vault 在 ...
- Cocos2d-x V3.2+Cocos Studio1.6 实现一个简单的uibutton点击功能
好久没写博客了 这几天在学习cocos studio,这个软件可以很方便的设计游戏的一些界面,并导入到cocos2dx中,今天就用button来做个样例 首先我们打开Cocos Studio1.6,选 ...