启动时不显示 Direct2D DC 上下文渲染图像

Direct2D DC context rendered images doesn't appear on startup

我有一个使用本机 winapi window 的简单应用程序,以及作为 child window 的静态控件。我想使用 Direct2D 在 child window 上绘图。

绘图发生在 WM_PAINT 消息之外,所以我决定使用 ID2D1DCRenderTarget。除了一件事,一切都有效。我无法在应用程序启动时使绘图可见。

如果我在任何情况下画画(例如在 WM_LBUTTONDOWN 中),画就会出现。但是如果我在 WM_CREATE 中绘画,我在屏幕上什么也看不到。

这是我的示例代码:

#include <iostream>

#include <windows.h>
#include <windowsx.h>

#include <d2d1.h>

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

HWND windowHandle = NULL;
HWND childWindowHandle = NULL;

ID2D1Factory* direct2DFactory = nullptr;
ID2D1DCRenderTarget* renderTarget = nullptr;

static void InitDirect2D ()
{
    D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, &direct2DFactory);

    D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
        D2D1_RENDER_TARGET_TYPE_DEFAULT,
        D2D1::PixelFormat(
            DXGI_FORMAT_B8G8R8A8_UNORM,
            D2D1_ALPHA_MODE_IGNORE),
        0,
        0,
        D2D1_RENDER_TARGET_USAGE_NONE,
        D2D1_FEATURE_LEVEL_DEFAULT
        );

    direct2DFactory->CreateDCRenderTarget (&props, &renderTarget);
    renderTarget->SetAntialiasMode (D2D1_ANTIALIAS_MODE_ALIASED);
}

static void Paint ()
{
    ID2D1SolidColorBrush* blackBrush = nullptr;
    renderTarget->CreateSolidColorBrush (
        D2D1::ColorF (D2D1::ColorF::Black),
        &blackBrush
    );

    renderTarget->BeginDraw ();
    renderTarget->SetTransform (D2D1::Matrix3x2F::Identity ());
    renderTarget->Clear (D2D1::ColorF (D2D1::ColorF::LightGray));
    D2D1_RECT_F rect = D2D1::RectF (10.0f, 10.0f, 100.0f, 100.0f);
    renderTarget->DrawRectangle (&rect, blackBrush);
    renderTarget->EndDraw ();
}

static LRESULT CALLBACK ChildWindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
        case WM_CREATE:
            {
                RECT rc;
                GetClientRect (hwnd, &rc);
                renderTarget->BindDC (GetDC (hwnd), &rc);
                Paint ();
            }
            break;
        case WM_LBUTTONDOWN:
            Paint ();
            break;
        case WM_PAINT:
            break;
        case WM_SIZE:
            {
                RECT rc;
                GetClientRect (hwnd, &rc);
                renderTarget->BindDC (GetDC (hwnd), &rc);
                Paint ();
            }
            break;
        case WM_CLOSE:
            DestroyWindow (hwnd);
            break;
        case WM_DESTROY:
            PostQuitMessage (0);
            break;
            break;
    }

    LRESULT res = DefWindowProc (hwnd, msg, wParam, lParam);
    return res;
}

static void CreateChildWindow (HWND parentHandle)
{
    WNDCLASSEX windowClass;
    memset (&windowClass, 0, sizeof (WNDCLASSEX));
    windowClass.cbSize = sizeof(WNDCLASSEX);
    windowClass.style = 0;
    windowClass.lpfnWndProc = ChildWindowProc;
    windowClass.style = CS_DBLCLKS;
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hInstance = NULL;
    windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
    windowClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    windowClass.lpszMenuName = NULL;
    windowClass.lpszClassName = L"ChildWindowClass";

    RegisterClassEx (&windowClass);
    childWindowHandle = CreateWindowEx (
        0, windowClass.lpszClassName, L"", WS_CHILD,
        CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, parentHandle, NULL, NULL, nullptr
    );

    ShowWindow (childWindowHandle, SW_SHOW);
    UpdateWindow (childWindowHandle);
    MoveWindow (childWindowHandle, 10, 10, 780, 580, TRUE);
}

static LRESULT CALLBACK MainWindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_CREATE) {
        CreateChildWindow (hwnd);
        InvalidateRect (hwnd, NULL, FALSE);
    }

    switch (msg) {
        case WM_SIZE:
            {
                int newWidth = LOWORD (lParam);
                int newHeight = HIWORD (lParam);
                MoveWindow (childWindowHandle, 10, 10, newWidth - 20, newHeight - 20, TRUE);
            }
            break;
        case WM_CLOSE:
            DestroyWindow (hwnd);
            break;
        case WM_DESTROY:
            PostQuitMessage (0);
            break;
    }

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

static int CreateMainWindow ()
{
    WNDCLASSEX windowClass;
    memset (&windowClass, 0, sizeof (WNDCLASSEX));
    windowClass.cbSize = sizeof (WNDCLASSEX);
    windowClass.style = 0;
    windowClass.lpfnWndProc = MainWindowProc;
    windowClass.style = CS_DBLCLKS;
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hInstance = NULL;
    windowClass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    windowClass.hCursor = LoadCursor (NULL, IDC_ARROW);
    windowClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    windowClass.lpszMenuName = NULL;
    windowClass.lpszClassName = L"WindowClass";
    windowClass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);

    RegisterClassEx (&windowClass);

    RECT requiredRect = { 0, 0, 800, 600 };
    HWND windowHandle = CreateWindowEx (
        WS_EX_WINDOWEDGE, windowClass.lpszClassName, L"Example", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, requiredRect.right - requiredRect.left, requiredRect.bottom - requiredRect.top, NULL, NULL, NULL, nullptr
    );

    if (windowHandle == NULL) {
        return 1;
    }

    ShowWindow (windowHandle, SW_SHOW);
    UpdateWindow (windowHandle);

    MSG msg;
    while (GetMessage (&msg, NULL, 0, 0)) {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }

    return 0;
}

int main (int argc, char* argv[])
{
    InitDirect2D ();
    return CreateMainWindow ();
}

您知道如何让绘图在应用程序启动时可见吗?

WM_CREATE期间 window尚未完全创建,因此无法绘制。您应该编写适当的 WM_PAINT(和 WM_ERASEBKGND)消息处理程序并在那里执行所有绘制操作。然后,当您需要重绘作为对任何其他消息(例如 WM_LBUTTONDOWN)的响应时,您需要使 window 无效并触发 WM_PAINT 而不是立即调用 Paint();.