WPF实现自绘仪表盘Gauge

 更新时间:2024年12月31日 10:12:42   作者:WPF开发者  
这篇文章主要为大家详细介绍了如何使用WPF实现自绘仪表盘Gauge效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

自绘仪表盘 Gauge

框架支持.NET4 至 .NET8;

Visual Studio 2022;

WPFDevelopers 1.1.0.3-preview 版本已正式发布!

该版本为预览版,欢迎各位开发者下载并体验。

来自 Issue

逻辑实现

Gauge 继承 RangeBase,支持设置最大值、最小值以及当前值,并且可以显示一个动态的百分比值、标题、刻度。

1. 新增 Gauge.cs

依赖属性

  • Title:仪表盘的标题,默认值为 "WD"。
  • ValueFormat:定义数值显示的格式( {0:0}%),以百分比形式显示当前值。
  • Thickness:设置仪表盘的边框,默认值 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:设置为 0.0,表示当前的仪表盘值(默认 0%)。
  • Minimum:设置为 0.0,表示仪表盘的最小值。
  • Maximum:设置为 100.0,表示仪表盘的最大值。
public Gauge()
{
    SetValue(ValueProperty, 0.0);
    SetValue(MinimumProperty, 0.0);
    SetValue(MaximumProperty, 100.0);
}

2. 重写 OnRender 方法绘制控件

背景:使用 Ellipse 作为仪表盘的背景,背景色通过 Background 属性设置,默认为 #293950

指针:根据当前值(Value),计算出指针的角度,并使用 DrawLine 绘制红色指针。

外边框:使用渐变颜色绘制仪表盘的外边框。

刻度和标签:绘制了从最小值到最大值的刻度线,并在每个刻度绘制了对应的刻度值。

当前值显示:根据 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>

效果图

到此这篇关于WPF实现自绘仪表盘Gauge的文章就介绍到这了,更多相关WPF自绘仪表盘内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • unity实现延迟回调工具

    unity实现延迟回调工具

    这篇文章主要为大家详细介绍了unity实现延迟回调工具,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • C# Csv实现基本的读写和转换DataTable

    C# Csv实现基本的读写和转换DataTable

    本文主要介绍了C# Csv实现基本的读写和转换DataTable,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • C#网站生成静态页面的实例讲解

    C#网站生成静态页面的实例讲解

    今天小编就为大家分享一篇关于C#网站生成静态页面的实例讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • 解析C#中如何把控件的边框角画为圆弧

    解析C#中如何把控件的边框角画为圆弧

    以下是对C#中把控件的边框角画为圆弧的实现代码进行了介绍,需要的朋友可以参考下
    2013-07-07
  • C#多线程系列之任务基础(二)

    C#多线程系列之任务基础(二)

    本文详细讲解了C#多线程的任务基础,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-02-02
  • C#入门教程之集合ArrayList用法详解

    C#入门教程之集合ArrayList用法详解

    这篇文章主要介绍了C#入门教程之集合ArrayList用法,结合具体实例分析了C#中集合的概念、功能、创建与使用方法,需要的朋友可以参考下
    2017-06-06
  • C#中的问号(?号)用法小结

    C#中的问号(?号)用法小结

    这篇文章主要介绍了C#中的问号(?号)用法小结,本文介绍了3种用法,分别作为修饰符、运算符的用法,需要的朋友可以参考下
    2014-07-07
  • C#开发可播放摄像头及任意格式视频的播放器

    C#开发可播放摄像头及任意格式视频的播放器

    这篇文章主要介绍了C#开发可播放摄像头及任意格式视频的播放器 ,在WPF中,借助Vlc.DotNet调用VLC类库,实现视频播功能。需要的朋友可以参考下
    2019-08-08
  • C#中增加SQLite事务操作支持与使用方法

    C#中增加SQLite事务操作支持与使用方法

    这篇文章主要介绍了C#中增加SQLite事务操作支持与使用方法,结合实例形式分析了C#中针对SQLite事务操作的添加及使用技巧,需要的朋友可以参考下
    2017-07-07
  • newtonsoft.json解析天气数据出错解决方法

    newtonsoft.json解析天气数据出错解决方法

    这篇文章主要介绍了NewtonSoft.JSon解析天气数据时出错的解决方法,需要的朋友可以参考下
    2014-02-02

最新评论