WPF:在UI线程和后台线程之间传递对象 [英] WPF: Passing objects between UI Thread and Background thread

查看:416
本文介绍了WPF:在UI线程和后台线程之间传递对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



奇怪的是,如果我尝试设置Dispatcher.invoke内部具有的矩形的可见性,则可以,但是DataGrid.setbinding无效!有任何想法吗?我已经尝试过使用后台工作程序和threadstart进行相同的操作,但是我一直收到相同的错误。我无法访问DataGrid对象,即使它发生在调度程序调用委托中。我对这是如何工作的肯定会遗漏一些东西。任何建议将不胜感激。谢谢!

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

内部CreateModel()
{
Model d = new Model();
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action< Model>(
代表(Model d1)
{
mModel = d1; // mModel是一个属性在窗口中定义
Binding b = new Binding();
b.Source = mModel;
MainDataGrid.SetBinding(TreeView.ItemsSourceProperty,mainb); //<在这里死于-
}
}



,调用线程无法访问该对象。

更新
最后使用了仅运行一次 的调度程序定时器。将绑定代码放入其Tick委托中可以工作,但是我仍然很好奇为什么

解决方案

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



我猜这是从后台线程执行 CreateModel的Dispatcher ,而不是UI线程中的那个。



DataGrid是一个控件,因此是从 DispatcherObject 。每个此类对象都通过其 Dispatcher 属性公开其所有者线程的调度程序,这是调用控件上的方法时应使用的对象。



更改呼叫中的调度程序应该可以:

 内部CreateModel()
{
Model d = new Model();

//调用DataGrid调度程序上的操作
MainDataGrid.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action< Model>(
代表(Model d1)
{
mModel = d1; // mModel是在窗口中定义的属性
Binding b = new Binding();
b.Source = mModel;
MainDataGrid.SetBinding(TreeView.ItemsSourceProperty,mainb);
}
}

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

更新:我刚刚意识到这是一个即时性一种控制方法,因此您使用的调度程序实例是正确的。对于深夜回答来说,太多了​​。另外,您的代码对我有用,用IEnumerable替换您的模型。您的模型有什么特别之处吗?


In my Window constructor after InitializeComponents, I need to create an object and bind it to a datagrid. Since the object creation is taking too much time, the windows takes a while to show up. So I decided to move the creation of the object to a background thread and "delegate back" to the UI thread by doing a dispatcher.invoke to perform the binding. But this fails.

The weird thing is if I try to set the visibility of a rectangle I have inside the Dispatcher.invoke, that works but the DataGrid.setbinding doesnt! Any ideas? I have tried the same thing with background worker, and threadstart, but I keep getting the same error. I am not able to access the DataGrid object even though its happening inside the dispatcher invoke delegate. Am sure am missing something in my understanding of how this works. Any suggestions would be greatly appreciated. Thanks!

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.
                          }            
}

UPDATE: Ended up using a dispatchertimer that would run just once. Putting the binding code in its Tick delegate worked. But I am still curious why the above code doesnt.

解决方案

On what dispatcher instance are you calling Invoke ?

I'm guessing it is the Dispatcher from the background thread that is executing CreateModel, not the one from the UI thread.

The DataGrid is a control, and so derives from DispatcherObject. Each such object exposes the dispatcher of its owner thread through its Dispatcher property, this is the one you should use to call methods on the control.

Changing the dispatcher in the call should work:

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);
                          }            
}

You could also store the Dispatcher of the UI thread in a field before executing the background action, but using the Dispatcher of the control better shows the intentions of the code: "I want to call this on whatever thread this control belongs to".

Update: I just realized that this is an instance method of your control, so the dispatcher instance you are using is the right one. So much for answering late in the night. Also, your code works for me, replacing your model by an IEnumerable. Is there something special in your model ?

这篇关于WPF:在UI线程和后台线程之间传递对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆