65.9K
CodeProject 正在变化。 阅读更多。
Home

修复 MFC CToolBar 的工具栏按钮位置错误

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (5投票s)

2007年7月16日

2分钟阅读

viewsIcon

85656

downloadIcon

1545

一篇关于 MFC CToolBar 错误的文章,该错误可能导致为工具栏按钮显示不正确的工具提示。

Screenshot - ScreenShot.png

引言

MFC 的 CToolBar 类在显示工具栏按钮的工具提示时存在一个错误。在计算哪个按钮应该显示工具提示时,工具栏按钮的位置向右移动了 1 个像素。 结果,当鼠标光标指向工具栏按钮的最左侧像素时,实际上显示的是其左侧按钮的工具提示和状态栏提示!

很容易重现这个错误。创建一个 MFC 项目,无论是 SDI 还是 MDI,默认情况下都包含一个工具栏。 将所有其他项目设置保留为其默认值。运行程序并将鼠标光标小心地移动到“打开”工具栏按钮的最左侧像素。 CToolBar 将显示“新建”按钮的工具提示,如上图所示。 这个错误非常具有误导性。

许多著名的、基于 MFC 的软件都受到了这个错误的影响,包括 Spy++ 和 Dependency Walker

背景

在深入研究 CToolBar 源代码后,我发现了这个错误的原因。当工具栏可能需要工具提示时,框架会调用 CToolBar::OnToolHitTest() 来确定光标位于哪个按钮上。 此方法的源代码如下所示,从 VC++ 6.0 复制。 VC++ 8.0 代码几乎没有改变,除了返回类型 int 更改为 INT_PTR

int CToolBar::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
    ASSERT_VALID(this);
    ASSERT(::IsWindow(m_hWnd));

    // check child windows first by calling CControlBar
    int nHit = CControlBar::OnToolHitTest(point, pTI);
    if (nHit != -1)
        return nHit;

    // now hit test against CToolBar buttons
    CToolBar* pBar = (CToolBar*)this;
    int nButtons = (int)pBar->DefWindowProc(TB_BUTTONCOUNT, 0, 0);
    for (int i = 0; i < nButtons; i++)
    {
        CRect rect;
        TBBUTTON button;
        if (pBar->DefWindowProc(TB_GETITEMRECT, i, (LPARAM)&rect))
        {
            ++rect.bottom;    // Buggy line
            ++rect.right;    // Buggy Line
            if (rect.PtInRect(point) &&
                pBar->DefWindowProc(TB_GETBUTTON, i, (LPARAM)&button) &&
                !(button.fsStyle & TBSTYLE_SEP))
            {
                int nHit = GetItemID(i);
                if (pTI != NULL && pTI->cbSize >= sizeof(AFX_OLDTOOLINFO))
                {
                    pTI->hwnd = m_hWnd;
                    pTI->rect = rect;
                    pTI->uId = nHit;
                    pTI->lpszText = LPSTR_TEXTCALLBACK;
                }
                // found matching rect, return the ID of the button
                return nHit != 0 ? nHit : -1;
            }
        }
    }
    return -1;
}

与该错误相关的行被注释为“Buggy Line”。 似乎没有理由将 rect.bottomrect.right 增加 1 个像素,并且 MFC 没有提供这样做的注释。 这些行导致检索到不正确的按钮 RECT,因此显示错误的工具提示。 :(

使用代码

要修复此错误,只需创建一个派生自 CToolBar 的类并覆盖 CToolBar::OnToolHitTest() 方法。 复制 CToolBar::OnToolHitTest() 的原始实现并注释掉这两行有问题的行。 然后用新类替换项目中使用的所有 CToolBar。 附件 CFixedToolBar 是一个例子。

还有一些事情要做。 如果你编译这个新类,你会得到一个错误,提示 AFX_OLDTOOLINFO 未定义。 它实际上是在 <afximpl.h> 中定义的。 包含该代码是为了确保传入兼容版本的 TOOLINFO。 因此,与其提供 AFX_OLDTOOLINFO 的定义,不如简单地将 sizeof(AFX_OLDTOOLINFO) 更改为 40,即 AFX_OLDTOOLINFO 结构的大小。 现在一切都正常了。 享受。 :)

历史

  • 2007 年 7 月 16 日 -- 发布原始版本
© . All rights reserved.