×

一套能跑的 WPF 设备监控界面(菜单、设备树、卡片看板)

独孤求败 独孤求败 发表于2026-09-13 11:49:22 浏览74 评论0

抢沙发发表评论

前言

工业上位机这个领域,WPF 一直是主力。设备要连、数据要采、曲线要画、报警要管、历史还要能查,事情一件不少。但很多项目卡在第一步:界面怎么写才不别扭,代码怎么放才不越写越乱。这篇聊的是一个设备监控系统的实现思路。

项目介绍

一个基于 WPF 和 MVVM 的工业设备监控系统,支持实时数据采集、曲线显示、报警管理、SQLite 存储与历史查询。主窗口采用菜单栏 + 内容区 + 状态栏的经典三段式布局,左侧设备树负责分类导航,右侧用卡片看板展示设备状态。项目定位是工业现场的监控上位机,兼顾实时性和可维护性。

项目功能

功能
说明
设备管理
添加设备、查看设备列表,左侧树按 S7、Modbus、模拟设备分类
实时数据采集
对接 PLC 与 Modbus 设备,持续读取点位数据
曲线显示
将采集到的数值按时间轴绘制成趋势曲线
报警管理
实时报警与历史报警分开,状态栏同步显示报警数量
数据存储
采集数据与报警记录写入 SQLite
历史查询
按时间范围或设备条件检索历史数据
用户与系统设置
用户管理、系统设置、关于等入口集中在系统管理菜单

项目特点

界面结构清晰:菜单栏、内容区、状态栏三层划分,功能入口和状态信息各归其位。

设备分类直观:左侧树按协议类型分组,S7、Modbus、模拟设备一眼区分,方便现场快速定位。

卡片式看板:右侧用 WrapPanel 排列设备卡片,状态点用颜色区分,鼠标点击即可进入详情。

状态实时可见:状态栏绑定当前时间、设备总数、在线数和报警数,不用切页面就能掌握全局。

MVVM 分离:界面通过 Binding 取数据,设备集合、状态色、统计值都由 ViewModel 提供,XAML 里不写业务逻辑。

存储轻量:SQLite 免安装、单文件,适合部署在现场工控机上。

项目技术

技术项
用途
WPF
桌面界面框架
XAML
界面布局与样式定义
MVVM
界面与逻辑分离
Data Binding
设备集合、状态、统计值的双向/单向绑定
ItemsControl + WrapPanel
设备卡片的自适应排列
TreeView
设备分组导航
SQLite
采集数据与报警记录存储
DropShadowEffect
卡片阴影,提升层次感

项目代码

展示了整个界面的骨架:菜单栏、设备树、卡片看板和状态栏。

<Window x:Class="EquipmentMonitor.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:EquipmentMonitor.ViewModels"
        Title="设备监控系统" Height="700" Width="1100"
        WindowStartupLocation="CenterScreen">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- ===== 菜单栏 ===== -->
        <Menu Grid.Row="0" Background="#2c3e50">
            <MenuItem Header="系统管理" Foreground="White">
                <MenuItem Header="用户管理" Foreground="#333333" Click="UserManagement_Click"/>
                <MenuItem Header="系统设置" Foreground="#333333" Click="SystemSetting_Click"/>
                <Separator/>
                <MenuItem Header="退出" Foreground="#333333" Click="Exit_Click"/>
            </MenuItem>
            <MenuItem Header="设备管理" Foreground="White">
                <MenuItem Header="添加设备" Foreground="#333333" Click="AddDevice_Click"/>
                <MenuItem Header="设备列表" Foreground="#333333"/>
            </MenuItem>
            <MenuItem Header="报警" Foreground="White">
                <MenuItem Header="实时报警" Foreground="#333333" Click="Alarm_Click"/>
                <MenuItem Header="历史报警" Foreground="#333333" Click="Alarm_Click"/>
            </MenuItem>
            <MenuItem Header="帮助" Foreground="White">
                <MenuItem Header="关于" Foreground="#333333" Click="About_Click"/>
            </MenuItem>
        </Menu>

        <!-- ===== 内容区 ===== -->
        <Grid Grid.Row="1" Margin="10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="220"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <!-- 左侧:设备树 -->
            <TreeView x:Name="tvDevices" Background="#ecf0f1">
                <TreeViewItem Header="S7 设备" IsExpanded="True"/>
                <TreeViewItem Header="Modbus 设备" IsExpanded="True"/>
                <TreeViewItem Header="模拟设备" IsExpanded="True"/>
            </TreeView>

            <!-- 右侧:设备卡片看板 -->
            <ScrollViewer Grid.Column="1">
                <ItemsControl ItemsSource="{Binding Devices}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Border Width="240" MinHeight="160" Margin="8"
                                    Background="White" CornerRadius="8"
                                    BorderBrush="#ddd" BorderThickness="1"
                                    Cursor="Hand"
                                    MouseLeftButtonDown="DeviceCard_Click">
                                <Border.Effect>
                                    <DropShadowEffect ShadowDepth="2" Opacity="0.15"/>
                                </Border.Effect>
                                <StackPanel Margin="12">
                                    <StackPanel Orientation="Horizontal" Margin="0,0,0,8">
                                        <Ellipse Width="12" Height="12"
                                                 Fill="{Binding StatusColor}" Margin="0,0,8,0"
                                                 VerticalAlignment="Center"/>
                                        <TextBlock Text="{Binding Name}" FontWeight="Bold"
                                                 FontSize="14"/>
                                    </StackPanel>
                                    <TextBlock Text="{Binding DeviceType}" Foreground="#7f8c8d"
                                             FontSize="12" Margin="0,0,0,8"/>
                                    <Rectangle Height="1" Fill="#ecf0f1" Margin="0,0,0,8"/>
                                    <TextBlock Text="数据采集中..." Foreground="#95a5a6"
                                             FontSize="12"/>
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
        </Grid>

        <!-- ===== 状态栏 ===== -->
        <StatusBar Grid.Row="2" Background="#2c3e50" Foreground="White">
            <StatusBarItem Content="{Binding CurrentTime}"/>
            <Separator/>
            <StatusBarItem>
                <TextBlock>
                    <Run Text="总设备: "/>
                    <Run Text="{Binding TotalDevices}"/>
                </TextBlock>
            </StatusBarItem>
            <Separator/>
            <StatusBarItem>
                <TextBlock>
                    <Run Text="在线: "/>
                    <Run Text="{Binding OnlineDevices}"/>
                </TextBlock>
            </StatusBarItem>
            <Separator/>
            <StatusBarItem>
                <TextBlock>
                    <Run Text="报警: "/>
                    <Run Text="{Binding AlarmCount}" Foreground="Red"/>
                </TextBlock>
            </StatusBarItem>
        </StatusBar>
    </Grid>
</Window>

项目效果

界面跑起来大致是这样一个布局:顶部深色菜单栏,四个主菜单项,下拉里是具体功能入口;中间左侧是浅灰底的设备树,三种设备类型默认展开;

图片

图片

右侧是卡片看板,每张卡片 240 宽、圆角 8、带浅阴影,卡片内第一行是状态圆点加设备名,第二行是设备类型,下面一条分隔线,最下面是采集状态文字;

图片

图片

底部状态栏同样是深色,从左到右依次是当前时间、总设备数、在线数、报警数,报警数字用红色标出。

图片

图片

总结

这套设备监控系统的主界面结构不复杂,但该有的都有了:菜单栏管功能入口,设备树管分类导航,卡片看板管状态展示,状态栏管全局统计。技术上用 WPF 加 MVVM,界面靠 Binding 取数据,逻辑不往 XAML 里塞,后续加实时采集、曲线、报警和历史查询时,扩展点比较清楚。SQLite 做存储对现场部署友好,免安装、单文件,维护成本低。整体思路是先搭好界面骨架,再往上挂数据链路,一步一步来,不容易乱


群贤毕至

访客