WPF:在UI线程和后台线程之间传递对象

时间:2009-07-25 12:29:00

标签: c# wpf multithreading dispatcher

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

奇怪的是,如果我尝试在Dispatcher.invoke中设置一个矩形的可见性,那可行,但DataGrid.setbinding不行!有任何想法吗?我已经尝试了与后台工作程序和threadstart相同的东西,但我一直得到同样的错误。我无法访问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委托中。但我仍然很好奇为什么上面的代码没有。

2 个答案:

答案 0 :(得分:0)

我建议另辟蹊径。

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

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

您可以定义CurrentModel到DataGrid的绑定或XAML中的哪个控件。

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

答案 1 :(得分:0)

您在调用Invoke的调度程序实例?

我猜测它是正在执行CreateModel的后台线程的Dispatcher,而不是来自UI线程的Dispatcher。

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替换您的模型。您的模型中有什么特别的东西吗?