是否有内置的快捷键用于选择当前活动表单的所有者?

时间:2019-02-24 17:02:54

标签: c# winforms shortcut-key alt-tab modeless-dialog

我有一个主表单,其中有两个子非模态表单,例如所有形式都可以同时激活:

class MainForm : Form
{
    Form child1;
    Form child2;

    public MainForm()
    {
        Text = "MainForm";
        child1 = new Form { Text = "Child1" };
        child2 = new Form { Text = "Child2" };
        child1.Show(this);
        child2.Show(this);
    }
}

我希望允许用户Alt+Tab进入所有用户,但是令人惊讶的是,我发现如果任何子表单都处于活动状态,则无法从Alt+Tab菜单中选择所有者表单。

这三种形式都显示在列表中,但是很显然,当您选择所有者窗口并且有一个活动的子代时,将选择该子代而不是所有者。在任务栏中选择表单时也会发生同样的事情。

我想念什么吗?我开始考虑明确配置快捷键以允许从无模式子窗体导航到所有者窗口,但是在此之前,我想确认是否已经有一些内置的键盘快捷键可以执行此操作,因为我不想破坏用户的期望。

令人惊讶的是,我找不到任何提及此行为的问题,这也很奇怪。

2 个答案:

答案 0 :(得分:2)

设置表单的所有者,会使该表单作为非模式窗口停留在其所有者的顶部。
如果 owned 表单的ShowInTaskbar属性设置为true,则标准ALT+TABWIN+TAB组合键可用于迭代打开的Windows。系统,将下一个拥有的表单而不是所有者带到前面(激活)。
哪个 child 表单被激活,取决于表单在任务栏中的当前位置。

如果将子级的ShowInTaskbar属性设置为false,则会激活“所有者表单”。
请注意,如果可以最小化子窗体,则可以观察到一些尴尬的行为:Alt或Control-Tab键,导致子窗体以不愉快的方式出现和消失。

无论如何,CONTROL+F6键的标准组合可用于将焦点移到已打开的子窗体(以及这种形式的所有者窗体)上,并在必要时将它们放在最前面(如果形式最小化。)
此处,覆盖ProcessCmdKey,因此无论哪个子控件捕获了光标,键的组合都被拦截

此处的代码通过同时按下 CONTROL+F6 CONTROL+SHIFT+F6 激活表单,将焦点移至每个打开的子表单和所有者。当子窗体(或所有它们)最小化时,它也适用。

在所有者表格中:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    bool isControlF6 = keyData == (Keys.Control | Keys.F6);
    bool isCtrlShiftF6 = keyData == (Keys.Control | Keys.Shift | Keys.F6);

    if (isControlF6 || isCtrlShiftF6)
    {
        Form frm = isCtrlShiftF6 
                 ? Application.OpenForms.OfType<Form>().LastOrDefault(f => f.Owner == this)
                 : Application.OpenForms.OfType<Form>().FirstOrDefault(f => f.Owner == this);
        if (frm is null) return true;
        frm.WindowState = FormWindowState.Normal;
        frm.Focus();
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

在子表单中:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    bool isControlF6 = keyData == (Keys.Control | Keys.F6);
    bool isCtrlShiftF6 = keyData == (Keys.Control | Keys.Shift | Keys.F6);
    if (isControlF6 || isCtrlShiftF6) {
        int frmNext = 0;
        var formsList = Application.OpenForms.OfType<Form>()
                                   .Where(f => (f.Owner == this.Owner) || (f == this.Owner)).ToList();
        for (int i = 0; i < formsList.Count; i++) {
            if (formsList[i] == this) {
                if (isCtrlShiftF6) { frmNext = i == 0 ? formsList.Count - 1 : i - 1; }
                if (isControlF6) { frmNext = i == formsList.Count - 1 ? 0 : i + 1; }
                formsList[frmNext].WindowState = FormWindowState.Normal;
                formsList[frmNext].Focus();
                return true;
            }
        }
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

答案 1 :(得分:0)

修改后的答案:

我不知道为什么,但是我不能放弃。似乎应该有一个简单的解决方案。

@glopes我相信这是您根据评论所要查找的内容。

此代码将在子窗口失去焦点之前将焦点设置回父窗口。这种行为类似于单击窗口,并允许您将Alt-Tab切换到所需的任何窗口。

public class MainForm : Form
{
    Form child1;
    Form child2;

    public MainForm()
    {
        Text = "MainForm";
        child1 = new ChildForm { Text = "Child1", ParentPtr = Handle };
        child2 = new ChildForm { Text = "Child2", ParentPtr = Handle };
        child1.Show(this);
        child2.Show(this);
    }
}

public class ChildForm : Form
{
    [DllImport("user32.dll")]
    public static extern bool SetFocus(IntPtr hWnd);

    private const int WM_KILLFOCUS = 0x0008;

    public IntPtr ParentPtr { get; set; }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_KILLFOCUS) SetFocus(ParentPtr);

        base.WndProc(ref m);
    }
}