使用ListView实现十六进制查看器出现加载卡顿,该如何优化
ListView十六进制查看器性能优化方案
问题根因
你当前代码卡顿的核心原因有两个:
- ListView默认每次添加条目都会触发重绘,批量添加时会产生大量无用的UI渲染开销
- 所有文件读取、条目生成逻辑都在UI线程执行,执行过程中UI线程被完全阻塞,导致程序无响应
快速修复方案(小文件适用,改造成本极低)
只需要在添加条目前后增加BeginUpdate和EndUpdate调用,合并所有重绘操作,即可获得10倍以上的性能提升,修改后的核心代码如下:
private void Button_SearchFile_Click(object sender, EventArgs e) { OpenFileDialog_SearchFile.Filter = "SFX Files (*.sfx)|*.sfx"; DialogResult openEuroSoundFileDlg = OpenFileDialog_SearchFile.ShowDialog(); if (openEuroSoundFileDlg == DialogResult.OK) { string filePath = OpenFileDialog_SearchFile.FileName; Textbox_FilePath.Text = filePath; ListView_HexEditor.Items.Clear(); // 暂停ListView重绘 ListView_HexEditor.BeginUpdate(); using (BinaryReader BReader = new BinaryReader(File.OpenRead(filePath))) { byte[] bytesRow = new byte[16]; while (BReader.BaseStream.Position != BReader.BaseStream.Length) { long offset = BReader.BaseStream.Position; byte[] readedBytes = BReader.ReadBytes(16); Buffer.BlockCopy(readedBytes, 0, bytesRow, 0, readedBytes.Length); ListView_HexEditor.Items.Add(new ListViewItem(new[] { offset.ToString("X8"), ((bytesRow[0] << 8) | bytesRow[1]).ToString("X4"), ((bytesRow[2] << 8) | bytesRow[3]).ToString("X4"), ((bytesRow[4] << 8) | bytesRow[5]).ToString("X4"), ((bytesRow[6] << 8) | bytesRow[7]).ToString("X4"), ((bytesRow[8] << 8) | bytesRow[9]).ToString("X4"), ((bytesRow[10] << 8) | bytesRow[11]).ToString("X4"), ((bytesRow[12] << 8) | bytesRow[13]).ToString("X4"), ((bytesRow[14] << 8) | bytesRow[15]).ToString("X4"), GetHexStringFormat(bytesRow) })); } } // 恢复重绘,一次性渲染所有条目 ListView_HexEditor.EndUpdate(); } } private string GetHexStringFormat(byte[] inputBytes) { char[] arrayChars = Encoding.ASCII.GetChars(inputBytes); for (int i = 0; i < inputBytes.Length; i++) { if (char.IsControl(arrayChars[i]) || (arrayChars[i] == '?' && inputBytes[i] != 63)) { arrayChars[i] = '.'; } } return new string(arrayChars); }
终极优化方案(大文件适用,无卡顿)
如果需要打开几十MB以上的大文件,推荐使用ListView的虚拟模式,核心逻辑是:只渲染当前可视区域的条目(你的控件高度固定795px,可视区域最多30行左右),不需要提前加载所有条目,滚动时动态生成对应行的内容即可,完全避免卡顿,实现步骤如下:
- 在设计器中将
ListView_HexEditor的VirtualMode属性设置为True - 全局定义存储文件内容的字节数组:
private byte[] _fileBytes; - 文件读取完成后设置虚拟列表总长度:
ListView_HexEditor.VirtualListSize = (int)Math.Ceiling(_fileBytes.Length / 16.0); - 订阅
RetrieveVirtualItem事件,动态生成对应行的内容:
private void ListView_HexEditor_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) { int startOffset = e.ItemIndex * 16; int length = Math.Min(16, _fileBytes.Length - startOffset); byte[] bytesRow = new byte[16]; Buffer.BlockCopy(_fileBytes, startOffset, bytesRow, 0, length); e.Item = new ListViewItem(new[] { startOffset.ToString("X8"), ((bytesRow[0] << 8) | bytesRow[1]).ToString("X4"), ((bytesRow[2] << 8) | bytesRow[3]).ToString("X4"), ((bytesRow[4] << 8) | bytesRow[5]).ToString("X4"), ((bytesRow[6] << 8) | bytesRow[7]).ToString("X4"), ((bytesRow[8] << 8) | bytesRow[9]).ToString("X4"), ((bytesRow[10] << 8) | bytesRow[11]).ToString("X4"), ((bytesRow[12] << 8) | bytesRow[13]).ToString("X4"), ((bytesRow[14] << 8) | bytesRow[15]).ToString("X4"), GetHexStringFormat(bytesRow) }); }
程序运行效果

内容的提问来源于stack exchange,提问作者jms2505
相关产品推荐
相关产品推荐

