【问题标题】:I can't cycle through all the controls in the window, including tabs我无法循环浏览窗口中的所有控件,包括选项卡
【发布时间】: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


【解决方案1】:

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

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

同时,你只能遍历tab控件一次。如果需要遍历tab控件,需要另外遍历tab控件。

【讨论】:

    【解决方案2】:

    程序终于按我想要的方式运行了!
    @RaymondChen 是对的:选项卡控件和静态(窗格)控件必须是同级控件。
    以下是我所做的更改:


    第 76 行:hTabhWnd - 现在 Tab 控件和 Pane 是同级的。

    // OLD
    hTab, ( HMENU )PANE_ID( i ), hInst, NULL );
    // NEW
    hWnd, ( HMENU )PANE_ID( i ), hInst, NULL );
    

    第 74 行:我删除了 WS_CLIPSIBLINGSWS_TABSTOP,因为 WS_TABSTOP 不适用于静态控制和因为选项卡控件已经有 WS_CLIPSIBLINGS。

    // OLD
    hPane[ i ] = CreateWindowEx( WS_EX_CONTROLPARENT, WC_STATIC, _T(""), WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP,
    // NEW
    hPane[ i ] = CreateWindowEx( WS_EX_CONTROLPARENT, WC_STATIC, _T(""), WS_VISIBLE | WS_CHILD,
    

    第 57 行:我从 Tab 控件中删除了 WS_EX_CONTROLPARENT,因为他没有更多的孩子。

    // OLD
    hTab = CreateWindowEx( WS_EX_CONTROLPARENT, WC_TABCONTROL, _T(""),
    // NEW
    hTab = CreateWindowEx( 0, WC_TABCONTROL, _T(""),
    

    第 131 行:我删除了整行,因为它毫无意义

    SetFocus( GetDlgItem( hPane[ new_pane_id ], id_first_button + new_pane_id*IDS_PER_PANE ) );
    

    You can see the result

    感谢所有花时间帮助我的人!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-01
      • 2012-03-14
      • 2023-03-06
      • 1970-01-01
      • 1970-01-01
      • 2022-09-29
      • 2012-02-10
      相关资源
      最近更新 更多