Click here to Skip to main content
15,881,172 members
Articles / Desktop Programming / WPF

C# WPF listview Drag & Drop a Custom Item

Rate me:
Please Sign up or sign in to vote.
5.00/5 (10 votes)
26 Mar 2018CPOL2 min read 38.5K   16   3
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.

Pic.1 - "Row 3" has been moved from last position to the first position

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.

XML
<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>
Code 1 - This XAML code must be inserted between the <Grid></Grid> data block.

Create a new class and add the following code:

C#
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:

C#
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.

C#
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();

C#
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.

C#
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Palladio Software House s.r.l.
Italy Italy
My main experience as software developer in short

From 2014-today : WPF C# developing of desktop application and WPF controls (NPoco ORM). Web developer using C# ASP.NET MVC5 and Entity Framework
17/03/2008-2014 : WinForm C# 2005-2008 developer and Web developer (HTML, PHP, MySQL), QlikView Business Intelligence Developer
14/03/2001-15/03/2008 : WinForm VisualBasic 6.0, C#2003, C# 2005 developer and finally, in the 2007-8, also web developer (HTML, PHP, MySQL)
01/11/1992-13/03/2001 : MS-DOS Turbo Pascal 7.0 (OOP), Quick Basic 4.5 developer and WinForm Visual Basic 3.0 / 5.0 developer.
----------------------------

Comments and Discussions

 
GeneralMy vote of 5 Pin
AMMaui22-May-23 18:52
AMMaui22-May-23 18:52 
QuestionConverting to VB.Net Pin
Member 1571849126-Jul-22 10:37
Member 1571849126-Jul-22 10:37 
Hey I'm trying to use this in some VB code. I was able to rewrite most of the code but I'm having issues with two aspects.

1.When I call FindAnchestor, I get an error that ListViewItem does not inherit or implement DependencyObject. My reference for ListViewItem is in System.Windows.Forms, is that correct?

2. Similar issue, ListView does not have an attribute ItemContainerGenerator. I also have it defined in System.Windows.Forms.

I've highlighted the offending lines in bold. Thank you in advance!

VB
Private Sub ListView_MouseMove(sender As Object, e As MouseEventArgs)
     Dim mousePosition As Point = e.GetPosition(Nothing)
     Dim difference As Vector = startPoint - mousePosition

     Dim isInWindow = (Math.Abs(difference.X) > SystemParameters.MinimumHorizontalDragDistance Or Math.Abs(difference.Y) > SystemParameters.MinimumVerticalDragDistance)
     If e.LeftButton = MouseButtonState.Pressed And isInWindow Then
         Dim listView As ListView = DirectCast(sender, ListView)
         Dim listViewItem As ListViewItem = FindAncestor(Of ListViewItem)(CType(e.OriginalSource, DependencyObject))>
         If listViewItem Is Nothing Then Exit Sub

         Dim item As PositionListViewItem = CType(listView.ItemContainerGenerator.ItemFromContainer(listViewItem), PositionListViewItem)
         If item Is Nothing Then Exit Sub

         startIndex = PositionsListView.SelectedIndex
         Dim dragData As DataObject = New DataObject("WorkItem", item)
         DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Copy Or DragDropEffects.Move)
     End If
 End Sub

GeneralMy vote of 5 Pin
James Box1-May-19 6:20
James Box1-May-19 6:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.