为什么这段代码会抛出异常?我认为 RunWorkerCompleted 可以访问 UI 个元素

Why does this code throw an exception? I thought RunWorkerCompleted could access UI elements

我的 WPF 应用程序中有一个数据网格,当应用程序启动时,我从数据库中获取部署记录并将它们加载到绑定到数据网格的 ObservableCollection 中。

通过计时器,我使用 BackgroundWorker 从数据库中获取任何新记录并将它们放入新的 ObservableCollection 中。

然后,通过 RunWorkerCompleted,我尝试使用新 ObservableCollection 中的项目更新数据网格。

但是,我收到 'System.NotSupportedException'。

Additional information: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

我以为我可以从 RunWorkerCompleted 方法访问 UI 控件,但似乎发生了一些我不明白的事情。

My code is below:

private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
    {
        //Init the deployment collection
        deployments = DataAccess.GetDeployments();
        dgDeployments.ItemsSource = deployments;

        //Setup the background worker
        bw = new BackgroundWorker();
        bw.DoWork += bw_DoWork;
        bw.RunWorkerCompleted += bw_RunWorkerCompleted;
        bw.WorkerReportsProgress = true;
        bw.WorkerSupportsCancellation = true;

        //Setup timer
        System.Threading.Timer t = new System.Threading.Timer(new System.Threading.TimerCallback(TimerProc));
        t.Change(10000, 0);           
    }

    private void TimerProc(object state)
    {
        //MessageBox.Show("Timer fired!");
        //Get the latest currentTime from the items in the grid
        DateTime? latestTime = DataAccess.GetDeployments().ToList()[0].CurrentTime;
        bw.RunWorkerAsync(latestTime);
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {         
        //Get new records that have changed since that time
        e.Result = (ObservableCollection<Deployment>)DataAccess.GetDeployments((DateTime)e.Argument);
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if(e.Error != null)
        {
            MessageBox.Show(e.Error.Message);
        }
        else if (e.Cancelled)
        {
            MessageBox.Show("The operation was cancelled");
        }
        else
        {
            ObservableCollection<Deployment> newDeployments = (ObservableCollection<Deployment>)e.Result;

            foreach (Deployment d in newDeployments)
            {
                //Remove this new/changed deployment from the collection bound to the datagrid
                int index = deployments.IndexOf(deployments.Where(x => x.UniqueID == d.UniqueID).FirstOrDefault());
                if (index > -1)
                {
                    deployments.RemoveAt(index);
                }

                //Now add the new deployments
                deployments.Add(d);
                deployments.Move(deployments.IndexOf(d), 0);
            }
        }
    }

计时器进程starts不同线程中的后台工作者。尝试在 GUI 线程中启动后台工作程序,这样完成的操作将 运行 在 GUI 线程上而不是从计时器启动的线程。

要么 Invoke 对 GUI 线程的操作,要么只使计时器成为 DispatchTimer

谢谢大家提供的好资料。它帮助我更好地理解了这个问题。这是我使用的代码,最终对我有用。

Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
                    {
                        //Remove the old deployment
                        deployments.RemoveAt(index);

                        //Now add the new deployments
                        deployments.Add(d);
                        deployments.Move(deployments.IndexOf(d), 0);
                    });