WPF: передача объектов между потоком пользовательского интерфейса и фоновым потоком

2

В моем конструкторе Window после InitializeComponents мне нужно создать объект и привязать его к datagrid. Поскольку создание объекта занимает слишком много времени, окна требуют времени, чтобы отобразиться. Поэтому я решил переместить создание объекта в фоновый поток и "делегировать обратно" в поток пользовательского интерфейса, выполнив команду dispatcher.invoke для выполнения привязки. Но это не удается.

Странно, если я попытаюсь установить видимость прямоугольника, который у меня внутри 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.
                          }            
}

UPDATE: Закончилось использование диспетчера, который будет запускаться только один раз. Положите код привязки в делегат Tick. Но мне все еще интересно, почему этот код не делает.

Теги:
multithreading
wpf
dispatcher

2 ответа

0

В каком экземпляре диспетчера вы вызываете Invoke?

Я предполагаю, что это Диспетчер из фонового потока, который выполняет CreateModel, а не тот, что из потока пользовательского интерфейса.

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

Вы также можете сохранить Диспетчер потока пользовательского интерфейса в поле перед выполнением фонового действия, но с помощью диспетчера элемента управления лучше показать намерения кода: "Я хочу называть это на любом потоке, которым этот элемент управления принадлежит".

Обновить. Я просто понял, что это метод экземпляра вашего элемента управления, поэтому выбранный вами экземпляр диспетчера является правильным. Так много для ответа поздно ночью. Кроме того, ваш код работает для меня, заменив вашу модель на IEnumerable. Есть ли что-то особенное в вашей модели?

0

Я бы предложил другой способ.

Связывание не следует вызывать из кода, вместо этого вы должны определить его в XAML.

Вы можете добавить еще одно DependencyProperty типа Model в ваше окно и называть его "CurrentModel" и установить его начальное значение в NULL. Похоже, у вас уже есть свойство mModel, это DependencyProperty?

Вы можете определить привязку CurrentModel к DataGrid или какой-либо элемент управления в XAML.

И в конце делегирования Dispatcher.Invoke должен установить только CurrentModel, привязка будет выполнена автоматически.

  • 0
    Я пробовал это не в xaml, а просто устанавливая привязку к свойству, а свойство инициализировалось в null. Таким образом, у делегата была только одна строка mModel = d1. Но ничего не случилось. Это не свойство зависимости, а обычное свойство, реализующее notifypropertychanged.
  • 0
    Я сомневаюсь, потому что, если объект получен из DependencyObject (ваше окно), INotifyPropertyChanged не работает, даже у меня была похожая проблема, поэтому я изменил его на DependencyProperty.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню