【问题标题】:How to correctly draw simple non-client area (4 px red border)?如何正确绘制简单的非客户区(4 px 红色边框)?
【发布时间】:2018-05-02 10:45:37
【问题描述】:

我正在尝试绘制自定义绘制的非客户区,而不是默认主题边框(Windows 10)。

我处理WM_NCCALCSIZE 将非客户区的大小调整为每边4 个像素,然后处理WM_NCPAINT 绘制红色边框。

我的自定义绘画在首次显示应用程序时成功,但在调整应用程序大小或最小化和恢复时无法重绘,尽管在调整大小或恢复窗口时调用了 WM_NCCALCSIZEWM_NCPAINT .

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

【问题讨论】:

    标签: c++ winapi nonclient-area


    【解决方案1】:

    WM_NCPAINT 消息的wParam 有时会返回 1 而不是区域句柄 (HRGN)。在这种情况下,必须使用 CreateRectRgn 函数创建 HRGN

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

    【讨论】:

    • wParam=1 问题未记录在案,但众所周知。还知道GetDCEx() 接受(HRGN)1。不好的是DCX_INTERSECTRGN 拥有HRGN 的所有权并将释放它,但WM_NCPAINT 的调用者并不期望这样。当你自己调用CreateRectRgn() 时获得所有权是可以的,但是当你传递WM_NCPAINT 提供的HRGN 时就不好了,你必须复制它并传递副本。或者,有一个未记录的DCX_NODELETERGN 标志可以传递给GetDCEx(),然后在使用CreateRectRgn() 时可以使用DeleteObject(region)
    • 那些效果更好,我没有尝试你的新链接,但如果我添加WS_CAPTION 它会显示,但只有WS_THICKLINE 标题栏仍然只是一个小白条。
    猜你喜欢
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 2010-09-11
    • 2021-12-15
    • 1970-01-01
    • 2014-12-18
    相关资源
    最近更新 更多