WPF Gauge Value changed via System.Timers.Timer
//cs using System; using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Media; namespace WPFDevelopers.Controls { 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("WD")); 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)); } } } //xaml <Window x:Class="WpfApp141.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:WpfApp141" WindowState="Maximized" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <local:Gauge x:Name="gauge1" Width="800" Height="800" Minimum="0" Maximum="100" Background="Cyan" BorderBrush="Blue" BorderThickness="5" FontSize="20" ValueFormat="{}{0:0}" Value="{Binding GaugeValue,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/> </Grid> </Window> //xaml.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp141 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window, INotifyPropertyChanged { Random rnd { get; set; } public MainWindow() { InitializeComponent(); this.DataContext = this; rnd = new Random(); System.Timers.Timer tmr = new System.Timers.Timer(); tmr.Elapsed += Tmr_Elapsed; tmr.Interval = 1000; tmr.Start(); } private void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { GaugeValue = rnd.NextDouble() * 100; } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) { handler?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } private double gaugeValue; public double GaugeValue { get { return gaugeValue; } set { if(value!=gaugeValue) { gaugeValue = value; OnPropertyChanged(nameof(GaugeValue)); } } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2020-01-19 C# aggregateexception flatten innerexceptions