CWindowScroller
一个 MFC 控件,用于将鼠标中键滚动添加到任何 CWnd 派生窗口。
引言
CWindowScroller
是一个小的 MFC 类,用于为任何 CWnd
派生窗口添加鼠标中键滚动功能(也称为平移)。该控件类似于 Internet Explorer 和 Visual Studio .NET 中的控件。只需一行代码即可启动并运行窗口滚动器。窗口滚动器使用的所有资源(图标和光标)都是在运行时动态创建的。它们不需要添加到您的资源文件中。
使用代码
使用 CWindowScroller
类的第一步(也是最明显的一步)是将类文件(WindowScroller.h 和 WindowScroller.cpp)添加到您的项目中。
在要添加滚动器的窗口的 .cpp 文件中,请确保包含 WindowScroller.h 文件。
#include "WindowScroller.h"
然后,使用类向导,为您的窗口类添加 OnMButtonDown
函数。(一个小提示,消息过滤器必须设置为“Window”)
现在,在 OnMButtonDown
函数中,您只需在堆上创建一个 CWindowScroller
类的实例。
void CChildView::OnMButtonDown (UINT, CPoint point) { new CWindowScroller (this, point); }
备注
- 如果您在堆栈上创建
CWindowScroller
,它将在屏幕上短暂闪烁,然后在变量超出范围时被销毁。 - 保存指针供以后使用是毫无意义的,因为一旦用户完成使用,CWindowScroller 将被销毁,内存也将被删除。
当 CWindowScroller
启动时,它会向它将要滚动的窗口发送一个 RWM_ONSTARTWINDOWSCROLLER
注册窗口消息。如果处理函数返回非零值,则滚动器将被阻止。
当 CWindowScroller
被销毁时,它会将一个 RWM_ONDESTROYWINDOWSCROLLER
注册窗口消息发布到它已经滚动的窗口。有关处理注册消息的更多信息,请参阅 MSDN 中的 ON_REGISTERED_MESSAGE。
成员函数
类构造函数是唯一公共成员函数。
CWindowScroller::CWindowScroller (CWnd *pParent, CPoint Point, int Pixels, // = 15 UINT Elapse, // = 30 int Direction, // = Both BOOL bUseThumbPos) // = TRUE
参数
pParent | 需要滚动的窗口的指针 |
Point | 在父窗口客户区坐标系中的点,它将是 CWindowScroller 窗口的中心 |
像素 | 在鼠标指针距离 CWindowScroller 窗口中心多远(以像素为单位)时,滚动速度会增加的距离间隔。 |
Elapse | 滚动消息之间的时间(以毫秒为单位)。此值将传递给 SetTimer() 函数。 |
方向 | 滚动的方向。允许的值包括 CWindowScroller::Vertical 、CWindowScroller::Horizontal 或 CWindowScroller::Both 。 |
bUseThumbPos | 设置与 WM_HSCROLL 和 WM_VSCROLL 消息一起使用的消息标志。 TRUE 将标志设置为 SB_THUMBPOSITION。 FALSE 将标志设置为 SB_THUMBTRACK。 |
滚动速度
滚动速度的计算方法是将鼠标指针距离 CWindowScroller
窗口中心的距离(以像素为单位)除以 Pixels 参数的值。这将给出每 Elapse 毫秒滚动父窗口的距离。
准备您的窗口使用 CWindowScroller
大多数窗口应该可以与 CWindowScroller
正常工作,但有些可能需要一些特殊的处理,无论是通过 CWindowScroller
设置还是通过窗口处理滚动消息的方式。
CWindowScroller
目前通过调用 CWnd::SetScrollPos()
来设置滚动条位置,然后向窗口发送 WM_HSCROLL
或 WM_VSCROLL
消息来处理任何常规的 CWnd
派生类。它还内置了处理 CScrollView
、CTreeView
和 CListView
的代码。
如何向 CWindowScroller 添加更多支持的窗口类型
如果您遇到一个 CWindowScroller
无法处理的窗口,因为它使用了某种非标准的滚动方法(例如列表视图,它使用 LVM_SCROLL
消息而不是 WM_HSCROLL
/WM_VSCROLL
),并且您希望 CWindowScroller
处理它,那么您需要在 WindowScroller.cpp 文件中的以下位置添加一些代码。
首先,在文件顶部的 WindowTypes 枚举中添加一个新的窗口类型标识符。
enum WindowTypes { Type_CWnd = 0, Type_CScrollView, Type_CTreeView, Type_CListView_Report, Type_CListView_List, Type_CListView_Icon // Add more types here };
其次,在 GetParentWndType()
函数中添加代码,该函数可以使用传递给 CWindowScroller
构造函数的指针来识别窗口类型。让该代码返回您添加到 WindowTypes 枚举并添加的标识符。
UINT CWindowScroller::GetParentWndType() { TCHAR szClassName[256] = {0}; ::GetClassName(*m_pParentWnd, szClassName, sizeof szClassName); if (!_tcscmp(szClassName, _T("SysTreeView32"))) return Type_CTreeView; if (!_tcscmp(szClassName, _T("SysListView32"))) { if ((m_pParentWnd->GetStyle() & 0x3) == LVS_REPORT) return Type_CListView_Report; if ((m_pParentWnd->GetStyle() & 0x3) == LVS_LIST) return Type_CListView_List; return Type_CListView_Icon; } if (m_pParentWnd->IsKindOf(RUNTIME_CLASS(CScrollView))) return Type_CScrollView; // Add additional window identifier code here return Type_CWnd; }
第三步是在 OnTimer()
函数中添加实际的滚动代码。在 switch (m_ParentWndType)
块中添加代码作为一个 case
,使用您添加到 WindowTypes 枚举并从 GetParentWndType()
函数返回的标识符作为要处理的 case。
void CWindowScroller::OnTimer(UINT nIDEvent) { < snip > if (nIDEvent == 0x1FEB && rc.PtInRect(pt) && (m_HorzScroll || m_VertScroll)) { < snip > if (NewPoint != OriginalPoint) { switch (m_ParentWndType) { case Type_CListView_Report: < snip > break; case Type_CListView_List: < snip > break; case Type_CListView_Icon: < snip > break; case Type_CTreeView: < snip > break; case Type_CScrollView: < snip > break; case Type_CWnd: < snip > break; // Add additional cases here default: TRACE (_T("CWindowScroller::OnTimer - Unknown window type")); break; } // switch (m_ParentWndType) } // if (NewPoint != OriginalPoint) } // if (nIDEvent == 0x1FEB && rc.PtInRect(pt) && (m_HorzScroll || m_VertScroll)) }
最后一步是写下您在下面的论坛中所做的工作,我将更新文章以包含新代码。
即使您完成了所有这些,仍然可能有一些窗口 CWindowScroller
无法正常工作。我遇到问题的一个窗口是 Chris Maunder's 的 MFC Grid control 2.24。水平滚动工作正常,但垂直滚动存在一个小问题。事实证明,控件左侧的固定列不会与控件的其余部分一起滚动。问题(对我而言,不是对 Chris)在于 SB_THUMBTRACK
和 SB_THUMBPOSITION
标志被相同地处理,但它们必须分开处理。因此,需要在 CGridCtrl::OnVScroll()
函数中进行一些小的更改才能使 CWindowScroller
正常工作。
// Handle vert scrollbar notifications void CGridCtrl::OnVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/) { < snip > // pja - 9/1/2003 - changed case SB_THUMBPOSITION: // so it properly handles the CWindowScroller case SB_THUMBPOSITION: m_idTopLeftCell.row = -1; rect.top = GetFixedRowHeight(); InvalidateRect(rect); break; case SB_THUMBTRACK: { < snip >
如果您发现 CWindowScroller
有问题的其他窗口,并且您设法找到了解决方案,请告诉我,我可以在这里发布修复程序。谢谢。
历史
2003年1月10日 | 首次发布 |
2003年1月20日 | 添加了 滚动列表视图 的代码 - 感谢 Jean-Michel LE FOL |
2003年1月23日 | 修复了鼠标中键单击并保持的错误 |
2003年2月16日 | 添加了鼠标按钮抬起处理程序 |
2003年2月16日 | 清理了演示应用程序的绘图代码,以减少使用窗口滚动器时的闪烁 |
2003年3月8日 | 修复了窗口创建失败时导致程序崩溃的错误 |
2003年3月10日 | 滚动代码完全重写,现在支持所有模式下的树视图和列表视图。 添加了 GetParentWndType 函数 |
2016年5月7日 | 更新了绘图代码。现在使用分层、透明的工具窗口,而不是窗口区域。 更新代码以在 32 位和 64 位模式下使用 VS2015 进行编译。 添加了 WMR_ONSTARTWINDOWSCROLLER 通知消息。 |