WPF實(shí)現(xiàn)自繪儀表盤Gauge
自繪儀表盤 Gauge
框架支持.NET4 至 .NET8;
Visual Studio 2022;
WPFDevelopers 1.1.0.3-preview 版本已正式發(fā)布!
該版本為預(yù)覽版,歡迎各位開發(fā)者下載并體驗(yàn)。
來自 Issue
邏輯實(shí)現(xiàn)
Gauge
繼承 RangeBase
,支持設(shè)置最大值、最小值以及當(dāng)前值,并且可以顯示一個(gè)動態(tài)的百分比值、標(biāo)題、刻度。
1. 新增 Gauge.cs
依賴屬性
Title
:儀表盤的標(biāo)題,默認(rèn)值為 "WD
"。ValueFormat
:定義數(shù)值顯示的格式({0:0}%
),以百分比形式顯示當(dāng)前值。Thickness
:設(shè)置儀表盤的邊框,默認(rèn)值10
。
public string Title { get { return (string)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } public string ValueFormat { get { return (string)GetValue(ValueFormatProperty); } set { SetValue(ValueFormatProperty, value); } } public double Thickness { get { return (double)GetValue(ThicknessProperty); } set { SetValue(ThicknessProperty, value); } }
RangeBase
依賴屬性
Value
:設(shè)置為0.0
,表示當(dāng)前的儀表盤值(默認(rèn)0%
)。Minimum
:設(shè)置為0.0
,表示儀表盤的最小值。Maximum
:設(shè)置為100.0
,表示儀表盤的最大值。
public Gauge() { SetValue(ValueProperty, 0.0); SetValue(MinimumProperty, 0.0); SetValue(MaximumProperty, 100.0); }
2. 重寫 OnRender 方法繪制控件
背景:使用 Ellipse
作為儀表盤的背景,背景色通過 Background
屬性設(shè)置,默認(rèn)為 #293950
。
指針:根據(jù)當(dāng)前值(Value
),計(jì)算出指針的角度
,并使用 DrawLine
繪制紅色指針。
外邊框:使用漸變顏色繪制儀表盤的外邊框。
刻度和標(biāo)簽:繪制了從最小值到最大值的刻度線,并在每個(gè)刻度繪制了對應(yīng)的刻度值。
當(dāng)前值顯示:根據(jù) ValueFormat
屬性格式化顯示當(dāng)前值,并顯示在儀表盤的底部位置。
protected override void OnRender(DrawingContext drawingContext) { base.OnRender(drawingContext); if (Background == null) Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#293950")); var width = ActualWidth; var height = ActualHeight; var radius = Math.Min(width, height) / 2; drawingContext.DrawEllipse(Background, new Pen(Background, Thickness), new Point(width / 2, height / 2), radius, radius); var normalizedValue = (Value - Minimum) / (Maximum - Minimum); var mappedAngle = -220 + normalizedValue * 260; var angleInRadians = mappedAngle * Math.PI / 180; var pointerLength = radius * 0.7; var pointerX = width / 2 + pointerLength * Math.Cos(angleInRadians); var pointerY = height / 2 + pointerLength * Math.Sin(angleInRadians); drawingContext.DrawLine(new Pen(Brushes.Red, 2), new Point(width / 2, height / 2), new Point(pointerX, pointerY)); drawingContext.DrawEllipse(Brushes.White, new Pen(Brushes.Red, 2), new Point(width / 2, height / 2), width / 20, width / 20); var pathGeometry = new PathGeometry(); var startAngle = -220; angleInRadians = startAngle * Math.PI / 180; var startX = width / 2 + radius * Math.Cos(angleInRadians); var startY = height / 2 + radius * Math.Sin(angleInRadians); var pathFigure = new PathFigure() { StartPoint = new Point(startX, startY), }; var endAngle = 40; angleInRadians = endAngle * Math.PI / 180; var endX = width / 2 + radius * Math.Cos(angleInRadians); var endY = height / 2 + radius * Math.Sin(angleInRadians); var isLargeArc = (endAngle - startAngle > 180); var arcSegment = new ArcSegment() { Point = new Point(endX, endY), Size = new Size(radius, radius), RotationAngle = 0, SweepDirection = SweepDirection.Clockwise, IsLargeArc = isLargeArc, }; pathFigure.Segments.Add(arcSegment); pathGeometry.Figures.Add(pathFigure); if (BorderBrush == null) { var gradientBrush = new LinearGradientBrush { StartPoint = new Point(0, 0), EndPoint = new Point(1, 0) }; gradientBrush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#37D2C2"), 0.0)); gradientBrush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#5AD2B2"), 0.01)); gradientBrush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#B77D29"), 0.49)); gradientBrush.GradientStops.Add(new GradientStop(Colors.Red, 1.0)); gradientBrush.Freeze(); BorderBrush = gradientBrush; } drawingContext.DrawGeometry(null, new Pen(BorderBrush, Thickness), pathGeometry); var tickLength = radius * 0.1; var step = (Maximum - Minimum) / 10; for (int i = 0; i <= 10; i++) { var angle = startAngle + (i * (endAngle - startAngle) / 10); var tickStartX = width / 2 + (radius - tickLength) * Math.Cos(angle * Math.PI / 180); var tickStartY = height / 2 + (radius - tickLength) * Math.Sin(angle * Math.PI / 180); var tickEndX = width / 2 + (radius + Thickness / 2) * Math.Cos(angle * Math.PI / 180); var tickEndY = height / 2 + (radius + Thickness / 2) * Math.Sin(angle * Math.PI / 180); drawingContext.DrawLine(new Pen(Brushes.White, 2), new Point(tickStartX, tickStartY), new Point(tickEndX, tickEndY)); var labelValue = Minimum + step * i; var formattedText = DrawingContextHelper.GetFormattedText(labelValue.ToString(),Brushes.White, FlowDirection.LeftToRight,FontSize); var labelRadius = radius - tickLength * 2; var labelX = width / 2 + labelRadius * Math.Cos(angle * Math.PI / 180) - formattedText.Width / 2; var labelY = height / 2 + labelRadius * Math.Sin(angle * Math.PI / 180) - formattedText.Height / 2; drawingContext.DrawText(formattedText, new Point(labelX, labelY)); } var formattedValue = "{0:0}%"; try { formattedValue = string.Format(ValueFormat, Value); } catch (FormatException ex) { throw new InvalidOperationException("Formatting failed ", ex); } var currentValueText = DrawingContextHelper.GetFormattedText(formattedValue, Brushes.White, FlowDirection.LeftToRight, FontSize * 2); var valueX = width / 2 - currentValueText.Width / 2; var valueY = height / 2 + radius * 0.4; drawingContext.DrawText(currentValueText, new Point(valueX, valueY)); var titleValue = DrawingContextHelper.GetFormattedText(Title, Brushes.White, FlowDirection.LeftToRight, FontSize); valueX = width / 2 - titleValue.Width / 2; valueY = height / 2 + radius * 0.8; drawingContext.DrawText(titleValue, new Point(valueX, valueY)); }
XAML 示例
示例引入 WPFDevelopers 1.1.0.3-preview
的 Nuget
正式包
<WrapPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel VerticalAlignment="Bottom"> <wd:Gauge Title="Min" Width="100" Height="100" Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Black" BorderBrush="Red" FontSize="8" Maximum="90" Minimum="30" Thickness="3" ValueFormat="{}{0:0}值" Value="{Binding ElementName=MySlider2, Path=Value}" /> <Slider Name="MySlider2" Width="200" Margin="0,0,0,20" HorizontalAlignment="Center" VerticalAlignment="Bottom" Maximum="90" Minimum="30" /> </StackPanel> <StackPanel> <wd:Gauge Title="反對率" Width="200" Height="200" Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center" ValueFormat="{}{0:0}%" Value="{Binding ElementName=MySlider, Path=Value}" /> <Slider Name="MySlider" Width="200" Margin="0,0,0,20" HorizontalAlignment="Center" VerticalAlignment="Bottom" Maximum="100" Minimum="0" /> </StackPanel> <StackPanel VerticalAlignment="Bottom"> <wd:Gauge Title="Max" Width="100" Height="100" Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Black" BorderBrush="DodgerBlue" FontSize="8" Maximum="90" Minimum="30" Thickness="3" ValueFormat="{}{0:0}值" Value="{Binding ElementName=MySlider3, Path=Value}" /> <Slider Name="MySlider3" Width="200" Margin="0,0,0,20" HorizontalAlignment="Center" VerticalAlignment="Bottom" Maximum="90" Minimum="30" /> </StackPanel> </WrapPanel>
效果圖
到此這篇關(guān)于WPF實(shí)現(xiàn)自繪儀表盤Gauge的文章就介紹到這了,更多相關(guān)WPF自繪儀表盤內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SMTP客戶端未通過身份驗(yàn)證等多種錯(cuò)誤解決方案分享
這篇文章主要介紹了SMTP服務(wù)器要求安全連接或客戶端未通過身份驗(yàn)證的多種解決方案,感興趣的小伙伴們可以參考一下2016-05-05WinForm實(shí)現(xiàn)移除控件某個(gè)事件的方法
這篇文章主要介紹了WinForm實(shí)現(xiàn)移除控件某個(gè)事件的方法,對C#初學(xué)者有一定的借鑒價(jià)值,需要的朋友可以參考下2014-08-08C#中的小數(shù)和百分?jǐn)?shù)計(jì)算與byte數(shù)組操作
這篇文章介紹了C#中的小數(shù)和百分?jǐn)?shù)計(jì)算與byte數(shù)組操作,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04C#讀取txt文件數(shù)據(jù)的方法實(shí)例
讀取txt文本數(shù)據(jù)的內(nèi)容,是我們開發(fā)中經(jīng)常會遇到的一個(gè)功能,這篇文章主要給大家介紹了關(guān)于C#讀取txt文件數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2021-05-05