打造WPF任务管理器:HandyControl数据网格应用

打造WPF任务管理器:HandyControl数据网格应用

【免费下载链接】HandyControl Contains some simple and commonly used WPF controls 【免费下载链接】HandyControl 项目地址: https://gitcode.com/gh_mirrors/ha/HandyControl

你是否还在为WPF原生DataGrid控件功能单一、样式陈旧而烦恼?是否需要一个既能高效展示系统进程数据,又能提供丰富交互体验的数据表格解决方案?本文将带你使用HandyControl的增强型数据网格(DataGrid)控件,从零构建一个功能完备的WPF任务管理器,解决进程监控中的数据展示、交互和性能痛点。

读完本文你将掌握:

  • HandyControl数据网格的高级配置与样式定制
  • 进程数据实时采集与绑定技巧
  • 复杂交互功能(排序、筛选、详情展开)实现
  • 性能优化策略与内存管理方案

技术选型与架构设计

为什么选择HandyControl?

HandyControl作为WPF领域流行的开源控件库,其数据网格控件相比原生DataGrid具有显著优势:

特性原生DataGridHandyControl DataGrid
默认样式系统原生风格现代化扁平化设计
扩展属性基础属性支持行号、复选框、行高亮
性能优化无特殊优化虚拟化容器+延迟加载
交互体验基础排序筛选内置搜索框+多列排序
自定义模板支持复杂简化的模板绑定机制

系统架构设计

mermaid

核心模块说明:

  • 进程数据采集服务:使用System.Diagnostics命名空间API获取进程信息
  • 数据模型ProcessInfo类封装进程ID、名称、CPU占用等属性
  • UI展示层:HandyControl DataGrid作为核心控件,配合其他辅助组件
  • 性能监控:独立线程采集系统资源占用,避免UI阻塞

开发环境准备

项目搭建与依赖配置

  1. 创建WPF应用(.NET 6+)
  2. 通过NuGet安装HandyControl:
Install-Package HandyControl -Version 3.5.0
  1. 在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);
        }
    });
}

数据采集优化

  1. 减少不必要的属性更新
private double _cpuUsage;
public double CpuUsage
{
    get => _cpuUsage;
    set
    {
        // 仅当变化超过阈值时才触发属性更新
        if (Math.Abs(value - _cpuUsage) > 0.5)
        {
            _cpuUsage = value;
            OnPropertyChanged();
        }
    }
}
  1. 批量更新数据
using (ProcessList.DeferRefresh())
{
    foreach (var process in updatedProcesses)
    {
        // 更新操作
    }
}

测试与部署

功能测试矩阵

测试场景测试方法预期结果
进程列表加载启动应用观察初始加载时间50个进程数据加载<1秒
实时更新功能观察CPU占用变化数据每秒更新,UI无明显卡顿
筛选功能输入进程名称关键词结果即时过滤,响应<300ms
详情展开点击行展开详情面板面板平滑展开,显示完整进程信息
排序功能点击列标题切换排序方向数据正确排序,保持实时更新

常见问题解决方案

  1. UI卡顿问题

    • 确保所有数据采集在非UI线程执行
    • 使用Dispatcher.Invoke更新UI属性
    • 实现数据增量更新而非全量替换
  2. 内存泄漏风险

    • 正确解绑事件处理器
    • 使用弱引用保存上下文对象
    • 及时释放Process对象资源
  3. 高CPU占用

    • 调整数据采集间隔(建议1-2秒)
    • 实现数据缓存机制
    • 非活跃状态降低更新频率

总结与扩展

本文详细介绍了如何使用HandyControl DataGrid控件构建功能完备的WPF任务管理器,涵盖了从基础配置到高级功能实现的全过程。通过合理的数据绑定、样式定制和性能优化,可以打造出既美观又高效的系统监控工具。

后续可扩展方向:

  • 进程终止/优先级调整功能
  • 系统资源使用图表展示(结合HandyControl Chart控件)
  • 进程启动时间线与资源占用历史记录
  • 主题切换与个性化设置

希望本文能帮助你更好地掌握HandyControl数据网格的应用技巧,提升WPF应用开发效率。如有任何问题或建议,欢迎在项目仓库提交issue交流讨论。

点赞+收藏+关注,获取更多WPF开发实战技巧!下期预告:"HandyControl主题定制与深色模式实现"

【免费下载链接】HandyControl Contains some simple and commonly used WPF controls 【免费下载链接】HandyControl 项目地址: https://gitcode.com/gh_mirrors/ha/HandyControl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值