How to preserve selection when dragging multiple items in WPF

Amelia Moseley 20 Reputation points
2023-07-17T13:23:07.18+00:00

Edit Updated the post to include more of the source code, please let me know if you have any issues, I could not copy and paste the code in so had to type it out on here so it's possible there is the odd mistake here or there. (e.g. the MouseState you mentioned before should have been MouseButtonState which is part of C# not user defined)

I have a ListView (with a GridView), with selection mode set to multiple. This is on one side of a grid, and on the other side is a TreeView. I have got it so that I can drag multiple items over from the ListView to the Tree view; using a MouseMove event on the ListView and a Drop Event on the TreeView. The Issue I am having is that if you select multiple items, then click and drag across, the item getting clicked to initiate the drag gets deselected.

I've been browsing through other related questions and documentation and I am struggling to find an answer. I have tried using separate mouseup and mousedown events, and keeping track of the selected items in a list.

Below is the code behind:

namespace TaskPlanner
{
 
	public partial class MainWindow : Window
	{
		 public class Task
		 {
			public string Headline {get; set;}
			public string TaskType {get; set;}
			public int Priority {get; set;}
		 }
			
		 public ObservableCollection<TreeViewItem> TreeViewSourceItems { get; set; }
		 public ObservableCollection<Task> ListViewSourceItems { get; set; }
	
		 public MainWindow()
		 {
			InitializeComponent();
			DataContext = this;
			
			TreeViewSourceItems = new ObservableCollection<TreeViewItem>();
			ListViewSourceItems = new ObervableCollection<Task>();
			
			/* Normally the task data comes from a database, but the data source is not a factor in this issue*/
			ListViewSourceItems.Add(new Task { Headline = "Task 1", TaskType = "Review", Priority = 1});
			ListViewSourceItems.Add(new Task { Headline = "Task 2", TaskType = "Dev", Priority = 2});
			ListViewSourceItems.Add(new Task { Headline = "Task 3", TaskType = "Write", Priority = 3});
			
			taskListView.ItemsSource = ListViewSourceItems;
			taskTreeView.ItemsSource = TreeViewSourceItems.
			
	     }
	
	
		 
		
		 private void OnMouseMove(object sender, MouseEventArgs e)
		 {
		 	if (e.LeftButton == MouseButtonState.Pressed)
		    {
		        List<Task> taskList = new List<Task>();
		        
		        foreach (Task task in taskListView.SelectedItems)
		        {
		            taskList.Add(task);
		        }
		
		        DataObject data = new DataObject(taskList);
		        DragDrop.DoDragDrop(taskListView, data, DragDropEffects.Move);
		    }
		 }
		
		 private void OnDropEvent(object sender, DragEventArgs e)
		 {
		 	Type type = typeof(List<Task>);
		    
		    List<Task> selectedItems = e.Data.GetData(type) as List<Task>;
		    
		    foreach (Task task in selectedItems)
		    {
		    	TreeViewItem treeViewItem = new TreeViewItem();
		        
		        treeViewItem.Header = task.Headline;
		        TreeViewSourceItems.Add(treeViewItem);
		        ListViewSourceItems.Remove(task);
		        
		        ICollection dataView =
		    CollectionViewSource.GetDefaultView(taskListView.ItemsSource);
		        dataView.Refresh();
		        
		        dataView = CollectionViewSource.GetDefaultView(taskTreeView.ItemsSource);
		            dataView.Refresh();
		    }
		        
		}
	
	}
}
<Window x:Class="TaskPlanner.MainWindow"
		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:d="http://schemas.microsoft.com/expressions/blend/2008"
		xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
		xmlns:local="clr-namespace:TaskPlanner"
		mc:Ignorable="d"
		Title="MainWindow" Height="450" Width="800" WindowState="Maximized">
	<Grid>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="*"/>
			<ColumnDefinition Width="5"/>
			<ColumnDefinition Width="*"/>
		</Grid.ColumnDefinitions>
		<Grid.RowDefinitions>
			<RowDefinition Height="30"/>
			<RowDefinition Height="30"/>
			<RowDefinition Height="*"/>
			<RowDefinition Height="100"/>
		</Grid.RowDefinitions>

		<StackPanel Grid.Row="0" Grid.ColumnSpan="3" Orientation="Horizontal" 					Background="LightGray">
		</StackPanel>

		<ScrollViewer Grid.Column="0" Grid.Row="1" Grid.RowSpan="2">
			<ListView x:Name="taskListView" AllowDrop="True" MouseMove="OnMouseMove" SelectionMode="Multiple">
				<ListView.View>
					<GridView>
						<GridViewColumn DisplayMemberBinding="{Binding Headline}" Header="Headline" Width="400"/>
						<GridViewColumn DisplayMemberBinding="{Binding Task}" Header="Headline" Width="80"/>
						<GridViewColumn DisplayMemberBinding="{Binding Headline}" Header="Headline" Width="80"/>
					</GridView>
				</ListView.View>
			</ListView>
		</ScrollViewer>

		<GridSplitter Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Width="5" HorizontalAlignment="Stretch"/>
		<TreeView x:Name="taskTreeView" Grid.Column="2" Grid.Row="2" AllowDrop="True" Drop="OnDropEvent"/>

		<StackPanel Grid.Row="3" Grid.ColumnSpan="3" Orientation="Horizontal" Background="LightGray">
		</StackPanel>
	</Grid>

</Window>

The drag and drop functionality mostly works it's just the issue as mentioned above is with selection/deselection when dragging items from the ListView, if you select some items and then click one of said items to initiate the drag/drop that clicked item is deselected and not included in the dragged items. The desired result is to keep it so that items can be deselected if clicked, but not deselected if that click is followed by a drag.

The target framework is .Net Framework 4.7.2, as this is the most recent version available to me on the system this work is being carried out on.

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,686 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,362 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
769 questions
{count} votes

Accepted answer
  1. Hui Liu-MSFT 41,146 Reputation points Microsoft Vendor
    2023-07-20T08:16:28.9033333+00:00

    Hi,@Amelia Moseley. To ensure that the selection is preserved during the drag-and-drop operation, you could use the following code to preserve the selection.

    The following code handles the mouse events and drag-and-drop operations for transferring items from the ListView to the TreeView.

    xaml:

    <ListView x:Name="taskListView" AllowDrop="True" PreviewMouseLeftButtonDown="OnPreviewMouseLeftButtonDown" MouseMove="OnMouseMove" SelectionMode="Multiple">
    

    codebedhind:

    
      private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                
                DependencyObject originalSource = e.OriginalSource as DependencyObject;
                ListViewItem clickedItem = FindVisualParent<ListViewItem>(originalSource);
    
                if (clickedItem != null && clickedItem.IsSelected)
                {
                   
                    clickedItem.IsSelected = false;
                }
            }
    
       private void OnMouseMove(object sender, MouseEventArgs e)
            {
                if (e.LeftButton == MouseButtonState.Pressed)
                {
                    List<Task> taskList = new List<Task>();
    
                    foreach (Task task in taskListView.SelectedItems)
                    {
                        taskList.Add(task);
                    }
    
                    DataObject data = new DataObject(taskList);
                    DragDrop.DoDragDrop(taskListView, data, DragDropEffects.Move);
                }
            }
    
            private void OnDropEvent(object sender, DragEventArgs e)
            {
                Type type = typeof(List<Task>);
    
                List<Task> selectedItems = e.Data.GetData(type) as List<Task>;
    
                foreach (Task task in selectedItems)
                {
                    TreeViewItem treeViewItem = new TreeViewItem();
    
                    treeViewItem.Header = task.Headline;
                    TreeViewSourceItems.Add(treeViewItem);
                    ListViewSourceItems.Remove(task);
    
                    ICollectionView dataView = CollectionViewSource.GetDefaultView(taskListView.ItemsSource);
                    dataView.Refresh();
    
                    dataView = CollectionViewSource.GetDefaultView(taskTreeView.ItemsSource);
                    dataView.Refresh();
                }
            }
    
            private static T FindVisualParent<T>(DependencyObject obj) where T : DependencyObject
            {
                while (obj != null)
                {
                    if (obj is T parent)
                    {
                        return parent;
                    }
                    obj = VisualTreeHelper.GetParent(obj);
                }
                return null;
            }
    
    
    

    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful