【问题标题】:Add many items to ListBox while keeping the UI resposive向 ListBox 添加许多项目,同时保持 UI 响应
【发布时间】:2021-07-21 20:00:41
【问题描述】:

我正在尝试使用大量数据更新 ListBox 并同时保持 UI 响应。

这是我用来实现此目的的代码,遍历 10000 个项目,将它们收集到 100 个项目的批次中,然后一次性插入这 100 个项目,这样我就可以避免每次添加单个项目时更新 UI,但不幸的是,代码不起作用,只有在将所有 10000 个项目实际添加到 ListBox 后,才会更新 UI。

public partial class Form1 : Form
{
    private SynchronizationContext synchronizationContext;

    public Form1()
    {
        InitializeComponent();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        synchronizationContext = SynchronizationContext.Current;

        await Task.Run(() =>
        {
            ConcurrentDictionary<int, int> batch = new ConcurrentDictionary<int, int>();
            int count = 0;
            for (var i = 0; i <= 10000; i++)
            {
                batch[i] = i;
                count++;
                if (count == 100)
                {
                    count = 0;
                    UpdateUI(batch);
                    batch = new ConcurrentDictionary<int, int>();
                }

                
            }
        });
    }

    private void UpdateUI(ConcurrentDictionary<int, int> items)
    {
        synchronizationContext.Post(o =>
        {
            listBox1.SuspendLayout();
            foreach (var item in items)
            {
                listBox1.Items.Add(item.Value);
            }

            listBox1.ResumeLayout();

        }, null);
    }
}

【问题讨论】:

  • 您可以用BeginInvoke() 替换整个 SynchronizationContext 和 Post() 方法调用(因为所有方法都在同一个 Form 类中工作。否则,传递一个 Progress<T> 委托:这是一个简化的方法将 Post() 发送到 SynchronizationContext 而不显式捕获它,IProgress&lt;T&gt; 委托会为您执行此操作)。那么,你不需要SuspendLayout()/ResumeLayout()(无关),而是BeginUpdate()/EndUpdate()
  • 也许这会有所帮助:stackoverflow.com/questions/2341731/…
  • a) AddRange b) 如果你关心 UI,为什么要添加这么多项目??

标签: c# winforms async-await listbox


【解决方案1】:

您不需要多线程方法来更新 UI。您只需使用ListBox.BeginUpdateListBox.EndUpdate 方法在批量插入期间暂停ListBox 的绘制:

private void button1_Click(object sender, EventArgs e)
{
    listBox1.BeginUpdate();
    for (var i = 1; i <= 10000; i++)
    {
        listBox1.Items.Add(i);
    }
    listBox1.EndUpdate();
}

Control.SuspendLayoutControl.ResumeLayout 方法用于在灵活容器中动态添加控件,并且希望在添加每个新控件时防止控件跳来跳去。

【讨论】:

  • 那么只需:listBox1.DataSource = Enumerable.Range(0, 10000).Select(i =&gt; i).ToArray();。 -- 确实,OP 没有指定是否调用阻塞方法来获取此项目集合,但可能是这种情况。不过,OP 应该澄清,数据源很重要
猜你喜欢
  • 2014-09-14
  • 2020-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-26
  • 1970-01-01
相关资源
最近更新 更多