我的MDI应用程序使用KeyPreview = true
处理主窗口的keypress处理程序中的常规击键(A-Z,0-9等)。它会过滤一些传入字符序列,将它们隐藏在后续的KeyPress
处理程序中。这在MDI窗口中工作正常,但如果模态窗口处于活动状态(因为主窗体不再聚焦),则不会调用主窗口的KeyPress
。我通过简单的ShowDialog()
打开模态窗口。
是否有某种方法可以普遍捕获和过滤KeyPress事件,无论哪个应用程序窗口具有焦点(包括模态窗口?)
我正在搜索在应用程序级别处理KeyPresses,而不是全局级别。 =>如果应用程序没有焦点,我不希望收到有关KeyPresses的通知。
答案 0 :(得分:2)
问题是,当您调用ShowDialog
时,您的主UI线程将停止调度消息。看看这个简化的消息循环:
while(true)
{
Message m;
GetMessage(out m);
// if close button pressed etc.
if (m.Msg == WM_QUIT) break;
DispatchMessage(m);
}
激活对话框时,会在DispatchMessage中执行此操作并启动类似的新消息循环。由于这个新循环,主窗口中的循环在DispatchMessage调用上被阻止,并且不会处理任何键盘消息。使用多个UI循环很棘手,因此我建议使用常规Show()
消息(不会阻止主应用程序循环)并找到另一种方法将用户焦点指向该窗口。
我过去使用的ap / invoke调用将父窗口设置为禁用,并将用户焦点指向子窗口,它几乎与ShowDialog
调用的行为相同,但没有阻止线程。您要做的是设置子表单的所有者,调用常规Show()
方法,最后将父窗口的启用状态设置为false。您可以使用以下代码执行更新操作:
const int GWL_STYLE = -16;
const int WS_DISABLED = 0x08000000;
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
void SetWindowEnabled(Form form, bool enabled)
{
SetWindowLong(form.Handle, GWL_STYLE, GetWindowLong(form.Handle, GWL_STYLE) &
~WS_DISABLED | (enabled ? 0 : WS_DISABLED));
}
关于堆叠模型窗口的评论,我认为这是可能的,但确保在关闭对话框时调用本机方法重新启用父窗口非常重要。我会实现一个看起来像这样的扩展方法:
public static class FormsExtensions
{
public static Task<bool> ShowNativeDialog(this Form child, Form owner)
{
var tcs = new TaskCompletionSource<bool>();
child.Show();
SetWindowEnabled(owner, false);
child.Closed += (sender, args) => {
SetWindowEnabled(owner, true);
tcs.SetResult(true);
}
return tcs.Task;
}
}
用法看起来像这样:
DialogForm dialog = new DialogForm();
await dialog.ShowNativeDialog(this);
通过使用await,您可以在不阻止UI消息传递循环的情况下停止执行流程。
答案 1 :(得分:1)
根据msdn,系统只向前台线程发送键盘消息,模态窗口是否在另一个线程中启动? 如果是的话,您可以使用您在主窗口窗体中使用的相同句柄,使用一些简单的线程消息向模态窗口线程发送主消息, 现在如果你不知道什么是线程......你可以随时学习
这里有一些可以帮助你的好文章 about keyboards
附加说明: 如果需要,您可以随时使用sendkeys函数将密钥发送到另一个目标
编辑:Showdialog是一个阻塞异步调用,它&#34;冻结&#34;线程和其他窗口仅在启动的窗口关闭时返回,这就是当您激活模态窗口时句柄不响应的原因
答案 2 :(得分:1)
有没有办法普遍捕获和过滤KeyPress事件 无论哪个应用程序窗口具有焦点(包括模态 windows?)我正在寻找应用程序上的KeyPress处理 等级...
这可以通过IMessageFilter()完成。
这是一个简单的例子:
public partial class MainMdiParent : Form
{
private MyFilter MF = new MyFilter();
public MainMdiParent()
{
InitializeComponent();
}
private void MainMdiParent_Load(object sender, EventArgs e)
{
MF.EscapeKey += MF_EscapeKey;
Application.AddMessageFilter(MF);
childA a = new childA();
a.MdiParent = this;
a.Show();
a = new childA();
a.MdiParent = this;
a.Show();
}
private void MF_EscapeKey()
{
Console.WriteLine("Escape Key Trapped in Main Form");
}
private void button1_Click(object sender, EventArgs e)
{
Form dlg = new Form();
dlg.ShowDialog();
}
}
public class MyFilter : IMessageFilter
{
public delegate void EscapeKeyDelegate();
public event EscapeKeyDelegate EscapeKey;
private const int WM_KEYDOWN = 0x100;
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg )
{
case WM_KEYDOWN:
switch (m.WParam.ToInt32())
{
case (int)Keys.Escape:
if (EscapeKey != null)
{
EscapeKey();
}
return true; // suppress it?
break;
}
break;
}
return false; // returning false allows messages to be processed normally
}
}