MFC ownerdrawn 列表框滚动问题

Posted

技术标签:

【中文标题】MFC ownerdrawn 列表框滚动问题【英文标题】:MFC ownerdrawn listbox scroll issue 【发布时间】:2013-09-17 09:27:55 【问题描述】:

我正在使用this logging listbox,它恰好是一个所有者绘制的列表框。我注意到,当一次滚动多行时,例如使用鼠标滚轮或单击滚动条,它会以一种奇怪的方式进行。

通常,当您使用滚轮滚动时,您可以看到整个列表平滑地上升,例如 3 行,以及从底部开始的 3 行新行。我在这个控件中看到的好像是一个新页面,从下面 3 行开始,从顶部开始,这让用户感到非常困惑。

我也尝试过this other listbox,它显示了相同的行为,所以它似乎与 ownerdrawn 相关。

关于如何解决这个问题的任何想法?

【问题讨论】:

这里也描述了这个问题:microsoft.public.vc.mfc.narkive.com/rrGhkTp3/… 【参考方案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 被定义为 CMyListBox 类的私有 int 成员。在类构造函数中初始化为零;

【讨论】:

谢谢,就像一个魅力。我添加了一些代码以考虑到用户可以将滚动的行数设置为 3 以外的值。所以我将 SCROLL_DELTA 设为非 const 并调用 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &SCROLL_DELTA, 0); 另外,如果上述调用将 SCROLL_DELTA 设置为 WHEEL_PAGESCROLL,我添加了一个函数来计算可见页面的大小并将 SCROLL_DELTA 设置为该值(减 1)。 顺便说一句,作为一个小小的奖励,我可以为您提供一小段代码,允许“鼠标滚轮”在控件上,而无需首先真正关注它们(许多现代程序都这样做,例如 MS外表)。只需在对话框的 preTranslateMessage 中添加这些行:if(pMsg-&gt;message == WM_MOUSEWHEEL &amp;&amp; GetCapture() == NULL) // send Mouse wheel message to the mouse over control pMsg-&gt;hwnd = ::WindowFromPoint(pMsg-&gt;pt); pMsg-&gt;lParam = MAKELPARAM(pMsg-&gt;pt.x, pMsg-&gt;pt.y); return CDialog::PreTranslateMessage(pMsg); 谢谢。我试试看

以上是关于MFC ownerdrawn 列表框滚动问题的主要内容,如果未能解决你的问题,请参考以下文章

MFC 组合框下拉/下拉列表:仅显示一项

如何在 MFC 中垂直同步两个列表控件

MFC编程入门之二十六(常用控件:滚动条控件ScrollBar)

VC\MFC界面怎么设置列表框第一列文字居中,其他列的文本左对齐

VS2010-MFC(常用控件:滚动条控件Scroll Bar)

如何获得MFC列表框中选中的数据