WPF 使用 DrawingContext 绘制温度计

讨论 未结 1 33
yanjinhua
yanjinhua 会员 2022年9月29日 09:49 发表
<p><span> <strong>WPF 使用 DrawingContext 绘制温度计</strong></span> </p> <blockquote> <p>控件名:Thermometer</p> <p>作者:WPFDevelopersOrg</p> <p>原文链接: <a href="https://github.com/WPFDevelopersOrg/WPFDevelopers" rel="nofollow">https://github.com/WPFDevelopersOrg/WPFDevelopers</a></p> </blockquote> <ul> <li>框架使用<strong>大于等于</strong><code>.NET40</code>;</li> <li><code>Visual Studio 2022</code>;</li> <li>项目使用 <strong>MIT</strong> 开源许可协议; </li> <li>定义<code>Interval</code>步长、<code>MaxValue</code>最大温度值、<code>MinValue</code>最小温度值。</li> <li><code>CurrentGeometry</code> 重新绘制当前刻度的<code>Path</code>值。</li> <li><code>CurrentValue</code> 当前值如果发生变化时则去重新<code>CurrentGeometry</code> 。 </li> <li> <code>OnRender</code> 绘制如下 <ul> <li><code>RoundedRectangle</code>温度计的外边框。 </li> <li>使用方法<code>DrawText</code> 单字绘制 <code>华氏温度</code>文本<code>Y</code>轴变化。 </li> <li>使用方法<code>DrawText</code> 单字绘制 <code>摄氏温度</code>文本<code>Y</code>轴变化。</li> <li>使用方法<code>DrawText</code> 绘制温度计两侧的刻度数值。</li> <li>使用方法<code>DrawLine</code> 绘制温度计两侧的刻度线。</li> </ul> </li> </ul> <p><img alt="" class="embedded_image" loading="lazy" referrerpolicy="no-referrer" rel="noreferrer" src="https://files.mdnice.com/user/24276/9fb94516-e2f4-4155-ad90-2d3a05f4e871.png"></p> <p>1 ) 准备<strong>Thermometer.cs</strong>如下:</p> <pre><code class="language-C#">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)); /// &lt;summary&gt; /// 当前值 /// &lt;/summary&gt; public static readonly DependencyProperty CurrentValueProperty = DependencyProperty.Register("CurrentValue", typeof(double), typeof(Thermometer), new UIPropertyMetadata(OnCurrentValueChanged)); /// &lt;summary&gt; /// 步长 /// &lt;/summary&gt; public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register("Interval", typeof(double), typeof(Thermometer), new UIPropertyMetadata(10.0)); /// &lt;summary&gt; /// 当前值的图形坐标点 /// &lt;/summary&gt; 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"))); /// &lt;summary&gt; /// 构造函数 /// &lt;/summary&gt; static Thermometer() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Thermometer), new FrameworkPropertyMetadata(typeof(Thermometer))); } public double MaxValue { get =&gt; (double)GetValue(MaxValueProperty); set =&gt; SetValue(MaxValueProperty, value); } public double MinValue { get =&gt; (double)GetValue(MinValueProperty); set =&gt; SetValue(MinValueProperty, value); } public double CurrentValue { get =&gt; (double)GetValue(CurrentValueProperty); set { SetValue(CurrentValueProperty, value); PaintPath(); } } public double Interval { get =&gt; (double)GetValue(IntervalProperty); set =&gt; SetValue(IntervalProperty, value); } public Geometry CurrentGeometry { get =&gt; (Geometry)GetValue(CurrentGeometryProperty); set =&gt; 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 &lt;= 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 &amp;&amp; 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 } /// &lt;summary&gt; /// 动态计算当前值图形坐标点 /// &lt;/summary&gt; 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(<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ddf99d">[email&nbsp;protected]</a>"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"); } } } </code></pre> <p>2 ) 使用<strong>ThermometerExample.xaml.cs</strong>如下:</p> <pre><code class="language-xml">&lt;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"&gt; &lt;Grid&gt; &lt;Border Background="{DynamicResource BackgroundSolidColorBrush}" CornerRadius="12" Width="400" Height="400" Effect="{StaticResource NormalShadowDepth}"&gt; &lt;Grid&gt; &lt;Grid.ColumnDefinitions&gt; &lt;ColumnDefinition/&gt; &lt;ColumnDefinition/&gt; &lt;/Grid.ColumnDefinitions&gt; &lt;Slider x:Name="PART_Slider" IsSnapToTickEnabled="True" Value="10" Minimum="-10" Maximum="40" Orientation="Vertical" Height="300"/&gt; &lt;Grid VerticalAlignment="Center" Margin="160,0,0,0"&gt; &lt;Path Fill="{StaticResource PrimaryMouseOverSolidColorBrush}" Stroke="{StaticResource PrimaryMouseOverSolidColorBrush}" StrokeThickness="1" Opacity=".6" Data="{Binding ElementName=PART_Thermometer, Path=CurrentGeometry,Mode=TwoWay}"/&gt; &lt;wpfdev:Thermometer x:Name="PART_Thermometer" CurrentValue="{Binding ElementName=PART_Slider,Path=Value,Mode=TwoWay}"/&gt; &lt;/Grid&gt; &lt;TextBlock Text="{Binding ElementName=PART_Thermometer,Path=CurrentValue,StringFormat={}{0}℃}" FontSize="24" Grid.Column="1" Foreground="{StaticResource PrimaryPressedSolidColorBrush}" FontFamily="Bahnschrift" HorizontalAlignment="Center" VerticalAlignment="Center"/&gt; &lt;/Grid&gt; &lt;/Border&gt; &lt;/Grid&gt; &lt;/UserControl&gt; </code></pre> <p><span> 鸣谢 - <strong>帅嘉欣</strong></span> </p> <p><img alt="" class="embedded_image" loading="lazy" referrerpolicy="no-referrer" rel="noreferrer" src="https://files.mdnice.com/user/24276/80848233-1f33-48e7-9499-9eee969582a3.gif"></p> <p><a href="https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Samples/ExampleViews/ThermometerExample.xaml" rel="nofollow" title="Github|ThermometerExample">Github|ThermometerExample</a><br> <a href="https://gitee.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Samples/ExampleViews/ThermometerExample.xaml" rel="nofollow" title="码云|ThermometerExample">码云|ThermometerExample</a></p>
收藏(0)  分享
相关标签: 灌水交流
注意:本文归作者所有,未经作者允许,不得转载
1个回复
  • sinnosong1
    2022年9月29日 11:47
    我说大佬怎么一直在发 WPF 的东西,原来是 WPF 方向的 MVP
    0 0