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, UINT Elapse, int Direction, BOOL bUseThumbPos)
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 CScrollView
s, CTreeView
s, and CListView
s.
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
};
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;
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;
default:
TRACE (_T("CWindowScroller::OnTimer - Unknown window type"));
break;
} } } }
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.
void CGridCtrl::OnVScroll(UINT nSBCode,
UINT ,
CScrollBar* )
{
< snip >
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. |