【问题标题】:UI frozen while populating TreeViewUI 在填充 TreeView 时冻结
【发布时间】:2021-04-20 15:50:08
【问题描述】:

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.BeginInvoke 在 UI 线程上调用 FillNodes。您要么需要确定用户正在查看哪些节点并仅绘制这些节点,要么需要virtualize your treeView
  • 向 TreeView 添加 100000 个节点会使 UI 线程太忙,无论您使用 Invoke 还是 BeginInvoke,因为它们也在运行/调度运行 UI 线程中的代码。就是这样。

标签: .net multithreading winforms user-interface c#-2.0


【解决方案1】:

向 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());
}

【讨论】:

  • Task 和 async/await 相同,但正如已经提到的,...//Just for example:
  • 我知道这是一个例子,但也许建议 OP 在他们的 C# 版本中确实可以访问的功能可能会更有帮助。例如:BackgroundWorker.
  • 我同意,但重点就在AddRange :)
  • 很公平! :)
  • 好吧,我注意到 OP 已经应用了所有的点。最后一点是:向 TreeView 添加 100000 个节点会使 UI 线程过于繁忙,无论您使用 Invoke 还是 BeginInvoke,因为它们也在运行/调度运行 UI 线程中的代码。 就是这样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-06
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 2012-12-09
  • 1970-01-01
相关资源
最近更新 更多