Pergunta

I'm using this logging listbox, which happens to be an ownerdrawn listbox. I've noticed that when scrolling more than one line at a time, such as by using the mouse wheel or clicking on the scroll bar, it does it in a strange way.

Normally, when you scroll with the wheel, you can see the whole list going up smoothly by, say, 3 lines, and 3 new lines coming from the bottom. What I'm seeing with this control is as if a new page, starting 3 rows below, were coming from the top, which is quite confusing for the users.

I've also tried this other listbox and it shows the same behaviour, so it seems something ownerdrawn-related.

Any ideas on how to fix this?

Foi útil?

Solução

I do not know why you've been downvoted. It is a real problem. This is what I did to fix it. I have created my own CListBox derived class. There, I have created the handlers for WM_TIMER and WM_MOUSEWHEEL events.

In the handler for Mouse Wheel I have specified this:

#define TIMER_SCROLLING 8
BOOL CMyListBox::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
  //return CListBox::OnMouseWheel(nFlags, zDelta, pt);

  // It turns out that a Listbox control created with the LBS_OWNERDRAWVARIABLE  style does not 
  // handle the mouse wheel correctly. The scroll effect is very jumpy; so bad in fact, that if 
  // you want to use that style, it is advisable to intercept WM_MOUSEWHEEL to either disable it 
  // or write your own handler.
  // 'return TRUE' disables the scroll effect

#if 0
  // This will scroll one item at a time.
  // uncomment these lines if this is what you are after
  if(zDelta > 0)
  {
    SetTopIndex(GetTopIndex()-1);
  }
  else
  {
    SetTopIndex(GetTopIndex()+1);
  }

  return TRUE;
#endif

  // Will use timer to scroll the items smoothly. 
  // The scrolling is done in OnTimer event
  KillTimer(TIMER_SCROLLING);
  const int SCROLL_DELTA = 3; 
  if(zDelta > 0)
  {
    // scrolling up
    m_nStep -= SCROLL_DELTA;
  }
  else
  {
    // scrolling down
    m_nStep += SCROLL_DELTA;
  }
  SetTimer(TIMER_SCROLLING, 20, NULL);

  return TRUE;
}

And this is what I have coded in WM_TIMER handler:

void CMyListBox::OnTimer(UINT_PTR nIDEvent)
{
  if(nIDEvent == TIMER_SCROLLING)
  {
    if(m_nStep < 0)
    {
      // scrolling up
      int nPos = GetTopIndex();
      if(nPos == 0)
      {
        m_nStep = 0;
        KillTimer(TIMER_SCROLLING);
      }
      else
      {
        SetTopIndex(nPos-1);
        m_nStep++;
      }
    }
    else if(m_nStep > 0)
    {
      // scrolling down
      int nPos = GetTopIndex();
      if(nPos == GetCount()-1)
      {
        m_nStep = 0;
        KillTimer(TIMER_SCROLLING);
      }
      else
      {
        SetTopIndex(nPos+1);
        m_nStep--;
      }
    }
    else
      KillTimer(TIMER_SCROLLING);
  }
  else
    CListBox::OnTimer(nIDEvent);
}

Hope, this will help you and the others. I might need to consider putting it on Codeproject

Update: m_nStep is defined as a private int member of CMyListBox class. Initialised as zero in the class constructor;

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top