Click here to Skip to main content
15,887,820 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I've just been learning how to draw UI for a few months. Now I have a problem. We can implement the top tab with owner draw TabControl, but in terms of UI, it's not what I need
What I want is a side tab of the type shown in the following figure [windows 10 settings application]. Click and execute the corresponding message processing
picture↓
查看图片view picture
please do it with WIN32 and no MFC
Does anyone can tell me how to create sidetab like the picture?

thank you.

What I have tried:

I have tired to use ownerdraw button to do it,but it cannot Click to switch the interface,
Want sidetab to use this style for ownerdraw
C++
<pre>HWND g_bt1;
HWND g_bt2;
WNDPROC BToldProc;

typedef struct tagBTN
{// 
    HWND hWnd;          // handle of owner window
    BOOL x1; //默认是185
    BOOL x2; //默认是170
    LPWSTR in;
    UINT style;
} BTN_PARAM;
//
//
LRESULT CALLBACK BTProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{// 
    PAINTSTRUCT ps;
    HDC hdc = NULL;
    RECT rc;
    GetClientRect(hWnd, &rc);
    LOGFONT lf;
    HFONT hFont = 0;
    HBRUSH br;
    TCHAR txt[40];
    switch (Msg)
    {//
    case WM_PAINT:
    {
        hdc = BeginPaint(hWnd, &ps);
        br = CreateSolidBrush(RGB(7, 130, 215));
        FillRect(hdc, &rc, br);
        GetWindowText(hWnd, txt, 40);
        SetBkMode(hdc, TRANSPARENT);
        if (hFont == 0)
        {
            memset(&lf, 0, sizeof(LOGFONT));
            lf.lfHeight = -16;
            wcscpy(lf.lfFaceName, L"黑体");
            hFont = CreateFontIndirect(&lf);  // create the font
        }
        HFONT old = (HFONT)SelectObject(hdc, hFont);
        TextOut(hdc, 18, 7, txt, wcslen(txt) /*&ps.rcPaint*//*DT_SINGLELINE | DT_CENTER | DT_VCENTER*/);
        //DrawText(hdc, txt, wcslen(txt), &ps.rcPaint, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
        ReleaseDC(hWnd, hdc);
        UpdateWindow(hWnd);
        DeleteObject(br);
        EndPaint(hWnd, &ps);
        break;
    }
    case WM_MOUSEMOVE:
    {
        TRACKMOUSEEVENT tme;
        tme.cbSize = sizeof(tme);
        tme.dwFlags = TME_HOVER | TME_LEAVE;
        tme.dwHoverTime = 1;
        tme.hwndTrack = hWnd;
        BOOL boRet = _TrackMouseEvent(&tme);
    }break;
    case WM_MOUSELEAVE:
    {
        
        PAINTSTRUCT ps1;
        HBRUSH hbr = NULL;
        hdc = GetDC(hWnd);
        hbr = CreateSolidBrush(RGB(0,255,191));
        SelectObject(hdc, hbr);
        FillRect(hdc, &rc, hbr);
        //SetWindowText(hWnd, L"关闭");
        ReleaseDC(hWnd, hdc);
        DeleteObject(hbr);
        OutputDebugStringA("Leave\n");
        break;
    }
    case WM_MOUSEHOVER:
    {
        HBRUSH hbr = NULL;
        hdc = GetDC(hWnd);
        hbr = CreateSolidBrush(RGB(99, 184, 255));
        SelectObject(hdc, hbr);
        FillRect(hdc, &rc, hbr);
        GetWindowText(hWnd, txt, 40);
        SetBkMode(hdc, TRANSPARENT);
        if (hFont == 0)
        {
            memset(&lf, 0, sizeof(LOGFONT));
            lf.lfHeight = -16;
            wcscpy(lf.lfFaceName, L"黑体");
            hFont = CreateFontIndirect(&lf);  // create the font
        }
        HFONT old = (HFONT)SelectObject(hdc, hFont);
        TextOut(hdc, 18, 7, txt, wcslen(txt) /*&ps.rcPaint*//*DT_SINGLELINE | DT_CENTER | DT_VCENTER*/);
        //SetWindowText(hWnd, L"关闭");
        ReleaseDC(hWnd, hdc);
        //SetWindowText(hWnd, L"[关闭]");
        DeleteObject(hbr);
        OutputDebugStringA("Over\n");
        break;
    }
    }
    //
    return CallWindowProc(BToldProc, hWnd, Msg, wParam, lParam);
}
//________________HWND 主窗口,__x1 x坐标,__x2 y坐标,L"按钮文字",从定义获取控件
//如果需要进行消息处理,case IDC_XX:
int Button(
    HWND hWnd,         // handle of owner window
    BOOL x1,
    BOOL x2,
    LPWSTR in,
    UINT style //从定义获取按钮控件依赖
)
{
    static HFONT hFont;
    BTN_PARAM btn;
    btn.hWnd = hWnd;
    btn.style = style;
    g_bt1 = CreateWindow(L"Button", in, WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,//默认按钮样式
        x1, x2, 180, 60, hWnd, (HMENU)style, b_hInstance, NULL);
    BToldProc = (WNDPROC)SetWindowLong(g_bt1, GWL_WNDPROC, (LONG)BTProc);
    return -1;
}


use
#define test 0xcc1
Button(hwnd,x,y,"TEXT",test);
case test:
{}
to use button

But this is not the normal way


Does anyone can tell me how to create sidetab like the picture?
Posted
Updated 28-Jul-20 21:46pm

1 solution

You need to create two child Windows in the client space. The one on the left is your tab, and the one on the right is your main processing space. Handle the WM_SIZE message in your WndProc, and resize the two child windows based on the client space and the relative sizes you want.

[edit]
Sample WM_SIZE handler:
C++
void OnSize(
    HWND	hWnd,
    UINT	nAction,
    int	nWidth,
    int	nHeight
)
{
    POINT  ptTopLeft = { 0, 0 };
    int    nTabWidth = nWidth / 3; // tab is 1/3 of main window width
    
    // resize the views
    MoveWindow(hTabWindow,
        ptTopLeft.x,	// starting x-coordinate
        ptTopLeft.y,	// starting y-coordinate
        nTabWidth,	// width of left client area 
        nHeight,	// height of client area 
        TRUE		// repaint window 
    );
    ptTopLeft.x += nTabWidth;	// becomes left edge of right view
    nWidth -= ptTopLeft.x;	// width of right-hand view
    
    MoveWindow(hEditWindow,
        ptTopLeft.x,
        ptTopLeft.y,
        nWidth,
        nHeight,
        TRUE
    );
}

[/edit]
 
Share this answer
 
v2
Comments
EnderMo233 29-Jul-20 22:27pm    
Now my UI looks like the following link

http://db.endermo.xyz/lnk/UI.png

Each control is Child Window of hwnd

LargeTitlebtn(hwnd, 1, 1, 160, 50, L"Title", NULL);
FrameClose(hwnd, rect.right - 20, 16, CLOSE);
FrameMinimize(hwnd, rect.right - 60, 16,IDC_MINIMIZE);
FrameMenuBox(hwnd, rect.right - 100, 16, IDC_MENUBOX);
ConsoleUI(hwnd, rect.left + 161, 51, 669,459, NULL);

I want to add the sidetab in the left(DarkGrey Area)
Should I Make the tab in a child window?How to switch the interface when switching the tab?
Richard MacCutchan 30-Jul-20 4:50am    
You should not use fixed sizes for your windows. If the user resizes the main Window then the child Windows will look wrong. You need to set the sizes and postions of the child Windows in the WM_SIZE handler. Use the width value to calculate the width and position of your child windows, and the height to make sure they fit correctly into your frame. This will ensure correct size and position whenever the main window is resized. See my update solution for a simple example of two child Windows, where the left hand side should be one third of the width:
EnderMo233 30-Jul-20 7:12am    
My window is a self - drawn borderless window and disabled resizing.
I used the above code to make a sub window, but how to switch the interface when the user presses different tab buttons
For example, by default, "console UI" is the most interface, and the "console UI" tab is "console". If I want to create a new "testtab" button, I need to hide the "console UI" sub window and display a new sub window when I click it
Richard MacCutchan 30-Jul-20 7:21am    
You can create other child Windows of the same shape and size in your framewindow. You can then use the SetWindowPos function (winuser.h) - Win32 apps | Microsoft Docs[^] to bring them to the top (i.e to make them visible).
EnderMo233 30-Jul-20 8:34am    
I don't seem to have used setwindowpos
Because I have added the into tab, each button is a sub window. If I want to hide the "console UI" sub window when I click the "testtab" button, then it is not possible to hide the "console UI" sub window when I click the "testtab" button, because they had different hwnd. How can I hide the "console UI" child window and display another one when I click the "testtab" button?
thank you.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900