Как нарисовать дочернее окно с помощью FillRect()?

У меня есть главное окно, созданное со следующими стилями
WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_TABSTOP | WS_GROUP | WS_VISIBLE
и с ex-stles
WS_EX_ACCEPTFILES | WS_EX_CONTROLPARENT | WS_EX_LEFT | WS_EX_LTRREADING.

Это главное окно имеет дочернее окно, которое представляет собой элемент управления редактирования, созданный с помощью стилей
WS_VISIBLE | WS_CHILD | ES_READONLY
и ex-style
WS_EX_CLIENTEDGE.

Я собираюсь использовать этот элемент управления редактированием в качестве элемента управления индикатором выполнения. Я не хочу использовать стандартный элемент управления индикатором выполнения Wind32 (PROGRESS_CLASS), потому что я хочу сделать на нем какую-то пользовательскую прорисовку (например, динамически изменить цвет заливки, отобразить на нем текст и т. д.).

Я могу нарисовать любую область главного окна с помощью следующего кода:

// hWnd: Handle of the main window  
case WM_PAINT:
    hDc = BeginPaint(hWnd, &Ps);
        Rect = AFunctionToGetCornerThePointsOfTheEditControl();
        Rect.right = Rect.left + 3 * (Rect.right - Rect.left) / 4; // Fill 3/4 (75%) of it
        Rect.left   -= 10; // Enlarge the paint region a little
        Rect.top    -= 10; // so that we can see it if it stays
        Rect.bottom += 10; // under the edit control.
        hBrush = CreateSolidBrush(RGB(50,100,255));
        ret = FillRect(hDc, &Rect, hBrush); // ret = 1 always
        ler = GetLastError();               // ler = 0 
    EndPaint(hWnd, &Ps);
    break;

Выглядит это так:
внешний вид нарисованного окна

Я немного изменил этот код, чтобы вместо этого нарисовать дочерний элемент управления:

// hWndEdit: Handle of the edit control
case WM_PAINT:
    hDc = BeginPaint(hWndEdit, &Ps);
        Rect = AFunctionToGetCornerThePointsOfTheEditControl();
        Rect.right = Rect.left + 3 * (Rect.right - Rect.left) / 4; // Fill 3/4 (75%) of it
        Rect.left   -= 10;
        Rect.top    -= 10;
        Rect.bottom += 10;
        hBrush = CreateSolidBrush(RGB(50,100,255));
        ret = FillRect(hDc, &Rect, hBrush); // ret = 0 always
        ler = GetLastError();               // ler = 6 (ERROR_INVALID_HANDLE) 
    EndPaint(hWndEdit, &Ps);
    break;

На этот раз это не работает. Главное окно полностью исчезает, как только я перетаскиваю какую-то его часть за пределы экрана, и оно полностью перестает отвечать на запросы. Значки на рабочем столе под ним видны, но не кликабельны.

Итак, что мне нужно сделать, чтобы нарисовать дочернее окно (элемент управления редактированием)?


person hkBattousai    schedule 12.08.2013    source источник
comment
Правильный способ сделать пользовательскую картину — создать подкласс.   -  person chris    schedule 12.08.2013


Ответы (2)


Мне очень помогла эта статья: Подкласс элементов управления

Во-первых, я создаю отдельную функцию обработки сообщений для обработки дочерних сообщений.

LRESULT CALLBACK MyClass::ChildWindowProc(  HWND        hWnd,
                                            UINT        uMsg,
                                            WPARAM      wParam,
                                            LPARAM      lParam,
                                            UINT_PTR    uIdSubclass,
                                            DWORD_PTR   dwRefData)
{
    static PAINTSTRUCT Ps;
    static RECT Rect;
    static HBRUSH hBrush1 = CreateSolidBrush(RGB(50,100,255));
    static HBRUSH hBrush2 = CreateSolidBrush(RGB(255,100,50));
    HDC hDc;
    LRESULT lResult;
    switch (uMsg)
    {
        case WM_PAINT:
            switch (uIdSubclass)
            {
                case 1:
                    hDc = BeginPaint(hWnd, &Ps);
                        Rect.left   = 0;
                        Rect.right  = (LONG) (((double) ITEM2_WIDTH) * Status::GI()->Get_JobCurPercentage());
                        Rect.top    = 0;
                        Rect.bottom = ITEM_HEIGHT - 3;
                        FillRect(hDc, &Rect, hBrush1);
                    EndPaint(hWnd, &Ps);
                    break;
                case 2:
                    hDc = BeginPaint(hWnd, &Ps);
                        Rect.left   = 0;
                        Rect.right  = (LONG) (((double) ITEM2_WIDTH) * Status::GI()->Get_JobTotPercentage());
                        Rect.top    = 0;
                        Rect.bottom = ITEM_HEIGHT - 3;
                        FillRect(hDc, &Rect, hBrush2);
                    EndPaint(hWnd, &Ps);
                    break;
                default:
                    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
            }
            break;
        case WM_NCDESTROY:
            //ReleaseDC(hWnd, hDc);
            return DefSubclassProc(hWnd, uMsg, wParam, lParam);
            break;
        default:
            return DefSubclassProc(hWnd, uMsg, wParam, lParam);
    }
    return 0;
}

Далее я привязываю эту функцию к элементам управления:

SetWindowSubclass(  /*_In_  HWND            hWnd*/          ed_cur_Progress.hWnd,
                    /*_In_  SUBCLASSPROC    pfnSubclass*/   ChildWindowProc,
                    /*_In_  UINT_PTR        uIdSubclass*/   1,
                    /*_In_  DWORD_PTR       dwRefData*/     (DWORD_PTR) NULL);
SetWindowSubclass(  /*_In_  HWND            hWnd*/          ed_tot_Progress.hWnd,
                    /*_In_  SUBCLASSPROC    pfnSubclass*/   ChildWindowProc,
                    /*_In_  UINT_PTR        uIdSubclass*/   2,
                    /*_In_  DWORD_PTR       dwRefData*/     (DWORD_PTR) NULL);

И это все! Результат потрясающий.

внешний вид

person hkBattousai    schedule 12.08.2013
comment
Контекст устройства, возвращенный из BeginPaint, не должен освобождаться вызовом ReleaseDC. Хуже того, что вы передаете неинициализированную переменную HDC в ReleaseDC в своем обработчике WM_DESTROY. Еще одно замечание по очистке: WM_NCDESTROY — это последнее сообщение, отправляемое окну. Вот куда вы должны поместить свой код очистки. - person IInspectable; 12.08.2013
comment
@Tim Спасибо за информацию; Я изменю свой код в соответствии с ним. Как мне тогда удалить контекст устройства? Я выпускаю его в первую очередь? - person hkBattousai; 12.08.2013
comment
Ресурсы, связанные с контекстом устройства, возвращенным из BeginPaint, освобождаются системой при вызове EndPaint. Я понимаю, что оставить "висячий" HDC выглядит неправильно, но это не так. Для справки см. Рисование в клиентской области. - person IInspectable; 12.08.2013

WM_PAINT, с которым вы работаете, - это главное окно. вам нужно нарисовать поле редактирования в сообщении WM_PAINT его владельца. Я предполагаю, что вы получаете ошибку от «hDc = BeginPaint (hWndEdit, & Ps);», вы можете проверить это.

person Matthew    schedule 12.08.2013