在工业领域,实时数据监控已经成为生产效率的核心竞争力。工业软件都需要处理实时数据展示,开发者大都在DataGrid性能优化上踩过坑,这块在Winform下其实也实现过。
今天,我就来分享一套WPF DataGrid实时显示数据的完整解决方案,不仅性能卓越,视觉效果也足够现代化,让你的工业应用瞬间提升一个档次!
在深入解决方案之前,我们先来分析一下传统DataGrid在处理实时数据时的痛点:
Markdown┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ 数据生产层 │────│ 数据处理层 │────│ UI展示层 │ │ (Data Source) │ │ (Data Processor) │ │ (DataGrid) │ └─────────────────┘ └─────────────────┘ └─────────────────┘
C#using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace AppDataGridRealTime
{
public class RealTimeDataModel : INotifyPropertyChanged
{
private string _deviceId;
private double _temperature;
private double _pressure;
private string _status;
private DateTime _timestamp;
private Brush _statusBrush;
public string DeviceId
{
get => _deviceId;
set
{
_deviceId = value;
OnPropertyChanged(nameof(DeviceId));
}
}
public double Temperature
{
get => _temperature;
set
{
_temperature = value;
OnPropertyChanged(nameof(Temperature));
UpdateStatusColor();
}
}
public double Pressure
{
get => _pressure;
set
{
_pressure = value;
OnPropertyChanged(nameof(Pressure));
}
}
public string Status
{
get => _status;
set
{
_status = value;
OnPropertyChanged(nameof(Status));
UpdateStatusColor();
}
}
public DateTime Timestamp
{
get => _timestamp;
set
{
_timestamp = value;
OnPropertyChanged(nameof(Timestamp));
}
}
public Brush StatusBrush
{
get => _statusBrush;
set
{
_statusBrush = value;
OnPropertyChanged(nameof(StatusBrush));
}
}
//根据状态和温度值更新颜色编码
private void UpdateStatusColor()
{
if (_status == "正常" && _temperature < 80)
StatusBrush = new SolidColorBrush(Colors.Green);
else if (_status == "警告" || _temperature >= 80)
StatusBrush = new SolidColorBrush(Colors.Orange);
else if (_status == "故障")
StatusBrush = new SolidColorBrush(Colors.Red);
else
StatusBrush = new SolidColorBrush(Colors.Gray);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
C#using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace AppDataGridRealTime
{
public class RealTimeDataManager : INotifyPropertyChanged
{
private readonly ObservableCollection<RealTimeDataModel> _dataCollection;
private readonly DispatcherTimer _updateTimer;
private readonly object _lockObject = new object();
private readonly Random _random = new Random();
// 性能监控属性
private int _updateCount;
private string _performanceInfo;
public ObservableCollection<RealTimeDataModel> DataCollection => _dataCollection;
public string PerformanceInfo
{
get => _performanceInfo;
set
{
_performanceInfo = value;
OnPropertyChanged(nameof(PerformanceInfo));
}
}
public RealTimeDataManager()
{
_dataCollection = new ObservableCollection<RealTimeDataModel>();
// 初始化定时器 - 关键:使用合适的更新频率
_updateTimer = new DispatcherTimer(DispatcherPriority.Background)
{
Interval = TimeSpan.FromMilliseconds(100) // 100ms更新一次,平衡性能和实时性
};
_updateTimer.Tick += UpdateTimer_Tick;
InitializeData();
}
private void InitializeData()
{
// 模拟20个工业设备
for (int i = 1; i <= 20; i++)
{
_dataCollection.Add(new RealTimeDataModel
{
DeviceId = $"Device_{i:D3}",
Temperature = _random.NextDouble() * 100,
Pressure = _random.NextDouble() * 50,
Status = "正常",
Timestamp = DateTime.Now
});
}
}
private void UpdateTimer_Tick(object sender, EventArgs e)
{
Task.Run(() =>
{
lock (_lockObject)
{
// 批量更新数据,减少UI更新频率
var updateTasks = _dataCollection.Select(UpdateSingleDevice).ToArray();
Task.WaitAll(updateTasks);
_updateCount++;
// 每100次更新显示一次性能信息
if (_updateCount % 100 == 0)
{
Application.Current.Dispatcher.Invoke(() =>
{
PerformanceInfo = $"更新次数: {_updateCount} | 数据量: {_dataCollection.Count} | 内存: {GC.GetTotalMemory(false) / 1024 / 1024:F2}MB";
});
}
}
});
}
private Task UpdateSingleDevice(RealTimeDataModel device)
{
return Task.Run(() =>
{
// 模拟实时数据变化
var tempChange = (_random.NextDouble() - 0.5) * 2;
var pressureChange = (_random.NextDouble() - 0.5) * 1;
Application.Current.Dispatcher.Invoke(() =>
{
device.Temperature = Math.Max(0, Math.Min(120, device.Temperature + tempChange));
device.Pressure = Math.Max(0, Math.Min(60, device.Pressure + pressureChange));
device.Timestamp = DateTime.Now;
// 根据数据状态更新设备状态
if (device.Temperature > 100)
device.Status = "故障";
else if (device.Temperature > 80)
device.Status = "警告";
else
device.Status = "正常";
});
});
}
public void StartRealTimeUpdate()
{
_updateTimer.Start();
}
public void StopRealTimeUpdate()
{
_updateTimer.Stop();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XML<Window x:Class="AppDataGridRealTime.MainWindow"
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:AppDataGridRealTime"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Background="#FF2D2D30">
<Window.Resources>
<Style x:Key="ModernDataGridStyle" TargetType="DataGrid">
<Setter Property="Background" Value="#FF3C3C3C"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="#FF565656"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="RowBackground" Value="#FF404040"/>
<Setter Property="AlternatingRowBackground" Value="#FF454545"/>
<Setter Property="GridLinesVisibility" Value="Horizontal"/>
<Setter Property="HorizontalGridLinesBrush" Value="#FF565656"/>
<Setter Property="VerticalGridLinesBrush" Value="#FF565656"/>
<Setter Property="HeadersVisibility" Value="Column"/>
<Setter Property="CanUserAddRows" Value="False"/>
<Setter Property="CanUserDeleteRows" Value="False"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="SelectionMode" Value="Single"/>
<Setter Property="SelectionUnit" Value="FullRow"/>
<Setter Property="EnableRowVirtualization" Value="True"/>
<Setter Property="EnableColumnVirtualization" Value="True"/>
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True"/>
<Setter Property="VirtualizingPanel.VirtualizationMode" Value="Recycling"/>
</Style>
<Style x:Key="DataGridColumnHeaderStyle" TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#FF007ACC"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Padding" Value="10,8"/>
<Setter Property="BorderBrush" Value="#FF005A9E"/>
<Setter Property="BorderThickness" Value="0,0,1,0"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DataGridRowStyle" TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="White"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF4A4A4A"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#FF007ACC"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="StatusLabelStyle" TargetType="Label">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Padding" Value="8,2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Border Background="{Binding StatusBrush}"
CornerRadius="3"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#FF007ACC" Height="60">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="20,0">
<TextBlock Text="🏭 工业设备实时监控系统"
FontSize="18"
FontWeight="Bold"
Foreground="White"
VerticalAlignment="Center"/>
<Button Content="▶️ 开始监控"
Name="btnStart"
Click="BtnStart_Click"
Margin="30,0,10,0"
Padding="15,5"
Background="#FF28A745"
Foreground="White"
BorderThickness="0"
FontWeight="Bold"/>
<Button Content="⏸️ 停止监控"
Name="btnStop"
Click="BtnStop_Click"
Margin="10,0"
Padding="15,5"
Background="#FFDC3545"
Foreground="White"
BorderThickness="0"
FontWeight="Bold"/>
</StackPanel>
</Grid>
<DataGrid Grid.Row="1"
Name="dataGrid"
ItemsSource="{Binding DataCollection}"
Style="{StaticResource ModernDataGridStyle}"
ColumnHeaderStyle="{StaticResource DataGridColumnHeaderStyle}"
RowStyle="{StaticResource DataGridRowStyle}"
Margin="10">
<DataGrid.Columns>
<DataGridTextColumn Header="设备编号"
Binding="{Binding DeviceId}"
Width="120"/>
<DataGridTextColumn Header="温度 (°C)"
Binding="{Binding Temperature, StringFormat=F2}"
Width="100"/>
<DataGridTextColumn Header="压力 (bar)"
Binding="{Binding Pressure, StringFormat=F2}"
Width="100"/>
<DataGridTemplateColumn Header="状态" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Status}"
Style="{StaticResource StatusLabelStyle}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="更新时间"
Binding="{Binding Timestamp, StringFormat=HH:mm:ss.fff}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
<StatusBar Grid.Row="2" Background="#FF404040" Height="30">
<StatusBarItem>
<TextBlock Text="{Binding PerformanceInfo}"
Foreground="White"
FontSize="11"/>
</StatusBarItem>
</StatusBar>
</Grid>
</Window>
C#using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace AppDataGridRealTime
{
public partial class MainWindow : Window
{
private readonly RealTimeDataManager _dataManager;
public MainWindow()
{
InitializeComponent();
_dataManager = new RealTimeDataManager();
DataContext = _dataManager;
_dataManager.StartRealTimeUpdate();
}
private void BtnStart_Click(object sender, RoutedEventArgs e)
{
_dataManager.StartRealTimeUpdate();
btnStart.IsEnabled = false;
btnStop.IsEnabled = true;
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
_dataManager.StopRealTimeUpdate();
btnStart.IsEnabled = true;
btnStop.IsEnabled = false;
}
protected override void OnClosed(EventArgs e)
{
_dataManager?.StopRealTimeUpdate();
base.OnClosed(e);
}
}
}

C#// ❌ 错误:在UI线程中处理大量数据
private void ProcessData()
{
foreach(var item in largeDataSet)
{
// 长时间处理,阻塞UI线程
ProcessSingleItem(item);
}
}
// ✅ 正确:异步处理
private async Task ProcessDataAsync()
{
await Task.Run(() =>
{
foreach(var item in largeDataSet)
{
ProcessSingleItem(item);
}
});
}
C#// ❌ 错误:频繁触发PropertyChanged
public string DisplayText
{
get => $"{Value:F2} {Unit}"; // 每次都重新计算
}
// ✅ 正确:缓存计算结果
private string _displayText;
public string DisplayText
{
get => _displayText;
private set
{
_displayText = value;
OnPropertyChanged();
}
}
C#// ❌ 错误:事件未正确解除绑定
timer.Tick += Timer_Tick; // 没有在适当时候解除绑定
// ✅ 正确:及时清理资源
protected override void OnClosed(EventArgs e)
{
timer.Tick -= Timer_Tick;
timer.Stop();
base.OnClosed(e);
}
通过本文的完整解决方案,我们成功实现了一个高性能、现代化的WPF DataGrid实时数据显示系统。核心要点包括:
这套方案不仅适用于工业监控系统,在金融、物联网等领域同样表现出色。记住,好的技术方案不仅要解决问题,更要提供卓越的用户体验。
你在实际项目中遇到过哪些实时数据展示的挑战?或者对这套方案有什么改进建议?欢迎在评论区分享你的经验!
觉得这篇文章对你有帮助,请转发给更多同行,让更多C#开发者受益!
更多C#开发技巧和最佳实践,请关注我们的公众号,每周为你带来实用的编程干货!
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!