WPF Gauge

Copy from WPFDevelopers examples

复制代码
//cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows;
using System.Globalization;

namespace WpfApp140
{
    public class Gauge : RangeBase
    {
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("TitleProperty", typeof(string),
                typeof(Gauge), new PropertyMetadata("Gauge"));

        public static readonly DependencyProperty ValueFormatProperty =
       DependencyProperty.Register("ValueFormat", typeof(string), typeof(Gauge),
           new PropertyMetadata("{0:0}%", OnValueFormatChanged));

        private static void OnValueFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var gauge = d as Gauge;
            gauge?.InvalidateVisual();
        }

        public string ValueFormat
        {
            get { return (string)GetValue(ValueFormatProperty); }
            set { SetValue(ValueFormatProperty, value); }
        }
        public double Thickness
        {
            get { return (double)GetValue(ThicknessProperty); }
            set { SetValue(ThicknessProperty, value); }
        }

        public static readonly DependencyProperty ThicknessProperty =
            DependencyProperty.Register("Thickness", typeof(double), typeof(Gauge),
                new PropertyMetadata(10.0, OnValueFormatChanged));


        static Gauge()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Gauge),
                new FrameworkPropertyMetadata(typeof(Gauge)));
        }

        public Gauge()
        {
            SetValue(ValueProperty, 0.0);
            SetValue(MinimumProperty, 0.0);
            SetValue(MaximumProperty, 100.0);
        }

        protected override void OnValueChanged(double oldValue, double newValue)
        {
            InvalidateVisual();
        }

        protected override void OnMinimumChanged(double oldValue, double newValue)
        {
            InvalidateVisual();
        }

        protected override void OnMaximumChanged(double oldValue, double newValue)
        {
            InvalidateVisual();
        }

        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));
        }

    }


    internal class DrawingContextHelper
    {
        public static FormattedText GetFormattedText(string text, Brush color = null,
            FlowDirection flowDirection = FlowDirection.RightToLeft, double textSize = 12.0D,
            FontWeight fontWeight = default)
        {
            if (fontWeight == default)
            {
                fontWeight = FontWeights.Thin;
            }

            return new FormattedText(
                text,
                CultureInfo.CurrentCulture,
                flowDirection,
                new Typeface(new FontFamily(), FontStyles.Normal, fontWeight, FontStretches.Normal),
                textSize, color == null ? null : color, 1.25)
            {
                MaxLineCount = 1,
                TextAlignment = TextAlignment.Justify,
                Trimming = TextTrimming.CharacterEllipsis
            };
        }
    }
}
复制代码

 

 

复制代码
//xaml
<Window x:Class="WpfApp140.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp140"
        WindowState="Maximized"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <WrapPanel HorizontalAlignment="Center"
               VerticalAlignment="Center">
        <StackPanel VerticalAlignment="Bottom">
            <local:Gauge
                   Title="Gauge1"
                   Width="300"
                   Height="300"
                   Margin="10"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   Background="Black"
                   BorderBrush="Red"
                   FontSize="8"
                   Maximum="120"
                   Minimum="0"
                   Thickness="3"
                   ValueFormat="{}{0:0}"
                   Value="{Binding ElementName=slider1, Path=Value}" />
            <Slider
                   Name="slider1"
                   Width="200"
                   Margin="0,0,0,20"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Bottom"
                   Maximum="120"
                   Minimum="0" />
        </StackPanel>

        <StackPanel>
            <local:Gauge
                Title="Gauge2"
                Width="300"
                Height="300"
                Margin="10"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                ValueFormat="{}{0:0}%"
                Value="{Binding ElementName=slider2,Path=Value}"/>
            <Slider x:Name="slider2"
                    Width="200"
                    Margin="0,0,0,20"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Bottom"
                    Maximum="100"
                    Minimum="0"/>
        </StackPanel>

        <StackPanel VerticalAlignment="Bottom">
            <local:Gauge
                Title="Gauge3"
                Width="300"
                Height="300"
                Margin="10"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Background="Black"
                BorderBrush="DodgerBlue"
                FontSize="8"
                Maximum="90"
                Minimum="30"
                Thickness="3"
                ValueFormat="{}{0:0}"
                Value="{Binding ElementName=slider3,Path=Value}"/>
            <Slider x:Name="slider3"
                    Width="200"
                    Margin="0,0,0,20"
                    HorizontalAlignment="Center"
                    Maximum="90"
                    Minimum="30"/>
        </StackPanel>

    </WrapPanel>
</Window>
复制代码

 

 

 

 

 

posted @   FredGrit  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2020-01-19 C# aggregateexception flatten innerexceptions
点击右上角即可分享
微信分享提示