打造WPF任务管理器:HandyControl数据网格应用
你是否还在为WPF原生DataGrid控件功能单一、样式陈旧而烦恼?是否需要一个既能高效展示系统进程数据,又能提供丰富交互体验的数据表格解决方案?本文将带你使用HandyControl的增强型数据网格(DataGrid)控件,从零构建一个功能完备的WPF任务管理器,解决进程监控中的数据展示、交互和性能痛点。
读完本文你将掌握:
- HandyControl数据网格的高级配置与样式定制
- 进程数据实时采集与绑定技巧
- 复杂交互功能(排序、筛选、详情展开)实现
- 性能优化策略与内存管理方案
技术选型与架构设计
为什么选择HandyControl?
HandyControl作为WPF领域流行的开源控件库,其数据网格控件相比原生DataGrid具有显著优势:
| 特性 | 原生DataGrid | HandyControl DataGrid |
|---|---|---|
| 默认样式 | 系统原生风格 | 现代化扁平化设计 |
| 扩展属性 | 基础属性 | 支持行号、复选框、行高亮 |
| 性能优化 | 无特殊优化 | 虚拟化容器+延迟加载 |
| 交互体验 | 基础排序筛选 | 内置搜索框+多列排序 |
| 自定义模板支持 | 复杂 | 简化的模板绑定机制 |
系统架构设计
核心模块说明:
- 进程数据采集服务:使用
System.Diagnostics命名空间API获取进程信息 - 数据模型:
ProcessInfo类封装进程ID、名称、CPU占用等属性 - UI展示层:HandyControl DataGrid作为核心控件,配合其他辅助组件
- 性能监控:独立线程采集系统资源占用,避免UI阻塞
开发环境准备
项目搭建与依赖配置
- 创建WPF应用(.NET 6+)
- 通过NuGet安装HandyControl:
Install-Package HandyControl -Version 3.5.0
- 在App.xaml中引入HandyControl资源:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
数据模型定义
public class ProcessInfo : ObservableObject
{
private int _processId;
private string _processName;
private double _cpuUsage;
private long _memoryUsage;
private bool _isSelected;
// 属性定义(省略getter/setter)
// 格式化内存大小显示
public string MemoryUsageFormatted => $"{MemoryUsage / (1024 * 1024):F2} MB";
// 进程图标获取
public ImageSource Icon => IconHelper.GetProcessIcon(ProcessId);
}
DataGrid核心功能实现
基础配置与数据绑定
<hc:DataGrid x:Name="ProcessDataGrid"
ItemsSource="{Binding ProcessList}"
AutoGenerateColumns="False"
CanUserSortColumns="True"
RowHeaderWidth="40"
SelectionMode="Extended"
CellStyle="{StaticResource DataGridCellStyle}"
RowStyle="{StaticResource DataGridRowStyle}">
<hc:DataGrid.RowHeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=hc:DataGridRow}}"/>
</DataTemplate>
</hc:DataGrid.RowHeaderTemplate>
<hc:DataGrid.Columns>
<!-- 进程ID列 -->
<hc:DataGridTextColumn Header="PID"
Binding="{Binding ProcessId}"
Width="60"
CellStyle="{StaticResource DataGridTextCenterColumnStyle}"/>
<!-- 进程名称列 -->
<hc:DataGridTemplateColumn Header="进程名称" Width="*">
<hc:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5">
<Image Source="{Binding Icon}" Width="16" Height="16" Margin="0,0,5,0"/>
<TextBlock Text="{Binding ProcessName}"/>
</StackPanel>
</DataTemplate>
</hc:DataGridTemplateColumn.CellTemplate>
</hc:DataGridTemplateColumn>
<!-- CPU占用列 -->
<hc:DataGridTextColumn Header="CPU(%)"
Binding="{Binding CpuUsage, StringFormat={}{0:F1}}"
Width="80"
CellStyle="{StaticResource DataGridTextCenterColumnStyle}"/>
<!-- 内存占用列 -->
<hc:DataGridTextColumn Header="内存"
Binding="{Binding MemoryUsageFormatted}"
Width="100"
CellStyle="{StaticResource DataGridTextCenterColumnStyle}"/>
<!-- 操作列 -->
<hc:DataGridTemplateColumn Header="操作" Width="100">
<hc:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<hc:Button Icon="Search"
Command="{Binding DataContext.ViewDetailsCommand, RelativeSource={RelativeSource AncestorType=hc:DataGrid}}"
CommandParameter="{Binding}"
Style="{StaticResource ButtonIcon}"/>
</DataTemplate>
</hc:DataGridTemplateColumn.CellTemplate>
</hc:DataGridTemplateColumn>
</hc:DataGrid.Columns>
</hc:DataGrid>
样式定制
居中对齐单元格样式
<Style x:Key="DataGridTextCenterColumnStyle"
TargetType="hc:DataGridCell"
BasedOn="{StaticResource DataGridCellStyle}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
交替行背景色
<Style TargetType="hc:DataGridRow" BasedOn="{StaticResource DataGridRowStyle}">
<Setter Property="Background" Value="#F5F5F5"/>
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="#FFFFFF"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#E6F7FF"/>
<Setter Property="BorderBrush" Value="#91D5FF"/>
<Setter Property="BorderThickness" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
高级功能实现
实时数据更新
public class ProcessMonitorService : IDisposable
{
private readonly DispatcherTimer _updateTimer;
private readonly ObservableCollection<ProcessInfo> _processList;
private readonly object _lockObj = new object();
public ProcessMonitorService(ObservableCollection<ProcessInfo> processList)
{
_processList = processList;
_updateTimer = new DispatcherTimer(DispatcherPriority.Background)
{
Interval = TimeSpan.FromSeconds(1)
};
_updateTimer.Tick += UpdateProcessData;
_updateTimer.Start();
}
private void UpdateProcessData(object? sender, EventArgs e)
{
lock (_lockObj)
{
// 获取系统进程列表
var processes = Process.GetProcesses()
.OrderByDescending(p => p.WorkingSet64)
.Take(50);
// 更新ObservableCollection(使用Dispatcher确保UI线程安全)
Application.Current.Dispatcher.Invoke(() =>
{
// 实现增量更新逻辑,避免全量替换导致的UI闪烁
UpdateObservableCollection(_processList, processes.Select(MapToProcessInfo));
});
}
}
// 其他实现代码...
}
高级筛选与搜索
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
<hc:SearchBar x:Name="SearchBox"
Width="200"
Hint="搜索进程名称..."
TextChanged="SearchBox_TextChanged"/>
<hc:ComboBox x:Name="FilterCombo"
Width="150"
Margin="10,0,0,0"
ItemsSource="{Binding FilterOptions}">
<hc:ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}"/>
</DataTemplate>
</hc:ComboBox.ItemTemplate>
</hc:ComboBox>
</StackPanel>
筛选逻辑实现:
private void ApplyFilter()
{
var searchText = SearchBox.Text.ToLower();
var selectedFilter = FilterCombo.SelectedItem as FilterOption;
ICollectionView view = CollectionViewSource.GetDefaultView(ProcessList);
view.Filter = item =>
{
var process = item as ProcessInfo;
if (process == null) return false;
// 文本搜索筛选
bool matchesSearch = string.IsNullOrEmpty(searchText) ||
process.ProcessName.ToLower().Contains(searchText);
// 类型筛选
bool matchesFilter = selectedFilter?.FilterFunc == null ||
selectedFilter.FilterFunc(process);
return matchesSearch && matchesFilter;
};
}
进程详情展开面板
<hc:DataGrid x:Name="ProcessDataGrid" ...>
<hc:DataGrid.RowDetailsTemplate>
<DataTemplate>
<hc:Expander Header="进程详情" IsExpanded="True">
<hc:Grid Margin="10" ColumnDefinitions="Auto,*">
<hc:Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</hc:Grid.RowDefinitions>
<!-- 进程详细信息展示 -->
<TextBlock Grid.Row="0" Grid.Column="0" Text="路径:" FontWeight="Bold"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="命令行:" FontWeight="Bold"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding CommandLine}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="线程数:" FontWeight="Bold" Margin="0,10,0,0"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ThreadCount}" Margin="0,10,0,0"/>
</hc:Grid>
</hc:Expander>
</DataTemplate>
</hc:DataGrid.RowDetailsTemplate>
</hc:DataGrid>
性能优化策略
UI虚拟化与数据分页
HandyControl DataGrid内置支持UI虚拟化,只需确保正确设置:
<hc:DataGrid EnableRowVirtualization="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
MaxHeight="600"/>
对于超大数据集(1000+进程),实现分页加载:
private int _currentPage = 1;
private const int PageSize = 50;
private void LoadPage(int pageNumber)
{
var skip = (pageNumber - 1) * PageSize;
var pagedData = AllProcesses.Skip(skip).Take(PageSize).ToList();
// 清空并添加分页数据(使用Dispatcher确保UI线程安全)
Application.Current.Dispatcher.Invoke(() =>
{
ProcessList.Clear();
foreach (var item in pagedData)
{
ProcessList.Add(item);
}
});
}
数据采集优化
- 减少不必要的属性更新:
private double _cpuUsage;
public double CpuUsage
{
get => _cpuUsage;
set
{
// 仅当变化超过阈值时才触发属性更新
if (Math.Abs(value - _cpuUsage) > 0.5)
{
_cpuUsage = value;
OnPropertyChanged();
}
}
}
- 批量更新数据:
using (ProcessList.DeferRefresh())
{
foreach (var process in updatedProcesses)
{
// 更新操作
}
}
测试与部署
功能测试矩阵
| 测试场景 | 测试方法 | 预期结果 |
|---|---|---|
| 进程列表加载 | 启动应用观察初始加载时间 | 50个进程数据加载<1秒 |
| 实时更新功能 | 观察CPU占用变化 | 数据每秒更新,UI无明显卡顿 |
| 筛选功能 | 输入进程名称关键词 | 结果即时过滤,响应<300ms |
| 详情展开 | 点击行展开详情面板 | 面板平滑展开,显示完整进程信息 |
| 排序功能 | 点击列标题切换排序方向 | 数据正确排序,保持实时更新 |
常见问题解决方案
-
UI卡顿问题:
- 确保所有数据采集在非UI线程执行
- 使用
Dispatcher.Invoke更新UI属性 - 实现数据增量更新而非全量替换
-
内存泄漏风险:
- 正确解绑事件处理器
- 使用弱引用保存上下文对象
- 及时释放
Process对象资源
-
高CPU占用:
- 调整数据采集间隔(建议1-2秒)
- 实现数据缓存机制
- 非活跃状态降低更新频率
总结与扩展
本文详细介绍了如何使用HandyControl DataGrid控件构建功能完备的WPF任务管理器,涵盖了从基础配置到高级功能实现的全过程。通过合理的数据绑定、样式定制和性能优化,可以打造出既美观又高效的系统监控工具。
后续可扩展方向:
- 进程终止/优先级调整功能
- 系统资源使用图表展示(结合HandyControl Chart控件)
- 进程启动时间线与资源占用历史记录
- 主题切换与个性化设置
希望本文能帮助你更好地掌握HandyControl数据网格的应用技巧,提升WPF应用开发效率。如有任何问题或建议,欢迎在项目仓库提交issue交流讨论。
点赞+收藏+关注,获取更多WPF开发实战技巧!下期预告:"HandyControl主题定制与深色模式实现"
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



