You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让仿Windows任务管理器的窗体拖动与滚动更流畅?

问题描述

运行仿Windows任务管理器的应用时,加载进程列表需数秒才能在ListView中显示;拖动窗体或上下滚动时,应用频繁卡顿约1秒,无法达到Windows任务管理器的流畅度。该如何解决此问题?

原始Form1代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Process_List_App
{
    public partial class Form1 : Form
    {
        private DoubleBufferedListView customListView;

        public Form1()
        {
            InitializeComponent();

            customListView = new DoubleBufferedListView();
            InitializeListView(customListView);
            StartProcessListUpdater();
        }

        private void InitializeListView(DoubleBufferedListView listView)
        {
            // 添加ListView列
            listView.Columns.Add("Process Name", 200);
            listView.Columns.Add("PID", 80);
            listView.Columns.Add("Memory Usage (MB)", 120);

            // 设置ListView属性
            listView.View = View.Details;
            listView.FullRowSelect = true;
            listView.Dock = DockStyle.Fill;

            // 将自定义ListView添加到窗体控件
            Controls.Add(listView);
        }

        private void StartProcessListUpdater()
        {
            // 启动定时器更新进程列表
            timer1.Start();
        }

        private void UpdateProcessList()
        {
            // 获取运行中的进程列表
            Process[] processes = Process.GetProcesses();

            foreach (Process process in processes)
            {
                // 通过PID查找对应的ListView项
                ListViewItem item = customListView.Items.Cast<ListViewItem>()
                    .FirstOrDefault(x => x.SubItems[1].Text == process.Id.ToString());

                if (item != null)
                {
                    // 更新现有项的内存占用
                    item.SubItems[2].Text = (process.WorkingSet64 / (1024 * 1024)).ToString("N2"); // 内存占用(MB)
                }
                else
                {
                    // 创建新的ListView项
                    item = new ListViewItem(process.ProcessName);
                    item.SubItems.Add(process.Id.ToString());
                    item.SubItems.Add((process.WorkingSet64 / (1024 * 1024)).ToString("N2")); // 内存占用(MB)

                    // 添加到ListView
                    customListView.Items.Add(item);
                }
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            // 定时器触发时更新进程列表
            UpdateProcessList();
        }
    }
}

对应的DoubleBufferedListView类

using System;
using System.Windows.Forms;

public class DoubleBufferedListView : ListView
{
    public DoubleBufferedListView()
    {
        DoubleBuffered = true;
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x02000000;  // WS_EX_DOUBLEBUFFER
            return cp;
        }
    }
}

尝试修改后的Form1代码(使用async Task但问题未解决)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Process_List_App
{
    public partial class Form1 : Form
    {
        private DoubleBufferedListView customListView;
        private bool isScrollingOrMoving = false;

        public Form1()
        {
            InitializeComponent();

            customListView = new DoubleBufferedListView();
            InitializeListView(customListView);
            StartProcessListUpdater();
        }

        private void InitializeListView(DoubleBufferedListView listView)
        {
            // 添加ListView列
            listView.Columns.Add("Process Name", 200);
            listView.Columns.Add("PID", 80);
            listView.Columns.Add("Memory Usage (MB)", 120);

            // 设置ListView属性
            listView.View = View.Details;
            listView.FullRowSelect = true;
            listView.Dock = DockStyle.Fill;

            // 将自定义ListView添加到窗体控件
            Controls.Add(listView);

            // 订阅滚动和移动事件
            this.Scroll += Form1_Scroll;
            this.Move += Form1_Move;
        }

        private void StartProcessListUpdater()
        {
            // 启动定时器更新进程列表
            timer1.Start();
        }

        private async Task UpdateProcessListAsync()
        {
            // 异步获取进程列表
            await Task.Run(() =>
            {
                Process[] processes = Process.GetProcesses();

                foreach (Process process in processes)
                {
                    // 检查是否正在滚动或移动窗体
                    if (!isScrollingOrMoving)
                    {
                        ListViewItem item = null;

                        // 在UI线程访问ListView项
                        customListView.Invoke(new Action(() =>
                        {
                            // 通过PID查找对应的ListView项
                            item = customListView.Items.Cast<ListViewItem>()
                                .FirstOrDefault(x => x.SubItems[1].Text == process.Id.ToString());
                        }));

                        if (item != null)
                        {
                            // 更新现有项的内存占用
                            customListView.Invoke(new Action(() =>
                            {
                                item.SubItems[2].Text = (process.WorkingSet64 / (1024 * 1024)).ToString("N2"); // 内存占用(MB)
                            }));
                        }
                        else
                        {
                            // 创建新的ListView项
                            item = new ListViewItem(process.ProcessName);
                            item.SubItems.Add(process.Id.ToString());
                            item.SubItems.Add((process.WorkingSet64 / (1024 * 1024)).ToString("N2")); // 内存占用(MB)

                            // 在UI线程添加新项
                            customListView.Invoke(new Action(() =>
                            {
                                customListView.Items.Add(item);
                            }));
                        }
                    }
                }
            });
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            // 定时器触发时异步更新进程列表
            _ = UpdateProcessListAsync();
        }

        private void Form1_Scroll(object sender, ScrollEventArgs e)
        {
            // 设置滚动标记,暂停更新
            isScrollingOrMoving = true;

            // 延迟后清除标记,恢复更新
            Task.Delay(500).ContinueWith(_ =>
            {
                isScrollingOrMoving = false;
            });
        }

        private void Form1_Move(object sender, EventArgs e)
        {
            // 设置移动标记,暂停更新
            isScrollingOrMoving = true;

            // 延迟后清除标记,恢复更新
            Task.Delay(500).ContinueWith(_ =>
            {
                isScrollingOrMoving = false;
            });
        }
    }
}
解决方案

核心问题分析

  1. UI线程阻塞:原始代码中进程获取、项查找操作全在UI线程执行,直接占用UI资源导致卡顿。
  2. 频繁跨线程调用:修改后的代码每个进程单独触发UI更新,大量跨线程同步操作反而增加开销。
  3. 低效项查找:每次遍历所有ListView项通过文本匹配PID,进程数量多时效率极低。
  4. 事件监听错误:订阅的是窗体的滚动事件而非ListView自身的,无法准确捕获滚动状态。

具体优化步骤

  1. 异步批量处理:后台线程一次性获取所有进程数据,整理后批量更新UI,减少跨线程调用次数。
  2. 字典缓存优化查找:用Dictionary<int, ListViewItem>存储PID与ListView项的映射,将查找复杂度从O(n)降为O(1)。
  3. 正确监听滚动事件:订阅ListView自身的滚动事件,确保准确捕获滚动状态。
  4. 批量更新时暂停绘制:更新前后调用BeginUpdate()和EndUpdate(),避免频繁重绘。

优化后的完整代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Process_List_App
{
    public partial class Form1 : Form
    {
        private DoubleBufferedListView customListView;
        private bool isUpdating = false;
        private Dictionary<int, ListViewItem> processItemCache = new Dictionary<int, ListViewItem>();
        private readonly object cacheLock = new object();

        public Form1()
        {
            InitializeComponent();
            customListView = new DoubleBufferedListView();
            InitializeListView(customListView);
            // 设置合理的更新间隔(与系统任务管理器一致)
            timer1.Interval = 1000;
            StartProcessListUpdater();
        }

        private void InitializeListView(DoubleBufferedListView listView)
        {
            listView.Columns.Add("进程名称", 200);
            listView.Columns.Add("PID", 80);
            listView.Columns.Add("内存占用(MB)", 120);
            listView.View = View.Details;
            listView.FullRowSelect = true;
            listView.Dock = DockStyle.Fill;
            Controls.Add(listView);

            // 订阅ListView自身的滚动事件
            listView.Scroll += ListView_Scroll;
            // 订阅窗体移动事件
            this.Move += Form1_Move;
        }

        private void StartProcessListUpdater()
        {
            timer1.Start();
        }

        private async void timer1_Tick(object sender, EventArgs e)
        {
            // 避免并发更新
            if (isUpdating) return;
            isUpdating = true;
            await UpdateProcessListAsync();
            isUpdating = false;
        }

        private async Task UpdateProcessListAsync()
        {
            // 后台线程获取并处理进程数据,捕获权限异常
            var currentProcesses = await Task.Run(() =>
            {
                var processDict = new Dictionary<int, (string Name, double MemoryMB)>();
                foreach (var process in Process.GetProcesses())
                {
                    try
                    {
                        double memoryMB = process.WorkingSet64 / (1024.0 * 1024.0);
                        processDict[process.Id] = (process.ProcessName, memoryMB);
                    }
                    catch { }
                }
                return processDict;
            });

            // UI线程批量更新ListView
            customListView.Invoke(new Action(() =>
            {
                customListView.BeginUpdate();

                lock (cacheLock)
                {
                    // 移除已退出的进程项
                    var removedPids = processItemCache.Keys.Except(currentProcesses.Keys).ToList();
                    foreach (var pid in removedPids)
                    {
                        if (processItemCache.TryGetValue(pid, out var item))
                        {
                            customListView.Items.Remove(item);
                            processItemCache.Remove(pid);
                        }
                    }

                    // 更新现有进程或添加新进程
                    foreach (var kvp in currentProcesses)
                    {
                        int pid = kvp.Key;
                        var (name, memoryMB) = kvp.Value;

                        if (processItemCache.TryGetValue(pid, out var item))
                        {
                            item.SubItems[2].Text = memoryMB.ToString("N2");
                        }
                        else
                        {
                            item = new ListViewItem(name);
                            item.SubItems.Add(pid.ToString());
                            item.SubItems.Add(memoryMB.ToString("N2"));
                            customListView.Items.Add(item);
                            processItemCache.Add(pid, item);
                        }
                    }
                }

                customListView.EndUpdate();
            }));
        }

        private void ListView_Scroll(object sender, ScrollEventArgs e)
        {
            // 滚动时暂停更新,延迟后恢复
            timer1.Stop();
            Task.Delay(300).ContinueWith(_ =>
            {
                Invoke(new Action(() => timer1.Start()));
            });
        }

        private void Form1_Move(object sender, EventArgs e)
        {
            // 移动窗体时暂停更新,延迟后恢复
            timer1.Stop();
            Task.Delay(300).ContinueWith(_ =>
            {
                Invoke(new Action(() => timer1.Start()));
            });
        }
    }
}

额外优化建议

  • 启用虚拟模式:进程数量超过1000时,开启ListView的VirtualMode,仅绘制可见区域项,降低内存和重绘开销。
  • 调整更新间隔:无需设置过短的定时器间隔,1秒间隔足够匹配系统任务管理器的体验。
  • 优化异常处理:针对系统进程的访问权限问题,添加更细致的异常捕获逻辑,避免程序崩溃。

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 14:27:03