向上和向下移动ListViewItems

我有一个ListView(WinForms),我想通过单击按钮上下移动项目。 要移动的项目是被检查的项目。 因此,如果选择第2,6和9项,当我按下按钮向上移动时,它们将变为1,5和8,并且这些位置上的项目向下移动一步。

我觉得我已经让这个变得不必要地变得复杂了,你可以在下面看到。 每个ListViewItem的第二个SubItem是一个数字,代表它在列表中的位置(从1开始)。

我责怪以下代码缺乏睡眠和咖啡,但如果你能想出一个更简单的方法来完成这个任务,我将感激不尽。

private void sourceMoveUpButton_Click(object sender, EventArgs e) { List affectedNumbers = new List(); bool foundNonChecked = false; List<KeyValuePair> newList = new List<KeyValuePair>(); foreach (ListViewItem item in this.sourceListView.CheckedItems) { int newNum = int.Parse(item.SubItems[1].Text) - 1; if (newNum >= 1) { foreach (ListViewItem testItem in this.sourceListView.Items) { if (int.Parse(testItem.SubItems[1].Text) == newNum && !testItem.Checked) { foundNonChecked = true; } } if (foundNonChecked) { item.SubItems[1].Text = newNum.ToString(); affectedNumbers.Add(newNum); } } } foreach (ListViewItem item in this.sourceListView.Items) { int num = int.Parse(item.SubItems[1].Text); if (affectedNumbers.Contains(num) && !item.Checked) { item.SubItems[1].Text = (num + affectedNumbers.Count).ToString(); } newList.Add(new KeyValuePair(int.Parse(item.SubItems[1].Text), item)); item.Remove(); } newList.Sort((firstPair, secondPair) => { return firstPair.Key.CompareTo(secondPair.Key); } ); foreach (KeyValuePair pair in newList) { this.sourceListView.Items.Add(pair.Value); } } 

编辑我已将其缩短为以下内容:

 foreach (ListViewItem item in this.sourceListView.CheckedItems) { if (item.Index > 0) { int newIndex = item.Index - 1; this.sourceListView.Items.RemoveAt(item.Index); this.sourceListView.Items.Insert(newIndex, item); } } int index = 1; foreach (ListViewItem item in this.sourceListView.Items) { item.SubItems[1].Text = index.ToString(); index++; } 

但现在,如果我选择两个最顶层的项目(或类似的),当我点击按钮向上移动时,它们将切换到位。

第二次编辑所有内容都适用于向上移动,具体如下:

 if (this.sourceListView.CheckedItems[0].Index != 0) { this.sourceListView.BeginUpdate(); foreach (ListViewItem item in this.sourceListView.CheckedItems) { if (item.Index > 0) { int newIndex = item.Index - 1; this.sourceListView.Items.RemoveAt(item.Index); this.sourceListView.Items.Insert(newIndex, item); } } this.updateListIndexText(); this.sourceListView.EndUpdate(); } 

但对于向下运动,我似乎无法做到正确:

 if (this.sourceListView.CheckedItems[this.sourceListView.CheckedItems.Count - 1].Index < this.sourceListView.Items.Count - 1) { this.sourceListView.BeginUpdate(); foreach (ListViewItem item in this.sourceListView.CheckedItems) { if (item.Index < this.sourceListView.Items.Count - 1) { int newIndex = item.Index + 1; this.sourceListView.Items.RemoveAt(item.Index); this.sourceListView.Items.Insert(newIndex, item); } } this.updateListIndexText(); this.sourceListView.EndUpdate(); } 

它适用于移动单个项目,但是当我选择多个项目时,它不会。

尝试这样的事情:

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

基本上只是移除项目然后将其插入过去的位置。 ListView会在插入后自动处理重新排列顺序中的项目,因此不必担心。

编辑:两个最顶层的项目交换的原因是顶部项目永远不会移动(即我没有实现wrap-around移动。但是,第二个项目可以自由移动,因此到达列表的顶部。

要解决此问题,您可以执行以下两项操作之一:

  1. 实施环绕式重组(即顶部项目到底)
  2. 如果选择了顶部项目,则阻止任何移动(检查listview.Items [0] .Selected)

至于文本的重做,只需在原始循环中执行。

环绕式实施:

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

编辑2:

静态助手实现,没有环绕:

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

例:

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

只是为了完成@Jason Larkes的答案,使其支持“向下移动”,在foreach的前提下将它添加到他提供的MoveListViewItems函数中:

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

然后迭代使用:

 foreach (ListViewItem item in itemsTobemovedEnum) 

而不是原来的foreach。

奇迹般有效。 @EClaesson – 我希望这可以克服你在评论中写的问题。

这是一个包装,所以如果你在索引0处向下移动项目它将到达最后一个位置,如果你向上移动最后一个项目它将在列表中首先:

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

包装代码:

  private enum MoveDirection { Up = -1, Down = 1 }; private void MoveListViewItems(ListView sourceListView, MoveDirection direction) { int dir = (int)direction; foreach (ListViewItem lvi in sourceListView.SelectedItems) { int index = lvi.Index + dir; if(index >= sourceListView.Items.Count) index = 0; else if(index < 0) index = sourceListView.Items.Count + dir; sourceListView.Items.RemoveAt(lvi.Index); sourceListView.Items.Insert(index, lvi); } } 
  private void MoveItems(ListView sender, MoveDirection direction) { 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) { bool start = true; int first_idx = 0; List items = new List(); // ambil data foreach (ListViewItem i in sender.SelectedItems) { if (start) { first_idx = i.Index; start = false; } items.Add(i); } sender.BeginUpdate(); // hapus foreach (ListViewItem i in sender.SelectedItems) i.Remove(); // insert if (direction == MoveDirection.Up) { int insert_to = first_idx - 1; foreach (ListViewItem i in items) { sender.Items.Insert(insert_to, i); insert_to++; } } else { int insert_to = first_idx + 1; foreach (ListViewItem i in items) { sender.Items.Insert(insert_to, i); insert_to++; } } sender.EndUpdate(); } } 

你的答案不能很好地运作。 在这里我的代码完美…

只有完整的,基于’Jason Larke’的静态助手解决方案:

该解决方案不会移动相邻的项目,可以使用堆栈来完成:

 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) { Stack aPila = new Stack(); ListViewItem item = default(ListViewItem); foreach ( item in sender.SelectedItems) { aPila.Push(item); } for (int iaux = 1; iaux <= aPila.Count; iaux++) { { item = (ListViewItem)aPila.Pop(); 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(); } } }