Использование потока пользовательского интерфейса WPF всегда должно обеспечивать режим квартиры STA, верно?

В моем приложении WPF я асинхронно общаюсь с сервером. Следовательно, обратный вызов не будет запускаться в потоке пользовательского интерфейса, и, поскольку мне нужно сделать там некоторые вещи WPF (создать объект InkPresenter), мне нужно, чтобы он запускался в потоке пользовательского интерфейса. Ну, на самом деле требование состоит в том, чтобы он запускался в потоке с режимом квартиры STA. Я попытался создать новый поток в режиме STA, но в результате поток пользовательского интерфейса не смог получить доступ к InkPresenter, поскольку он «принадлежал другому потоку».

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

private void UpdateAnnotationsForCurrentFrameCollection()
{
    if (Dispatcher.CurrentDispatcher.CheckAccess())
    {
        DoSomethingIncludingInkPresenter();
    }
    else
    {
        Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal,
           new Action(DoSomethingIncludingInkPresenter));
    }
}

private void DoSomethingIncludingInkPresenter()
{
    var inkPresenter = XamlReader.Parse(_someXamlString) as InkPresenter;
    // Do something with the inkPresenter.. 
}

Как видно из примера, я использую CheckAccess (), чтобы гарантировать, что я вызываю функцию только в том случае, если она еще не запущена в потоке пользовательского интерфейса. Когда мой обратный вызов вызывает эту функцию, CheckAccess () всегда истинно, но Dispatcher.CurrentDispatcher.Thread.ApartmentState - это MTA. Почему? Я попытался удалить CheckAccess () и всегда выполнял Invoke, но ApartmentState остается MTA, и создание InkPresenter не удается.

Может ли кто-нибудь объяснить мне, что я здесь делаю не так? У меня не тот Диспетчер или что-то в этом роде? Это правильный подход к обеспечению того, чтобы что-то запускалось в потоке пользовательского интерфейса?


person stiank81    schedule 05.03.2010    source источник


Ответы (2)


Я думаю, вы путаете 2 требования. Основные потоки WinForms и WPF помечены как STA, чтобы разрешить вызовы COM (и они могут происходить внутри элементов управления).

Ваша проблема, похоже, является классической проблемой «пользовательский интерфейс не является потокобезопасным», и ее следует решать путем отправки частей, которые касаются пользовательского интерфейса.

Но вы должны вызывать CheckAccess не для CurrentDispatche, а для своей цели:
someControl.Dispatcher.CheckAccess

person Henk Holterman    schedule 05.03.2010
comment
Спасибо! Эхр .. Могу я побудить вас уточнить? :-) - person stiank81; 05.03.2010
comment
Спасибо. Однако это модель ViewModel, поэтому нет элементов управления, из которых можно было бы взять диспетчер. Нет ли такого понятия, как общий диспетчер для потока пользовательского интерфейса? Может быть, ответ состоит в том, что я никогда не должен помещать объекты WPF, такие как InkPresenter, в ViewModel ..? Но могу ли я? И если да - как мне получить нужного Диспетчера? - person stiank81; 05.03.2010
comment
Об этом спрашивали здесь, я не могу найти его прямо сейчас. Вне времени. - person Henk Holterman; 05.03.2010
comment
Без проблем. Спасибо за вашу помощь. У меня появилась идея протестировать что-то новое ... - person stiank81; 05.03.2010
comment
@Stian: Я нашел этот, но он обсуждался чаще: stackoverflow.com/questions/2354438 - person Henk Holterman; 05.03.2010

Думаю, проблема в том, что вы используете не тот Диспетчер. Один из проверенных и надежных методов, которые я использовал, - передать диспетчеру элемента управления, в котором выполняется код.

private void SomeMethod(Dispatcher dispatcher)
{
  DoOtherThingsThatCanDoMTA();

  dispatcher.Invoke(new Action(()=>
  {
    DoSomethingThatRequiresSTA();
  }));
}

Если каким-то образом невозможно передать Dispatcher, вы можете раскрыть его в свойстве или любыми другими методами. Надеюсь, это поможет.

person Jaya Wijaya    schedule 05.03.2010
comment
Спасибо. Это, вероятно, часто срабатывает, но не похоже, что у меня есть доступ к нужному диспетчеру. Я попробую другой подход, но я ценю ваш вклад по этому поводу. - person stiank81; 05.03.2010