Почему у моего списка, нарисованного не владельцем, возникают проблемы с рисованием после добавления строк? - PullRequest
1 голос
/ 12 января 2012

Я работаю над своей собственной структурой пользовательского интерфейса для настольных приложений Windows, основанной на ATL, которая должна иметь почти такой же список классов и структуру программирования, как AWT.(Я сделал почти всю предыдущую разработку GUI в WTL или WinForms, если я использую C #.)

У меня возникла проблема, когда дело доходит до класса listbox.Любой другой элемент управления может перерисовывать себя правильно без моего участия в WM_PAINT.Listbox?Не так много.Вот как это выглядит:

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

Вот мой код для обработчика WM_CTLCOLORLISTBOX:

virtual LRESULT WmCtlColorListbox(MSG& msg, bool& handled)
    {
        handled = true;
        return colorControl(msg.message, reinterpret_cast<HDC>(msg.wParam), reinterpret_cast<HWND>(msg.lParam));
    }

LRESULT colorControl(UINT origMsg, HDC hdc, HWND window)
    {
        if (window == *this)
        {
            ::SetBkMode(hdc, _backMode);
            ::SetBkColor(hdc, _backColor);
            ::SetTextColor(hdc, _foreColor);
            return (LRESULT) _backBrush;
        }

        return ::SendMessage(window, origMsg, reinterpret_cast<WPARAM>(hdc), reinterpret_cast<LPARAM>(window));
    }

Обработчик WM_PAINT:

inline virtual LRESULT WmPaint(MSG& msg, bool& handled)
{
    OnPaint(handled);
    return handled ? 0 : 1;
}

inline virtual void OnPaint(bool& handled) { }

Обработчик WM_ERASEBKGND:

virtual LRESULT WmEraseBkgnd(MSG& msg, bool& handled)
{
    HDC hdcWindow = (HDC)msg.wParam;
    RECT r;
    this->GetWindowRect(&r);
    ::FillRect(hdcWindow, &r, _backBrush);
    this->Invalidate(FALSE);
    handled = true;
    return 1;
}

Виртуальная функция, которая вызывается непосредственно перед регистрацией ATL класса:

virtual void OnRegistering(CreationParameters& createParams)
{
    createParams.BaseClassName = WC_LISTBOX;
    createParams.WindowExStyles |= WS_EX_CLIENTEDGE;
}

I используется для этот код внутри OnPaint:

PAINTSTRUCT ps;
HDC hdcWindow = this->BeginPaint(&ps);

// This class automatically handles the background
// drawing for any other window
if (_backBrush == NULL)
    _backBrush = ::CreateSolidBrush(_backColor);

::FillRect(hdcWindow, &ps.rcPaint, _backBrush);
this->EndPaint(&ps);

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

Для небольшого пояснения:

CreationParameters превращается в соответствующий CWndClassInfo, и для него вызывается Register после выхода из метода OnRegistering.

  • _backMode является закрытым членом для базового класса "окна" и может либоПРОЗРАЧНЫЙ или МЯГКИЙ;в случае списка это в настоящее время жестко закодировано, чтобы быть OPAQUE
  • _backColor и _foreColor являются частными членами COLORREF.
  • _backBrush является частным HBRUSH, который является просто кистью _backColor и егоуничтожается при деконструкции класса управления.

Обработчик WM_PAINT просто вызывает активатор события (OnPaint), который абсолютно ничего не делает;обработчик WM_PAINT затем вызывает DefWindowProc, если обработано значение false.

Я поиграл с CS_HREDRAW и CS_VREDRAW в стилях классов перед созданием, вручную переклассифицировав WC_LISTBOX и OR в этих значениях с ужасными результатами (как втот факт, что текст, добавленный в список, не отображается, несмотря на то, что он возвращается в LB_GETCOUNT, возвращающем правильное количество элементов, а список не перерисовывается вообще ), так что я не в курсе.

Кто-нибудь видел что-то подобное раньше и, может быть, может сказать мне, что мне не хватает (или какое оконное сообщение я должен обрабатывать)?

1 Ответ

4 голосов
/ 12 января 2012

У вас есть несоответствующие координаты в WmEraseBkgnd. GetWindowRect дает координаты экрана, в то время как FillRect использует локальные координаты. Вы должны либо использовать GetClientRect, либо перевести верхний левый и нижний правый углы с помощью ScreenToClient.

...