Synchronization of scrolling of two list controls





Click here for larger image

Environment: Visual C++ 6.0 with Service Pack 5.

Introduction

Developing one of my projects I became interested in the idea of synchronizing of scrolling of two list controls – both lists have always had information that coincides in the first column and differs in others. That is why parallel browsing of lists would be very comfortable.

The Solution

My solution is the following.

1. Override the class CListCtrl and add three new functions in new class CListCtrlEx.


void CListCtrlEx::RedirectHScroll(UINT nSBCode,
UINT nPos,
CScrollBar* pScrollBar)
{
// Only for WinNT/2000/XP – the beginning of the code
if (nSBCode/256==SB_THUMBTRACK || (nSBCode & 0xFF)==SB_THUMBTRACK)
{
int iX = ((int)nPos – (int)GetScrollPos(SB_HORZ));
Scroll(CSize(iX, 0));
}
// Only for WinNT/2000/XP – the end of the code
CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CListCtrlEx::RedirectVScroll(UINT nSBCode,
UINT nPos,
CScrollBar* pScrollBar)
{
// Only for WinNT/2000/XP – the beginning of the code
if (nSBCode/256==SB_THUMBTRACK || (nSBCode & 0xFF)==SB_THUMBTRACK)
{
int iY = ((int)nPos – (int)GetScrollPos(SB_VERT)) * m_nItemHeight;
Scroll(CSize(0, iY));
}
// Only for WinNT/2000/XP – the end of the code
CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CListCtrlEx::RedirectKeyScroll(UINT nChar)
{
switch (nChar)
{
case VK_UP:
OnVScroll(SB_LINEUP, 0, NULL);
break;
case VK_DOWN:
OnVScroll(SB_LINEDOWN, 0, NULL);
break;
case VK_LEFT:
OnHScroll(SB_LINELEFT, 0, NULL);
break;
case VK_RIGHT:
OnHScroll(SB_LINERIGHT, 0, NULL);
break;
case VK_HOME:
OnHScroll(SB_LEFT, 0, NULL);
break;
case VK_END:
OnHScroll(SB_RIGHT,0,NULL);
break;
case VK_PRIOR:
OnVScroll(SB_PAGEUP, 0, NULL);
break;
case VK_NEXT:
OnVScroll(SB_PAGEDOWN, 0, NULL);
break;
}
}

These functions will initiate operation of OnHScroll/OnVScroll handler in another list control.

2. Add to the class CListCtrlEx three variables.


CSynchScrollView* m_pViewPos; // pointer to View aplications

This variable will be used for functions call from View application.


int m_nNumCtrl; // number of class copy – number of list control

By means of this variable we will be able to define what list control has got the
message WM_HSCROLL/WM_VSCROLL/WM_KEYDOWN.


int m_nItemHeight; // height of an item

To define an item height we use the following function:


int CListCtrlEx::GetItemHeight()
{
CRect ItemRect;
GetSubItemRect(1, 1, LVIR_BOUNDS, ItemRect);
return ItemRect.bottom – ItemRect.top;
}

3. Override in the class CListCtrlEx OnHScroll/OnVScroll/OnKeyDown handlers.


void CListCtrlEx::OnHScroll(UINT nSBCode,
UINT nPos,
CScrollBar* pScrollBar)
{
if (m_pViewPos->m_boolCheckSynchro)
{
CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
m_pViewPos->HorzSynchro(m_nNumCtrl, nSBCode, nPos, pScrollBar);
}
else
CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CListCtrlEx::OnVScroll(UINT nSBCode,
UINT nPos,
CScrollBar* pScrollBar)
{
if (nSBCode/256==SB_THUMBTRACK ||
(nSBCode & 0xFF)==SB_THUMBTRACK ||
nSBCode/256==SB_THUMBPOSITION ||
(nSBCode & 0xFF)==SB_THUMBPOSITION)
{
SCROLLINFO sinfo;
sinfo.cbSize=sizeof(sinfo);
sinfo.fMask=SIF_TRACKPOS;
::GetScrollInfo(m_hWnd, SB_VERT, &sinfo);
nPos=sinfo.nTrackPos;
}

if (m_pViewPos->m_boolCheckSynchro)
{
CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
m_pViewPos->VertSynchro(m_nNumCtrl, nSBCode, nPos, pScrollBar);
}
else
CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CListCtrlEx::OnKeyDown(UINT nChar,
UINT nRepCnt,
UINT nFlags)
{
if (m_pViewPos->m_boolCheckSynchro)
m_pViewPos->KeySynchro(m_nNumCtrl, nChar);
else
CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}

Everything is simple for OnHScroll/OnVScroll functions: if synchronization is on,
than we call synchronizer-function from View application after a standard processing.
For OnKeyDown function, if synchronization is on, standard WM_KEYDOWN handler is not used
at all – for both list controls OnHScroll/OnVScroll functions are performed.

4. In View application we define three functions, which will initiate synchronization of scrolling of list controls.

Function for horizontal synchronization.


void CSynchScrollView::HorzSynchro(int NumCtrl,
int SBCode,
int Pos,
CScrollBar* pSB)
{
switch (NumCtrl)
{
case 1:
if (SBCode == SB_THUMBTRACK)
{
m_ctrlSecondList.RedirectHScroll(SB_THUMBTRACK, Pos, NULL);
m_ctrlSecondList.RedirectHScroll(SB_THUMBPOSITION, Pos, NULL);
m_ctrlSecondList.RedirectHScroll(SB_ENDSCROLL, 0, NULL);
}
else
m_ctrlSecondList.RedirectHScroll(SBCode, Pos, pSB);
break;
case 2:
if (SBCode == SB_THUMBTRACK)
{
m_ctrlFirstList.RedirectHScroll(SB_THUMBTRACK, Pos, NULL);
m_ctrlFirstList.RedirectHScroll(SB_THUMBPOSITION, Pos, NULL);
m_ctrlFirstList.RedirectHScroll(SB_ENDSCROLL, 0, NULL);
}
else
m_ctrlFirstList.RedirectHScroll(SBCode, Pos, pSB);
break;
}
}

Function for vertical synchronization.


void CSynchScrollView::VertSynchro(int NumCtrl,
int SBCode,
int Pos,
CScrollBar* pSB)
{
switch (NumCtrl)
{
case 1:
if (SBCode == SB_THUMBTRACK)
{
m_ctrlSecondList.RedirectVScroll(SB_THUMBTRACK, Pos, NULL);
m_ctrlSecondList.RedirectVScroll(SB_THUMBPOSITION, Pos, NULL);
m_ctrlSecondList.RedirectVScroll(SB_ENDSCROLL, 0, NULL);
}
else
m_ctrlSecondList.RedirectVScroll(SBCode, Pos, pSB);
break;
case 2:
if (SBCode == SB_THUMBTRACK)
{
m_ctrlFirstList.RedirectVScroll(SB_THUMBTRACK, Pos, NULL);
m_ctrlFirstList.RedirectVScroll(SB_THUMBPOSITION, Pos, NULL);
m_ctrlFirstList.RedirectVScroll(SB_ENDSCROLL, 0, NULL);
}
else
m_ctrlFirstList.RedirectVScroll(SBCode, Pos, pSB);
break;
}
}

Function for keyboard synchronization.


void CSynchScrollView::KeySynchro(int NumCtrl,
UINT nChar)
{
switch (NumCtrl)
{
case 1:
m_ctrlSecondList.RedirectKeyScroll(nChar);
break;
case 2:
m_ctrlFirstList.RedirectKeyScroll(nChar);
break;
}
}

In these functions the number of list control that received WM_HSCROLL/WM_VSCROLL/WM_KEYDOWN
message is defined, then operation of OnHScroll/OnVScroll handler in another
list control will be initiated.

Important note – SB_THUMBTRACK processing

When scroll box is dragged and the mouse button is pressed and not released,
SB_THUMBTRACK message is generated with each change of a scroll box position.
When the mouse button is released, first the SB_THUMBPOSITION message will be generated and then – SB_ENDSCROLL message. Therefore, at each generation of the SB_THUMBTRACK message sequential processing of the SB_THUMBTRACK – SB_THUMBPOSITION – SB_ENDSCROLL messages in appropriate handlers of opposite list controls should be initiated. Such design correctly works under Win95/98/Me, but does not work under WinNT/2000 and, probably, XP – I have no opportunity to check it up. I don’t know how to explain it. May be the reason lies in some peculiarities of realization of some Win32 API functions for different versions of Windows. Solution of this problem was offered by Jonathan Liu and I am truly grateful to him. While processing of SB_THUMBTRACK in RedirectHScroll/RedirectVScroll functions
Scroll (CSize (X, Y)) is called for direct scrolling of contents of list controls. I repeat one more time, that there is no need in it, if Win95/98/Me is used.

Look for details of realization on enclosed demo project. I believe that this solution will be useful for somebody.
Alexander Khudyakov.

Downloads

Download demo project – 19 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read