Как правильно нарисовать простую не-клиентскую область (красная граница 4 пикселя)? - PullRequest
0 голосов
/ 02 мая 2018

Я пытаюсь нарисовать нестандартную закрашенную не-клиентскую область вместо границы темы по умолчанию (Windows 10).

Я обработал WM_NCCALCSIZE, чтобы изменить размер не клиентской области до 4 пикселей с каждой стороны, а затем обработал WM_NCPAINT, чтобы нарисовать красную границу.

Моя пользовательская рисование завершается успешно при первом отображении приложения, но не может перерисовываться ни при изменении размера приложения, либо при сворачивании и восстановлении, несмотря на то, что и WM_NCCALCSIZE, и WM_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);
}

1 Ответ

0 голосов
/ 02 мая 2018

Сообщение wParam из WM_NCPAINT иногда возвращает 1 вместо дескриптора региона (HRGN). В этом случае HRGN должно быть создано с использованием функции CreateRectRgn.

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