WPF 使用 DrawingContext 绘制温度计 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yanjinhua
V2EX    .NET

WPF 使用 DrawingContext 绘制温度计

  •  
  •   yanjinhua 2022-09-29 17:28:05 +08:00 1915 次点击
    这是一个创建于 1107 天前的主题,其中的信息可能已经有所发展或是发生改变。

    WPF 使用 DrawingContext 绘制温度计

    控件名:Thermometer

    作者:WPFDevelopersOrg

    原链接: https://github.com/WPFDevelopersOrg/WPFDevelopers

    • 框架使用大于等于.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> 

    鸣谢 - 帅嘉欣

    Github|ThermometerExample
    码云|ThermometerExample

    2 条回复    2022-10-13 15:51:55 +08:00
    sinnosong1
        1
    sinnosong1  
       2022-09-29 19:35:44 +08:00
    我说大佬怎么一直在发 WPF 的东西,原来是 WPF 方向的 MVP
    yanjinhua
        2
    yanjinhua  
    OP
       2022-10-13 15:51:55 +08:00
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2970 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 13:34 PVG 21:34 LAX 06:34 JFK 09:34
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86