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






4.71/5 (5投票s)
2007年7月16日
2分钟阅读

85656

1545
一篇关于 MFC CToolBar 错误的文章,该错误可能导致为工具栏按钮显示不正确的工具提示。
引言
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.bottom
和 rect.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 日 -- 发布原始版本