从不同线程触发事件

  • 本文关键字:事件 线程 c# wpf
  • 更新时间 :
  • 英文 :


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

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

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

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

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

事件侦听器代码将在触发事件的线程上隐式运行,因此事件侦听器不受线程限制。

如果要在 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 的一部分,你将免费拥有调度程序。否则,您可以从任何控件(例如,control.Dispatcher )中获取调度程序。

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

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

当 UI 线程

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

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

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

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

最新更新