2012-04-25 2 views
4

В моем главном окне (Thread A), я создаю новую тему (Thread B), которая выполняет некоторую работу во время ожидания пользователя.Fire events from different thread

Thread B запускает события, если есть ошибка или требуется дополнительная информация от пользователя, Thread A будет прослушивать эти события.

В прослушивателе событий Thread A мне нужно показать диалоговое сообщение пользователю, у меня есть собственное диалоговое окно и показать его с помощью dialogWindow.showDialog(). Это работает отлично, но вызывает ошибку, когда я пытаюсь установить владельца диалога, я делаю это dialogWindow.Owner = Window.GetWindow(this).

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

Каков правильный способ прослушивания событий, которые запускаются из другого потока?

ответ

3

Правильный способ поднять событие в поток пользовательского интерфейса из фонового потока - это то, что событие должно быть поднято на этом Диспетчере, Ключ здесь - получить диспетчер UIthread перед началом работы.

UIDisaptcher.BeginInvoke ((ThreadStart) (() => RaiseEventToUIThread()));

Когда поток пользовательского интерфейса прослушивает поднятое событие, он может установить свойство Owner (так как окно было создано потоком пользовательского интерфейса).

+0

Спасибо, отлично работает. Я использую Invoke, а не BeginInvoke, но то же самое. – Drahcir

+2

Пожалуйста, используйте BeginInvoke Invoke, который не освободит дескриптор. – peterincumbria

3

Уверенный -> мы используем SynchronizationContext. Итак, когда вы начинаете новый поток, вы захватываете (в потоке пользовательского интерфейса) текущий контекст и передаете его во второй поток в качестве параметра.

Затем на второй поток, когда вы хотите, чтобы поднять событие, сделать это таким образом:

if (_uiThreadId != Thread.CurrentThread.ManagedThreadId) 
    { 
     _uiContext.Post(
      new SendOrPostCallback(delegate(object o) { OnYourEvent((EventArgs)o); }), 
      e); 
    } 
    else 
     OnYourEvent(e); 
+0

какое действительно хорошее решение! не знаю о sync-контексте, хорошая ссылка! Пальцы вверх! – inva

7

Код прослушиватель событий будет работать неявно в потоке, который выстреливает событие, поэтому слушатель события не нить переплете.

Если вы хотите показать что-то в пользовательском интерфейсе в результате обработки событий, вы должны сделать маршаллинг самостоятельно. Нечто подобное:

void OnEvent(object sender, EventArgs args) 
{ 
    // runs in the event sender's thread 
    string x = ComputeChanges(args); 
    Dispatcher.BeginInvoke((Action)(
     () => UpdateUI(x) 
    )); 
} 

void UpdateUI(string x) 
{ 
    // runs in the UI thread 
    control.Content = x; 
    // - or - 
    new DialogWindow() { Content = x, Owner = this }.ShowDialog(); 
    // - or whatever 
} 

Итак: вы выполняете свои вычисления (если таковые имеются) предпочтительн в фоновом потоке, не касаясь пользовательского интерфейса; после этого, когда вы знаете, какие из них необходимы в пользовательском интерфейсе, вы выполняете код обновления UI в потоке пользовательского интерфейса.

Dispatcher является собственностью управления, поэтому, если ваш код является частью пользовательского интерфейса, у вас будет ваш Диспетчер бесплатно. В противном случае вы можете принять диспетчера с любого из элементов управления (например, control.Dispatcher).