Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
314 views
in Technique[技术] by (71.8m points)

c++ - How to correctly draw simple non-client area (4 px red border)?

I'm trying to draw a custom painted non-client area, instead of default theme border (Windows 10).

I handled WM_NCCALCSIZE to resize the non-client area to 4 pixels on each side and then handled WM_NCPAINT to draw the red border.

My custom painting succeeds when application is first displayed, but fails to redraw either when application is resized, or when minimized and restored, despite the fact that both WM_NCCALCSIZE and WM_NCPAINT are called during resizing or when window is restored.

#pragma comment(lib, "UxTheme")
#include <windows.h>
#include <uxtheme.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = NULL;
    wcex.hCursor = (HICON) LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = CreateSolidBrush(RGB(0,128,0));
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = "window";
    wcex.hIconSm = NULL;

    RegisterClassEx(&wcex);

    HWND hWnd = CreateWindowEx(
        WS_EX_COMPOSITED,
        "window",
        NULL,
        WS_OVERLAPPEDWINDOW,
        100,
        100,
        600,
        400,
        NULL,
        NULL,
        hInstance, 
        NULL); 

    ShowWindow(hWnd, nCmdShow);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return static_cast<int>(msg.wParam);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
        case WM_CREATE:
            SetWindowTheme(hWnd, L"", L"");
            return 0;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        case WM_NCCALCSIZE:
        {
            RECT rect;
            GetWindowRect(hWnd, &rect);
            LPNCCALCSIZE_PARAMS ncParams = (LPNCCALCSIZE_PARAMS) lParam;
            ncParams->rgrc[0].top = rect.top + 4;
            ncParams->rgrc[0].left = rect.left + 4;
            ncParams->rgrc[0].bottom = rect.bottom - 4;
            ncParams->rgrc[0].right = rect.right - 4;
            return 0;
        }
        case WM_NCPAINT:
        {
            RECT rect;
            GetWindowRect(hWnd, &rect);
            HDC dc = GetDCEx(hWnd, (HRGN) wParam, DCX_WINDOW | DCX_CACHE | DCX_INTERSECTRGN | DCX_LOCKWINDOWUPDATE);
            HPEN pen = CreatePen(PS_INSIDEFRAME, 4, RGB(255, 0, 0));
            HGDIOBJ old = SelectObject(dc, pen);
            int width = rect.right - rect.left;
            int height = rect.bottom - rect.top;
            Rectangle(dc, 0, 0, width, height);
            SelectObject(dc, old);
            DeleteObject(pen);
            ReleaseDC(hWnd, dc);
            return 0;
        }
        case WM_NCACTIVATE:
            RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);
            return 0;
        break;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The wParam of WM_NCPAINT message sometimes returns 1 instead of a handle to a region (HRGN). In that case HRGN must be created using CreateRectRgn function.

#pragma comment(lib, "UxTheme")
#include <windows.h>
#include <uxtheme.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = NULL;
    wcex.hCursor = (HICON) LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = CreateSolidBrush(RGB(0,128,0));
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = "window";
    wcex.hIconSm = NULL;

    RegisterClassEx(&wcex);

    HWND hWnd = CreateWindowEx(
        NULL,
        "window",
        NULL,
        WS_OVERLAPPEDWINDOW,
        100,
        100,
        600,
        400,
        NULL,
        NULL,
        hInstance, 
        NULL); 

    ShowWindow(hWnd, nCmdShow);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return static_cast<int>(msg.wParam);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
        case WM_CREATE:
            SetWindowTheme(hWnd, L"", L"");
            return 0;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        case WM_NCCALCSIZE:
        {
            LPNCCALCSIZE_PARAMS ncParams = (LPNCCALCSIZE_PARAMS) lParam;
            ncParams->rgrc[0].top += 4;
            ncParams->rgrc[0].left += 4;
            ncParams->rgrc[0].bottom -= 4;
            ncParams->rgrc[0].right -= 4;
            return 0;
        }
        case WM_NCPAINT:
        {
            RECT rect;
            GetWindowRect(hWnd, &rect);
            HRGN region = NULL;
            if (wParam == NULLREGION) {
                region = CreateRectRgn(rect.left, rect.top, rect.right, rect.bottom);
            } else {
                HRGN copy = CreateRectRgn(0, 0, 0, 0);
                if (CombineRgn(copy, (HRGN) wParam, NULL, RGN_COPY)) {
                    region = copy;
                } else {
                    DeleteObject(copy);
                }
            }
            HDC dc = GetDCEx(hWnd, region, DCX_WINDOW | DCX_CACHE | DCX_INTERSECTRGN | DCX_LOCKWINDOWUPDATE);
            if (!dc && region) {
                DeleteObject(region);
            }
            HPEN pen = CreatePen(PS_INSIDEFRAME, 4, RGB(255, 0, 0));
            HGDIOBJ old = SelectObject(dc, pen);
            int width = rect.right - rect.left;
            int height = rect.bottom - rect.top;
            Rectangle(dc, 0, 0, width, height);
            SelectObject(dc, old);
            ReleaseDC(hWnd, dc);
            DeleteObject(pen);
            return 0;
        }
        case WM_NCACTIVATE:
            RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);
            return 0;
        break;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...