【问题标题】:Why is my non-owner-drawn listbox having drawing issues after strings are added?为什么我的非所有者绘制的列表框在添加字符串后出现绘制问题?
【发布时间】:2012-02-08 19:15:18
【问题描述】:

我正在开发我自己的基于 ATL 的私有 Windows 桌面应用程序用户界面框架,它应该具有与 AWT 几乎相同的类列表和编程结构。 (如果我使用 C#,我几乎在 WTL 或 WinForms 中完成了所有以前的 GUI 开发。)

我遇到了列表框类的问题。在我不参与 WM_PAINT 的情况下,所有其他控件都能够正确地重新绘制自己。列表框?没那么多。下面是它的外观:

由于我不知道实际问题出在哪里,这里是我能想到的所有涉及绘图的代码。 如果有任何其他源代码有帮助,请告诉我。

这是我的 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;
}

曾经在 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 并在 OnRegistering 方法退出后对其调用 Register。

  • _backMode 是基本“窗口”类的私有成员,并且是透明的或不透明的;在列表框的情况下,它目前被硬编码为不透明
  • _backColor 和 _foreColor 是私有 COLORREF 成员。
  • _backBrush 是一个私有 HBRUSH,它只是一个 _backColor 的画笔,并在解构控件类时销毁。

WM_PAINT 处理程序只是调用一个事件激活器(OnPaint),它完全不做任何事情; WM_PAINT 处理程序然后在处理为 false 时调用 DefWindowProc。

在创建之前,我在类样式上玩弄了 CS_HREDRAW 和 CS_VREDRAW,方法是手动对 WC_LISTBOX 进行超类化并在这些值中进行 OR'ing,但结果很糟糕(事实上,添加到列表框中的文本不会出现,尽管返回LB_GETCOUNT 返回正确数量的项目并且列表框不会重绘根本)所以我没有想法。

以前有没有人见过这样的事情,也许可以告诉我我错过了什么(或者我应该处理什么窗口消息)?

【问题讨论】:

  • 在这里在黑暗中摸索,但我记得在尝试用 NULL_BRUSH 填充编辑框时看起来相似的背景。也许 CreateSolidBrush 在某处返回 NULL。另外,为什么要验证 WM_ERASEBACKGROUND 上的窗口?
  • 解决了部分问题... _backBrush 仅检查 NULL 当且仅当 _backColor 被分配给不同于它已经设置的东西...我解决了这个问题,这就是现在的样子:i.imgur.com/ix1gM.png
  • @pezcode: NULL_BRUSH等同于NULL
  • 不管怎样,每次发送 WM_ERASEBKGND 时 _backBrush 都是 0,因此有助于缩小范围。

标签: c++ windows winapi atl


【解决方案1】:

WmEraseBkgnd 中的坐标不匹配。 GetWindowRect 给出屏幕坐标,而FillRect 使用本地坐标。您应该使用GetClientRect 或使用ScreenToClient 转换左上角和右下角。

【讨论】:

  • 埃加德,谢谢。我不敢相信我没听懂。我有一个名为 getMappedRect 的函数,它调用 GetClientRect 或 GetWindowRect 取决于窗口是否是顶级的。我想我忘了换掉它。
猜你喜欢
  • 1970-01-01
  • 2011-03-25
  • 2010-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多