WPF 使用 DrawingContext 绘制温度计
<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));
/// <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(<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ddf99d">[email 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"><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>
</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)
分享
相关标签:
注意:本文归作者所有,未经作者允许,不得转载