【问题标题】:WPF: Passing objects between UI Thread and Background threadWPF:在 UI 线程和后台线程之间传递对象
【发布时间】:2009-07-25 12:29:00
【问题描述】:

在 InitializeComponents 之后的 Window 构造函数中,我需要创建一个对象并将其绑定到数据网格。由于创建对象花费了太多时间,因此窗口需要一段时间才能显示出来。因此,我决定将对象的创建移至后台线程,并通过执行 dispatcher.invoke 执行绑定“委托回” UI 线程。但这失败了。

奇怪的是,如果我尝试设置我在 Dispatcher.invoke 中的矩形的可见性,这可行,但 DataGrid.setbinding 不行!有任何想法吗?我在后台工作人员和线程启动中尝试过同样的事情,但我一直收到同样的错误。我无法访问 DataGrid 对象,即使它发生在调度程序调用委托中。在我对它的工作原理的理解中,我肯定会遗漏一些东西。任何建议将不胜感激。谢谢!

StartupDelegate s = new StartupDelegate(CreateModel);
s.BeginInvoke(delegate(IAsyncResult aysncResult) { s.EndInvoke(aysncResult); }, null);

internal CreateModel()
{
    Model d = new Model();
    Dispatcher.Invoke( DispatcherPriority.Normal, 
                       new Action<Model>(
                          delegate(Model d1)
                          {
                              mModel = d1;   // mModel is a property defined in Window
                              Binding b = new Binding();
                              b.Source = mModel; 
                              MainDataGrid.SetBinding(TreeView.ItemsSourceProperty, mainb); // << dies here with - The calling thread cannot access this object because a different thread owns it.
                          }            
}

更新: 最终使用了只运行一次的调度程序定时器。将绑定代码放在其 Tick 委托中是可行的。但我仍然很好奇为什么上面的代码没有。

【问题讨论】:

    标签: c# wpf multithreading dispatcher


    【解决方案1】:

    我会建议另一种方式。

    不应从代码中调用绑定,而应在 XAML 中定义它。

    您可以在窗口上再添加一个 Model 类型的 DependencyProperty,并将其命名为“CurrentModel”,并将其初始值设置为 NULL。看起来您已经有一个名为 mModel 的属性,是 DependencyProperty 吗?

    您可以将 CurrentModel 绑定到 DataGrid 或 XAML 中的任何控件。

    在你的委托结束时,Dispatcher.Invoke 应该只设置 CurrentModel,绑定将自动完成。

    【讨论】:

    • 我没有在 xaml 中尝试过,而只是将绑定设置为属性,并将属性初始化为 null。所以委托只有一行 mModel=d1。但是什么也没发生。它不是依赖属性,而是实现 notifypropertychanged 的​​普通属性。
    • 我怀疑,因为如果对象是从 DependencyObject 派生的(你的窗口是),INotifyPropertyChanged 不起作用,即使我有类似的问题,这就是为什么我将其更改为 DependencyProperty。
    【解决方案2】:

    您在哪个调度程序实例上调用Invoke

    我猜是后台线程中的 Dispatcher 正在执行 CreateModel,而不是 UI 线程中的。

    DataGrid 是一个控件,因此派生自DispatcherObject。每个这样的对象都通过其Dispatcher 属性公开其所有者线程的调度程序,这是您应该用来调用控件上的方法的那个。

    在调用中更改调度程序应该可以工作:

    internal CreateModel()
    {
        Model d = new Model();
    
        // Invoke the action on the dispatcher of the DataGrid
        MainDataGrid.Dispatcher.Invoke( DispatcherPriority.Normal, 
                           new Action<Model>(
                              delegate(Model d1)
                              {
                                  mModel = d1;   // mModel is a property defined in Window
                                  Binding b = new Binding();
                                  b.Source = mModel; 
                                  MainDataGrid.SetBinding(TreeView.ItemsSourceProperty, mainb);
                              }            
    }
    

    您也可以在执行后台操作之前将 UI 线程的 Dispatcher 存储在一个字段中,但是使用控件的 Dispatcher 可以更好地显示代码的意图:“我想在此控件所属的任何线程上调用它”。

    更新:我刚刚意识到这是您控件的实例方法,因此您使用的调度程序实例是正确的。这么晚才回复。此外,您的代码对我有用,将您的模型替换为 IEnumerable。你的模型有什么特别之处吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多