C# WPF listview Drag & Drop a Custom Item





5.00/5 (9 votes)
This article explains how to implement the drag & drop of a custom item within a ListView control with WPF technology.
Introduction
This article shows step by step how to implement drag & drop in a ListView
object of the current selected item to change the presentation sequence. Row items are defined using a custom class. It will also be shown how to move an item using two buttons.
Using the Code
Create a new project and in the XAML code editor that defines the main window, insert this code to add a couple of buttons and a ListView
object. In the ListView
object, it is important to set this property AllowDrop = "True"
, otherwise the drag & drop will not be enabled.
<Button x:Name="btnUp" Content="Move Up" HorizontalAlignment="Left" Height="28"
Margin="10,12,0,0" VerticalAlignment="Top" Width="68" Click="btnUp_Click"/>
<Button x:Name="btnDown" Content="Move Dn" HorizontalAlignment="Left" Height="28"
Margin="83,12,0,0" VerticalAlignment="Top" Width="68" Click="btnDown_Click"/>
<ListView Margin="10,50,10,10" Name="lstView" BorderBrush="WhiteSmoke"
AllowDrop="True" PreviewMouseLeftButtonDown="lstView_PreviewMouseLeftButtonDown"
MouseMove="lstView_MouseMove" DragEnter="lstView_DragEnter" Drop="lstView_Drop">
<ListView.View>
<GridView>
<GridViewColumn Header="Sel." Width="32">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Title" Width="120" DisplayMemberBinding="{Binding Title}" />
<GridViewColumn Header="Note" Width="150" DisplayMemberBinding="{Binding Note}" />
</GridView>
</ListView.View>
</ListView>
Create a new class and add the following code:
public class WorkItem
{
public bool IsSelected { get; set; }
public string Title { get; set; }
public string Note { get; set; }
public WorkItem(bool isSelected, string title, string note)
{
this.IsSelected = isSelected;
this.Title = title;
this.Note = note;
}
}
This class is the model that represents the data item of the ListView
object. To display your custom item, you must modify the properties of the WorkItem
class and the XAML code to define your data columns and the correct data binding to your custom class.
Open the code editor for the main window and add the following code in the using
section:
using System.Collections.ObjectModel; // ObservableCollection class
The list of items will be managed through the ObservableCollection
object because this exposes methods to make it easier to move items within the collection.
Add the following private
variables at the window class level.
private Point startPoint = new Point();
private ObservableCollection<WorkItem> Items = new ObservableCollection<WorkItem>();
private int startIndex = -1;
To insert test data in the ListView
control, call the InitializeListView();
function from the class constructor, for example after the call of the standard function InitializeComponent();
private void InitializeListView()
{
// Clear data
lstView.Items.Clear();
Items.Clear();
// Add rows
Items.Add(new WorkItem(true, "Row 1", "First orw"));
Items.Add(new WorkItem(false, "Row 2", "Second row"));
Items.Add(new WorkItem(true, "Row 3", "Third row"));
lstView.ItemsSource = Items;
}
Add the following code to implement the events associated with the on-screen objects. Using the btnUp
and btnDown
buttons, you can move the selected item in the ListView
control without using the drag & drop function.
private void lstView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Get current mouse position
startPoint = e.GetPosition(null);
}
// Helper to search up the VisualTree
private static T FindAnchestor<T>(DependencyObject current)
where T : DependencyObject
{
do
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
}
while (current != null);
return null;
}
private void lstView_MouseMove(object sender, MouseEventArgs e)
{
// Get the current mouse position
Point mousePos = e.GetPosition(null);
Vector diff = startPoint - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
// Get the dragged ListViewItem
ListView listView = sender as ListView;
ListViewItem listViewItem = FindAnchestor<ListViewItem>((DependencyObject)e.OriginalSource);
if (listViewItem == null) return; // Abort
// Find the data behind the ListViewItem
WorkItem item = (WorkItem)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
if (item == null) return; // Abort
// Initialize the drag & drop operation
startIndex = lstView.SelectedIndex;
DataObject dragData = new DataObject("WorkItem", item);
DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Copy | DragDropEffects.Move);
}
}
private void lstView_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("WorkItem") || sender != e.Source)
{
e.Effects = DragDropEffects.None;
}
}
private void lstView_Drop(object sender, DragEventArgs e)
{
int index = -1;
if (e.Data.GetDataPresent("WorkItem") && sender == e.Source)
{
// Get the drop ListViewItem destination
ListView listView = sender as ListView;
ListViewItem listViewItem = FindAnchestor<ListViewItem>((DependencyObject)e.OriginalSource);
if (listViewItem == null)
{
// Abort
e.Effects = DragDropEffects.None;
return;
}
// Find the data behind the ListViewItem
WorkItem item = (WorkItem)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
// Move item into observable collection
// (this will be automatically reflected to lstView.ItemsSource)
e.Effects = DragDropEffects.Move;
index = Items.IndexOf(item);
if (startIndex >=0 && index >= 0)
{
Items.Move(startIndex, index);
}
startIndex = -1; // Done!
}
}
private void btnUp_Click(object sender, RoutedEventArgs e)
{
WorkItem item = null;
int index = -1;
if (lstView.SelectedItems.Count != 1) return;
item = (WorkItem)lstView.SelectedItems[0];
index = Items.IndexOf(item);
if (index > 0)
{
Items.Move(index, index - 1);
}
}
private void btnDown_Click(object sender, RoutedEventArgs e)
{
WorkItem item = null;
int index = -1;
if (lstView.SelectedItems.Count != 1) return;
item = (WorkItem)lstView.SelectedItems[0];
index = Items.IndexOf(item);
if (index < Items.Count - 1)
{
Items.Move(index, index + 1);
}
}
That's all! Thank you for taking the time to read this article. If you found it useful, please rate it.
History
- Version 1.0.0 - 26/03/2018