winapi - 我无法循环浏览窗口中的所有控件,包括选项卡
问题描述
很长一段时间以来,我一直在尝试使用选项卡控件。
我遇到了下一个问题,那就是:
我无法循环浏览所有控件,包括选项卡。
如果我创建选项卡控件
WS_EX_CONTROLPARENT
我可以循环浏览所有子控件,但循环会跳过选项卡。
如果我删除 WS_EX_CONTROLPARENT,则相反:
我可以在子控件中循环,但我可以在选项卡中循环。
这是我的 winapi 测试表。
“标签?” 和“按钮清除”按钮是主窗口的子级。
所有窗格窗口都是选项卡控件的子级。
每个“按钮 1”、“按钮 2”和“按钮 3”组都是每个窗格窗口的子级。
我希望表单按以下顺序或类似顺序循环控件,但还包括选项卡:
选项卡 1 -> 按钮 1 -> 按钮 2 -> 按钮 3 -> 按钮清除 ->
选项卡 2 -> 按钮 1 ->按钮 2 -> 按钮 3 ->
Tab 3 -> Button 1 -> Button 2 -> Button 3 -> Button Clear -> Tab 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;
}
解决方案
可以通过EnumChildWindows
函数遍历所有子控件。
不过需要注意的是,你的清除按钮和其他三个按钮不在同一个控件中,所以不能按你的要求循环(每次都遍历清除)。
同时,您只能遍历选项卡控件一次。如果需要遍历tab控件,还需要另外遍历tab控件。
推荐阅读
- firebase - 如何仅重新渲染 Flatlist 中的一项?
- c++ - 如何在 Windows 上的控制台应用程序中显示 UTF-8 而无需用户更改控制台中的任何内容?
- r - 用 R 按一组间隔计算两列中的条目数
- r - 如何协调 purrr::map 与 case_when
- javascript - 当元素在视图中时,Slick-Carousel 自动播放开始
- python - 请解释此代码 list = ' '.join(i for i in list_text_from_file if not i in chars)
- vue.js - 基于项目类型的 Vuetify 格式单元格
- python - 硬编码逻辑回归评分代码
- python - 如何更改将 float('inf') 转换为 str 的 python 方式?
- reactjs - 胜利图,堆叠的条形一内一