BeginDraw() 在状态栏上绘制 - 不尊重 renderTarget 的大小调整

BeginDraw() paints over statusbar - resizing of renderTarget not respected

我有一个 windowed Direct2D 应用程序,并从常用控件向 window 添加了一个状态栏:

InitCommonControls();
HWND hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
        WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
        hWnd, (HMENU)ID_STATUSBAR, GetModuleHandle(NULL), NULL);

状态栏显示得很好,但是一旦我在消息循环中激活 BeginDraw()&EndDRaw() 函数,状态栏就会被涂掉,尽管我定义了高度初始化时 renderTarget

res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);

GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);

res = factory->CreateHwndRenderTarget(
    D2D1::RenderTargetProperties(),
    D2D1::HwndRenderTargetProperties(windowHandle, rectRender),
    &renderTarget);

我还创建了一个调整大小的函数

RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };

GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);

rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
renderTarget->Resize(rectRender);
InvalidateRect(windowHandle, NULL, FALSE);

并在 WM_SIZING 和 WM_SIZE 中调用:

case WM_SIZE:
case WM_SIZING:
    hStatus = GetDlgItem(hWnd, ID_STATUSBAR);
    gfx->Resize(hWnd, hStatus);
    SendMessage(hStatus, WM_SIZE, 0, 0);

难道 BeginDraw() 不尊重 rendertarget 的尺寸而只占用整个 window 吗?如果是这样,我应该考虑使用图层还是我的代码有问题?

编辑:我收到了一些对这个问题的反对票。如果我的 post 有问题,请告诉我,我会尽力改进。我对 win32 世界还很陌生,但我从这个平台学到了很多东西。我很乐意提供有趣的问题和答案,但简单的 -1 并没有给我任何改进的线索。我已经在 MSDN 和各种论坛上阅读了 2 个晚上关于该主题的内容,但没有看到我做错了什么。我尝试通过编写说明问题的完整示例代码来尽可能完整。

整个代码供参考

#include <windows.h>
#include <CommCtrl.h>
#include <d2d1.h>

#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "d2d1.lib")

#define ID_STATUSBAR 1000

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

class Graphics
{
    ID2D1Factory* factory;
    ID2D1HwndRenderTarget* renderTarget;
    ID2D1SolidColorBrush* brush;

public:
    Graphics()
    {
        factory = NULL;
        renderTarget = NULL;
        brush = NULL;
    }
    ~Graphics()
    {
        if (factory) factory->Release();
        if (renderTarget) renderTarget->Release();
        if (brush) brush->Release();
    }
    bool Init(HWND windowHandle, HWND statusHandle);
    void BeginDraw() { renderTarget->BeginDraw(); }
    void EndDraw() { renderTarget->EndDraw(); }
    void Resize(HWND windowHandle, HWND statusHandle);
    void DrawCircle(float x, float y, float r);
};

HINSTANCE hInstance;
Graphics* gfx;

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

    WNDCLASS wc = { };
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = TEXT("mainwindow");
    RegisterClass(&wc);

    HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, TEXT("mainwindow"),
        TEXT("MainWindow"), WS_OVERLAPPEDWINDOW, 100, 100, 800, 600,
        NULL, NULL, hInstance, NULL);
    if (!hWnd) return -1;

    HWND hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
        WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
        hWnd, (HMENU)ID_STATUSBAR, GetModuleHandle(NULL), NULL);

    gfx = new Graphics;
    if (!gfx->Init(hWnd, hStatus))
    {
        delete gfx;
        return -1;
    }

    ShowWindow(hWnd, nCmdShow);

    MSG message{ 0 };
    bool runGame = true;

    while (runGame)
    {
        while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&message);
            DispatchMessage(&message);

            if (message.message == WM_QUIT)
                runGame = false;
        }
        gfx->BeginDraw();
        gfx->DrawCircle(400.0f, 100.0f, 100.0f);
        gfx->DrawCircle(400.0f, 300.0f, 100.0f);
        gfx->DrawCircle(400.0f, 500.0f, 100.0f);
        gfx->EndDraw();
    }
    return 0;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HWND hStatus;

    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    case WM_SIZE:
    case WM_SIZING:
        hStatus = GetDlgItem(hWnd, ID_STATUSBAR);
        gfx->Resize(hWnd, hStatus);
        SendMessage(hStatus, WM_SIZE, 0, 0);
        return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

bool Graphics::Init(HWND windowHandle, HWND statusHandle)
{
    RECT rectWindow{ 0 }, rectStatus{ 0 };
    D2D1_SIZE_U rectRender{ 0 };

    HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
    if (res != S_OK) return false;

    GetClientRect(windowHandle, &rectWindow);
    GetClientRect(statusHandle, &rectStatus);
    rectRender.width = rectWindow.right;
    rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);

    res = factory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(),
        D2D1::HwndRenderTargetProperties(windowHandle, rectRender),
        &renderTarget);
    if (res != S_OK) return false;

    res = renderTarget->CreateSolidColorBrush(D2D1::ColorF(1, 0, 0, 0), &brush);
    if (res != S_OK) return false;

    return true;
}
void Graphics::Resize(HWND windowHandle, HWND statusHandle)
{
    if (renderTarget != NULL)
    {
        RECT rectWindow{ 0 }, rectStatus{ 0 };
        D2D1_SIZE_U rectRender{ 0 };

        GetClientRect(windowHandle, &rectWindow);
        GetClientRect(statusHandle, &rectStatus);

        rectRender.width = rectWindow.right;
        rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
        renderTarget->Resize(rectRender);
    }
}
void Graphics::DrawCircle(float x, float y, float r)
{
    brush->SetColor(D2D1::ColorF(1.0f, 0.0f, 0.0f, 1.0f));
    renderTarget->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(x, y), r, r), brush, 1.0f);
}

您可以将 WS_CLIPCHILDREN window 样式添加到您的 MainWindow:

HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, TEXT("mainwindow"),
  TEXT("MainWindow"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 100, 100, 800, 600,
  NULL, NULL, hInstance, NULL);

或者,您可以创建另一个子项 window(状态栏的同级项)并将其用于您的 Direct2D 目标。