且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2022-10-24 09:15:51

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



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



DataGrid是一个控件,因此是从 DispatcherObject $ c派生的$ c>。每个此类对象都通过其 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 ?