65.9K
CodeProject is changing. Read more.
Home

Synchronization of scrolling between two list controls

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (10 votes)

Dec 26, 2001

2 min read

viewsIcon

109875

downloadIcon

1204

This simple example shows the synchronization of scrolling of two list controls

Sample Image - SynchScroll.gif

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 the scroll box is dragged and the mouse button is pressed and not released, a  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.

The solution of this problem was offered by Jonathan Liu and I am truly grateful to him. While  SB_THUMBTRACK in the RedirectHScroll/RedirectVScroll functions Scroll (CSize (X, Y)) is called for direct scrolling of contents of the list controls. I repeat one more time: 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.