Проблема с прокруткой списка, нарисованного владельцем MFC

Я использую этот список журналов, который нарисован владельцем. Я заметил, что при прокрутке более одной строки за раз, например, с помощью колесика мыши или щелчка по полосе прокрутки, это происходит странным образом.

Обычно, когда вы прокручиваете колесо, вы можете видеть, как весь список плавно поднимается, скажем, на 3 строки и 3 новые строки, идущие снизу. То, что я вижу с этим элементом управления, похоже на то, что новая страница, начинающаяся на 3 строки ниже, идет сверху, что довольно сбивает с толку пользователей.

Я также попробовал другой список, и он показывает такое же поведение, так что это кажется чем-то связанным с владельцем.

Любые идеи о том, как это исправить?


person MikMik    schedule 17.09.2013    source источник
comment
Эта проблема также описана здесь: microsoft.public.vc.mfc.narkive.com/rrGhkTp3/   -  person UweBaemayr    schedule 16.04.2019


Ответы (1)


Я не знаю, почему вас заминусовали. Это настоящая проблема. Это то, что я сделал, чтобы исправить это. Я создал свой собственный класс, производный от CListBox. Там я создал обработчики событий WM_TIMER и WM_MOUSEWHEEL.

В обработчике колеса мыши я указал это:

#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;
}

И это то, что я закодировал в обработчике WM_TIMER:

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);
}

Надеюсь, это поможет вам и другим. Возможно, мне придется подумать о том, чтобы разместить его на Codeproject.

Обновление: m_nStep определяется как частный int член класса CMyListBox. Инициализируется нулем в конструкторе класса;

person cha    schedule 18.09.2013
comment
Спасибо, работает как шарм. Я добавил некоторый код, чтобы учесть, что пользователь может установить количество прокручиваемых строк, отличное от 3. Поэтому я сделал SCROLL_DELTA неконстантным и вызываю SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &SCROLL_DELTA, 0); - person MikMik; 18.09.2013
comment
Кроме того, я добавил функцию для вычисления размера видимой страницы и установил для SCROLL_DELTA значение (минус 1), если приведенный выше вызов устанавливает для SCROLL_DELTA значение WHEEL_PAGESCROLL. - person MikMik; 18.09.2013
comment
Кстати, в качестве небольшого бонуса я могу предоставить вам небольшой фрагмент кода, который позволяет перемещать колесико мыши по элементам управления, не фокусируясь на них в первую очередь (многие современные программы делают это, например, MS Outlook). Просто добавьте эти строки в preTranslateMessage вашего диалога: if(pMsg->message == WM_MOUSEWHEEL && GetCapture() == NULL) { // send Mouse wheel message to the mouse over control pMsg->hwnd = ::WindowFromPoint(pMsg->pt); pMsg->lParam = MAKELPARAM(pMsg->pt.x, pMsg->pt.y); } return CDialog::PreTranslateMessage(pMsg); - person cha; 20.09.2013
comment
Спасибо. я попробую - person MikMik; 20.09.2013