我想显示一个只为单个表单的模态MessageBox
,并允许该线程上的其他顶级窗口保持活动状态。 WinForms禁用当前线程上的所有窗口,无论是显示自定义模式窗体还是使用MessageBox
类。
我尝试了许多不同的方法来解决问题,包括重新启用任何其他顶级窗口(使用Win32 EnableWindow
函数)和p /调用本机MessageBox
函数。这两种方法都有效,但是键盘快捷键无法操作,包括用于在控件之间移动的标签,用于取消打开菜单的转义以及最重要的菜单快捷键。
当我显示MessageBox
然后直接在活动表单上调用ProcessCmdKey
时,我可以通过安装键盘挂钩来使菜单快捷键工作,但其他键盘快捷键仍然不起作用。我想我可以按下并添加更多黑客以使这些其他案例有效,但我想知道是否有更好的方法。
注意,这不是关于being non-blocking,而是关于不禁用当前线程上的所有窗口。然而,碰巧解决方案可能类似。
答案 0 :(得分:2)
你在这里遇到的基本问题是MessageBox.Show()抽取自己的消息循环来使自己成为模态。此消息循环内置于Windows中,并且完全不知道Winforms消息循环的外观。因此,当您使用MessageBox时,Winforms在其消息循环中所做的任何特殊操作都不会发生。这是键盘处理:检测助记符,实现导航和调用ProcessCmdKey()等方法,以便表单可以实现自己的快捷键击。通常不是问题,因为假设是模态的并忽略用户输入。
恢复此功能的唯一实用方法是在自己的线程上显示消息框。这在winapi中是正式允许的,窗口的所有者可以是另一个线程拥有的窗口。但是,当他们将代码添加到检测线程错误的.NET 2.0时,微软没有实现这一规则。解决该代码需要IWin32Window作为消息框所有者,而不是控件。
在项目中添加一个新类并粘贴此代码:
using System;
using System.Threading;
using System.Windows.Forms;
public class NonModalMessageBox : IWin32Window {
public NonModalMessageBox(Form owner, Action<IWin32Window> display) {
this.handle = owner.Handle;
var t = new Thread(new ThreadStart(() => display(this)));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
public IntPtr Handle {
get { return handle; }
}
private IntPtr handle;
}
并像这样使用它:
new NonModalMessageBox(this, (owner) => {
MessageBox.Show(owner, "Testing", "Non-modal");
});
此在显示消息框时应禁用的Form对象。我无能为力让你对这个黑客感觉更好,如果FUD过于强大,你真的需要用你自己的Form类重新实现MessageBox,这样你就可以使用Show()而不是ShowDialog()。它已经完成。
答案 1 :(得分:0)
您可以传递对Form的引用,在打开对话框时将Enabled属性设置为false,并在对话框关闭时将其设置为true。