WPF實現(xiàn)自繪儀表盤Gauge
自繪儀表盤 Gauge
框架支持.NET4 至 .NET8;
Visual Studio 2022;
WPFDevelopers 1.1.0.3-preview 版本已正式發(fā)布!
該版本為預(yù)覽版,歡迎各位開發(fā)者下載并體驗。
來自 Issue
邏輯實現(xiàn)
Gauge 繼承 RangeBase,支持設(shè)置最大值、最小值以及當前值,并且可以顯示一個動態(tài)的百分比值、標題、刻度。
1. 新增 Gauge.cs
依賴屬性
Title:儀表盤的標題,默認值為 "WD"。ValueFormat:定義數(shù)值顯示的格式({0:0}%),以百分比形式顯示當前值。Thickness:設(shè)置儀表盤的邊框,默認值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,表示當前的儀表盤值(默認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è)置,默認為 #293950。
指針:根據(jù)當前值(Value),計算出指針的角度,并使用 DrawLine 繪制紅色指針。
外邊框:使用漸變顏色繪制儀表盤的外邊框。
刻度和標簽:繪制了從最小值到最大值的刻度線,并在每個刻度繪制了對應(yīng)的刻度值。
當前值顯示:根據(jù) ValueFormat 屬性格式化顯示當前值,并顯示在儀表盤的底部位置。
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實現(xiàn)自繪儀表盤Gauge的文章就介紹到這了,更多相關(guān)WPF自繪儀表盤內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#中的小數(shù)和百分數(shù)計算與byte數(shù)組操作
這篇文章介紹了C#中的小數(shù)和百分數(shù)計算與byte數(shù)組操作,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04

