如何设置在运行时创建的Windows控件之间移动的顺序

How do I set order of moving between Windows controls created at runtime?

本文关键字:Windows 控件 之间 顺序 移动 创建 运行时 何设置 设置      更新时间:2023-10-16

我们的应用程序中有一系列对话框,对话框模板在屏幕底部为它们定义了4个按钮。然而(取决于应用程序运行的硬件版本),我们有时会创建2个额外的按钮,然后沿着底部排列6个按钮(4个来自模板,2个通过调用CButton:: create()创建)。

我的问题是,通常用户可以使用左/右方向键在这些按钮之间移动焦点(没有鼠标或触摸屏,只有一个受限的键盘)。这遵循控件tab顺序,就像您期望的模板中的4个按钮一样。然而这两个动态创建的控件似乎是在tab顺序的开始插入的,这意味着(因为它们被放在屏幕的右侧)就光标键而言,它们处于"错误"的顺序。换句话说,当焦点到达左侧按钮(TAB order 1)时,按下左箭头将焦点跳转到右侧按钮,这只是简单的混淆…

似乎有一些z顺序(我可以影响SetWindowPos())和tab顺序之间的联系,但它似乎不是一个简单的1对1:通过改变z顺序,我可以移动序列周围,使按钮完全在错误的顺序,所以我可以改变z顺序,但我不能弄清楚如何让他们在正确的顺序。

谁能给一个简明的解释TAB-order是如何工作的,以及如何在运行时控制控件的顺序?

编辑:

kol建议下面使用SetWindowPos()设置z轴顺序。我以前试过这个,但是它不让光标键像预期的那样控制焦点。

然而,通过将其组合起来以便我可以使用TAB(作为测试—这对于最终用户解决方案是不实用的),我发现kol的解决方案确实解析了TAB顺序。我遇到的问题是,这是不是与光标键使用的顺序相同!

所以,修改后的问题:我如何指定左/右光标键在控件之间移动焦点的顺序?

解决方案:在kol和MarkRansom的帮助下,我现在已经开始工作了。

我使用SetWindowPos()的建议kol把我的新按钮后,现有的按钮在TAB顺序,然后(如马克建议)使第一个按钮WS_GROUP | WS_TABSTOP,但清除这些标志从其他按钮。

然而,这还不足以解决问题,当使用方向键(不是TAB键)移动时,两个新按钮仍然出现在第一个按钮之前,而不是在第二个按钮之后。

我看着我的对话框模板,它是这样的:

IDD_QUERY DIALOG  0, 0, 156, 34
STYLE DS_SETFONT | WS_POPUP | WS_CAPTION
FONT 8, "MS Sans Serif"
BEGIN
    PUSHBUTTON      "+++Skey1+++",IDC_SKEY_1,1,21,36,12
    PUSHBUTTON      "+++Skey2+++",IDC_SKEY_2,40,21,37,12
    PUSHBUTTON      "+++Skey3+++",IDC_SKEY_3,79,21,36,12
    PUSHBUTTON      "+++Skey4+++",IDC_SKEY_4,118,21,36,12
    LTEXT           "Static",IDC_QUERY_MSG,2,1,153,15
END
因此,用于向用户显示信息的静态IDC_QUERY_MSG位于模板中的第4个按钮之后。为了解决这个问题,我将IDC_QUERY_MSG 移动到第一个按钮(IDC_SKEY_1)之前:这意味着6个按钮不会被静态分割,并解决了问题。

感谢大家的帮助!

使用按钮的SetWindowPos成员。在按钮A上调用它并将其第一个参数设置为按钮B,按TAB顺序将按钮A放在按钮B之后。如果你想设置两个控件的顺序,你必须知道它们之前和之后的控件按TAB顺序排列——这个例子展示了如何做到这一点(它不是MFC,而是纯WinAPI,但它很容易理解)。

== UPDATE ==

我创建了一个对话框模板,底部有四个按钮,顶部有一个编辑框,标签顺序为button1 -> button2 -> button3 -> button4 -> editbox -> button1 -> ...在OnInitDialog中,我在运行时添加了两个额外的按钮,并使用SetWindowPosGetNextWindow将它们插入button4editbox之间的现有标签顺序。重复按TAB表示TAB顺序正确:button1 -> button2 -> button3 -> button4 -> button5 -> button6 -> editbox -> button1 -> ...

class CTestDlg : public CDialogEx
{
public:
    CTestDlg() : CDialogEx(CTestDlg::IDD) {}
    enum { IDD = IDD_TESTDIALOG };
protected:
    CButton button5;
    CButton button6;
    virtual BOOL OnInitDialog();
};
BOOL CTestDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    CButton* button4 = (CButton*)GetDlgItem(IDBUTTON4);
    CWnd* next = button4->GetNextWindow(GW_HWNDNEXT);
    button5.Create("Button5", WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, CRect(340, 172, 415, 195), this, 1005);
    button5.SetWindowPos(button4, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); 
    button6.Create("Button6", WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_PUSHBUTTON, CRect(422, 172, 497, 195), this, 1006);
    button6.SetWindowPos(&button5, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); 
    if (next != NULL)
        next->SetWindowPos(&button6, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); 
    return TRUE;
}
void CDynamicMfcButtonTestApp::OnTestRun()
{
    CTestDlg testDlg;
    testDlg.DoModal();  
}

== UPDATE2 ==

控件的顺序可以使用SetWindowPosGetNextWindow设置,如上所述。"制表符顺序"answers"箭头顺序"可以通过设置控件的WS_TABSTOP和WS_GROUP样式来设置:

  • WS_TABSTOP样式指定了用户可以使用的控件按TAB键或SHIFT+TAB键移动

  • WS_GROUP样式标志一组控件的开始。如果是组中的控制组当用户开始按方向键时,输入焦点是否集中焦点仍在集团。一般来说,第一组控制必须有WS_GROUP样式和组中的所有其他控件必须没有这种款式。组中的所有控件必须为连续的,也就是说,它们必须是连续创建的,没有干预控制。

更多细节可以在MSDN上找到,在这里。

试试这个:

  1. 创建一个dlg id向量,并按您希望的制表符顺序填充它:

    typedef std::vector<int> TABLIST;
    TABLIST lst;
    lst.push_back( IDC_CONTROL1 );
    lst.push_back( IDC_CONTROL2 );
    lst.push_back( IDC_CONTROL3 );
    
  2. 然后用列表调用setTabOrder方法:

    void setTabOrder( TABLIST * plstTabs)
    {
        // Iterate through the list and put each item at the top of the tab order.
        // Remember to do this backwards so the last item is actually the first item
        // in the tab order
        HDWP hDefer = BeginDeferWindowPos( (int)plstTabs->size() );
        for ( TABLIST::reverse_iterator itTab = plstTabs->rbegin(); itTab != plstTabs->rend(); ++itTab )
        {
            DeferWindowPos( hDefer, GetDlgItem( *itTab )->m_hWnd, HWND_TOP, 0,0,0,0, 
                        SWP_NOSIZE | SWP_NOMOVE | SWP_NOREPOSITION );
        }
        EndDeferWindowPos( hDefer );
    }
    

修改了@ snowdded的代码,确保WS_TABSTOP样式也在。

// Build your order in a vector.
std::vector<WORD> vCtrlIds;
vCtrlIds.push_back(IDC_CONTROL1);
vCtrlIds.push_back(IDC_CONTROL2);
vCtrlIds.push_back(IDC_CONTROL3);
// ... keep doing it!
// Iterate through the list and put each item at the top of the tab order.
// Remember to do this backwards so the last item is actually the first item
// in the tab order.
HDWP vDefer = BeginDeferWindowPos(vCtrlIds.size());
for(auto vCtrlId(vCtrlIds.rbegin()); vCtrlId != vCtrlIds.rend(); ++vCtrlId){
    HWND vCtrl(GetDlgItem(*vCtrlId)); // Grab the handle.
    SetWindowLongPtr(vCtrl, GWL_STYLE, // Make sure we have WS_TABSTOP.
        GetWindowLongPtr(vCtrl, GWL_STYLE) | WS_TABSTOP);
    DeferWindowPos(vDefer, vCtrl, HWND_TOP, 0, 0, 0, 0, // Reorder.
        SWP_NOSIZE | SWP_NOMOVE | SWP_NOREPOSITION );
}
EndDeferWindowPos(vDefer);

@snowdude是一个优秀的可重用解决方案。

如果你在谈论对话框上不同控件的制表符顺序:

试试这个打开"dlg in"资源,按"Ctrl+D"现在您可以通过一个接一个地选择控件来设置顺序。

不能在运行时更改制表符顺序。你可以做的是把这两个按钮放在你的对话框资源中(以正确的选项卡顺序),并使它们不可见。然后,您可以在需要时显示/排列按钮。