如何解决填充 TreeView 时 UI 冻结
Windows 窗体方法有什么问题。它由单独的非 UI 线程运行,但当我有 100 000 个树节点要添加时,它仍会冻结应用程序 UI 10 秒钟。
private static void FillNodes(TreeView treeView,TreeNode[] nodes)
{
if (treeView.Invokerequired)
{
Action<TreeView,TreeNode[]> action = FillNodes;
treeView.BeginInvoke(action,treeView,nodes);
}
else
{
treeView.SuspendLayout();
treeView.BeginUpdate();
treeView.Nodes.Clear();
treeView.Nodes.AddRange(nodes);
treeView.sort();
treeView.EndUpdate();
treeView.ResumeLayout();
}
}
解决方法
向 TreeView 添加 100000 个节点会使 UI 线程太忙,无论您使用 Invoke
还是 BeginInvoke
,因为它们也在 UI 线程中运行代码。原来如此。
一般来说,在 TreeView 中显示 100000 个节点绝对是一个坏主意。为了显示树中的节点数量,如果您延迟加载和添加节点会更好。这个想法是加载节点的第一级根节点并为它们添加虚拟子节点,然后传递 BeforeExpand
以加载真正的子节点并添加到节点(并再次将虚拟子节点添加到该级别)。
但无论如何,对于那些可能想要加载该数量的节点并使加载体验更流畅的人,请考虑以下事实(我看到您已经在代码中考虑了这些点):
- 当您调用
treeView.BeginInvoke
或treeView.Invoke
时,它实际上在 UI 线程中运行,因此如果您在那里进行繁重的工作,它会阻塞线程一段时间。 - 在更改树节点之前调用
BeginUpdate
并调用EndUpdate
可以防止大量绘制并使加载更流畅。 - 使用
AddRange
(不需要 BeginUpdate 和 EndUpdate),可以更快地加载树。
所以你可以:
- 在另一个线程(而不是 UI 线程)中加载数据(或尽可能使用异步等待)
- 在另一个线程中创建一个节点数组
- 在 UI 线程中使用
AddRange
添加节点。 (如果您使用 async/await,则不需要 Invoke,但使用线程或 BackgroundWorker,则需要 Invoke。)
例如:
private async void Form1_Load(object sender,EventArgs e)
{
var nodes = await GetNodes();
treeView1.Nodes.AddRange(nodes);
}
public async Task<TreeNode[]> GetNodes()
{
//Get data asynchronously from db or weherever it is
//Create nodes
//Return nodes
//Just for example:
return await Task.Run<TreeNode[]>(() =>
Enumerable.Range(1,100000)
.Select(x => new TreeNode(x.ToString())).ToArray());
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。