如何根据文本大小自动适当地增加标签?

How to properly grow label automatically according to text's size?

我确实在 WM_PAINT 中创建了一个新矩形,但为了更改文本,我还 SetWindowText(), I need a InvalidateRect() so it seems quite hacky to me. What's the proper way to do that? I'm not sure if I'm calculating the proper height and width properly either. I've tried grow using SetWindowPos() from WM_PAINT but it failed so I switch to draw it using Rectangle(). I did that because I wanted the text o vertical center but static control doesn't support that, so I went to do the paiting myself so I can use DrawText() 带有 DT_CENTER | DT_SINGLELINE | DT_VCENTER 标志。这是我处理 VM_PAINT:

的方式
case WM_PAINT:
        { 
            PAINTSTRUCT ps;
            RECT rt = {0};
            GetClientRect(hwnd, &rt);
            
            int height = rt.right - rt.left;
            int width = rt.bottom - rt.top;
            int len = GetWindowTextLength(hwnd);
            wchar_t s[len+1];
            GetWindowText(hwnd, s, len+1);
            int flags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
            HDC dc = BeginPaint(hwnd, &ps);
            
            // calculate the width and height
            DrawText(dc, s, -1, &rt, DT_CALCRECT | flags);
            width += rt.right;
            height += rt.bottom;
            
            // update width and height
            rt.right = width;
            rt.bottom = height;

            Rectangle(dc, rt.left, rt.top, rt.right, rt.bottom);

            // this prevent from painting the border. 
            InflateRect(&rt, -1, -1);
            FillRect(dc, &rt, GetSysColorBrush(COLOR_BTNFACE));
            SetBkMode(dc, TRANSPARENT);
            DrawText(dc, s, -1, &rt, flags);
            EndPaint(hwnd, &ps);
            return 0;
        }
        

完整代码:

#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")

#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE

#include <windows.h>
#include <Commctrl.h>
#include <crtdbg.h>
#include <strsafe.h>
#include <string.h>
#include <assert.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ButtonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

WNDPROC oldButtonProc;
HINSTANCE ghInstance;
HWND hTab;
HFONT hdDfaultFont;
HWND btn;

enum 
{
    BTN_ID = 10,
    BTN2_ID,
};


int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PWSTR pCmdLine, int nCmdShow)
{

    MSG  msg = {0};
    HWND hwnd;
    WNDCLASSW wc = {0};

    wc.lpszClassName = L"Window";
    wc.hInstance     = hInstance;
    wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
    wc.lpfnWndProc   = WndProc;
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    
    //InitComControls();
    if(!RegisterClass(&wc)) {
        return -1;
    }

    int width = 540;
    int height = 460;
    int screenWidth = GetSystemMetrics(SM_CXSCREEN);
    int screenHeight = GetSystemMetrics(SM_CYSCREEN);
    int cx = (screenWidth - width) / 2;
    int cy = (screenHeight - height) / 2;
    hwnd = CreateWindowW(wc.lpszClassName, L"Window",
                        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                        cx, cy, width, height, NULL, NULL, 
                        hInstance, NULL);
    ghInstance = hInstance;

    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!IsDialogMessage(hwnd, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    DeleteObject(hdDfaultFont);
    return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

  switch(msg)
  {
      case WM_CREATE:
      {
            btn =
            CreateWindow(L"static", L"init text",
                SS_NOTIFY |
                WS_VISIBLE | WS_CHILD | WS_TABSTOP,
                0, 0, 20, 30,
                hwnd, (HMENU) BTN_ID, NULL, NULL);
            oldButtonProc = (WNDPROC) SetWindowLongPtr(btn,
                                        GWLP_WNDPROC,
                                        (LONG_PTR) ButtonProc);
            HWND btn2 =
            CreateWindow(L"Button", L"Click me!",
                        WS_VISIBLE | WS_CHILD | WS_TABSTOP,
                        10, 50, 70, 25, 
                        hwnd, (HMENU) BTN2_ID,
                        NULL, NULL);
            SetDefaultFont(btn2);
      }
      break;

    case WM_COMMAND:
    {
        switch(LOWORD(wParam))
        {
            case BTN2_ID:
            {
                SetWindowText(btn, L"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0");
                InvalidateRect(btn, NULL, TRUE);
            }
            break;
        }
    }
    break;    
    
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
  }

  return DefWindowProc(hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK ButtonProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_PAINT:
        { 
            PAINTSTRUCT ps;
            RECT rt = {0};
            GetClientRect(hwnd, &rt);
            
            int height = rt.right - rt.left;
            int width = rt.bottom - rt.top;
            
            int len = GetWindowTextLength(hwnd);
            wchar_t s[len+1];
            GetWindowText(hwnd, s, len+1);

            int flags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
            HDC dc = BeginPaint(hwnd, &ps);
            
            // calculate the width and height
            DrawText(dc, s, -1, &rt, DT_CALCRECT | flags);
            width += rt.right;
            height += rt.bottom;
            
            // update width and height
            rt.right = width;
            rt.bottom = height;
            Rectangle(dc, rt.left, rt.top, rt.right, rt.bottom);
            InflateRect(&rt, -1, -1);
            FillRect(dc, &rt, GetSysColorBrush(COLOR_BTNFACE));
            SetBkMode(dc, TRANSPARENT);
            DrawText(dc, s, -1, &rt, flags);
            EndPaint(hwnd, &ps);
            return 0;
        }
        break;
    }

    return CallWindowProc(oldButtonProc, hwnd, msg, wParam, lParam);
}

为了回答 XY 问题的 X 部分,DT_VCENTER | DT_SINGLELINE 表示单行文本,在这种情况下,具有 SS_CENTERIMAGE 样式的静态控件将将文本垂直居中。

SS_CENTERIMAGE - A bitmap is centered in the static control that contains it. The control is not resized, so that a bitmap too large for the control will be clipped. If the static control contains a single line of text, the text is centered vertically in the client area of the control.

你可以用GetTextExtentPoint32求出字符串的长度,这样就不用再计算了。

case WM_PAINT:
{
    PAINTSTRUCT ps;
    RECT rt = { 0 };
    GetClientRect(hwnd, &rt);
    int len = GetWindowTextLength(hwnd);
    wchar_t s[len + 1];
    GetWindowText(hwnd, s, len + 1);
    int flags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
    HDC dc = BeginPaint(hwnd, &ps);

    SIZE sz;
    GetTextExtentPoint32(dc, s, len, &sz);
    rt.right = sz.cx + 10;  //I added 10 to make the display less crowded.
    rt.bottom = sz.cy;

    Rectangle(dc, rt.left, rt.top, rt.right, rt.bottom);

    // this prevent from painting the border. 
    InflateRect(&rt, -1, -1);
    FillRect(dc, &rt, GetSysColorBrush(COLOR_BTNFACE));
    SetBkMode(dc, TRANSPARENT);
    DrawText(dc, s, -1, &rt, flags);
    EndPaint(hwnd, &ps);
    return 0;
}