WPF工控组态软件之温度计

简介: WPF工控组态软件之温度计

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

涉及知识点

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

  • WPF阴影效果,线性渐变的设置,主要设置温度计的边框,填充等效果,形成一种金属质感。
  • WPF依赖属性设置,主要设置最大温度,最低温度,和当前温度值
  • WPF线条绘制,主要用于刻度

温度计截图

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

温度计源码

示例源码分为以下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等】从硬件获取,并实时的显示在页面中。

相关文章
|
10月前
|
C#
WPF工控组态软件之冷却塔和空气压缩机开发
WPF工控组态软件之冷却塔和空气压缩机开发
178 0
|
10月前
|
C#
WPF工控组态软件之管道和冷却风扇开发
WPF工控组态软件之管道和冷却风扇开发
166 0
|
1月前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
|
5月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
66 1
|
9月前
|
C# Windows
WPF技术之图形系列Polygon控件
WPF Polygon是Windows Presentation Foundation (WPF)框架中的一个标记元素,用于绘制多边形形状。它可以通过设置多个点的坐标来定义多边形的形状,可以绘制任意复杂度的多边形。
481 0
|
9月前
|
C# Windows
WPF技术之RichTextBox控件
WPF RichTextBox是Windows Presentation Foundation (WPF)中提供的一个强大的文本编辑控件,它可以显示富文本格式的文本,支持多种文本处理操作。
355 0
|
5月前
|
前端开发 C# 容器
浅谈WPF之控件拖拽与拖动
使用过office的visio软件画图的小伙伴都知道,画图软件分为两部分,左侧图形库,存放各种图标,右侧是一个画布,将左侧图形库的图标控件拖拽到右侧画布,就会生成一个新的控件,并且可以自由拖动。那如何在WPF程序中,实现类似的功能呢?今天就以一个简单的小例子,简述如何在WPF中实现控件的拖拽和拖动,仅供学习分享使用,如有不足之处,还请指正。
116 2
|
9月前
|
数据挖掘 数据处理 C#
WPF技术之DataGrid控件
WPF DataGrid是一种可以显示和编辑数据的界面控件。它可以作为表格形式展示数据,支持添加、删除、修改、排序和分组操作。
189 0
|
1月前
|
C# 开发者 C++
一套开源、强大且美观的WPF UI控件库
一套开源、强大且美观的WPF UI控件库
142 0
|
6月前
|
算法 C# UED
浅谈WPF之控件模板和数据模板
WPF不仅支持传统的Windows Forms编程的用户界面和用户体验设计,同时还推出了以模板为核心的新一代设计理念。在WPF中,通过引入模板,将数据和算法的“内容”和“形式”进行解耦。模板主要分为两大类:数据模板【Data Template】和控件模板【Control Template】。
105 8