In my last post, I described how to drag and drop items to
ListView control. This time I'm going to
describe the exact same technique, but this time for the more
The code below assumes you are working in a new class named
ListBox that inherits from
As it's only implementation details that are different between the two versions, I'll include the pertinent code and point out the differences but that's about it. As always a full example project is available from the link at the end of the article.
As with the previous article, you must set
ListBoxyou wish to make use of this functionality.
Just like the
ListBox control is a native
control that is drawn by the operating system and so overriding
OnPaint doesn't work. The
ListBox also has a unique
behaviour of built in owner draw support, so you have to make
sure your painting works with all modes.
Fortunately, the exact same method of painting I used with the
ListView works fine here too - that is, I capture
messages and use
Graphics.FromControl to get something I can
The only real difference is getting the boundaries of the item
to draw due to the differences in the API's of the two controls
ListViewItem.GetBounds whilst the
ListBox version is
ListBox is a flickery old beast when owner draw is being
used. Unlike the
ListView control where I just invalidate the
entire control and trust the double buffering, unfortunately
setting double buffering on the
ListBox seems to have no
effect and it flickers like crazy as you drag things around.
To help combat this, I've added a custom
that accepts the index of a single item to redraw. It also
checks if an insertion mode is set, and if so adjusts the bounds
of the rectangle to include the next/previous item (otherwise,
bits of the insertion guides will be left behind as it tries to
flicker free paint). It will then invalidate only that specific
rectangle and reduce overall flickering. It's not perfect but
it's a lot better than invalidating the whole control.
When you call
Control.Invalidateit does not trigger an immediate repaint. Instead it sends a
WM_PAINTmessage to the control to do a paint when next possible. This means multiple calls to
Invalidatewith custom rectangles will more than likely have them all combined into a single large rectangle, thus repainting more of the control that you might anticipate.
ListView control and its
ItemDrag event, the
ListBox doesn't have one. So we'll roll our own using similar
techniques to those I've described before.
As it would be somewhat confusing to the user (not to mention
rude) if we suddenly initiated drag events whenever they click
the control and their mouse wiggles during it, we check to see
if the mouse cursor has moved sufficient pixels away from the
drag origin using metrics obtained from
If the user has dragged the mouse outside this region, then we
DoDragDrop to initialize the drag and drop operation.
In exactly the same way as with the
ListView version, we can
DragOver event to determine which item the mouse is
hovered over, and from there calculate if this is a "before" or
The logic is the same, just the implementation differences in
getting the hovered item (use
ListBox.IndexFromPoint and the
item bounds). I've also added a dedicated
option this time, which is mainly so I don't unnecessarily
invalidate larger regions that I wanted as described in "Flicker
flicker flicker" above.
If the mouse leaves the confines of the control, then we use the
DragLeave event to reset the insertion status. Again no
differences per se, I set the insertion mode now, and I also
Invalidate first with the current index before resetting
When the user releases the mouse, the
DragDrop event is
raised. Here, we'll do the actual removal and re-insertion of
the source item.
Just as simple as the
An example demonstration project with an extended version of the above code is available for download from the link below.
- 2014-07-27 - First published
- 2020-11-21 - Updated formatting
Like what you're reading? Perhaps you like to buy us a coffee?