我正在開發一個具有用作十六進制查看器的串列視圖的應用程式,但存在重要的性能問題。我不是一個很有經驗的程式員,所以我不知道如何優化代碼。
這個想法是為了避免在將專案添加到串列視圖時應用程式凍結。我正在考慮以 100 個專案為一組添加專案,但我不知道如何處理上下滾動,我不確定這是否能解決這些性能問題。
控制元件將始終具有相同的高度,即 795 像素。這是我正在使用的代碼:
private void Button_SearchFile_Click(object sender, EventArgs e)
{
//Open files explorer.
OpenFileDialog_SearchFile.Filter = "SFX Files (*.sfx)|*.sfx";
DialogResult openEuroSoundFileDlg = OpenFileDialog_SearchFile.ShowDialog();
if (openEuroSoundFileDlg == DialogResult.OK)
{
//Get the selected file
string filePath = OpenFileDialog_SearchFile.FileName;
Textbox_FilePath.Text = filePath;
//Clear list.
ListView_HexEditor.Items.Clear();
//Start Reading.
using (BinaryReader BReader = new BinaryReader(File.OpenRead(filePath)))
{
//Each line will contain 16 bits.
byte[] bytesRow = new byte[16];
while (BReader.BaseStream.Position != BReader.BaseStream.Length)
{
//Get current offset.
long offset = BReader.BaseStream.Position;
//Read 16 bytes.
byte[] readedBytes = BReader.ReadBytes(16);
//Sometimes the last read could not contain 16 bits, with this we ensure to have a 16 bits array.
Buffer.BlockCopy(readedBytes, 0, bytesRow, 0, readedBytes.Length);
//Add item to the list.
ListView_HexEditor.Items.Add(new ListViewItem(new[]
{
//Print offset
offset.ToString("X8"),
//Merge bits
((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"),
//Get hex ASCII representation
GetHexStringFormat(bytesRow)
}));
}
}
}
}
private string GetHexStringFormat(byte[] inputBytes)
{
//Get char in ascii encoding
char[] arrayChars = Encoding.ASCII.GetChars(inputBytes);
for (int i = 0; i < inputBytes.Length; i )
{
//Replace no printable chars with a dot.
if (char.IsControl(arrayChars[i]) || (arrayChars[i] == '?' && inputBytes[i] != 63))
{
arrayChars[i] = '.';
}
}
return new string(arrayChars);
}
這是程式的影像:

uj5u.com熱心網友回復:
這是一個常見問題,主 UI 執行緒上的任何長時間運行的操作都會凍結應用程式。正常的解決方案要么在后臺執行緒上運行操作,要么作為異步方法......或更常見的是作為兩者的混合。
但是,這里的主要問題是ListView加載大量資料時速度很慢。即使是最快的方法——批量加載整個ListViewItems集合——也很慢。一個 340KB 檔案的快速測驗需要大約 0.18 秒來加載專案,然后大約 2.3 秒將專案添加到控制元件中。由于最后一部分必須發生在 UI 執行緒上,因此大約有 2.3 秒的死區時間。
ListView處理大型串列的最流暢的解決方案是使用虛擬串列模式。在這種模式下,ListView每當串列滾動時都會請求可見專案,而不是維護自己的專案串列。
要實作虛擬,ListView您需要為RetrieveVirtualItem事件提供一個處理程式,將VirtualListSize屬性設定VirtualMode為串列的長度,然后設定為 true。該ListView會打電話給你的RetrieveVirtualItem處理程式時,它需要的專案進行顯示。
這是我的 PoC 代碼:
// Loaded data rows.
ListViewItem[]? _rows = null;
// Load data and setup ListView for virtual list.
// Uses Task.Run() to (hopefully) get off the UI thread.
private Task LoadData(string filename)
=> Task.Run(() =>
{
// Clear current virtual list
// NB: Invoke() makes this run on the main UI thread
Invoke((Action)(() =>
{
listView1.BeginUpdate();
listView1.VirtualMode = false;
listView1.VirtualListSize = 0;
listView1.EndUpdate();
}));
// Read data into '_rows' field.
using (var stream = File.OpenRead(filename))
{
var buffer = new byte[16];
var rows = new List<ListViewItem>();
int rc;
while ((rc = stream.Read(buffer, 0, buffer.Length)) > 0)
{
var items = new[]
{
(stream.Position - rc).ToString("X8"),
string.Join(" ", buffer.Take(rc).Select(b => $"{b:X2}")),
string.Join("", buffer.Take(rc).Select(b => (char)b).Select(b => char.IsControl(b) ? '.' : b)),
};
rows.Add(new ListViewItem(items));
}
_rows = rows.ToArray();
}
// Enable virtual list mode
Invoke((Action)(() =>
{
listView1.BeginUpdate();
listView1.VirtualListSize = _rows?.Length ?? 0;
listView1.VirtualMode = _rows?.Length > 0;
listView1.EndUpdate();
}));
});
// Fetch rows from loaded data.
private void ListView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
if (_rows is not null && e.ItemIndex >= 0 && e.ItemIndex < _rows.Length)
e.Item = _rows[e.ItemIndex];
}
(插入關于缺乏錯誤處理等的常見免責宣告。)
Hopefully that's fairly self-explanatory. I took some LINQ shortcuts in the content generation, and cut the column count down. More columns means slower refresh times during scrolling, whether you're using a virtual list or letting the control handle the item collection.
For really large files this method can still be an issue. If you're routinely loading files in the 10s of MB in size or larger then you need to get a bit more creative about loading chunks of data at a time, and maybe creating and caching the ListViewItems on-the-fly rather than during the initial load phase. Have a look at the ListView.VirtualMode documentation. The example code shows the full functionality... although their caching strategy is a little rudimentary.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/357979.html
標籤:C# winforms 列表显示 十六进制 二进制文件
