之前四路温度监控的时候有过这个想法,但是介于Winform用代码添加控件的方法过于蛋疼所以最终采用了4线波形图来实现。

但是在接触wpf后,这个问题的两个难点(控件设计麻烦和容器布局麻烦)迎刃而解,于是便有了这个实验

在项目中添加自定义控件

并在XAML中对其界面进行编辑

代码如下:

        <StackPanel VerticalAlignment="Center">
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <TextBlock  Text="温度计-" FontSize="30" HorizontalAlignment="Center"/>
                <TextBlock x:Name="txtName"  FontSize="30" HorizontalAlignment="Center"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <TextBlock  Text="温度:" FontSize="25" HorizontalAlignment="Center"/>
                <TextBlock x:Name="txtTemp"  FontSize="25" HorizontalAlignment="Center"/>
            </StackPanel>
        </StackPanel>

拥有连个可设置属性,一个是温度计的编号,一个是温度。同时在设计的时候应该限定其大小,否则可能会出现因为内容大小变化出现自适应现象破坏观感,这里采用设置MaxHeight和MinHeight为同一值来实现,宽度同理。

接下来编写后台代码,实现数据的自动刷新

本空控件拥有两组属性,一组是温度(私有),另一组是用于识别控件序号的的识别号(公有)

public int IDNo;
private double Temp;

控件初始化事件

初始化时默认温度为25℃,同时需要为控件输入一个编号

        public Moniter(int No)
        {
            InitializeComponent();
            txtName.Text = No.ToString();
            IDNo = No;
            Temp = 25;
            txtTemp.Text = "25℃";
        }

数据刷新事件,不采用反射模式(挺麻烦的)

        public void RefreshData(double data)
        {
            Temp += data;
            txtTemp.Text = Temp.ToString() + "℃";
        }

这样一个温度计自定义控件就设计完成了。

--------------------------------------------------------------------------分割线--------------------------------------------------------------------------------------

紧接着设计主界面,在主界面中我们需要一个容器来存放我们的控件,StackPanel、ListView和DataGrind都是可以的,但是由于我们设计的控件是图像类控件且可能数量角度,在这里采用ListView作为容器存放控件,XAML代码如下:

<StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button x:Name="btnAddTemp" FontSize="25" Content="添加温度计" Margin="2 2 2 2" Click="btnAddTemp_Click" />
                <TextBox x:Name="txtNo" Width="200">
                    <TextBox.Resources>
                        <VisualBrush x:Key="HintText" TileMode="None" Opacity="0.5" Stretch="None" AlignmentX="Left">
                            <VisualBrush.Visual>
                                <TextBlock FontStyle="Italic" Text="请输入温度计编号"/>
                            </VisualBrush.Visual>
                        </VisualBrush>
                    </TextBox.Resources>
                    <TextBox.Style>
                        <Style TargetType="TextBox">
                            <Style.Triggers>
                                <Trigger Property="Text" Value="{x:Null}">
                                    <Setter Property="Background" Value="{StaticResource HintText}"/>
                                </Trigger>
                                <Trigger Property="Text" Value="">
                                    <Setter Property="Background" Value="{StaticResource HintText}"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </TextBox.Style>
                </TextBox>
                <Button x:Name="btnChangeTemp" FontSize="25" Content="温度+1" Margin="2 2 2 2" Click="btnChangeTemp_Click" />
            </StackPanel>
            <ListView x:Name="GoodsView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="380"  ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel IsItemsHost="True"/>
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
            </ListView>
        </StackPanel>

效果如图:

在这里有两个小细节

      其一是ListView的拉动和自适应设计(默认的ListView会将控件竖排拉伸并且无法向下滑动,明显不符合我们的设计要求,所以对其ItemsPanel进行设置使其可以在空间允许的情况下横向摆放控件;同时如果不设置长度的话容器将无法拖动,所以我们将其height属性设置为一个合适的值)

                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel IsItemsHost="True"/>
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>

其次是用来选择控件编号的输入框,它有一组默认提示文字,实现方式如下

<TextBox x:Name="txtNo" Width="200">
                    <TextBox.Resources>
                        <VisualBrush x:Key="HintText" TileMode="None" Opacity="0.5" Stretch="None" AlignmentX="Left">
                            <VisualBrush.Visual>
                                <TextBlock FontStyle="Italic" Text="请输入温度计编号"/>
                            </VisualBrush.Visual>
                        </VisualBrush>
                    </TextBox.Resources>
                    <TextBox.Style>
                        <Style TargetType="TextBox">
                            <Style.Triggers>
                                <Trigger Property="Text" Value="{x:Null}">
                                    <Setter Property="Background" Value="{StaticResource HintText}"/>
                                </Trigger>
                                <Trigger Property="Text" Value="">
                                    <Setter Property="Background" Value="{StaticResource HintText}"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </TextBox.Style>
                </TextBox>

接着是后台代码:

按下添加按钮会一次性添加10个温度计控件到容器中

 for (int i = 1; i <= 10; i++)
            {
                GoodsView.Items.Add(new Moniter(i));
            }

而按下“温度+1”则会根据选择的温度计将其温度+1

foreach (Moniter item in GoodsView.ItemContainerGenerator.Items)
            {
                if (item.IDNo == Convert.ToInt32(txtNo.Text))
                {
                    item.RefreshData(1);
                }
            }

由于这种代码添加的控件并不能对其name属性进行预设,于是采用辨别ID号的方式来确定需要调用的控件。其原理是:先对容器内的所有控件进行遍历,将其ID号与用户选择进行对比,然后调用其refreshdata方法刷新数据

将这段代码插入到串口服务或者网络服务中即可实现对所需控件的数据实时刷新的要求

最后,实现效果如下(添加商品是另一个实验):

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐