10

ボタンをクリックしてアイテムを上下に移動したい ListView (WinForms) があります。移動する項目は、チェックされている項目です。アイテム 2、6、9 が選択されている場合、上に移動するボタンを押すと 1、5、8 になり、それらの場所にあったアイテムが 1 ステップ下に移動します。

以下に示すように、これを不必要に複雑にしたと感じています。各 ListViewItem の 2 番目の 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++;
        }

しかし今、一番上の 2 つのアイテム (または同様のもの) を選択すると、上に移動するボタンをクリックすると、それらの場所が切り替わります。

2番目の編集
次のようにすると、上向きの動きに対してすべて正常に機能します。

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

単一のアイテムを下に移動する場合は機能しますが、複数を選択すると機能しません。

4

6 に答える 6

15

次のようなものを試してください。

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は、挿入後にアイテムを順番に並べ替えることを自動的に処理するため、心配する必要はありません。

編集: 一番上の2つのアイテムが入れ替わる理由は、一番上のアイテムが移動しないためです(つまり、wrap-around移動を実装していません。ただし、2番目のアイテムは自由に移動できるため、リストの一番上に移動します。

これを解決するには、次の2つのいずれかを実行できます。

  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);
于 2012-07-24T04:05:49.430 に答える
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);
        }
    }
于 2015-12-30T01:55:56.843 に答える
1

@Jason Larkes の回答を完成させて「下に移動」を適切にサポートするには、彼が提供した MoveListViewItems 関数の foreach の直前にこれを追加します。

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 - これにより、コメントで書いた問題が解決されることを願っています。

于 2013-05-03T10:27:40.150 に答える
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();
        }            
    }

あなたの答えはうまくいきません。ここで私のコードは完璧に機能します...

于 2014-03-07T04:03:32.120 に答える
0

'Jason Larke' 静的ヘルパー ソリューションに基づく完全な場合のみ:

そのソリューションは隣接するアイテムを移動しません。これは Stack を使用して実行できます。

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();
    }
}
}
于 2016-04-21T11:35:30.287 に答える