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

CWindowScroller

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (37投票s)

2003 年 1 月 11 日

CPOL

5分钟阅读

viewsIcon

184169

downloadIcon

4571

一个 MFC 控件,用于将鼠标中键滚动添加到任何 CWnd 派生窗口。

Sample Image - WindowScroller.jpg

引言

CWindowScroller 是一个小的 MFC 类,用于为任何 CWnd 派生窗口添加鼠标中键滚动功能(也称为平移)。该控件类似于 Internet Explorer 和 Visual Studio .NET 中的控件。只需一行代码即可启动并运行窗口滚动器。窗口滚动器使用的所有资源(图标和光标)都是在运行时动态创建的。它们不需要添加到您的资源文件中。

使用代码

使用 CWindowScroller 类的第一步(也是最明显的一步)是将类文件(WindowScroller.hWindowScroller.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::VerticalCWindowScroller::HorizontalCWindowScroller::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 派生类。它还内置了处理 CScrollViewCTreeViewCListView 的代码。

如何向 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'sMFC 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 通知消息。
© . All rights reserved.