繁体   English   中英

从另一个线程更新 Datagrid 的 ItemSource

[英]Update the ItemSource of a Datagrid from another thread

我在这里看到了许多这类问题的提问和回答,但似乎没有一个能解决我的问题。

我有一个从数据库中检索并显示数据列表的页面。 我的初始代码看起来像这样

   private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
    {
       //''''''
        _invoices = Invoice.GetAll(); // returns a list of invoices
         InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
        DgInvoices.ItemsSource = InvoiceList.CurrentItems;
      //''''''''' 
    }

这工作正常,直到数据列表变大。 现在这个操作大约需要 6-8 秒。 然后我尝试从另一个线程获取数据并从那里更新 Datagrid ( DGInvoices )。

   private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
    {
       //''''''''

        new Thread(() =>
        {
            _invoices = Invoice.GetAll();
              InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
            DgInvoices.ItemsSource = InvoiceList.CurrentItems;
        }).Start();
    }

引发此异常

调用线程无法访问此 object,因为不同的线程拥有它

搜索了一圈,发现Dispatcher是关于这个的go的方式。 但我无法让它工作。

    private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
    {
       //''''''''
        new Thread(() =>
        {
            _invoices = Invoice.GetAll();
              InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
            Dispatcher.Invoke(() =>
            {
                DgInvoices.ItemsSource = InvoiceList.CurrentItems;
            });

        }).Start();
    }

这仍然会引发上述异常。

你能推荐一种让它工作的方法吗?

我个人认为BackgroundWorker将是最好的选择。 Dispatcher可能会工作,但它在 WPF 中是一个更“强制”的操作,它有时会出现一连串其他问题。 使用BackgroundWorker ,您可以在后台处理数据,然后在完成后在主线程上处理 UI。

举个例子:

BackgroundWorker bw = new BackgroundWorker();

public MainWindow()
{
    InitializeComponent();

    //Subscribe to the events
    bw.DoWork += Bw_DoWork;
    bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
}

private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
     //Start background worker on page load
     bw.RunWorkerAsync(); //This is the DoWork function
}

//Background worker executes on separate thread
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
     //Do long running operations
     _invoices = Invoice.GetAll();           
}

//Fires when the DoWork operation finishes. Executes on the main UI thread
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
     //Update UI when the worker completes on the main thread
     InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
     DgInvoices.ItemsSource = InvoiceList.CurrentItems;
}

如果您的操作变得很长,您甚至可以利用BackgrounWorker.ReportProgess操作并向 UI 提供状态更新。 它是加载操作的绝佳工具,可用于避免锁定 UI。

为什么在新线程中使用 Dispatcher?

您可以简单地在新线程之外使用 Dipatcher。

像这样:

Dispatcher.Invoke(() =>
        {
            DgInvoices.ItemsSource = InvoiceList.CurrentItems;
        });

所以你可以在主线程而不是新线程上调用

不要直接在线程内更新 DgInvoices.ItemsSource。 而是将 ItemSource 绑定到属性并更新线程中的属性。

在您最后一次编辑时,要使其正常工作,您必须将InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage)也移动到Dispatcher ,因为它的子CurrentItems已分配给 DitaGrid 的项目源。 因此,您不能从主 UI 线程以外的地方修改InvoiceList

此外,我建议使用Task而不是Thread ,因为创建线程的操作过于昂贵,并且Task可能会重用已创建的线程并节省您的时间和 PC 资源。 TaskThread的智能包装器。

private void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
    //''''''''
    Task.Run(() =>
    {
        _invoices = Invoice.GetAll();
        Dispatcher.Invoke(() =>
        {
            InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
            DgInvoices.ItemsSource = InvoiceList.CurrentItems;
        });
    });
}

或者,如果您的 API 有异步方法来获取数据,您可以使用异步方法。 但我不知道是否存在这种等待的方法。

private async void HistoryPage_OnLoaded(object sender, RoutedEventArgs e)
{
    //''''''''
    await _invoices = Invoice.GetAllAsync();
    InvoiceList = new PagingCollection<Invoice>(_invoices, _itemsPerPage);
    DgInvoices.ItemsSource = InvoiceList.CurrentItems;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM