Rotating the Z-order - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20030826-00/?p=42793
Raymond Chen 2003年8月26日
简要
《Rotating the Z-order - The Old New Thing》,由Raymond Chen在2003年8月26日发表。文章讨论了在设计对话框时可能会遇到的一个问题:为了使点击测试(hit-testing)工作正常,控件需要按照一种Z顺序排列,但为了使键盘的Tab顺序正确,又需要另一种顺序。文章通过一个简化的对话框模板示例来解释这个问题,并提出了一种解决方案。
有时候你会碰到这样的情况:为了让点击测试工作正常,你需要按照一种Z顺序排列控件,但为了让键盘的Tab顺序工作正常,你又需要另一种顺序。
例如,看看这个为了说明问题而简化的对话框模板。
//
// Note: The ListView control has to be listed BEFORE the tab control in
// order for drag-drop to work properly.
//
IDD_MAIN DIALOGEX 0, 0, 335, 270
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
| DS_CONTEXTHELP | DS_SHELLFONT
CAPTION "Blah blah"
FONT 8, "MS Shell Dlg"
BEGIN
CONTROL "List",IDC_LIST,WC_LISTVIEW,LVS_REPORT |
WS_BORDER | WS_TABSTOP |
LVS_SHOWSELALWAYS,15,46,305,111
CONTROL "Tab",IDC_TAB,WC_TABCONTROL,
WS_TABSTOP,7,24,321,141
PUSHBUTTON "&Import...",IDC_IMPORT,7,172,51,14
PUSHBUTTON "&Export...",IDC_EXPORT,62,172,51,14
PUSHBUTTON "&Remove",IDC_REMOVE,117,172,51,14
DEFPUSHBUTTON "&Close",IDOK,277,249,51,14
END
对话框看起来是这样的:
_______ _______ / Tab \/ Tab \_____________________ | \ | +-[List]-----------------------------+ | | | | | | | | | | | | | | | | | | +------------------------------------+ | |________________________________________| [ Import ] [ Export ] [ Remove ] [ Close ]
拖放操作会调用WindowFromPoint()来确定哪个窗口接收放置。Z顺序最顶端的窗口(在对话框模板的开始处)会被选中。
我们希望放置操作到ListView,而不是Tab控件,因此ListView(IDC_LIST)需要排在Tab控件(IDC_TAB)之前。
然而,这样做破坏了可访问性,因为Tab顺序也遵循Z顺序。因此,对话框上的Tab顺序是:
List -> Tab -> Import -> Export -> Remove -> Close
这是一个不理想的Tab顺序,因为它导致焦点向上跳转(从List到Tab)。为了使Tab顺序正确,你需要将Tab放在List之前。
这就是旋转发挥作用的地方。
首先,按照Tab顺序在页面上放置控件。
Tab -> List -> Import -> Export -> Remove -> Close
记住,Tab顺序是循环的。当你在最后一个控件上按下Tab键时,你会跳转到第一个控件。所以你实际上应该将Tab顺序视为一个圆:
/--> Tab ---\ | | Close v ^ List | | Remove v ^ Import | | \-- Export <--/
一旦你这样看待它,你就会意识到你可以旋转这个圆,而Tab顺序仍然不变。所以让我们旋转它,让List首先出现,因为我们需要List在Z顺序中排在最前面。
/--> List ---\ | | Tab v ^ Import | | Close v ^ Export | | \-- Remove <--/
现在,将圆切开,得到线性图:
List -> Import -> Export -> Remove -> Close -> Tab
这给了我们最终的对话框模板:
//
// Note: The ListView control has to be listed BEFORE the tab control in
// order for drag-drop to work properly.
//
IDD_MAIN DIALOGEX 0, 0, 335, 270
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
| DS_CONTEXTHELP | DS_SHELLFONT
CAPTION "Blah blah"
FONT 8, "MS Shell Dlg"
BEGIN
CONTROL "List",IDC_LIST,WC_LISTVIEW,LVS_REPORT |
WS_BORDER | WS_TABSTOP |
LVS_SHOWSELALWAYS,15,46,305,111
PUSHBUTTON "&Import...",IDC_IMPORT,7,172,51,14
PUSHBUTTON "&Export...",IDC_EXPORT,62,172,51,14
PUSHBUTTON "&Remove",IDC_REMOVE,117,172,51,14
DEFPUSHBUTTON "&Close",IDOK,277,249,51,14
CONTROL "Tab",IDC_TAB,WC_TABCONTROL,
WS_TABSTOP,7,24,321,141
END
现在你得到了两全其美的结果。List位于Z顺序的顶端,而Tab顺序仍然是正确的。✌