ListViewの要素をまとめて削除する - ListViewの要素をまとめて削除すると要素が残る - C#

ListViewの要素をまとめて削除すると要素が残ることがあります。この記事では、ListViewの要素をまとめて削除するコードを紹介します。

概要

ListViewから要素を削除する場合にはItemsプロパティのRemove()メソッドを用います。

サンプルプログラム

UI

下図のUIを作成します。ListViewとButtonを配置します。ListViewはMultiSelectプロパティをtrueにFullRowSelectプロパティをtrueに設定します。また、Viewプロパティを"Details"に設定します。

コード

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ListViewDemo
{
  public partial class FormMultiDelete : Form
  {
    public FormMultiDelete()
    {
      InitializeComponent();
    }

    private void FormMultiDelete_Load(object sender, EventArgs e)
    {
      int idx = 1;

      for (int i=0; i<100; i++){
        for (int j=0; j<10; j++){
          ListViewItem lvi = new ListViewItem();
          lvi.Text = Convert.ToString(idx);
          lvi.SubItems.Add(Convert.ToString(i));
          lvi.SubItems.Add(Guid.NewGuid().ToString());
          listView1.Items.Add(lvi);
          idx++;
        }
      }

    }

    private void button1_Click(object sender, EventArgs e)
    {
      foreach (ListViewItem item in listView1.SelectedItems) {
        listView1.Items.Remove(item);
      }
    }
  }
}

解説

LoadイベントのコードはListViewに要素を追加するコードです。10アイテムごとにカテゴリ番号を増やしています。Nameの値はGUID値を設定しています。
ボタンのClickイベントがListViewの選択された要素を削除するコードです。複数の選択にも対応しています。foreachで選択要素(ListView.SelectedItems)をループし選択されている要素を順番に取得します。ListView.Items.Removeメソッドの引数に選択されたListViewItemを与えることで、ListViewから要素を削除できます。

実行結果

実行すると下図のウィンドウが表示されます。ListViewにアイテムが表示されます。10アイテムごとにカテゴリ番号(Category)が増加しています。


Categoryの"1"を選択します。選択後、下部の[Delete]ボタンをクリックします。


Categoryの"1"の選択した要素が削除できました。

注意:要素が残ってしまう場合

以下のコードでは削除時に選択要素が削除されない場合があります。

コード

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ListViewDemo
{
  public partial class FormMultiDelete : Form
  {
    public FormMultiDelete()
    {
      InitializeComponent();
    }

    private void FormMultiDelete_Load(object sender, EventArgs e)
    {
      int idx = 1;

      for (int i=0; i<100; i++){
        for (int j=0; j<10; j++){
          ListViewItem lvi = new ListViewItem();
          lvi.Text = Convert.ToString(idx);
          lvi.SubItems.Add(Convert.ToString(i));
          lvi.SubItems.Add(Guid.NewGuid().ToString());
          listView1.Items.Add(lvi);
          idx++;
        }
      }

    }

    private void button1_Click(object sender, EventArgs e)
    {
      for (int i = 0; i < listView1.SelectedItems.Count; i++) {
        listView1.Items.Remove(listView1.SelectedItems[i]);
      }
    }
  }
}

実行結果

実行すると下図のウィンドウが表示されます。


categoryの"1"の要素を選択します。選択後[Delete]ボタンをクリックします。


要素が削除されるのですがすべての要素が削除できていません。

原因

for (int i = 0; i < listView1.SelectedItems.Count; i++) {
  listView1.Items.Remove(listView1.SelectedItems[i]);
}
のコードではi=0番目の要素を削除すると削除した瞬間にlistView1.SelectedItems.Countのカウントは1減少しi=1の要素がi=0になります。削除された要素がなくなり手前にずれる現象が起きます。次のループでi=1の要素を削除しますが、これは初期状態のi=2の要素の削除になります。削除後同様に削除された要素が抜け手前にずれるため、結果として要素を1つとばしで削除する動作になります。

対策

いくつかの対策があります。

foreachループを用いる

最初のコードで紹介したforeachループを用いるとこの問題を回避できます。選択要素をインデックス番号などで指定しないため、すべての要素を順にループできます。

削除用リストを別途用意する

削除により順番が手前にずれないよう別途削除用のリストを作成する方法があります。
private void button1_Click(object sender, EventArgs e)
{
  List<ListViewItem> selList = new List<ListViewItem>();
  for (int i = 0; i < listView1.SelectedItems.Count; i++) {
    selList.Add(listView1.SelectedItems[i]);
  }
  for (int i = 0; i <selList.Count; i++) {
    listView1.Items.Remove(selList[i]);
  }
}
最初のループで選択されている要素を削除用のリストseleListに格納します。2番目のループでseleList内の要素(ListViewItem)をListViewから削除します。

逆向きに削除する

後方から削除すると手前につまる現象を回避できるため、後方から削除する手法もあります。
private void button1_Click(object sender, EventArgs e)
{
  for (int i =listView1.SelectedItems.Count-1; i >= 0 ; i--) {
    listView1.Items.Remove(listView1.SelectedItems[i]);
  }
}

すべての要素の選択を確認する

すべての要素をスキャンして選択の有無を確認して削除します。この方法はパフォーマンス低下を招くためおすすめできません。
private void button1_Click(object sender, EventArgs e)
{
  foreach (ListViewItem listItem in listView1.Items){
    if (listItem.Selected == true) {
       listView1.Items.Remove(listItem);
    }
  }
}
または、後方から要素のチェックを確認し削除します。前方から確認すると先の手前に詰まる問題が発生します。
private void button1_Click(object sender, EventArgs e)
{
  for (int i = listView1.Items.Count - 1; i >= 0 ; i--) {
    if (listView1.Items[i].Selected == true) {
      listView1.Items.Remove(listView1.Items[i]);
    }
  }
}
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2024-01-06
作成日: 2014-03-10
iPentec all rights reserverd.