C# 是否有用于选择当前活动窗体所有者的内置快捷键?

C# 是否有用于选择当前活动窗体所有者的内置快捷键?,c#,winforms,keyboard-shortcuts,alt-tab,modeless-dialog,C#,Winforms,Keyboard Shortcuts,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" }; c

我有一个主窗体和两个子无模式窗体,例如,所有窗体都可以同时激活:

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
菜单中选择所有者表单

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

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

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

编辑答案:

我不知道为什么,但我就是不能让这件事过去。似乎应该有一个简单的解决办法

@根据你的评论,我相信这就是你想要的

此代码将在子窗口松开焦点之前将焦点设置回父窗口。这类似于单击窗口,并允许您将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);
    }
}

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

如果子项的
ShowInTaskbar
属性改为
false
,则会激活所有者表单。
要注意的是,如果可以最小化子窗体,则可以观察到一些尴尬的行为:Alt或Control tabbing会导致子窗体以不愉快的方式出现和消失

无论如何,可以使用
CONTROL+F6
键的标准组合将焦点移动到打开的子窗体(以及这种布局中的所有者窗体),必要时也可以将它们移到前面(如果窗体最小化)。
这里是重写,因此无论哪个子控件捕获了光标,都会截获键的组合

此处的代码激活一个表单,同时按下
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);
}
protectedoverride bool ProcessCmdKey(参考消息msg,Keys keyData)
{
bool isControlF6=keyData==(Keys.Control | Keys.F6);
bool isCtrlShiftF6=keyData==(Keys.Control | Keys.Shift | Keys.F6);
如果(isControlF6 | | isCtrlShiftF6)
{
表格frm=isCtrlShiftF6
?Application.OpenForms.OfType().LastOrDefault(f=>f.Owner==this)
:Application.OpenForms.OfType().FirstOrDefault(f=>f.Owner==this);
如果(frm为null),则返回true;
frm.WindowState=FormWindowState.Normal;
frm.Focus();
返回true;
}
返回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);
}
protectedoverride bool ProcessCmdKey(参考消息msg,Keys keyData)
{
bool isControlF6=keyData==(Keys.Control | Keys.F6);
bool isCtrlShiftF6=keyData==(Keys.Control | Keys.Shift | Keys.F6);
如果(isControlF6 | | isCtrlShiftF6){
int frmNext=0;
var formsList=Application.OpenForms.OfType()
.其中(f=>(f.Owner==this.Owner)| |(f==this.Owner)).ToList();
for(int i=0;i
是否设置所有者(
.Show(this);
)使新表单保持在其顶部?如果这是你想要的行为,你就会得到你想要的。如果您不希望出现这种行为,请不要设置所有者。我相信本文中包含的代码片段解决了您的第一个问题。关于“你得到了你想要的”,你能详细说明一下吗?既然无模式对话框都可以用鼠标独立激活,为什么不能用键盘独立激活呢?在我看来,这似乎是一个明显的可访问性问题(即无法访问键盘或Invisional的用户无法访问表单)。您可以始终覆盖
ProcessCmdKey
,检查用户是否按下了
CTRL+F6
,并将焦点传递给所有者:
this.Owner.focus()
。当然,您可以使用任意组合或单个键,这只是默认设置。当你按住ALT+TAB键时,子窗口位于其所有者的上方,因此它是接收焦点的窗口。是的,我怀疑我必须为此设置自己的快捷键机制。请问为什么要按CTRL+F6键?这是从子窗口移动到其所有者的Windows标准/建议吗?我之所以这么问,是因为知道有经验的Windows用户是否会期待这一点很重要,或者这是必须明确教授的内容。在Visual Studio中,打开一些文件,然后按Ctrl+F6