Win+M快捷键最小化无边框窗体

5
标准的键盘快捷方式 Win + M 可以最小化打开的窗口,但无法最小化一个无边框表单。
我正在考虑在 KeyDown 事件中捕获 Win + M 的键盘事件,但没有成功,因为它只能捕获第一个按键而不是两个按键都捕获。
我无法使用键盘快捷键以编程方式将其最小化。
有没有办法通过键盘快捷键最小化我的表单?或者可以捕获 Win + M 来触发最小化函数吗?
我正在使用无边框表单。要复制此问题,请创建一个表单并将其 BorderStyle 设置为 None,然后运行应用程序。当您按下 Win + M 时,所有窗口都会最小化,但我的无边框表单保持正常状态。

1
你知道为什么按下WIN+M时你的窗体没有最小化吗?你的窗体有特殊的类型或者在最小化时有特殊的事件处理吗? - Matteo Umili
@MatteoUmili 我正在使用无边框窗体。 - myelxx
谢谢!我会尝试在Zer0上提出新问题。 - myelxx
1
我打开这个问题,因为建议的重复问题并不是这个问题的恰当答案。当您按Win+M时,没有任何建议的解决方案可以工作。 - Reza Aghaei
如果OP想要使用其他的组合键来最小化窗体,并且不想在按Win+M时修复行为,那么ProcessCmdKey会是一个可行的方法;或者如果他们想要一个应用程序端的快捷方式,那么IMessageFilter也是一个可选方案。这两种方法都在建议的重复问题中有所涉及。 - Reza Aghaei
显示剩余3条评论
2个回答

3
作为一种选择,您可以为无边框窗体启用系统菜单(具有最小化窗口样式),这样在不显示控制框的情况下,最小化命令将按预期工作。
将以下代码片段添加到您的Form中,然后Win+M将按预期工作:
private const int WS_SYSMENU = 0x80000;
private const int WS_MINIMIZEBOX = 0x20000;
protected override CreateParams CreateParams
{
    get
    {
        CreateParams p = base.CreateParams;
        p.Style = WS_SYSMENU | WS_MINIMIZEBOX;
        return p;
    }
}

2
作为另一种选择,您可以使用SetWindowsHookEx注册一个低级键盘钩子,并检查是否收到了Win+M,然后最小化窗口并让该按键穿过其他窗口。
将以下代码片段添加到您的Form中,然后Win+M将按预期工作:
private const int WH_KEYBOARD_LL = 13;
[StructLayout(LayoutKind.Sequential)]
private struct KBDLLHOOKSTRUCT
{
    public Keys vkCode;
    public int scanCode;
    public int flags;
    public int time;
    public IntPtr dwExtraInfo;
}
private delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(
    int idHook, HookProc lpfn, IntPtr hmod, uint dwThreadId);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(
    IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern short GetKeyState(int keyCode);
private const int KEY_PRESSED = 0x8000;
private static bool IsKeyDown(Keys key)
{
    return Convert.ToBoolean(GetKeyState((int)key) & KEY_PRESSED);
}

private IntPtr ptrHook;
private HookProc hookProc;
private IntPtr CaptureKeys(int code, IntPtr wParam, IntPtr lParam)
{
    if (code >= 0)
    {
        KBDLLHOOKSTRUCT objKeyInfo = 
            (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(
                lParam, typeof(KBDLLHOOKSTRUCT));
        if (objKeyInfo.vkCode == (Keys.M) &&
            (IsKeyDown(Keys.LWin) || IsKeyDown(Keys.RWin)))
        {
            this.WindowState = FormWindowState.Minimized;
            return (IntPtr)0;
        }
    }
    return CallNextHookEx(ptrHook, code, wParam, lParam);
}
bool HasAltModifier(int flags)
{
    return (flags & 0x20) == 0x20;
}
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    var module = Process.GetCurrentProcess().MainModule;
    hookProc = new HookProc(CaptureKeys);
    ptrHook = SetWindowsHookEx(
        WH_KEYBOARD_LL, hookProc, GetModuleHandle(module.ModuleName), 0);
}

@Zer0 我先尝试了 IMessageFilter 但是没有得到想要的结果。也许我犯了错误,我会再试一次。 - Reza Aghaei
我会尝试一下,说实话,我有偏见使用钩子,所以在第一次 IMessageFilter 失败后就放弃了。 - Reza Aghaei
@Zer0 我相信我使用IMessageFilter的尝试是正确的,Windows+M不会被IMessageFilter捕获。当应用程序具有焦点时,我能够捕获非热键组合,例如Win+Z,但问题在于Win+M是标准的Windows热键,另一件事是期望的行为是即使您的应用程序没有焦点,Win+M也应该起作用(最小化所有窗口)。至少消息(如果应用程序接收到它,不是keydown/up/press消息,应该是其他东西)。 - Reza Aghaei
这个问题的两个解决方案都能满足要求:当按下Win+M时,最小化一个无边框窗体。我的另一个答案更加方便。 - Reza Aghaei
1
我也一样,我尝试了很多组合。即使使用Spy++,我也找不到任何有用的东西 - 无论是什么消息,我猜测,由于无边框窗口没有WS_SYSMENU和WS_MINIMIZEBOX样式,窗口不会接收此消息。 - Reza Aghaei

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接