大数跨境

WPF + MVVM 实现工业示波器界面与波形生成

WPF + MVVM 实现工业示波器界面与波形生成 dotNET跨平台
2026-07-08
9
导读:前言调试硬件设备的时候,手边没有示波器是常有的事。要么设备在产线上搬不出来,要么实验室的示波器被别人占着。这

前言

调试硬件设备时,常面临无示波器可用的困境:设备无法移出产线,或实验室仪器被占用。此时,若能通过软件直接读取数据并绘制波形,将显著提升调试效率。

本文介绍一套模拟工业示波器的实现方案。该方案基于 WPF 和 DevExpress Charts 控件,界面风格复刻真实示波器操作面板,支持波形生成、参数调节及测量数据显示。虽当前采用模拟数据源,但底层架构已预留真实数据接口。

项目介绍

这是一款模拟工业示波器的桌面软件,UI 配色与交互逻辑均参考真实数字示波器。主界面划分为左右两区:左侧为参数控制面板,右侧集成波形显示区与测量数值面板。

技术层面采用 WPF + MVVM 模式,图表渲染依托 DevExpress Charts 组件。波形生成逻辑封装于 ViewModel 中,通过数据绑定实现界面实时更新,确保操作响应流畅。

本项目代码适用于学习 WPF 复杂布局、MVVM 数据绑定及工业软件界面设计,亦可作为真实示波器软件的原型参考。

项目功能

功能
说明
波形类型选择
支持正弦波、方波、三角波、锯齿波四种常用波形
频率调节
1-100Hz 范围内调节信号频率
振幅调节
1-10V 范围内调节信号幅值
占空比设置
方波模式下可选 10%-90% 占空比
噪声模拟
低、中、高三档噪声强度,模拟真实采样环境
采样率控制
可选 1k-100k 采样率,影响波形显示精细度
时间基准调节
0.1-10 秒/格可调,控制波形水平缩放
垂直灵敏度调节
0.5-5V/格可调,控制波形垂直缩放
触发方式
支持自动、正常、单次三种触发模式
触发边沿
上升沿、下降沿可选
触发电平显示
波形区域显示红色触发电平参考线
波形启停控制
开始/停止按钮控制波形更新
测量数据显示
实时显示 Vpp、Vrms、频率、周期四个关键参数
保存设置
保存当前所有参数配置到本地
保存波形数据
将当前波形数据导出为文件

项目特点

MVVM 分层清晰:界面层仅负责布局与绑定,业务逻辑集中于 ViewModel,确保界面改动不影响核心逻辑。

DevExpress 图表专业:采用 DXChart 替代原生控件,内置十字光标、轴刻度及网格线,大幅减少样式代码编写。

实时数据绑定:波形数据集合绑定至图表,ViewModel 按采样率定时刷新,实现界面自动响应。

控件联动反馈:调节左侧参数时波形即时变化,无需额外确认操作。

暗色工业主题:背景、网格线及波形颜色均按真实示波器风格设计,适宜长时间观测。

测量参数自动计算:自动计算每个波形的峰峰值、有效值、频率和周期,免去手动读数繁琐。

项目技术

技术
用途
C# .NET
开发语言
WPF
桌面 UI 框架
MVVM
架构模式,界面与逻辑分离
DevExpress Charts
专业图表控件,渲染波形
DevExpress Editors
下拉框、数值输入等编辑控件
System.Windows.Threading
定时器驱动波形刷新
ICommand
按钮命令绑定
INotifyPropertyChanged
属性变更通知
IDataErrorInfo
输入验证

项目代码

MainWindow.xaml 关键结构

主界面采用左右分栏布局:左栏为参数设置面板,右栏为波形显示区。

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="300"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <!-- 左栏:控制面板 -->
    <Border Grid.Column="0" Background="#2D2D2D" CornerRadius="5" Padding="10">
        <ScrollViewer>
            <StackPanel>
                <TextBlock Text="信号源设置" Foreground="White" FontSize="16"/>
                <!-- 波形类型、频率、振幅、占空比、噪声 -->
                <dxe:ComboBoxEdit EditValue="{Binding Settings.WaveType}"/>
                <dxe:SpinEdit EditValue="{Binding Settings.Frequency}"/>
                <!-- 省略其他控件 -->
            </StackPanel>
        </ScrollViewer>
    </Border>

    <!-- 右栏:波形显示 -->
    <Grid Grid.Column="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="120"/>
        </Grid.RowDefinitions>

        <Border Background="Black" CornerRadius="5">
            <dxc:ChartControl DataSource="{Binding WavePoints}">
                <dxc:XYDiagram2D>
                    <!-- X 轴:时间 -->
                    <!-- Y 轴:电压 -->
                    <dxc:LineSeries2D ArgumentDataMember="Time"
                                     ValueDataMember="Value"
                                     Brush="Yellow"/>
                </dxc:XYDiagram2D>
            </dxc:ChartControl>
        </Border>

        <!-- 底部:测量数值 -->
        <Border Grid.Row="1">
            <UniformGrid Columns="4">
                <TextBlock Text="{Binding Vpp, StringFormat={}{0:F2} V}"/>
                <TextBlock Text="{Binding Vrms, StringFormat={}{0:F2} V}"/>
                <TextBlock Text="{Binding AvgFreq, StringFormat={}{0:F1} Hz}"/>
                <TextBlock Text="{Binding Period, StringFormat={}{0:F6} s}"/>
            </UniformGrid>
        </Border>
    </Grid>
</Grid>

ViewModel 核心逻辑

波形生成方法依据所选波形类型计算对应电压值。

private void GenerateWaveform()
{
    var points = new List<WavePoint>();
    double amplitude = Settings.Amplitude;
    double frequency = Settings.Frequency;
    double timeBase = Settings.TimeBase;
    double samplingRate = Settings.SamplingRate;
    int pointCount = (int)(timeBase * samplingRate * 2);

    for (int i = 0; i < pointCount; i++)
    {
        double t = (double)i / samplingRate;
        double value = 0;

        switch (Settings.WaveType)
        {
            case WaveType.Sine:
                value = amplitude * Math.Sin(2 * Math.PI * frequency * t);
                break;
            case WaveType.Square:
                double duty = (double)Settings.DutyCycle / 100;
                double period = 1 / frequency;
                value = (t % period) / period < duty ? amplitude : -amplitude;
                break;
            case WaveType.Triangle:
                double triPeriod = 1 / frequency;
                double triPhase = (t % triPeriod) / triPeriod;
                value = amplitude * (triPhase < 0.5 ? 4 * triPhase - 1 : 3 - 4 * triPhase);
                break;
            case WaveType.Sawtooth:
                double sawPeriod = 1 / frequency;
                double sawPhase = (t % sawPeriod) / sawPeriod;
                value = amplitude * (2 * sawPhase - 1);
                break;
        }

        // 叠加噪声
        value += GenerateNoise(Settings.NoiseLevel);
        points.Add(new WavePoint { Time = t, Value = value });
    }

    WavePoints = points;
    CalculateMeasurements(points);
}

触发逻辑处理

触发模式用于控制波形仅在满足特定条件时绘制。

private bool CheckTrigger(double currentValue)
{
    double level = Settings.TriggerLevel;
    bool edgeMatched = false;

    if (Settings.TriggerEdge == TriggerEdge.Rising)
        edgeMatched = currentValue >= level;
    elseif (Settings.TriggerEdge == TriggerEdge.Falling)
        edgeMatched = currentValue <= level;

    if (!edgeMatched) returnfalse;

    switch (Settings.TriggerMode)
    {
        case TriggerMode.Auto:
            returntrue;
        case TriggerMode.Normal:
            return _triggerArmed;
        case TriggerMode.Single:
            if (_triggerArmed)
            {
                _triggerArmed = false;
                returntrue;
            }
            returnfalse;
        default:
            returnfalse;
    }
}

项目效果

软件启动后,界面分为左右两区。左侧控制面板按功能分组(信号源、采样与时间轴、垂直系统与触发、控制按钮);右侧为波形显示区,黑色背景搭配黄色波形网格,右上角实时显示时间基准与垂直灵敏度。

调节左侧频率旋钮,右侧波形疏密即时变化;调节振幅,波形幅度同步响应。切换波形类型时形状实时更替,方波模式下占空比选项自动显现。

波形区域支持鼠标悬停查看十字光标读数,精确定位时间点电压值。红色虚线标示触发电平,左上角文字指示“已触发”或“等待触发”状态。

底部四个数值面板分别展示 Vpp、Vrms、频率和周期。运行时数值每秒更新,体验贴近真实示波器。

总结

本项目完整展示了工业风格桌面软件从界面到逻辑的实现路径。MVVM 模式实现解耦,DevExpress Charts 提供专业渲染能力,左侧参数面板的实时联动则是工业软件的典型特征。

对于学习 WPF 工业界面开发,本项目代码结构与风格极具参考价值。若接入真实波形数据源(如串口或 TCP 读取),仅需替换 GenerateWaveform 方法中的数据来源,即可将模拟示波器转化为真实的设备调试工具。


【声明】内容源于网络
0
0
dotNET跨平台
专注于.NET Core的技术传播。在这里你可以谈微软.NET,Mono的跨平台开发技术。在这里可以让你的.NET项目有新的思路,不局限于微软的技术栈,横跨Windows,
内容 2302
粉丝 0
dotNET跨平台 专注于.NET Core的技术传播。在这里你可以谈微软.NET,Mono的跨平台开发技术。在这里可以让你的.NET项目有新的思路,不局限于微软的技术栈,横跨Windows,
总阅读51.6k
粉丝0
内容2.3k