WPF DrawingContext温度计
驚鏵 人气:2WPF 使用 DrawingContext 绘制温度计
框架使用大于等于.NET40
;
Visual Studio 2022;
项目使用 MIT 开源许可协议;
定义Interval
步长、MaxValue
最大温度值、MinValue
最小温度值。
CurrentGeometry
重新绘制当前刻度的Path
值。
CurrentValue
当前值如果发生变化时则去重新CurrentGeometry
。
OnRender
绘制如下
RoundedRectangle
温度计的外边框。- 使用方法
DrawText
单字绘制华氏温度
文本Y
轴变化。 - 使用方法
DrawText
单字绘制摄氏温度
文本Y
轴变化。 - 使用方法
DrawText
绘制温度计两侧的刻度数值。 - 使用方法
DrawLine
绘制温度计两侧的刻度线。
实现代码
1) 准备Thermometer.cs如下:
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace WPFDevelopers.Controls { public class Thermometer : Control { public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(double), typeof(Thermometer), new UIPropertyMetadata(40.0)); public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register("MinValue", typeof(double), typeof(Thermometer), new UIPropertyMetadata(-10.0)); /// <summary> /// 当前值 /// </summary> public static readonly DependencyProperty CurrentValueProperty = DependencyProperty.Register("CurrentValue", typeof(double), typeof(Thermometer), new UIPropertyMetadata(OnCurrentValueChanged)); /// <summary> /// 步长 /// </summary> public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register("Interval", typeof(double), typeof(Thermometer), new UIPropertyMetadata(10.0)); /// <summary> /// 当前值的图形坐标点 /// </summary> public static readonly DependencyProperty CurrentGeometryProperty = DependencyProperty.Register("CurrentGeometry", typeof(Geometry), typeof(Thermometer), new PropertyMetadata( Geometry.Parse(@"M 2 132.8 a 4 4 0 0 1 4 -4 h 18 a 4 4 0 0 1 4 4 v 32.2 a 4 4 0 0 1 -4 4 h -18 a 4 4 0 0 1 -4 -4 z"))); /// <summary> /// 构造函数 /// </summary> static Thermometer() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Thermometer), new FrameworkPropertyMetadata(typeof(Thermometer))); } public double MaxValue { get => (double)GetValue(MaxValueProperty); set => SetValue(MaxValueProperty, value); } public double MinValue { get => (double)GetValue(MinValueProperty); set => SetValue(MinValueProperty, value); } public double CurrentValue { get => (double)GetValue(CurrentValueProperty); set { SetValue(CurrentValueProperty, value); PaintPath(); } } public double Interval { get => (double)GetValue(IntervalProperty); set => SetValue(IntervalProperty, value); } public Geometry CurrentGeometry { get => (Geometry)GetValue(CurrentGeometryProperty); set => SetValue(CurrentGeometryProperty, value); } private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var thermometer = d as Thermometer; thermometer.CurrentValue = Convert.ToDouble(e.NewValue); } public override void OnApplyTemplate() { base.OnApplyTemplate(); PaintPath(); } protected override void OnRender(DrawingContext drawingContext) { var brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#82848A")); var rect = new Rect(); rect.Width = 30; rect.Height = 169; drawingContext.DrawRoundedRectangle(Brushes.Transparent, new Pen(brush, 2d), rect, 8d, 8d); #region 华氏温度 drawingContext.DrawText( DrawingContextHelper.GetFormattedText("华", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), textSize: 14D), new Point(-49, 115)); drawingContext.DrawText( DrawingContextHelper.GetFormattedText("氏", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), textSize: 14D), new Point(-49, 115 + 14)); drawingContext.DrawText( DrawingContextHelper.GetFormattedText("温", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), textSize: 14D), new Point(-49, 115 + 28)); drawingContext.DrawText( DrawingContextHelper.GetFormattedText("度", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), textSize: 14D), new Point(-49, 115 + 42)); #endregion #region 摄氏温度 drawingContext.DrawText( DrawingContextHelper.GetFormattedText("摄", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), FlowDirection.LeftToRight, 14D), new Point(75, 115)); drawingContext.DrawText( DrawingContextHelper.GetFormattedText("氏", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), FlowDirection.LeftToRight, 14D), new Point(75, 115 + 14)); drawingContext.DrawText( DrawingContextHelper.GetFormattedText("温", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), FlowDirection.LeftToRight, 14D), new Point(75, 115 + 28)); drawingContext.DrawText( DrawingContextHelper.GetFormattedText("度", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), FlowDirection.LeftToRight, 14D), new Point(75, 115 + 42)); #endregion #region 画刻度 var total_Value = MaxValue - MinValue; var cnt = total_Value / Interval; var one_value = 161d / cnt; for (var i = 0; i <= cnt; i++) { var formattedText = DrawingContextHelper.GetFormattedText($"{MaxValue - i * Interval}", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), FlowDirection.LeftToRight, 14D); drawingContext.DrawText(formattedText, new Point(43, i * one_value - formattedText.Height / 2d)); //减去字体高度的一半 formattedText = DrawingContextHelper.GetFormattedText($"{(MaxValue - i * Interval) * 1.8d + 32d}", (Brush)DrawingContextHelper.BrushConverter.ConvertFromString("#82848A"), textSize: 14D); drawingContext.DrawText(formattedText, new Point(-13, i * one_value - formattedText.Height / 2d)); if (i != 0 && i != 5) { drawingContext.DrawLine(new Pen(Brushes.Black, 1d), new Point(4, i * one_value), new Point(6, i * one_value)); drawingContext.DrawLine(new Pen(Brushes.Black, 1d), new Point(24, i * one_value), new Point(26, i * one_value)); } } #endregion } /// <summary> /// 动态计算当前值图形坐标点 /// </summary> private void PaintPath() { var one_value = 161d / ((MaxValue - MinValue) / Interval); var width = 26d; var height = 169d - (MaxValue - CurrentValue) * (one_value / Interval); var x = 2d; var y = 169d - (169d - (MaxValue - CurrentValue) * (one_value / Interval)); CurrentGeometry = Geometry.Parse($@"M 2 {y + 4} a 4 4 0 0 1 4 -4 h {width - 8} a 4 4 0 0 1 4 4 v {height - 8} a 4 4 0 0 1 -4 4 h -{width - 8} a 4 4 0 0 1 -4 -4 z"); } } }
2) 使用ThermometerExample.xaml.cs如下:
<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.ThermometerExample" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers" xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <Border Background="{DynamicResource BackgroundSolidColorBrush}" CornerRadius="12" Width="400" Height="400" Effect="{StaticResource NormalShadowDepth}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Slider x:Name="PART_Slider" IsSnapToTickEnabled="True" Value="10" Minimum="-10" Maximum="40" Orientation="Vertical" Height="300"/> <Grid VerticalAlignment="Center" Margin="160,0,0,0"> <Path Fill="{StaticResource PrimaryMouseOverSolidColorBrush}" Stroke="{StaticResource PrimaryMouseOverSolidColorBrush}" StrokeThickness="1" Opacity=".6" Data="{Binding ElementName=PART_Thermometer, Path=CurrentGeometry,Mode=TwoWay}"/> <wpfdev:Thermometer x:Name="PART_Thermometer" CurrentValue="{Binding ElementName=PART_Slider,Path=Value,Mode=TwoWay}"/> </Grid> <TextBlock Text="{Binding ElementName=PART_Thermometer,Path=CurrentValue,StringFormat={}{0}℃}" FontSize="24" Grid.Column="1" Foreground="{StaticResource PrimaryPressedSolidColorBrush}" FontFamily="Bahnschrift" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </Border> </Grid> </UserControl>
实现效果
加载全部内容