Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C# WPF listview Drag & Drop a Custom Item

0.00/5 (No votes)
26 Mar 2018 1  
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.

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

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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here