如何设置在运行时创建的Windows控件之间移动的顺序
How do I set order of moving between Windows controls created at runtime?
我们的应用程序中有一系列对话框,对话框模板在屏幕底部为它们定义了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中,我在运行时添加了两个额外的按钮,并使用SetWindowPos
和GetNextWindow
将它们插入button4
和editbox
之间的现有标签顺序。重复按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 ==
控件的顺序可以使用SetWindowPos
和GetNextWindow
设置,如上所述。"制表符顺序"answers"箭头顺序"可以通过设置控件的WS_TABSTOP和WS_GROUP样式来设置:
WS_TABSTOP样式指定了用户可以使用的控件按TAB键或SHIFT+TAB键移动
WS_GROUP样式标志一组控件的开始。如果是组中的控制组当用户开始按方向键时,输入焦点是否集中焦点仍在集团。一般来说,第一组控制必须有WS_GROUP样式和组中的所有其他控件必须没有这种款式。组中的所有控件必须为连续的,也就是说,它们必须是连续创建的,没有干预控制。
更多细节可以在MSDN上找到,在这里。
试试这个:
-
创建一个dlg id向量,并按您希望的制表符顺序填充它:
typedef std::vector<int> TABLIST; TABLIST lst; lst.push_back( IDC_CONTROL1 ); lst.push_back( IDC_CONTROL2 ); lst.push_back( IDC_CONTROL3 );
-
然后用列表调用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"现在您可以通过一个接一个地选择控件来设置顺序。
不能在运行时更改制表符顺序。你可以做的是把这两个按钮放在你的对话框资源中(以正确的选项卡顺序),并使它们不可见。然后,您可以在需要时显示/排列按钮。
- 由非托管(C++)COM服务器实例化的托管(C#)控件在Windows更新后损坏
- 当我移动跟踪栏时,如何防止控件(选项卡)闪烁和消失与Windows通用控件6.0?
- 如何在Windows应用程序中启用通用控件
- 如何使Windows ListView控件和它们映射到的对象保持同步
- Windows 7:MFC ActiveX 控件不会在任何文件夹中创建文件
- 如何在Windows日历控件上设置字体
- 是否可以使用现有API修改windows功能区控件,使其看起来更像MS Word 2010中的功能区
- Windows 7 和 Windows 8 进度"ring"控件
- 检索 Windows 资源管理器的地址栏编辑控件的句柄
- 如何从windows编辑控件的格式中删除逗号?(C++)
- 如何正确处理来自MSFTEDIT_CLASS(RichEdit)控件的Windows消息
- Windows TreeView常见控件通知和多字节字符集的问题
- MFC图片控件不会根据Windows显示比例自动缩放
- 在线程中更改Windows窗体控件
- 如何设置在运行时创建的Windows控件之间移动的顺序
- 在c++, Windows Store Metro应用程序中设置控件的颜色
- 如何在指定的注册表值更改时在ATL ActiveX控件中获取事件通知以用作windows移动ActiveX控件
- 无法在Windows CE 6中重新加载ActiveX控件
- 可以有多列复选框的Windows ListView控件(或类似的控件)
- 如何在c++ /CLI中使用设计器继承现有的Windows窗体控件