none
ListBox/ListView Selection RRS feed

  • Question

  • We are implementing drag/drop on ListBox/ListView items with the extended selection mode. The extended selection mode allows multiple items to be selection simultaneously by holding the shift or control key while selecting additional items.

     

    In Windows Explorer, when an individual item among a group of selected items is clicked, all of the other items are not deselected until the up-click event. This allows an individual to select the desired group and then click and drag this group as a whole to the desired destination.

     

    In WPF, the group appears to be deselected and the individual item selected on the down-click event. This means that in order to drag a group of items, you must hold shift, click and hold the mouse on the desired range of items and then drag the group. If the group is selected first and then the mouse released, the individually clicked item is selected when a drag attempt is made on the group.

     

    We are using WPF, but would like to see the Windows Explorer selection functionality. Any thoughts on how to reconcile these two implementations? The most straight forward fix would be a custom control, possibly derived from ListBox/ListView, but this seems like it might be reinventing the wheel. Seems like the WPF controls are not designed properly here.

     

    Thanks.

    Friday, September 21, 2007 1:25 PM

All replies

  • Late to the party, but still... I found this:

    http://www.wiredprairie.us/journal/2007/03/extended_mode_listbox_selectio.html

    Did you manage to find a solution for this multiple-selection drag-and-drop issue? I'm currently struggling with it as well.

    Koen

     

     

    Wednesday, February 20, 2008 3:57 PM
  • Koen,

    Thanks for the response and the link. Looks like the link might contain a workaround... kind of a pain, but you have to do what you have to do sometimes.

    No, we never resolved this issue. I let it drop for now, because it wasn't important enough yet in light of all of the other things we are working on to release our project.

    You can do multiple drag drop on the original list, you just have to shift-click and drag all in one motion.
    Wednesday, February 20, 2008 4:04 PM
  • I have the same problem, maybe someone in the WPF team has an answer?

     

    Tuesday, May 6, 2008 8:02 AM

  • I have the same problem... I did implement the suggestion in the wired prairie article above... but then the problem is that the listbox swallows the "double click" event... DOH!
    Wednesday, August 6, 2008 10:25 AM
  • the wired prairie article approach has one non-standard behavior; if you have multiple items selected and then left click on one of them you should end up with only the clicked item being selected (that's how Windows Explorer works) but what you actually get is the clicked item being deselected (which would be the correct behavior if the user had done a Ctrl-Left click).

    My approach was to simply ignore button down events and treat up events as if they were down events, as far as I can tell, it seems to emulate Windows Explorer behavior with no undesirable side-effects:
    public class MyListBox : ListBox  
    {  
        protected override DependencyObject GetContainerForItemOverride()  
        {  
            return new MyListBoxItem();  
        }  
    }  
     
    public class MyListBoxItem : ListBoxItem  
    {  
        protected override void OnMouseEnter( MouseEventArgs e )  
        {  
        }  
     
        protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )  
        {  
        }  
     
        protected override void OnMouseLeftButtonUp( MouseButtonEventArgs e )  
        {  
            base.OnMouseLeftButtonDown( e );  
        }  
    }  
     


    • Proposed as answer by matte303 Wednesday, August 6, 2008 5:22 PM
    Wednesday, August 6, 2008 5:22 PM
  • hey guys...can anyone help me with implementing of dragging and dropping multiple items from one listbox to another...i was able to implement for one listbox item but was struggling with mutliple items..Any help will be appreciated...Thanks in Advance..
    • Proposed as answer by RamyaN Thursday, August 28, 2008 5:27 AM
    Thursday, August 28, 2008 5:27 AM
  • I currently use the multiple selection drag and drop.
    you have to bypass the second click for drag and drop by the same as described here: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/2c8f00ca-9c7d-4237-b2cf-f60911a008a9 on another thread. Even though it is for right mouse, can be used for left mouse also.
    Also the drag and drop has to be started on the mouse-move and not mouse down or up.
    I do not have a small sample, other wise I would post it.

    Hope this helps.

    noorbakhsh
    • Proposed as answer by noorbakhsh Friday, October 10, 2008 4:08 PM
    Friday, October 10, 2008 4:07 PM
  • I modified matte303 code and this is what I came up with. I drag multiple items from one listbox to another.

     public partial class MyListBox: ListBox  
     {  
      public MyListBox()  
      {  
       InitializeComponent();  
      }  
      protected override DependencyObject GetContainerForItemOverride()  
      {  
       return new MyListBoxItem();  
      }  
     }  
     
     public partial class MyListBoxItem: ListBoxItem  
     {  
      private bool selected = false;  
     
      protected override void OnMouseEnter(MouseEventArgs e)  
      {  
      }  
     
      protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)  
      {  
       selected = false;  
       if (!IsSelected)  
       {  
        base.OnMouseLeftButtonDown(e);  
        selected = true;  
       }  
      }  
     
      protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)  
      {  
       if (!selected)  
       {  
        base.OnMouseLeftButtonDown(e);  
       }  
       selected = false;  
      }  
     }    
    • Proposed as answer by _saga_ Monday, October 13, 2008 9:43 AM
    Monday, October 13, 2008 9:42 AM
  • That seems to work for me.  The only problem is that if you click to select and don't let up on the mouse button.  Then move your mouse to force the ListBox to scroll (assumes scroll is enabled and there are enough items).  As the ListBox auto scrolls, the selection changes as well.  I'm trying to figure out how to keep the selection locked.

    If you select by clicking, then release the mouse.  Then start to drag, the auto-scroll doesn't kick in and the selection doesn't change.  I'd rather have the auto scroll work but not have the selection change.
    Saturday, December 13, 2008 4:42 AM
  • I tried to encapsulate the DragDrop functionality based on Josh's ListViewDragDropManager published on CodeProject. I also wanted to DragDrop multiple items with an explorer-like behaviour. The problem was that i cannot inherit from the Listbox/ListView because the functionality should work for "normal" Listboxes too.

    The only (very dirty) solution I found is to invoke a private method to invoke the OnMouseLeftButtonDown event on the MouseLeftButtonUp event.

    MethodInfo method = this.listView.GetType().GetMethod("NotifyListItemClicked", BindingFlags.NonPublic | BindingFlags.Instance); 
    method.Invoke(this.listView, new object[] { targetItem, MouseButton.Left }); 

    The only good thing is that it works. I also tried to use RaiseEvent method of the listview, but it didn't work.
    Sunday, February 8, 2009 5:45 PM
  • strdusty,

    Did you ever figure out a solution for "locking" the selection during a drag/drop operation?  I'm having a similar issue.  Without drag/drop, if you select an item in a ListBox and then start moving the mouse, the selection changes based on which item you're over.  If I'm doing a drag/drop, of course, I don't want the selection to change.

    For the most part, I can use mouse capturing to prevent the selection from changing.  However, there is one case that I can't nail down.  If I click at the very top of a ListBox item, that item gets selected (the pixel at the top).  I capture the mouse at that point (in PreviewMouseLeftButtonDown).  If I then move to the next item above that item, even though I have the mouse captured the selection moves to that item.  I could certainly ensure that the item I'm dragging is the item the user originally clicked on, but it would appear based on the highlight that the item above that one is what is actually being dragged.

    If I could prevent the ListBox from changing its selection as soon as I see the PreviewMouseLeftButton down, that would be ideal.

    Thanks,

    David
    Thursday, March 12, 2009 6:58 PM
  • Hello strdusty,

    I know it's been like three months and I'm not 100% sure what your mean by locking the selection, but I implemented .Stefan.'s solution and it worked fine for me, except that I had to "translate" it to VB.net and extend it a little. Maybe this (also) helps someone else who reads this thread:

    Private Sub ListView1_MouseDown(ByVal sender As Object, ByVal e As Input.MouseEventArgs) Handles ListView1.PreviewMouseDown
            'Workaround Fix 
            'Selection should change on MouseUp rather than MouseDown to allow Drag&Drop of multiple items
            '
            '(1)  Selection change occurs between PreviewMouseDown and MouseDown, therefore e.Handled is set to True in PreviewMouseDown.
            '(1a) Special case: If entry below mouse is NOT selected, selection must occur on MouseDown to allow "direct" Drag&Drop of
            '     an unselected item rather the the selected one.
            '(2)  As this would disable selection in general, the internal method for selection update has to be called in the
            '     (Preview)MouseUp-event.
    
            'Check sender, if this is a "fake" MouseDown, sender will be the window (Me-Object) (defined by me in MouseUp)
            If sender Is ListView1 Then
                'Find item below mouse
                For i = 0 To ListView1.Items.Count - 1
                    Dim li As ListViewItem = ListView1.ItemContainerGenerator.ContainerFromIndex(i)
                    Dim r As Rect = VisualTreeHelper.GetDescendantBounds(li)
                    Dim m As Point = e.GetPosition(li)
                    If r.Contains(m) Then
                        'Check if item is selected
                        If li.IsSelected Then
                            'Selected - Selection change on MouseUp (allow D&D of all selected items, including this one)
                            e.Handled = True
                        Else
                            'No - select this item before D&D, happens anyway
                        End If
                        Exit For
                    End If
                Next
            End If
        End Sub
    
        Private Sub ListView1_MouseUp(ByVal sender As Object, ByVal e As Input.MouseEventArgs) Handles ListView1.PreviewMouseUp
            'Workaround Fix 
            '(see ListView1_MouseDown for further details)
    
            Dim Method As Reflection.MethodInfo = ListView1.GetType.GetMethod("NotifyListItemClicked", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
            Dim TargetItem As ListViewItem = Nothing
    
            For i = 0 To ListView1.Items.Count - 1
                Dim li As ListViewItem = ListView1.ItemContainerGenerator.ContainerFromIndex(i)
                Dim r As Rect = VisualTreeHelper.GetDescendantBounds(li)
                Dim m As Point = e.GetPosition(li)
                If r.Contains(m) Then
                    TargetItem = li
                    Exit For
                End If
            Next
            If TargetItem Is Nothing Then Exit Sub
    
            Method.Invoke(Me.ListView1, New Object() {TargetItem, MouseButton.Left})
        End Sub
    Best regards and thanks to .Stefan.,
    Dirk

    • Proposed as answer by Dirk. _ Thursday, June 25, 2009 11:00 PM
    Thursday, June 25, 2009 10:52 PM
  • The only good thing is that it works. I also tried to use RaiseEvent method of the listview, but it didn't work.

                    BlockedEvent.Handled = false;
                    BlockedEvent.RoutedEvent = Mouse.MouseDownEvent;
                    BlockedItem.RaiseEvent(BlockedEvent);
                    BlockedEvent = null;

    BlockedEvent - is a event stored in PreviewMouseDown
    BlockedItem - is a ListboxItem that has been clicked
    See my complete solution at: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3dec4461-a753-4493-a64e-3448f5b87d22

    Friday, September 11, 2009 1:21 PM
  • Thanks... this was really helpful!

    As a way of cutting down on the amount of code I'd like to offer up the following to help determine what was clicked:

    Private Sub ListView1_MouseDown(ByVal sender As Object, ByVal e As Input.MouseEventArgs) Handles ListView1.PreviewMouseDown
    	'Workaround Fix 
    	'Selection should change on MouseUp rather than MouseDown to allow Drag&Drop of multiple items
    	'
    	'(1) Selection change occurs between PreviewMouseDown and MouseDown, therefore e.Handled is set to True in PreviewMouseDown.
    	'(1a) Special case: If entry below mouse is NOT selected, selection must occur on MouseDown to allow "direct" Drag&Drop of
    	'   an unselected item rather the the selected one.
    	'(2) As this would disable selection in general, the internal method for selection update has to be called in the
    	'   (Preview)MouseUp-event.
    
    	'Check sender, if this is a "fake" MouseDown, sender will be the window (Me-Object) (defined by me in MouseUp)
    	If sender Is ListView1 Then
    		Dim hitTestResult = VisualTreeHelper.HitTest(ListView1, Mouse.GetPosition(ListView1))
    		Dim TargetItem = GetAncestor(Of ListViewItem)(hitTestResult.VisualHit, ListView1)
    		If TargetItem IsNot Nothing Then		
    			'Check if item is selected
    			If TargetItem.IsSelected Then
    				'Selected - Selection change on MouseUp (allow D&D of all selected items, including this one)
    				e.Handled = True
    			Else
    				'No - select this item before D&D, happens anyway
    			End If
    		End If
    	End If
    End Sub
    
    Private Sub ListView1_MouseUp(ByVal sender As Object, ByVal e As Input.MouseEventArgs) Handles ListView1.PreviewMouseUp
    	'Workaround Fix 
    	'(see ListView1_MouseDown for further details)
    
    	Dim Method As Reflection.MethodInfo = ListView1.GetType.GetMethod("NotifyListItemClicked", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
    	
    	Dim hitTestResult = VisualTreeHelper.HitTest(ListView1, Mouse.GetPosition(ListView1))
    	Dim TargetItem = GetAncestor(Of ListViewItem)(hitTestResult.VisualHit, ListView1)
    	If TargetItem IsNot Nothing Then
    		Method.Invoke(Me.ListView1, New Object() {TargetItem, MouseButton.Left})
    	End If
    End Sub
    
    Public Function GetAncestor(Of T As DependencyObject)(ByVal target As DependencyObject, Optional ByVal stopAt As DependencyObject = Nothing) As T
    	Dim l_parent = VisualTreeHelper.GetParent(target)
    	If l_parent Is stopAt Then
    		Return Nothing
    	ElseIf TypeOf l_parent Is T Then
    		Return l_parent
    	Else
    		Return l_parent.GetAncestor(Of T)(stopAt:=stopAt)
    	End If
    End Function
    Thursday, July 1, 2010 7:38 PM
  • Hi,

    I have a WPF listbox and set itemsource with a list of my customclass and set DisplayMemberPath as CustomClass Property.

    My problem is when i select listbox item its selectec but when i move to another control listboxitem selection is lost even it is selected yet.

    Tell me how can i persist my listbox selected item .

    Please help me

    Thanks

    Wednesday, September 1, 2010 11:22 AM