4

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

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

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

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

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

4

3 回答 3

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)中获取调度程序。

于 2012-04-25T12:19:58.103 回答
3

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

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

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

于 2012-04-25T12:24:56.043 回答
3

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

然后在第二个线程上,当你想引发一个事件时,你可以这样做:

    if (_uiThreadId != Thread.CurrentThread.ManagedThreadId)
    {
        _uiContext.Post(
            new SendOrPostCallback(delegate(object o) { OnYourEvent((EventArgs)o); }),
            e);
    }
    else
        OnYourEvent(e);
于 2012-04-25T12:18:55.593 回答