Стирание фона окна - PullRequest
       53

Стирание фона окна

0 голосов
/ 06 июня 2018

Насколько я понимаю, Windows делает разделение труда в отношении (пере) рисования данного окна;разделение на фоновое стирание и переднюю живопись.Сообщение WM_ERASEBKGND отправляется, чтобы подготовить недействительную часть данного окна к рисованию, и обычно эта подготовка состоит из стирания фона, чтобы фактическая живопись могла начаться с чистого холста.Мне кажется, что это сообщение всегда отправляется, когда Windows делает недействительной часть данного окна (и поэтому в основном всегда отправляется вместе с отправкой сообщения WM_PAINT).Всякий раз, когда само приложение делает недействительным (частью) данное окно, последний аргумент функции InvalidateRect указывает, следует ли отправлять WM_ERASEBKGND или нет.Поэтому я написал небольшую программу для проверки своих предположений, но ее поведение немного ускользает от меня.Это программа:

#ifndef UNICODE
#define UNICODE
#endif 

#include <windows.h>

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

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hwInstance, PWSTR pCmdLine, int nCmdShow)
{
    // Register the window class.
    const wchar_t CLASS_NAME[] = L"Sample Window Class";

    WNDCLASS wc = {0};

    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    // Create the window.

    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        L"Learn to Program Windows",    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style

                                        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
    );

    if (hwnd == NULL)
    {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // Run the message loop.

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

    return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static int eb_count = 0; // counts number of WM_ERASEBKGND messages

    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        RECT rect;

        wchar_t text[40]; 
        wsprintf(text, L"Number of WM_ERASEBKGND messages: %i\n", eb_count);

        GetClientRect(hwnd, &rect);
        DrawText(hdc, text, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

        EndPaint(hwnd, &ps);
        return 0;

    case WM_RBUTTONDOWN: // repaint whenever RBUTTONDOWN
        InvalidateRect(hwnd, NULL, FALSE);
        UpdateWindow(hwnd);
        return 0;

    case WM_ERASEBKGND:
        eb_count++;
        return 0;
    }

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

Я обрабатываю WM_ERASEBKGND в оконной процедуре (это случай в моем коммутаторе), поэтому она не должна передаваться оконной процедуре по умолчанию.Однако я не делаю никакого фактического стирания фона (я просто увеличиваю статическую переменную), и я возвращаю 0, чтобы указать, что никакого стирания фактически не было.Мне кажется, что в этой программе фон никогда не должен стираться.Это, однако, происходит в двух разных случаях.

Всякий раз, когда я максимизирую окно, фон недопустимой части стирается кистью фона класса.Но как это возможно?Оконная процедура, безусловно, не делает ничего подобного при получении сообщения WM_ERASEBKGND.

Подобное происходит, когда DrawText перерисовывает свою строку.Я ожидал бы, что увеличивающиеся числа будут нарисованы сверху друг друга (приводя к неразборчивому беспорядку, конечно).Так что, похоже, функция DrawText также каким-то образом стирает фон прямоугольника, в котором она рисует свою строку.

Мой последний вопрос касается моего предположения о том, что сообщение WM_ERASEBKGND отправляется всякий раз, когда Windows делает недействительной часть окна.Я заметил, что всякий раз, когда окно закрывается другим окном и впоследствии открывается, сообщение WM_ERASEBKGND не отправляется.Значит ли это, что мое предположение неверно?Извините за долгое чтение, но любая помощь в ответе на эти вопросы будет принята с благодарностью.

Ответы [ 2 ]

0 голосов
/ 07 июня 2018

Согласно ответу @Paul Sanders, совсем недавно Desktop Window Manager - это процесс, который на самом деле кэширует содержимое окон, чтобы он мог выполнять эффекты смешивания при создании рабочего стола, а это означает, что ваше Window не всегдаперекрасить так, как это делали в более ранних выпусках Windows.

До этого переход от совместной многозадачной работы к многопоточной ОС (эта модель рисования была в API Windows 3.0) приводил к некоторым условиям гонки.что, iirc, Windows будет пытаться скрыть, выполняя упреждающую заливку кистью фона в некоторых случаях, когда какой-либо процесс изменяет видимость окна другого процесса.Это то, что вы видите, когда вы максимизируете окно.

Ваш вызов DrawText работает, потому что DrawText - по умолчанию - стирает свой собственный фон - вам нужно вызвать SetBkMode, передаваяTRANSPARENT флаг для отображения только шрифта.

0 голосов
/ 06 июня 2018

... мое предположение, что сообщение WM_ERASEBKGND отправляется всякий раз, когда Windows делает недействительной часть окна.Я заметил, что всякий раз, когда окно закрывается другим окном и впоследствии раскрывается, сообщение WM_ERASEBKGND, похоже, не отправляется ...

Это потому, что в Vista и более поздних версиях у нас есть DesktopWindow Manager (DWM) скрывается на заднем плане.Это буферизует содержимое всех экранных окон, так что Windows не нужно отправлять запросы WM_ERASEBKGND или WM_PAINT, когда часть одного обнаружена - она ​​может просто скопировать так называемый обратный буфер обратно на экран.

[Части] окон по-прежнему становятся недействительными - либо вами, либо ОС - но не так часто, как это было в дни XP.Попробуйте, например, свернуть и восстановить окно - тогда оно должно быть перерисовано.Когда вы делаете это, DWM, вероятно, выбрасывает резервный буфер для сохранения памяти, пока окно свернуто.

Кроме того, что другие сказали в комментариях.

...