简体   繁体   中英

Moving an item up and down in a WPF list box

I have a list box with a bunch of values in it. I also have an UP button and a DOWN button. With these buttons, I would like to move the selected item in the list box up/down. I am having trouble doing this.

Here is my code so far:

    private void btnDataUp_Click(object sender, RoutedEventArgs e)
    {

        int selectedIndex = listBoxDatasetValues.SelectedIndex; //get the selected item in the data list

        if (selectedIndex != -1 && selectedIndex != 0) //if the selected item is selected and not at the top of the list
        {
            //swap items here
            listBoxDatasetValues.SelectedIndex = selectedIndex - 1; //keep the item selected
        }


    }

I do not know how to swap the values! Any help would be GREATLY appreciated!

Since you have populated the listbox by binding to a ObservableCollection using ItemsSource, you cant modify the Items property of the listbox.

ItemsSource can be set only when the Items collection is empty, and Items can be modified only when ItemsSource is null.

Otherwise you will get the error "Operation is not valid while ItemsSource is in use..."

What you have to do, is modify the underlying collection, and because it's an ObservableCollection, the ListBox will reflect the changes.

The following code shows how you can move an item up and down by swapping the item in the collection.

The corresponding XAML just contains a listbox called lbItems and 2 buttons which hook up the eventhandlers.

public partial class MainWindow : Window
{
    private ObservableCollection<string> ListItems = new ObservableCollection<string>  
    { 
        "Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6"
    };

    public MainWindow()
    {
        InitializeComponent();
        lbItems.ItemsSource = this.ListItems;
    }

    private void up_click(object sender, RoutedEventArgs e)
    {
        var selectedIndex = this.lbItems.SelectedIndex;

        if (selectedIndex > 0)
        {
            var itemToMoveUp = this.ListItems[selectedIndex];
            this.ListItems.RemoveAt(selectedIndex);
            this.ListItems.Insert(selectedIndex - 1, itemToMoveUp);
            this.lbItems.SelectedIndex = selectedIndex - 1;
        }
    }

    private void down_click(object sender, RoutedEventArgs e)
    {
        var selectedIndex = this.lbItems.SelectedIndex;

        if (selectedIndex + 1 < this.ListItems.Count)
        {
            var itemToMoveDown = this.ListItems[selectedIndex];
            this.ListItems.RemoveAt(selectedIndex);
            this.ListItems.Insert(selectedIndex + 1, itemToMoveDown);
            this.lbItems.SelectedIndex = selectedIndex + 1;
        }
    }
}

I make some extension methods for this:

    public static void MoveItemUp<T>(this ObservableCollection<T> baseCollection, int selectedIndex)
    {
        //# Check if move is possible
        if (selectedIndex <= 0)
            return;

        //# Move-Item
        baseCollection.Move(selectedIndex - 1, selectedIndex);
    }

    public static void MoveItemDown<T>(this ObservableCollection<T> baseCollection, int selectedIndex)
    {
        //# Check if move is possible
        if (selectedIndex < 0 || selectedIndex + 1 >= baseCollection.Count)
            return;

        //# Move-Item
        baseCollection.Move(selectedIndex + 1, selectedIndex);
    }

    public static void MoveItemDown<T>(this ObservableCollection<T> baseCollection, T selectedItem)
    {
        //# MoveDown based on Item
        baseCollection.MoveItemDown(baseCollection.IndexOf(selectedItem));
    }

    public static void MoveItemUp<T>(this ObservableCollection<T> baseCollection, T selectedItem)
    {
        //# MoveUp based on Item
        baseCollection.MoveItemUp(baseCollection.IndexOf(selectedItem));
    }

There is no need to know the ListBox for this.

This is the easiest way to do this and it fires all the right events so you don't have to worry about XAML. ObservableCollection has a nice method called

MoveItem(previousIndex, newIndex)

Given that you have a ObservableCollection named DataItemList

public void MoveUp()
{
  var currentIndex = DataItemList.SelectedIndex;

  //Index of the selected item
  if (currentIndex > 0)
  {
    int upIndex = currentIndex - 1;

    //move the items
    DataItemList.MoveItem(upIndex,currentIndex);         
  }
}

For Down you get the index of the preceding item.

Simple as that!

I would have added a comment, but I can't since I only have 3 reputation :/

Peter Hansen's solution is great, but if there isn't a selected element, down_click throws an ArgumentOutOfRange Exception. This is because if there is no element selected, the Index is equal to -1.

I'd edit down_click like this:

private void down_click(object sender, RoutedEventArgs e)
{
    if (this.lbItems.SelectedIndex != -1) //Added condition
    {
        var selectedIndex = this.lbItems.SelectedIndex;
        if (selectedIndex + 1 < this.ListItems.Count)
        {
            var itemToMoveDown = this.ListItems[selectedIndex];
            this.ListItems.RemoveAt(selectedIndex);
            this.ListItems.Insert(selectedIndex + 1, itemToMoveDown);
            this.lbItems.SelectedIndex = selectedIndex + 1;
        }
    }
}

try this:

if (listBoxDatasetValues.SelectedItems.Count > 0)
{
    object selected = listBoxDatasetValues.SelectedItem;
    int indx = listBoxDatasetValues.Items.IndexOf(selected);
    int totl = listBoxDatasetValues.Items.Count;

    if (indx == 0)
    {
        listBoxDatasetValues.Items.Remove(selected);
        listBoxDatasetValues.Items.Insert(totl - 1, selected);
        listBoxDatasetValues.SetSelected(totl - 1, true);
    }
    else{
        listBoxDatasetValues.Items.Remove(selected);
        listBoxDatasetValues.Items.Insert(indx - 1, selected);
        listBoxDatasetValues.SetSelected(indx - 1, true);
    }
}
 if(listBoxDatasetValues.ListIndex > 0)
    {
        // add a duplicate item up in the listbox
        listBoxDatasetValues.AddItem(listBoxDatasetValues.Text, listBoxDatasetValues.ListIndex - 1);
        // make it the current item
        listBoxDatasetValues.ListIndex = (listBoxDatasetValues.ListIndex - 2);
        // delete the old occurrence of this item
        listBoxDatasetValues.RemoveItem(listBoxDatasetValues.ListIndex + 2);
    }

You may try something like this:

For moving UP:

if (listboxName.SelectedIndex == -1 || listboxName.SelectedIndex == 0)
    return;

Object select, previous, temp;
select = listboxName.Items[listboxName.SelectedIndex];
previous = listboxName.Items[listboxName.SelectedIndex-1];

temp = select;
select = previous;
previous = temp;

listboxName.Items[listboxName.SelectedIndex] = select;
listboxName.Items[listboxName.SelectedIndex-1] = previous;

listboxName.SelectedIndex--;

For moving Down:

if (listboxName.SelectedIndex == -1 || listboxName.SelectedIndex == listboxName.Items.Count-1)
    return;     

Object select, next, temp;
select = listboxName.Items[listboxName.SelectedIndex];
next = listboxName.Items[listboxName.SelectedIndex+1];

temp = select;
select = next;
next = temp;

listboxName.Items[listboxName.SelectedIndex] = select;
listboxName.Items[listboxName.SelectedIndex+1] = next;

listboxName.SelectedIndex++;

No need to use observable collection you can do it simpler just by relying on the collection object inside the ListBox, here is the optimized version of @Peter Hansen`s answer:

private void up_click(object sender, RoutedEventArgs e)
{
    var selectedIndex = this.lbItems.SelectedIndex;

    if (selectedIndex > 0)
    {
        var itemToMoveUp = this.lbItems.Items[selectedIndex];
        this.lbItems.Items.RemoveAt(selectedIndex);
        this.lbItems.Items.Insert(selectedIndex - 1, itemToMoveUp);
        this.lbItems.SelectedIndex = selectedIndex - 1;
    }
}

private void down_click(object sender, RoutedEventArgs e)
{
    var selectedIndex = this.lbItems.SelectedIndex;

    if (selectedIndex + 1 < this.lbItems.Items.Count)
    {
        var itemToMoveDown = this.lbItems.Items[selectedIndex];
        this.lbItems.Items.RemoveAt(selectedIndex);
        this.lbItems.Items.Insert(selectedIndex + 1, itemToMoveDown);
        this.lbItems.SelectedIndex = selectedIndex + 1;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM