首页 > 解决方案 > 我无法循环浏览窗口中的所有控件,包括选项卡

问题描述

很长一段时间以来,我一直在尝试使用选项卡控件。
我遇到了下一个问题,那就是:
我无法循环浏览所有控件,包括选项卡。
如果我创建选项卡控件

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;
}

标签: winapivisual-c++tabs

解决方案


可以通过EnumChildWindows函数遍历所有子控件。

不过需要注意的是,你的清除按钮和其他三个按钮不在同一个控件中,所以不能按你的要求循环(每次都遍历清除)。

同时,您只能遍历选项卡控件一次。如果需要遍历tab控件,还需要另外遍历tab控件。


推荐阅读