微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

填充 TreeView 时 UI 冻结

如何解决填充 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.BeginInvoketreeView.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 举报,一经查实,本站将立刻删除。