【问题标题】:In-window popups: How to block UI thread?窗口内弹出窗口:如何阻止 UI 线程?
【发布时间】:2016-12-22 01:10:35
【问题描述】:

我目前正在实施 MessageDialog 控件。它可以完全替换 MessageBox,并显示为“窗口内弹出窗口”(需要正确的 UX 术语)

目前,它的构造函数是私有的,并且有一个方法Show,就像在MessageBox.Show 中一样。但是,MessageBox 会阻塞 UI 线程并返回结果。


我的MessageDialog 控件当前所做的是拥有一个Action<MessageDialogResult> callback 参数,该参数在单击按钮时被调用。

利用MessageDialog

// class MessageDialog
public static void MessageDialog.Confirmation(Window owner, string message, Action<MessageDialogResult> callback);

// When used by other controls
MessageDialog.Confirmation(WindowMain.Singleton, true, (result) =>
{
    if (result.Button == MessageDialogButton.Yes)
    {
        //...
    }
});

但是,使用回调而不是像MessageBox.Show 中的阻塞方法调用对我来说绝对没有任何好处。它使事情变得相当复杂。我更想实现的是......

if (MessageDialog.Confirmation(WindowMain.Singleton, true).Button == MessageDialogButton.Yes)
{
    //...
}

...在我看来这更干净。

目前后面的代码基本是

  1. 创建MessageDialog 的实例并使用文本填充内容
  2. 将其添加到Window.Content.Children 的子代中
  3. 单击按钮时,调用callback(result) 并从Window.Content.Children 中删除

问题:我想要实现的是调用阻塞方法而不是触发回调。

【问题讨论】:

  • 那你的问题是什么?
  • 截图后见代码块。问题是如何创建线程阻塞对话框而不是触发回调的对话框。
  • 为什么需要阻塞UI线程?如果您将其作为窗口内对话框执行,那么您可以只使用一个覆盖整个屏幕并在其顶部显示对话框的 Rectangle,使对话框成为唯一可交互的控件。
  • 交互没有问题。只是控件的使用很难阅读和维护。我宁愿阻止线程并将结果作为值返回,而不是传递回调。这样代码看起来就和MessageBox.Show 一样干净。
  • 您不能在不阻塞线程的情况下暂停代码,当对话框存在于同一个线程上时,您确实不想这样做。在这种情况下,您仅有的两个选择是使用回调或多线程整个过程。在这两个选项中,我强烈推荐回调选项作为更简单的选择。

标签: c# .net wpf mvvm mvvm-light


【解决方案1】:

即使接受的答案似乎有效,我还是建议使用TaskCompletionSource 提供更好的解决方案。这正是 await 的用途 - 它基本上仍然只是一个回调(不会阻塞线程),但使用它时您的代码看起来要简单得多。

TaskCompletionSource<DialogResult> taskSource;

Task<DialogResult> ShowAsync()
{
    return taskSource.Task;
}

public void OkButton_OnClick(EventArgs e, object sender)
{
    taskSource.SetResult(DialogResult.OK);
}

public void CancelButton_OnClick(EventArgs e, object sender)
{
    taskSource.SetResult(DialogResult.Cancel);
}

然后您必须等待电话:await Dialog.ShowAsync()

【讨论】:

  • 尽管TaskFunc 更优越且更新 - 让它异步不是问题,因为对话框不会导致延迟,它会立即弹出并等待用户输入.虽然有一个 while 循环并等待一个布尔值来切换似乎违反直觉 - 在这种情况下,它显然优于我在我的问题中发布的代码。而且由于 Task 不会使利用变得更容易,我会接受 John 的回答。不过,感谢您的意见。
  • @Abion47 你错了。这正是 UWP 中 MessageDialog 的工作方式。我只是想在这里做同样的事情。
  • 在等待异步任务时,您也在处理回调。你也可以这样做 ContinueWith() - await 只是通过使这些回调看起来像连续的线性代码来使这变得容易得多。
  • 我个人会使用这种方法,因为 async/await 是开发人员应该熟悉的常见模式。此外,Application.DoEvents 发生的非真正阻塞阻塞可能会导致一些在查看 API 时不容易预测的意外行为。至少使用await,您的方法的用户会意识到另一个事件处理程序可能会在当前处理程序恢复之前运行。
【解决方案2】:

这样的事情怎么样:

//DialogControl.cs
bool _closed = false;
DialogResult _result;

DialogResult ShowModal()
{
    this.Show();
    while(!_closed) Application.DoEvents();  //Infinite loop
    return _result;
}

public void OkButton_OnClick(EventArgs e, object sender)
{
    _result = DialogResult.OK;
    _closed = true;
}

public void CancelButton_OnClick(EventArgs e, object sender)
{
    _result = DialogResult.Cancel;
    _closed = true;
}

【讨论】:

  • 这个其实很好!我刚刚测试了它,它似乎工作。后台逻辑仍在运行(旁注:在这种情况下可以显示多个 MessageDialog,但这是首选行为)。另外,我使用 `Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));` 因为Application.DoEvents 用于 Windows 窗体。但尽管如此,这是一个很好的解决方案! +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-04
  • 1970-01-01
  • 2015-05-05
  • 1970-01-01
  • 2015-03-03
  • 1970-01-01
  • 2013-11-17
相关资源
最近更新 更多