Winforms' ListBox
appears to have a strange behavior. When I set the SelectionMode
to one, I expect that I can click an item, and it becomes selected. This holds true, but if I click an item, drag up and down the list, the selection changes.
Now, that wouldn't be too much of a big deal except that I need to perform drag and drop between some controls. So, when they select an item, and drag it down the list, a newly-selected item is actually the one it registers as dragging, and the wrong item gets sent over.
So, I then further bandage it by saving a reference to the selected item on mousedown, but it winds up as a bad user experience. My users drag an item to the other listbox, which does work, but the original listbox doesn't have the "correct" item selected anymore, and they become confused as to which item was actually dropped on the second control.
So, is there any way to change this sort of behavior? I want an item to be selected at MouseDown, ignoring the MouseUp part of things. Simply consuming the event doesn't seem to be enough, and I'd rather not have to override ListBox (we have to write documents for any new classes being created).
I guess if you call DoDragDrop
this behavior would disappear. Windows doesn't dispatch MouseOver
messages while in drag&drop mode.
Example of propper drag&drop:
private void listBox_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Get the index of the item the mouse is below.
indexOfItemUnderMouseToDrag = listBox.IndexFromPoint(e.X, e.Y);
if (indexOfItemUnderMouseToDrag != ListBox.NoMatches) {
// Remember the point where the mouse down occurred. The DragSize indicates
// the size that the mouse can move before a drag event should be started.
Size dragSize = SystemInformation.DragSize;
// Create a rectangle using the DragSize, with the mouse position being
// at the center of the rectangle.
dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width /2),
e.Y - (dragSize.Height /2)), dragSize);
} else
// Reset the rectangle if the mouse is not over an item in the ListBox.
dragBoxFromMouseDown = Rectangle.Empty;
}
private void listBox_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {
// Reset the drag rectangle when the mouse button is raised.
dragBoxFromMouseDown = Rectangle.Empty;
}
private void listBox_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left) {
// If the mouse moves outside the rectangle, start the drag.
if (dragBoxFromMouseDown != Rectangle.Empty &&
!dragBoxFromMouseDown.Contains(e.X, e.Y))
{
DragDropEffects dropEffect = listBox.DoDragDrop(listBox.Items[indexOfItemUnderMouseToDrag], DragDropEffects.All | DragDropEffects.Link);
// If the drag operation was a move then remove the item.
if (dropEffect == DragDropEffects.Move) {
listBox.Items.RemoveAt(indexOfItemUnderMouseToDrag);
// Selects the previous item in the list as long as the list has an item.
if (indexOfItemUnderMouseToDrag > 0)
listBox.SelectedIndex = indexOfItemUnderMouseToDrag -1;
else if (ListDragSource.Items.Count > 0)
// Selects the first item.
listBox.SelectedIndex =0;
}
}
}
}
}
...and SelectedIndexChanged
still works!