Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

CWindowScroller

0.00/5 (No votes)
7 May 2016 1  
An MFC control for adding middle mouse button scrolling to any CWnd derived window

Sample Image - WindowScroller.jpg

Introduction

CWindowScroller is a small MFC class that is used to add middle mouse button scrolling functionality (aka. panning) to any CWnd derived window. The control is similar to the one that is found in Internet Explorer and Visual Studio .NET. All that is needed is a single line of code to get the window scroller up and running. All the resources (icon and cursors) that the window scroller uses are created dynamically at run time. They do not have to be added to your resource file.

Using the code

The first (and most obvious) step to using the CWindowScroller class is to add the class files (WindowScroller.h and WindowScroller.cpp ) to your project.

In the .cpp file for the window that you want to add the scroller to, be sure to include the WindowScroller.h file

#include "WindowScroller.h"

Then, using class wizard, add the OnMButtonDown function to your window class. ( Just a small note, the message filter has to be set to "Window" )

Now, in the OnMButtonDown function, all you have to do is create an instance of the CWindowScroller class on the heap.

void CChildView::OnMButtonDown (UINT, CPoint point)
{
    new CWindowScroller (this, point);
}

Notes :

  • If you create the CWindowScroller on the stack, it will briefly flash on the screen and then be destroyed when your variable goes out of scope.
  • There is no use saving the pointer for later use, as the CWindowScroller will be destroyed, and the memory deleted as soon as your user is finished using it.

When tjhe CWindowScroller is started it sends a RWM_ONSTARTWINDOWSCROLLER registered windows message to the window that it will scroll. If the handler function returns a non-zero value then the scroller will be blocked.

When the CWindowScroller is destroyed, it posts a RWM_ONDESTROYWINDOWSCROLLER registered windows message to the window that it scrolled. See ON_REGISTERED_MESSAGE in MSDN for more information on handling registered messages.

Member Functions

The class constructor is the only public member function.

CWindowScroller::CWindowScroller (CWnd *pParent,
                                  CPoint Point,
                                  int Pixels,        // = 15
                                  UINT Elapse,       // = 30
                                  int Direction,     // = Both
                                  BOOL bUseThumbPos) // = TRUE

Parameters

pParent A pointer to the window that needs to be scrolled
Point The point, in parent window client coordinates, that will be the center of the CWindowScroller window
Pixels The distance interval, in pixels, that the mouse pointer has to be away from the center of the CWindowScroller window before the scrolling speed increases.
Elapse The time, in milliseconds, between scrolling messages. This value is passed on to the SetTimer() function
Direction The direction of the scroll. Allowable values are CWindowScroller::Vertical, CWindowScroller::Horizontal or CWindowScroller::Both
bUseThumbPos Sets the message flag to be used with WM_HSCROLL and WM_VSCROLL messages.
TRUE sets the flag to SB_THUMBPOSITION.
FALSE sets the flag to SB_THUMBTRACK.

Scrolling Speed

The scrolling speed is calculated by taking the distance, in pixels, that the mouse pointer is away from the center of the CWindowScroller window and dividing it by the value of the Pixels parameter. This will give the distance that the parent window will be scrolled every Elapse milliseconds.

Preparing your window to use the CWindowScroller

The majority of windows should work with the CWindowScroller without any problems, but some may need a little special attention, either with the CWindowScroller settings or with the way the window handles scrolling messages.

The CWindowScroller currently handles any regular CWnd derived classes by setting the scrollbar position with a call to CWnd::SetScrollPos(), and then sending either a WM_HSCROLL or WM_VSCROLL message to the window. It also has built in code for handling CScrollViews, CTreeViews, and CListViews.

How to add more supported window types to the CWindowScroller

If you get a window that the CWindowScroller does not handle because it uses some non-standard method for scrolling ( such as a list view, which uses the LVM_SCROLL message instead of WM_HSCROLL/WM_VSCROLL ) and you want the CWindowScroller to handle it, then you will have to add some code in the following places in the WindowScroller.cpp file.

First, add a new window type identifier to the WindowTypes enumeration found at the top of the file

enum WindowTypes
{
    Type_CWnd = 0,
    Type_CScrollView,
    Type_CTreeView,
    Type_CListView_Report,
    Type_CListView_List,
    Type_CListView_Icon
    // Add more types here
};

Second, add the code to the GetParentWndType() function that can use the pointer passed into the CWindowScroller constructor to identify the window type. Have that code return the identifier that you added to the WindowTypes enumeration.

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;
}

The third step is to add the actual scrolling code to the OnTimer() function. Add the code as a case within the switch (m_ParentWndType) block, using the identifier you added to the WindowTypes enumeration and returned from the GetParentWndType() function as the case to be handled.

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))
}

The final step is to write up what you did in the forum below, and I will update the article with the new code.

Even if you do all this, there may still be some windows that the CWindowScroller may still not work properly with. One such window that I had a problem with was Chris Maunder's MFC Grid control 2.24. The horizontal scrolling worked great, but the vertical scrolling had a small problem. It turned out that the fixed columns along the left hand side of the control would not scroll along with the rest of the control. The problem (for me, not for Chris) was that the SB_THUMBTRACK and SB_THUMBPOSITION flags were being handled the same, but they have to be handled seperately. So a small change was required in the CGridCtrl::OnVScroll() function to get the CWindowScroller to function properly.

// 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 >

If any of you find other windows that the CWindowScroller has problems with, and you manage to find a fix, let me know and I can post the fix here. Thanks

History

January 10, 2003 Initial release
January 20, 2003 Added code for scrolling list views - Thanks to Jean-Michel LE FOL
January 23, 2003 Fixed middle button click'n hold bug
February 16, 2003 Added mouse button up handlers
February 16, 2003 Cleaned up the demo app's drawing code in order to reduce flicker when using the window scroller
March 8, 2003 Fixed bug that crashed program if window creation fails
March 10, 2003 Total rewrite of scrolling code, now supports tree views and list views in all modes.
Added GetParentWndType function
May 7, 2016 Updated the drawing code. Now uses a layered, transparent, tool window instead of a window region.
Updated code to compile with VS2015 in both 32 and 64 bit modes.
Added the WMR_ONSTARTWINDOWSCROLLER notification message.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here