在 WPF 列表框中上下移动项目

10

我有一个列表框,里面有很多值。我也有一个向上按钮和一个向下按钮。 使用这些按钮,我想将选定的项目在列表框中向上/向下移动。但是我无法做到。

以下是我的代码:

    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
        }


    }

我不知道如何交换这些值!非常感谢任何帮助!

8个回答

26

由于您使用ItemsSource将ObservableCollection绑定到列表框,因此无法修改列表框的Items属性。

只有在Items集合为空时才能设置ItemsSource,而只有在ItemsSource为null时才能修改Items。

否则,您将收到“在使用ItemsSource时无效的操作…”的错误。

您需要做的是修改基础集合,因为它是一个ObservableCollection,所以ListBox将反映这些更改。

以下代码显示了如何通过交换集合中的项目来将项目上移和下移。

相应的XAML仅包含名为lbItems的列表框和两个按钮,这些按钮连接事件处理程序。

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;
        }
    }
}

10

我为此编写了一些扩展方法:

    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));
    }

这并不需要了解 ListBox。


5
这是最简单的方法,它会触发所有正确的事件,因此您不必担心XAML。ObservableCollection有一个很好的方法叫做:
MoveItem(previousIndex, newIndex)

鉴于您有一个名为DataItemList的ObservableCollection
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);         
  }
}

对于Down,您将获得前一项的索引。
就这么简单!

抱歉,我无法看到ObservableCollection的SelectedIndex方法。它在当前版本NET Framework 4.8以及之前的版本中都不存在。请参见此处:https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=netframework-4.8 - Willy

3

我本想添加评论,但由于我的声望值只有3,无法添加 :(

Peter Hansen的解决方案很好,但如果没有选定元素,down_click会抛出ArgumentOutOfRange异常。这是因为如果没有选定元素,则索引等于-1。

我会像这样编辑down_click:

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;
        }
    }
}

1

试试这个:

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);
    }
}

这会抛出相同的错误:在使用ItemsSource时,操作无效。请使用ItemsControl.ItemsSource访问和修改元素。 在此行:else{ listBoxDatasetValues.Items.Remove(selected); - DommyCastles
抱歉,我现在有点困了...我能想到的只有两种方法,一种是复制该项并将其添加到列表中,然后删除“旧”项,另一种是寻找支持移动项目的用户控件列表框 :) - HasanAboShally
好的,谢谢你的帮助!你能教我如何复制并添加它们,然后删除旧的吗?拜托了? - DommyCastles

1
 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);
    }

不,它指出AddItemListIndexRemoveItem是无效的。:( - DommyCastles
抱歉兄弟,使用 listBoxDatasetValues.Items.Add()listBoxDatasetValues.SelectedIndexlistBoxDatasetValues.Items.Remove() - HasanAboShally
那么 listBoxDatasetValues.Text 呢?它说明方法 Add 只需要一个参数。 - DommyCastles
我已经尝试过这种方法,但它仍然给我相同的错误信息:“在使用ItemsSource时,操作无效。请使用ItemsControl.ItemsSource来访问和修改元素。” :( - DommyCastles
嗯,这就是我能提供的帮助了,抱歉 :) 你可以尝试使用弯曲或者使用另一个控件,比如数据网格,或者构建自己的控件(例如基于流式布局面板)...祝你好运 ^_^ - HasanAboShally

0
你可以尝试这样做:
向上移动:
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--;

向下移动:

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++;

谢谢您的快速回复!当我尝试您的示例时,我遇到了以下错误:Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.有什么建议吗?我在我的列表中使用绑定,这会有问题吗? - DommyCastles
似乎由于绑定,ItemsSource正在使用中。 您在哪一行遇到了错误? - HasanAboShally
我在这一行代码上遇到了错误:listBoxDatasetValues.Items[listBoxDatasetValues.SelectedIndex] = select; - DommyCastles
在我的XAML中,我的ListBoxItemSourceItemsSource="{Binding Source={StaticResource datasetWizardViewModel}, Path=ListBoxDataTypes}",这是它获取数据以填充ListBox的地方。 - DommyCastles

0

不需要使用ObservableCollection,你可以更简单地依赖于ListBox内部的集合对象来完成,这里是@Peter Hansen答案的优化版本:

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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接