?????? 前段時間做過一個WPF的項目,需要用到這樣一個控件,在網上找了一下沒找到特別合適的,要么有的不是WPF的控件(無法使用WPF的綁定功能),要么有的是WPF的但是效果不太好,或者不滿足需求(比如沒有小數點.或者冒號:)。所以就參考著別人的東西自己做了一個。
?????? 本文大部分屬于原創內容,但是在圖像繪制的時候參考(其實是照搬)了這個文章里的內容:A Fine-looking Segmented LED Control
labview控件中怎么全部顯示??????
??????一 整個控件庫的目錄結構:
??????
?????? 其中DigitalControl.cs為單個LED數字控件,DigitalPanelControl.cs為多個DigitalControl的組合控件,Segment目錄下的類為數字的7條邊和冒號以及小數點的類。Data目錄下的DigitalData類存放一個DigitalControl的Segment數據,而DigitalParam存放單個DigitalControl的基本屬性,包括寬,高,線條粗細以及間距等。
自己實現控件?
?????? 控件構造的基本思路為:通過單個控件的基本屬性參數(寬、高、粗細等)——也就是DigitalParam類中的數據——去構造組成單個數字的10個Segment,然后通過組合Segment得到DigitalControl,再通過組合DigitControl得到DigitalPanelControl,普通的控件使用者主要是使用DigitalPanelControl。
?????? 二 Segment類?
??????
wpf style?
??????? ===========================
??????
wpf字體顏色、?????? Segment類為數字的7條邊(如上圖)以及冒號(:)的兩個點和小數點,一共10個子類,他們繼承共同的父類Segments,由一個或多個點構成的Path組成,每個Segment的畫法如上圖所示。
?????? (注:上圖照搬自 這里?)
?????? Segment類為一個抽象父類,類中定義了一個List<Point>列表,用來存放構成Segment的點,然后聲明了一個GetPoints的虛方法,在其子類中復寫了此方法,主要用于通過給定的DigitalParam數據得到Segment所需要的點(填充_points)。
wpf樣式??????? 下面是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類
WPF控件,?????? 該類為單個數字控件的數據類,主要包括10個支持數字顯示的Segment的字段和屬性,以及兩個控制數字顯示內容的方法。其中DisplayDigital根據給定的數字字符去調用LightDigitalSegments方法,通過LightDigitalSegments方法設置不同Segment的顯示或隱藏來顯示具體的數字。
public class DigitalData{#region Fieldsprivate 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 Propertiespublic 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類
??????
android自定義開關控件,?????? 單個數字控件的類,繼承自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的點集digitalSegmentDictSetSegmentsData();//畫數字dd = DrawSegments(digitalSegmentDict, LEDColor);//將線段添加到容器AddSegmentsToPanel(dd);dd.DisplayDigital(Value);}
wpf自定義控件教程。
?????? 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。
wpf 控件模板、
/// <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;//添加LedDrawDigitals(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;}
??????
LabVIEW的X控件。
源代碼