来自不同线程的火灾事件

时间:2012-04-25 12:13:54

标签: c# wpf

在我的主窗口(线程A )中,我启动了一个新线程(线程B ),它在用户等待时完成了一些工作。

如果出现错误或用户需要额外信息,线程B将触发事件,线程A将侦听这些事件。

在线程A的事件监听器中,我需要向用户显示对话框消息,我有一个自定义对话框窗口并使用dialogWindow.showDialog()显示它。这工作正常,但在我尝试设置对话框的所有者时会导致错误,我执行此操作dialogWindow.Owner = Window.GetWindow(this)

我得到的错误是:调用线程无法访问此对象,因为另一个线程拥有它。

侦听从其他线程触发的事件的正确方法是什么?

3 个答案:

答案 0 :(得分:7)

事件侦听器代码将在触发事件的线程中隐式运行,因此事件侦听器不是线程绑定的。

如果您希望在事件处理过程中在UI中显示某些内容,则应自行进行编组。这样的事情:

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;之后,当您知道UI中需要进行哪些更改时,您可以在UI线程中执行UI更新代码。

Dispatcher是控件的属性,因此如果您的代码是UI的一部分,您将免费获得Dispatcher。否则,您可以从任何控件(例如control.Dispatcher)中获取调度程序。

答案 1 :(得分:3)

当然 - >我们所做的是使用SynchronizationContext。 因此,当您启动一个新线程时,您捕获(在UI线程上)当前上下文并将其作为参数传递给第二个线程。

然后在第二个帖子上,当你想举起一个事件时,你就这样做了:

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

答案 2 :(得分:3)

从后台线程向UI线程引发事件的正确方法是,应该在该Dispatcher上引发事件, 这里的关键是获得UIthread的调度员。

  

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

当UI线程侦听raise事件时,它可以设置Owner属性(因为窗口是由UI线程创建的)。