OnDrawItem does not respect horizontal scroll position when called from OnPaint

183 views Asked by At

As the title says.

History: To reduce flicker in my ListBox with DrawMode.OwnerDrawFixed and a custom OnDrawItem() I used the subclassing example from this page: http://yacsharpblog.blogspot.no/2008/07/listbox-flicker.html which uses SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);

It works well except for one thing: When I scroll sideways to see long strings the OnDrawItem() does not seem to understand that it should draw with an offset. It seems like the horizontal scroll offset is lost somewhere on the way and the area OnPaint() has been called to fill in gets filled in by my OnDrawItem() as if the ListBox was not scrolled sideways at all.

Please note: If I disable ControlStyles.UserPaint and let the system call OnDrawItem() directly, it works fine and I can scroll side-ways normally. But it flickers and is too slow to be useful. I need the custom OnPaint() and the ControlStyles.OptimizedDoubleBuffer to make it smooth.

Can someone tell me where/how to get the horizontal scroll position, or what needs to be done to make this happen automatically, like when the system calls OnDrawItem?

1

There are 1 answers

0
Daniel Westerberg On

I found a solution here: http://www.codeproject.com/Articles/7554/Getting-Scroll-Events-for-a-Listbox

That is kind of a hack, since the ListBox simply doesn't expose any properties around the scrollbar, but it works. I used a simplified version where I extract the data directly from msg.WParam without calling any external dll function. OnPaint will be called immediately after anyway, so no reason messing about sending extra scroll events.

private int mHScroll;
protected override void WndProc(ref System.Windows.Forms.Message msg) {
  if (msg.Msg == WM_HSCROLL) {
    switch ((int)msg.WParam & 0xffff) {
    case SB_PAGELEFT:
      mHScroll = Math.Max(0, mHScroll - ClientSize.Width * 2 / 3); //A page is 2/3 the width.
      break;
    case SB_PAGERIGHT:
      mHScroll = Math.Min(HorizontalExtent, mHScroll + ClientSize.Width * 2 / 3);
      break;
    case SB_THUMBPOSITION:
    case SB_THUMBTRACK:
      mHScroll = ((int)msg.WParam >> 16) & 0xffff;
      break;
    }
  }
  base.WndProc(ref msg);
}

This is an internal tool, and it works for me. Solved.