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


Comments

  • Found potencial problem and a better solution

    Posted by z666zz666z on 11/05/2012 07:09am

    What if you select diferent cells on each grid and then use keyboard... it does not work well... I found a better solution if scrollbars are visible (still need help if hidden)... Basically is to put scrollbar value of one as the other: // Delhi code, sorry i do not know much about C++, but i think it can be understood Object1.Perform(WM_HSCROLL,GetScrollPos(SB_THUMBPOSITION+65536*Object2.Handle,SB_HORZ),0); The problem with catch windows messages is that you must catch all, and with keyboard it is horrible... each grid can have a different selection, etc... also active (the cell with keyboard focus) does not need to be on the visual area, but when you press left, etc.. the visual area moves to show the new selected cell, but no WM_xSCROLL is fired

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds