Moving ListViewItems Up & Down

Try something like this:

foreach (ListViewItem lvi in sourceListView.SelectedItems)
{
    if (lvi.Index > 0)
    {
        int index = lvi.Index - 1;
        sourceListView.Items.RemoveAt(lvi.Index);
        sourceListView.Items.Insert(index, lvi);
    }
}

Basically just removes the item then inserts it above of where it used to be. The ListView automatically handles reshuffling the items down the order after an insert so no worries.

Edit: The reason the two topmost items swap is that the top item will never move (i.e I haven't implemented a wrap-around move. The 2nd item, however, is free to move and thus goes to the top of the list.

To resolve this, you can do 1 of 2 things:

  1. Implement a wrap-around reshuffle (i.e top item goes to the bottom)
  2. Prevent any movement if the top item is selected (check listview.Items[0].Selected)

As for the re-doing of the text, just do it in the original loop.

Implementation with wraparound:

foreach (ListViewItem lvi in sourceListView.SelectedItems)
{
    int index = lvi.Index > 0 ? lvi.Index - 1 : sourceListView.Items.Count - 1;
    sourceListView.Items.RemoveAt(lvi.Index);
    sourceListView.Items.Insert(index, lvi);

    if (index != sourceListView.Items.Count - 1) //not a wraparound:
    {
        //just swap the indices over.
        sourceListView.Items[index + 1].SubItems[1].Text = (index + 1).ToString();
        lvi.SubItems[1].Text = index.ToString();
    }
    else //item wrapped around, have to manually update all items.
    {
        foreach (ListViewItem lvi2 in sourceListView.Items)
            lvi2.SubItems[1].Text = lvi2.Index.ToString();
    }
}

Edit 2:

Static helper implementation, no wrap-around:

private enum MoveDirection { Up = -1, Down = 1 };

private static void MoveListViewItems(ListView sender, MoveDirection direction)
{
    int dir = (int)direction;
    int opp = dir * -1;

    bool valid = sender.SelectedItems.Count > 0 &&
                    ((direction == MoveDirection.Down && (sender.SelectedItems[sender.SelectedItems.Count - 1].Index < sender.Items.Count - 1))
                || (direction == MoveDirection.Up && (sender.SelectedItems[0].Index > 0)));

    if (valid)
    {
        foreach (ListViewItem item in sender.SelectedItems)
        {
            int index = item.Index + dir;
            sender.Items.RemoveAt(item.Index);
            sender.Items.Insert(index, item);

            sender.Items[index + opp].SubItems[1].Text = (index + opp).ToString();
            item.SubItems[1].Text = (index).ToString();
        }
    }
}

Example:

MoveListViewItems(sourceListView, MoveDirection.Up);
MoveListviewItems(sourceListview, MoveDirection.Down);

This one is with wrapping, so if you move item at index 0 down it will come to last position, and if you move last item up it will be first on list:

    public static class ListExtensions
    {
        public static void MoveUp<T>(this IList<T> list, int index)
        {
            int newPosition = ((index > 0) ? index - 1 : list.Count - 1);
            var old = list[newPosition];
            list[newPosition] = list[index];
            list[index] = old;
        }

        public static void MoveDown<T>(this IList<T> list, int index)
        {
            int newPosition = ((index + 1 < list.Count) ? index + 1 : 0);
            var old = list[newPosition];
            list[newPosition] = list[index];
            list[index] = old;
        }
    }

Just to complete the @Jason Larkes answer to make it support "move down" properly, add this right before the foreach in the MoveListViewItems function he provided:

ListViewItem[] itemsToBeMoved = sender.SelectedItems.Cast<ListViewItem>().ToArray<ListViewItem>(); 
IEnumerable<ListViewItem> itemsToBeMovedEnum;
if (direction == MoveDirection.Down)
     itemsToBeMovedEnum = itemsToBeMoved.Reverse();
else
     itemsToBeMovedEnum = itemsToBeMoved;

and then iterate using:

foreach (ListViewItem item in itemsTobemovedEnum)

instead of the original foreach.

Works like a charm. @EClaesson - I hope this overcomes the issue you wrote about in the comments.

Tags:

C#

.Net

Winforms