我有一个ListView(WinForms),我想通过单击按钮来上下移动项目。要移动的项目是被检查的项目。因此,如果选择第2,6和9项,当我按下按钮向上移动时,它们将变为1,5和8,并且这些位置上的项目向下移动一步。
我觉得我已经让这个变得不必要地变得复杂了,你可以在下面看到。每个ListViewItem的第二个SubItem是一个数字,代表它在列表中的位置(从1开始)。
我指责以下代码缺乏睡眠和咖啡,但如果你能找到一种更简单的方法来完成这项任务,我将感激不尽。
private void sourceMoveUpButton_Click(object sender, EventArgs e)
{
List<Int32> affectedNumbers = new List<Int32>();
bool foundNonChecked = false;
List<KeyValuePair<int, ListViewItem>> newList = new List<KeyValuePair<int, ListViewItem>>();
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, ListViewItem>(int.Parse(item.SubItems[1].Text), item));
item.Remove();
}
newList.Sort((firstPair, secondPair) =>
{
return firstPair.Key.CompareTo(secondPair.Key);
}
);
foreach (KeyValuePair<int, ListViewItem> 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();
}
它适用于移动单个项目,但是当我选择多个项目时,它不会。
答案 0 :(得分:13)
尝试这样的事情:
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
移动。但是,第二个项目可以自由移动,因此会进入列表的顶部。
要解决此问题,您可以执行以下两项操作之一:
- 实施环绕式重组(即顶部项目到底)
- 如果选择了顶部项目,请阻止任何移动(检查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();
}
}
静态帮助器实现,没有环绕:
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);
答案 1 :(得分:1)
只是为了完成@Jason Larkes的答案,使其支持“向下移动”,在foreach的前提下将它添加到他提供的MoveListViewItems函数中:
ListViewItem[] itemsToBeMoved = sender.SelectedItems.Cast<ListViewItem>().ToArray<ListViewItem>();
IEnumerable<ListViewItem> itemsToBeMovedEnum;
if (direction == MoveDirection.Down)
itemsToBeMovedEnum = itemsToBeMoved.Reverse();
else
itemsToBeMovedEnum = itemsToBeMoved;
然后使用:
进行迭代foreach (ListViewItem item in itemsTobemovedEnum)
而不是原始的foreach。
像魅力一样工作。 @EClaesson - 我希望这可以克服你在评论中写到的问题。
答案 2 :(得分:1)
这个是包装,所以如果你在第0个位置移动项目,它将会到达最后一个位置,如果你向上移动最后一个项目,它将首先在列表中:
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;
}
}
答案 3 :(得分:1)
环绕代码:
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);
}
}
答案 4 :(得分:0)
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<ListViewItem> items = new List<ListViewItem>();
// 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();
}
}
你的答案不能很好地运作。在这里,我的代码工作得很完美......
答案 5 :(得分:0)
仅限完整,基于'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();
}
}
}