WPF工控组态软件之温度计

WPF以其丰富灵活的控件样式设计,相较于WinForm而言,一直是工控组态软件的宠儿。经过前两文章的学习,已经对WPF开发工控组态软件有了一个基本的了解, 今天继续学习温度计的开发,仅供学习分享使用,如有不足之处,还请指正。

415cb0a3ebba3a4230ab07c399d73653.png

涉及知识点

 在本示例中,主要知识点如下:

  • WPF阴影效果,线性渐变的设置,主要设置温度计的边框,填充等效果,形成一种金属质感。

  • WPF依赖属性设置,主要设置最大温度,最低温度,和当前温度值

  • WPF线条绘制,主要用于刻度

温度计截图

本示例主要实现功能为自定义刻度值,以及水银条随着当前温度值变化。具体如下所示:

023c88dc5785b26fe318013a94ddfe8c.png

温度计源码

示例源码分为以下2个部分:

1. Thermometer控件

Thermometer控件布局

<UserControl x:Class="WpfControl.UserControls.Thermometer"
             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:local="clr-namespace:WpfControl.UserControls"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="150">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Rectangle StrokeThickness="7" RadiusX="40" RadiusY="15" Fill="White" />
        <Rectangle StrokeThickness="7" RadiusX="40" RadiusY="15">
            <Rectangle.Effect>
                <DropShadowEffect ShadowDepth="0" Direction="0" BlurRadius="7" />
            </Rectangle.Effect>
            <Rectangle.Stroke>
                <LinearGradientBrush StartPoint="0,1" EndPoint="1,0">
                    <LinearGradientBrush.RelativeTransform>
                        <RotateTransform Angle="40" CenterX="0.5" CenterY="0.5" />
                    </LinearGradientBrush.RelativeTransform>
                    <GradientStop Color="Black" />
                    <GradientStop Color="White" Offset="0.7" />
                </LinearGradientBrush>
            </Rectangle.Stroke>
        </Rectangle>
        <TextBlock Text="℃" HorizontalAlignment="Center" VerticalAlignment="Top" FontWeight="Bold" FontSize="20" Margin="0, 20" Foreground="#555"/>
        <Canvas Name="MainCanvas" Width="75" Margin="0,70" />
        <Border Width="10" RenderTransformOrigin="0.5,0.5" CornerRadius="5" Margin="0,50">
            <Border.Effect>
                <DropShadowEffect ShadowDepth="0" Direction="0" Color="White" />
            </Border.Effect>
            <Border.Background>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                    <GradientStop Color="lightGray" Offset="0" />
                    <GradientStop Color="White" Offset="0.4" />
                    <GradientStop Color="lightGray" Offset="1" />
                </LinearGradientBrush>
            </Border.Background>
            <Border Height="75" VerticalAlignment="Bottom" Name="BorValue">
                <Border.Background>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                        <GradientStop Color="#CD3333"  />
                        <GradientStop Color="#FFC0CB" Offset="0.4" />
                        <GradientStop Color="#CD3333" Offset="1" />
                    </LinearGradientBrush>
                </Border.Background>
            </Border>
        </Border>
        <Border Height="25" Width="25" CornerRadius="15" VerticalAlignment="Bottom" Margin="0 0 0 30">
            <Border.Effect>
                <DropShadowEffect Direction="0" ShadowDepth="0" />
            </Border.Effect>
            <Border.Background>
                <RadialGradientBrush Center="0.3,0.2" GradientOrigin="0.4,0.4">
                    <GradientStop Color="White" Offset="0" />
                    <GradientStop Color="#CD3333" Offset="1" />
                </RadialGradientBrush>
            </Border.Background>
        </Border>
        <TextBox Grid.Row="1" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" HorizontalContentAlignment="Center" BorderThickness="0" BorderBrush="AliceBlue" VerticalAlignment="Bottom" FontSize="20" Name="ThermometerValue" />
    </Grid>
</UserControl>

依赖属性设置及后台生成刻度源码,如下所示:

namespace WpfControl.UserControls
{
    /// <summary>
    /// Thermometer.xaml 的交互逻辑
    /// </summary>
    public partial class Thermometer : UserControl
    {
        public int Minmum
        {
            get { return (int)GetValue(MinmumProperty); }
            set { SetValue(MinmumProperty, value); }
        }

        public static readonly DependencyProperty MinmumProperty =
            DependencyProperty.Register("Minmum", typeof(int), typeof(Thermometer), new PropertyMetadata(1, new PropertyChangedCallback(OnPropertyValueChanged)));


        public int Maxmum
        {
            get { return (int)GetValue(MaxmumProperty); }
            set { SetValue(MaxmumProperty, value); }
        }

        public static readonly DependencyProperty MaxmumProperty =
            DependencyProperty.Register("Maxmum", typeof(int), typeof(Thermometer), new PropertyMetadata(10, new PropertyChangedCallback(OnPropertyValueChanged)));



        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value);}
        }

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(Thermometer), new PropertyMetadata(0.0, new PropertyChangedCallback(OnPropertyValueChanged)));

        /// <summary>
        /// 当属性值发生变化的时候直接更新UI内容
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as Thermometer)?.RefreshComponet();
        }

        private double step = 10;

        public Thermometer()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        /// <summary>
        /// 刷新温度计上面的内容适应定义大小
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        private void RefreshComponet()
        {
            // 两种方式触发:尺寸变化、区间变化
            var h = this.MainCanvas.ActualHeight;//通过这个判断界面元素是否加载
            if (h == 0) return;
            double w = 75;
            // 类型
            double stepCount = Maxmum - Minmum;// 在这个区间内多少个间隔
            step = h / (Maxmum - Minmum);// 每个间隔距离

            this.MainCanvas.Children.Clear();

            for (int i = 0; i <= stepCount; i++)
            {
                Line line = new Line();
                line.Y1 = i * step;
                line.Y2 = i * step;
                line.Stroke = Brushes.Black;
                line.StrokeThickness = 1;
                this.MainCanvas.Children.Add(line);

                if (i % 10 == 0)
                {
                    line.X1 = 15;
                    line.X2 = w - 15;

                    // 添加文字
                    TextBlock text = new TextBlock
                    {
                        Text = (Maxmum - i).ToString(),
                        Width = 20,
                        TextAlignment = TextAlignment.Center,
                        FontSize = 9,
                        Margin = new Thickness(0, -5, -4, 0)
                    };
                    Canvas.SetLeft(text, w - 15);
                    Canvas.SetTop(text, i * step);
                    this.MainCanvas.Children.Add(text);

                    // 添加文字
                    text = new TextBlock
                    {
                        Text = (Maxmum - i).ToString(),
                        Width = 20,
                        TextAlignment = TextAlignment.Center,
                        FontSize = 9,
                        Margin = new Thickness(-4, -5, 0, 0)
                    };
                    Canvas.SetLeft(text, 0);
                    Canvas.SetTop(text, i * step);
                    this.MainCanvas.Children.Add(text);
                }
                else if (i % 5 == 0)
                {
                    line.X1 = 20;
                    line.X2 = w - 20;
                }
                else
                {
                    line.X1 = 25;
                    line.X2 = w - 25;
                }
            }
            ValueChanged();
        }


        private void ValueChanged() {
            // 限定值的变化范围
            var value = this.Value;
            if (this.Value < this.Minmum)
                value = this.Minmum;
            if (this.Value > this.Maxmum)
                value = this.Maxmum;

            // 温度值与Border的高度的一个转换
            var newValue = value - this.Minmum;
            newValue *= step;
            newValue += 20;

            // 动画
            DoubleAnimation doubleAnimation = new DoubleAnimation(newValue, TimeSpan.FromMilliseconds(500));
            this.BorValue.BeginAnimation(HeightProperty, doubleAnimation);
        }

    }

}

2. 控件调用

用户控件不可以独立展示,需要以窗口为载体,作为窗口的一部分展示,页面调用如下所示:

<Window
        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:WpfControl"
        xmlns:UserControls="clr-namespace:WpfControl.UserControls" x:Class="WpfControl.TestWindow3"
        mc:Ignorable="d"
        Title="工控组态软件--温度计" Height="450" Width="1000" Loaded="Window_Loaded">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <UserControls:Thermometer Grid.Column="0" Grid.Row="0" x:Name="t1" Width="100" Height="370" HorizontalAlignment="Center"  VerticalAlignment="Center"/>
        <UserControls:Thermometer Grid.Column="1" Grid.Row="0" x:Name="t2" Width="100" Height="370" HorizontalAlignment="Center"  VerticalAlignment="Center"/>
        <UserControls:Thermometer Grid.Column="2" Grid.Row="0" x:Name="t3" Width="100" Height="370" HorizontalAlignment="Center"  VerticalAlignment="Center"/>
        <UserControls:Thermometer Grid.Column="3" Grid.Row="0" x:Name="t4" Width="100" Height="370" HorizontalAlignment="Center"  VerticalAlignment="Center"/>
        <UserControls:Thermometer Grid.Column="4" Grid.Row="0" x:Name="t5" Width="100" Height="370" HorizontalAlignment="Center"  VerticalAlignment="Center"/>
        <UserControls:Thermometer Grid.Column="5" Grid.Row="0" x:Name="t6" Width="100" Height="370" HorizontalAlignment="Center"  VerticalAlignment="Center"/>
        <UserControls:Thermometer Grid.Column="6" Grid.Row="0" x:Name="t7" Width="100" Height="370" HorizontalAlignment="Center"  VerticalAlignment="Center"/>
        <UserControls:Thermometer Grid.Column="7" Grid.Row="0" x:Name="t8" Width="100" Height="370" HorizontalAlignment="Center"  VerticalAlignment="Center"/>
    </Grid>
</Window>

控件赋值,在窗口加载时,为控件赋初始值,如下所示:

namespace WpfControl
{
    /// <summary>
    /// TestWindow3.xaml 的交互逻辑
    /// </summary>
    public partial class TestWindow3 : Window
    {
        public TestWindow3()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            var controls = new Thermometer[8] { t1, t2 , t3, t4 , t5, t6 , t7, t8 };
            for (int i = 0; i < 8; i++) {
                controls[i].Maxmum = 100;
                controls[i].Minmum = -20;
                controls[i].Value = 10*(i+1);
            }
        }
    }
}

注意:在实际业务中,可以通过对应的传输协议【如:Modbus等】从硬件获取,并实时的显示在页面中。

源码下载

b06858748d92ac850639b039db88d25b.jpeg

备注

以上就是本篇文章的全部内容,旨在抛砖引玉,共同学习,一起进步。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老码识途呀

写作不易,多谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值