【发布时间】:2020-07-19 11:27:36
【问题描述】:
我一直在尝试使用标签控件。
我来到了下一个问题,它是:
我无法循环浏览所有控件,包括选项卡。
如果我用
WS_EX_CONTROLPARENT
我可以循环浏览所有子控件,但循环会跳过选项卡。
如果我删除 WS_EX_CONTROLPARENT,则相反:
我可以循环浏览子控件,但我可以循环浏览选项卡。
这是my winapi test form。
“标签?”和“按钮清除”按钮是主窗口的子窗口。
所有窗格窗口都是选项卡控件的子级。
每个“按钮 1”、“按钮 2”和“按钮 3”组都是每个窗格窗口的子级。
我希望表单按以下顺序或类似顺序循环控件,但还包括选项卡:
选项卡 1 -> 按钮 1 -> 按钮 2 -> 按钮 3 -> 按钮清除 ->
选项卡 2 -> 按钮 1 -> 按钮 2 -> 按钮 3 -> 按钮清除 ->
选项卡 3 -> 按钮 1 -> 按钮 2 -> 按钮 3 -> 按钮清除 -> 选项卡 1...
我还添加了我的测试程序,它只包含一个文件:
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <commctrl.h>
#pragma comment(lib,"comctl32.lib")
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define TAB_ITEMS 3
#define BUTTONS_PER_PANE 3
#define IDS_PER_PANE 20
typedef enum {
id_tab = 100,
id_pane,
id_group_box,
id_first_button,
id_edit = 1000,
id_clear
} controls_id;
#define PANE_ID(pane) (id_pane + pane * IDS_PER_PANE)
#define GR_BOX_ID(pane) (id_group_box + pane * IDS_PER_PANE)
#define BUTTON_ID(pane,num) (id_first_button + pane * IDS_PER_PANE + num)
HWND hWin, hTab, hPane[ TAB_ITEMS ];
LONG_PTR def_comctl_proc = NULL;
int current_pane_id = 0;
int new_pane_id = 0;
LRESULT CALLBACK PaneProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ) {
if( Msg == WM_COMMAND ) {
WORD button = LOWORD( wParam );
int ind; // Get pane index
for( ind = 0; ind < TAB_ITEMS; ind++ ) if( hWnd == hPane[ ind ] ) break;
// Check and find the pressed button
if( button >= BUTTON_ID( ind, 0 ) && button < BUTTON_ID( ind, BUTTONS_PER_PANE ) ) {
TCHAR bff[ 32 ];
_stprintf_s( bff, _T("Pane %u, Button %u"), ind + 1, (button - BUTTON_ID( ind, 0 )) + 1 );
SetDlgItemText( hWin, id_edit, bff );
}
return TRUE;
}
return CallWindowProc( ( WNDPROC )def_comctl_proc, hWnd, Msg, wParam, lParam );
}
void CreateControls( HWND hWnd ) {
HINSTANCE hInst = GetModuleHandle( NULL );
TCHAR bff[ 32 ];
TCITEM tie;
int dx, dy, tdx, tdy;
RECT rc, rc_tab;
GetClientRect( hWnd, &rc ); dx = rc.right - rc.left; dy = rc.bottom - rc.top;
// Create tab control
hTab = CreateWindowEx( WS_EX_CONTROLPARENT, WC_TABCONTROL, _T(""),
WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP, 0, 0 ,dx, 120,
hWnd, ( HMENU )id_tab, hInst, NULL );
// Create tab items with group box and buttons
for( int i = 0; i < TAB_ITEMS; i++ ) {
// Insert Item
ZeroMemory( &tie, sizeof( TCITEM ) );
_stprintf_s( bff, _T("Tab %u"), i + 1 );
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.pszText = bff;
tie.iImage = -1;
TabCtrl_InsertItem( hTab, i, &tie );
// Create tab item pane
GetClientRect( hTab, &rc_tab ); tdx = rc_tab.right - rc_tab.left; tdy = rc_tab.bottom - rc_tab.top;
TabCtrl_AdjustRect( hTab, FALSE, &rc_tab );
hPane[ i ] = CreateWindowEx( WS_EX_CONTROLPARENT, WC_STATIC, _T(""), WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP,
rc_tab.left, rc_tab.top, rc_tab.right - rc_tab.left, rc_tab.bottom - rc_tab.top,
hTab, ( HMENU )PANE_ID( i ), hInst, NULL );
// Set new Pane window procedure
def_comctl_proc = SetWindowLongPtr( hPane[ i ], GWLP_WNDPROC, ( LONG_PTR )PaneProc );
// Create group box in each tab item pane
_stprintf_s( bff, _T("Pane %u controls"), i + 1 );
CreateWindowEx( NULL, WC_BUTTON, bff,
WS_VISIBLE | WS_CHILD | BS_GROUPBOX, 10, 10, 10 + BUTTONS_PER_PANE*80, 70,
hPane[ i ], ( HMENU )GR_BOX_ID( i ), hInst, NULL );
// Create buttons in each tab item pane
for( int j = 0; j < BUTTONS_PER_PANE; j++ ) {
_stprintf_s( bff, _T("Button\r\n--- &%u ---"), j + 1 );
CreateWindowEx( NULL, WC_BUTTON, bff,
WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_MULTILINE, 20 + j*80, 30, 70, 40,
hPane[ i ], ( HMENU )BUTTON_ID( i, j ), hInst, NULL );
}
// Show only first pane controls
ShowWindow( hPane[ i ], i ? SW_HIDE : SW_SHOW );
}
TabCtrl_SetCurSel( hTab, 0 );
// Create edit box for messages
CreateWindowEx( WS_EX_CLIENTEDGE, WC_EDIT, _T(""),
WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL | ES_READONLY, 10, 130, 200, 20,
hWnd, ( HMENU )id_edit, hInst, NULL );
// Create Clear Button
CreateWindowEx( NULL, WC_BUTTON, _T("&Clear"),
WS_VISIBLE | WS_CHILD | WS_TABSTOP, 220, 129, 70, 22,
hWnd, ( HMENU )id_clear, hInst, NULL );
}
LRESULT CALLBACK WndProcedure( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ) {
switch( Msg ) {
case WM_CREATE:
CreateControls( hWnd );
break;
case WM_COMMAND:
if( LOWORD( wParam ) == id_clear ) SetDlgItemText( hWin, id_edit, _T("") );
break;
case WM_NOTIFY: {
NMHDR& nmh = *( LPNMHDR )lParam;
if( nmh.idFrom == id_tab ) {
switch( nmh.code ) {
case TCN_SELCHANGING:
return FALSE;
case TCN_SELCHANGE:
new_pane_id = TabCtrl_GetCurSel( nmh.hwndFrom );
ShowWindow( hPane[ current_pane_id ], SW_HIDE );
ShowWindow( hPane[ new_pane_id ], SW_SHOW );
SetFocus( GetDlgItem( hPane[ new_pane_id ], id_first_button + new_pane_id*IDS_PER_PANE ) );
current_pane_id = new_pane_id;
break;
}
return TRUE;
} }
return FALSE;
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, Msg, wParam, lParam));
}
return FALSE;
}
LPCTSTR ClsName = _T("Tab_Control_Tester");
LPCTSTR WndName = _T("Tab Control Tester");
INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
MSG Msg;
WNDCLASSEX WndClsEx;
RECT rc;
int dx = 320, dy = 200;
WndClsEx.cbSize = sizeof(WNDCLASSEX);
WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
WndClsEx.lpfnWndProc = WndProcedure;
WndClsEx.cbClsExtra = 0;
WndClsEx.cbWndExtra = 0;
WndClsEx.hIcon = LoadIcon( NULL, IDI_APPLICATION );
WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW );
WndClsEx.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
WndClsEx.lpszMenuName = NULL;
WndClsEx.lpszClassName = ClsName;
WndClsEx.hInstance = hInstance;
WndClsEx.hIconSm = NULL;
RegisterClassEx(&WndClsEx);
{
INITCOMMONCONTROLSEX icc = { sizeof( INITCOMMONCONTROLSEX ), ICC_WIN95_CLASSES };
InitCommonControlsEx( &icc );
}
Msg.wParam = 0;
GetWindowRect(GetDesktopWindow(),&rc);
if( rc.right < dx ) dx = rc.right - 96;
if( rc.bottom < dy ) dy = rc.bottom - 96;
hWin = CreateWindowEx( WS_EX_CONTROLPARENT, ClsName, WndName, WS_OVERLAPPEDWINDOW,
( rc.right - dx ) / 2, ( rc.bottom - dy ) / 2, dx, dy, NULL, NULL, hInstance, NULL );
if( !hWin ) goto _exit_WinMain;
ShowWindow( hWin, SW_SHOWNORMAL );
UpdateWindow( hWin );
while( GetMessage(&Msg, NULL, 0, 0) ) {
if( !IsDialogMessage( hWin, &Msg) ) {
TranslateMessage( &Msg );
DispatchMessage (&Msg );
}
}
_exit_WinMain:
return (int)Msg.wParam;
}
【问题讨论】:
-
不要将控件创建为子窗口。将它们创建为选项卡控件的同级,但具有更高的 z 顺序,以便它们在其上绘制。
-
感谢您的建议!你提供的东西是不可取的。想法是将一个窗格上的一个选项卡中的所有控件安排为子控件,以便它们可以轻松显示和隐藏,并且窗格必须是选项卡控件的子控件,否则它无法接收必要的消息。
-
您可以将控件嵌入到标记为
WS_EX_CONTROLPARENT的单个父级中,并且该单个父级是选项卡控件的兄弟。这就是标准属性表控件的工作方式。您可以使用 Spy 来查看它是如何组合在一起的。父级不能参与其子级的跳位顺序。 -
@RaymondChen,它不会那样发生。以您的方式,控件相互堆叠 - 变得一团糟。如果需要,请参阅 Microsoft 的 example。我们也是这样做的。
-
我必须修复这个例子。它“有效”是因为静态控件无法获得焦点,因此您无法选择静态控件这一事实不会影响示例。是的,控件堆叠在一起,但除了其中一个之外,所有控件都是隐藏的。您使用选项卡控件来决定显示哪一个,并隐藏其余的。
标签: winapi visual-c++ tabs