如何让仿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; }); } } }
解决方案
核心问题分析
- UI线程阻塞:原始代码中进程获取、项查找操作全在UI线程执行,直接占用UI资源导致卡顿。
- 频繁跨线程调用:修改后的代码每个进程单独触发UI更新,大量跨线程同步操作反而增加开销。
- 低效项查找:每次遍历所有ListView项通过文本匹配PID,进程数量多时效率极低。
- 事件监听错误:订阅的是窗体的滚动事件而非ListView自身的,无法准确捕获滚动状态。
具体优化步骤
- 异步批量处理:后台线程一次性获取所有进程数据,整理后批量更新UI,减少跨线程调用次数。
- 字典缓存优化查找:用
Dictionary<int, ListViewItem>存储PID与ListView项的映射,将查找复杂度从O(n)降为O(1)。 - 正确监听滚动事件:订阅ListView自身的滚动事件,确保准确捕获滚动状态。
- 批量更新时暂停绘制:更新前后调用
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
相关产品推荐
相关产品推荐

