C# ListViewの使い方完全ガイド|項目追加・削除・選択行取得・列幅調整までサンプルコードで解説
ListViewは、Windows Formsアプリケーションで一覧データを表示するときに便利なコントロールです。エクスプローラーのファイル一覧のような表示や、複数の列を持つ表形式の表示を作成できます。
一方で、ListViewを初めて使うと、次のような点で迷いやすいでしょう。
項目を追加したのに画面へ表示されない
2列目以降のデータを追加する方法が分からない
選択されていない状態でエラーが発生する
複数選択された行を正しく削除できない
列幅を内容に合わせて自動調整したい
Windows FormsとWPFのコードを混同してしまう
本記事では、Windows FormsのListViewを中心に、基本設定から項目の追加・削除、選択行の取得、列幅調整、大量データの表示までをサンプルコード付きで解説します。
1. C#のListViewとは?できることと本記事の対象範囲
1-1. ListViewで一覧データを表示する仕組み
Windows FormsのListViewは、複数の項目を一覧形式で表示するためのコントロールです。
ListViewでは、主に次の要素を組み合わせて一覧を作成します。
ListView:一覧全体を表すコントロールColumnHeader:列のヘッダーListViewItem:1行分のデータListViewSubItem:2列目以降のセルに相当するデータ
たとえば、次のようなユーザー一覧を表示するとします。
| ID | 名前 | メールアドレス |
|---|---|---|
| 1 | 山田太郎 | yamada@example.com |
| 2 | 佐藤花子 | sato@example.com |
この場合、1行目の「1」がListViewItemのTextプロパティに入り、「山田太郎」と「yamada@example.com」がSubItemsに入ります。
ListViewItem item = new ListViewItem("1");item.SubItems.Add("山田太郎");item.SubItems.Add("yamada@example.com");listView1.Items.Add(item);
ListViewでは、1列目だけが特別扱いされます。1列目の値はListViewItem.Text、2列目以降はListViewItem.SubItemsから操作するのが基本です。
1-2. ListBox・DataGridViewとの違いと使い分け
C#で一覧データを表示するコントロールには、ListView以外にもListBoxやDataGridViewがあります。
ListBoxは、基本的に1列の単純な一覧を表示するときに適しています。都道府県、商品名、カテゴリ名など、1つの文字列を並べる用途であればListBoxのほうが簡単です。
ListViewは、アイコン付き一覧や複数列の一覧を表示したい場合に向いています。ファイル一覧、処理結果、ログ、簡易的なマスター一覧などに適しています。
DataGridViewは、セル単位の編集、データバインディング、並べ替え、行追加、入力検証などが必要な本格的な表形式UIに向いています。DataGridViewはデータソースへ直接バインドでき、セル・行・列を細かく制御できるコントロールです。
目安として、次のように使い分けるとよいでしょう。
| 要件 | 適したコントロール |
| 1列の選択肢を表示する | ListBox |
| アイコン付き一覧を表示する | ListView |
| 複数列の読み取り専用一覧を表示する | ListView |
| セルを直接編集する | DataGridView |
| データベースの結果をバインドする | DataGridView |
| エクスプローラー風の画面を作る | ListView |
1-3. Windows Forms版とWPF版ListViewの違い
C#には、Windows Forms版とWPF版のListViewがあります。名前は同じですが、名前空間、設定方法、項目の追加方法、列の定義方法が異なります。
Windows Forms版の名前空間は次のとおりです。
using System.Windows.Forms;WPF版の名前空間は次のとおりです。
using System.Windows.Controls;Windows Formsでは、View、Columns、Items、SubItemsなどを使用します。
listView1.View = View.Details;listView1.Columns.Add("名前", 150);listView1.Items.Add(new ListViewItem("山田太郎"));WPFでは、複数列表示にGridViewを使用し、通常はItemsSourceへコレクションを設定します。
<ListView ItemsSource="{Binding Users}"><ListView.View><GridView><GridViewColumn Header="名前"DisplayMemberBinding="{Binding Name}" /><GridViewColumn Header="メールアドレス"DisplayMemberBinding="{Binding Email}" /></GridView></ListView.View></ListView>本記事で解説するコードは、特に断りがない限りWindows Forms用です。WPFのListViewへそのまま貼り付けても動作しないため注意してください。
1-4. 本記事で作成するListViewサンプルの完成イメージ
本記事の完成サンプルでは、次の3列を持つユーザー一覧を作成します。
ID
名前
メールアドレス
画面には、次のコントロールを配置します。
名前を入力するTextBox
メールアドレスを入力するTextBox
行を追加するボタン
選択行を取得するボタン
選択行を削除するボタン
列幅を自動調整するボタン
ユーザー一覧を表示するListView
追加ボタンを押すと入力内容が新しい行として追加され、取得ボタンを押すと選択中の行をメッセージボックスへ表示します。
2. ListViewを使うための準備と基本設定
2-1. Visual StudioでWindows Formsプロジェクトを作成する
Visual StudioでWindows Formsプロジェクトを作成する基本的な流れは次のとおりです。
Visual Studioを起動する
「新しいプロジェクトの作成」を選択する
「Windows フォーム アプリ」を選択する
言語にC#を選択する
プロジェクト名を入力する
使用する.NETのバージョンを選択する
プロジェクトを作成する
.NET Framework版と現在の.NET版では、プロジェクトテンプレートやプロジェクトファイルの形式が異なります。ただし、本記事で紹介する基本的なListViewの操作方法はほぼ共通です。
2-2. フォームにListViewと操作ボタンを配置する
Visual Studioのツールボックスから、フォームへ次のコントロールを配置します。
| コントロール | Nameプロパティ | 用途 |
| ListView | listViewUsers | ユーザー一覧 |
| TextBox | textBoxName | 名前入力 |
| TextBox | textBoxEmail | メール入力 |
| Button | buttonAdd | 行追加 |
| Button | buttonGet | 選択行取得 |
| Button | buttonDelete | 選択行削除 |
| Button | buttonResize | 列幅調整 |
ボタンのTextプロパティは、それぞれ「追加」「取得」「削除」「列幅調整」などに設定します。
ListViewをフォームのサイズに合わせて伸縮させたい場合は、Anchorプロパティを次のように設定します。
listViewUsers.Anchor =AnchorStyles.Top |AnchorStyles.Bottom |AnchorStyles.Left |AnchorStyles.Right;フォーム全体へListViewを配置する場合は、DockプロパティをFillにしても構いません。
listViewUsers.Dock = DockStyle.Fill;2-3. ViewプロパティをDetailsに設定して表形式で表示する
ListViewを複数列の表形式で表示するには、ViewプロパティをView.Detailsに設定します。
listViewUsers.View = View.Details;Viewプロパティには、次のような表示形式があります。
View.LargeIconView.DetailsView.SmallIconView.ListView.Tile列を持つ表形式で表示したい場合は、View.Detailsが必要です。
列を追加したのにヘッダーが表示されない場合や、SubItemsへ値を追加したのに2列目以降が表示されない場合は、最初にViewプロパティを確認してください。
2-4. FullRowSelectで行全体を選択できるようにする
Details表示では、初期設定のままだと1列目の文字部分だけが選択されたように見えることがあります。
行全体を選択状態として表示するには、FullRowSelectプロパティをtrueにします。
listViewUsers.FullRowSelect = true;複数列を持つ一覧では、基本的にtrueへ設定するのがおすすめです。
2-5. GridLinesで行と列の区切り線を表示する
GridLinesプロパティをtrueにすると、行と列の境界線が表示されます。
listViewUsers.GridLines = true;表形式でデータを確認する業務アプリでは、境界線を表示したほうが各値の対応関係を確認しやすくなります。
一方、エクスプローラーのような見た目にしたい場合は、falseのまま使用することもあります。
2-6. MultiSelectで単一選択・複数選択を切り替える
MultiSelectプロパティは、複数の行を同時に選択できるかどうかを設定します。
単一選択にする場合は、次のように設定します。
listViewUsers.MultiSelect = false;複数選択を許可する場合は、次のようにします。
listViewUsers.MultiSelect = true;複数選択を有効にすると、CtrlキーやShiftキーを使って複数の行を選択できます。
1件のデータを編集する画面では単一選択、一括削除や一括処理を行う画面では複数選択が適しています。
2-7. HeaderStyleで列ヘッダーの表示方法を設定する
HeaderStyleプロパティでは、列ヘッダーの表示方法を設定できます。
listViewUsers.HeaderStyle = ColumnHeaderStyle.Clickable;主な設定値は次のとおりです。
| 設定値 | 動作 |
| Clickable | ヘッダーを表示し、クリックできる |
| Nonclickable | ヘッダーを表示するがクリック操作を受け付けない |
| None | ヘッダーを表示しない |
列ヘッダーをクリックして並べ替える機能を実装する場合は、Clickableを使用します。
listViewUsers.HeaderStyle = ColumnHeaderStyle.Clickable;listViewUsers.ColumnClick += ListViewUsers_ColumnClick;3. ListViewに列を追加する方法
3-1. デザイナーから列を追加する
Visual Studioのデザイナーから列を追加する手順は次のとおりです。
フォーム上のListViewを選択する
プロパティウィンドウのColumnsを選択する
右側の「…」ボタンをクリックする
ColumnHeaderコレクションエディターで列を追加する
Text、Name、Width、TextAlignを設定する
たとえば、次の3列を追加します。
| Name | Text | Width |
| columnId | ID | 60 |
| columnName | 名前 | 150 |
| columnEmail | メールアドレス | 240 |
デザイナーから追加した列は、InitializeComponent内で初期化されます。
3-2. Columns.Addで列名と列幅を指定する
コードから列を追加するには、Columns.Addメソッドを使用します。
listViewUsers.Columns.Add("ID", 60);listViewUsers.Columns.Add("名前", 150);listViewUsers.Columns.Add("メールアドレス", 240);第1引数は列ヘッダーの文字列、第2引数は列幅です。
文字揃えも同時に指定できます。
listViewUsers.Columns.Add("ID", 60, HorizontalAlignment.Right);listViewUsers.Columns.Add("名前", 150, HorizontalAlignment.Left);listViewUsers.Columns.Add("メールアドレス",240,HorizontalAlignment.Left);すでにデザイナーで列を追加している場合、コードから同じ列を追加すると重複します。列をデザイナーで管理するか、コードで管理するかを統一すると分かりやすくなります。
3-3. 複数の列をまとめて追加する
複数のColumnHeaderを作成し、AddRangeメソッドでまとめて追加できます。
ColumnHeader columnId = new ColumnHeader{Text = "ID",Width = 60,TextAlign = HorizontalAlignment.Right};ColumnHeader columnName = new ColumnHeader{Text = "名前",Width = 150,TextAlign = HorizontalAlignment.Left};
ColumnHeader columnEmail = new ColumnHeader{Text = "メールアドレス",Width = 240,TextAlign = HorizontalAlignment.Left};
listViewUsers.Columns.AddRange(new[]{columnId,columnName,columnEmail});
列ごとの設定が多い場合は、AddRangeを使うと初期化処理を整理できます。
3-4. 列の文字揃えを左・中央・右に変更する
列の文字揃えは、ColumnHeaderのTextAlignプロパティで設定します。
listViewUsers.Columns[0].TextAlign = HorizontalAlignment.Left;listViewUsers.Columns[1].TextAlign = HorizontalAlignment.Center;listViewUsers.Columns[2].TextAlign = HorizontalAlignment.Right;指定できる値は次の3つです。
HorizontalAlignment.LeftHorizontalAlignment.CenterHorizontalAlignment.Rightただし、WindowsのListViewでは第1列の配置に制約があり、右揃えや中央揃えを指定しても期待どおりにならない場合があります。
数値列を右揃えにしたい場合は、IDや金額を2列目以降に配置する方法も検討してください。
3-5. 列が表示されないときに確認する設定
列を追加しても表示されない場合は、次の点を確認します。
まず、ViewプロパティがDetailsになっていることを確認します。
listViewUsers.View = View.Details;次に、列幅が0になっていないことを確認します。
listViewUsers.Columns[0].Width = 100;また、列を追加する処理が実行されているか確認します。
private void Form1_Load(object sender, EventArgs e){listViewUsers.Columns.Add("名前", 150);}デザイナーとコードの両方で列を初期化している場合は、意図せず列が重複していないかも確認してください。
4. ListViewに項目・行を追加する方法
4-1. Items.Addで1列だけの項目を追加する
1列だけの項目を追加する場合は、Items.Addメソッドへ文字列を渡します。
listViewUsers.Items.Add("山田太郎");listViewUsers.Items.Add("佐藤花子");listViewUsers.Items.Add("鈴木一郎");戻り値として追加されたListViewItemを受け取ることもできます。
ListViewItem item = listViewUsers.Items.Add("山田太郎");item.ForeColor = Color.Blue;1列表示であれば、この方法が最も簡単です。
4-2. ListViewItemを使って複数列の行を追加する
複数列の行を追加する場合は、ListViewItemを作成します。
ListViewItem item = new ListViewItem("1");item.SubItems.Add("山田太郎");item.SubItems.Add("yamada@example.com");listViewUsers.Items.Add(item);
このコードでは、各値が次のように対応します。
| 列 | 値 | 設定先 |
| 1列目 | 1 | ListViewItem.Text |
| 2列目 | 山田太郎 | SubItems[1] |
| 3列目 | yamada@example.com | SubItems[2] |
SubItems[0]には、1列目の値が含まれます。そのため、2列目はSubItems[1]、3列目はSubItems[2]です。
4-3. SubItems.Addで2列目以降のデータを追加する
SubItems.Addを使うと、2列目以降の値を順番に追加できます。
ListViewItem item = new ListViewItem();item.Text = "1001";item.SubItems.Add("田中美咲");item.SubItems.Add("tanaka@example.com");item.SubItems.Add("営業部");listViewUsers.Items.Add(item);
列数よりSubItemsが少ない場合、値が存在しないセルは空欄として表示されます。
逆に、表示用の列よりSubItemsが多い場合、余分な値は画面に表示されません。
4-4. 配列から複数列の行を追加する
文字列配列をListViewItemのコンストラクターへ渡すと、複数列を簡潔に追加できます。
string[] values ={"1","山田太郎","yamada@example.com"};ListViewItem item = new ListViewItem(values);listViewUsers.Items.Add(item);
さらに短く書くこともできます。
listViewUsers.Items.Add(new ListViewItem(new[]{"2","佐藤花子","sato@example.com"}));列数が決まっている単純なデータでは、配列を使う方法が便利です。
4-5. ボタンクリックで入力値をListViewに追加する
TextBoxへ入力した値を追加する例です。
private int _nextId = 1;private void buttonAdd_Click(object sender, EventArgs e){string name = textBoxName.Text.Trim();string email = textBoxEmail.Text.Trim();
if (string.IsNullOrWhiteSpace(name)){MessageBox.Show("名前を入力してください。","入力エラー",MessageBoxButtons.OK,MessageBoxIcon.Warning);textBoxName.Focus();return;}ListViewItem item = new ListViewItem(new[]{_nextId.ToString(),name,email});listViewUsers.Items.Add(item);_nextId++;textBoxName.Clear();textBoxEmail.Clear();textBoxName.Focus();
}
入力値を追加するときは、空欄チェックや形式チェックを先に行うのが基本です。
メールアドレスの厳密な判定が必要な場合は、用途に応じて追加の入力検証を行ってください。
4-6. AddRangeで複数の項目をまとめて追加する
複数のListViewItemをまとめて追加するには、Items.AddRangeを使用します。
ListViewItem[] items ={new ListViewItem(new[] { "1", "山田太郎", "yamada@example.com" }),new ListViewItem(new[] { "2", "佐藤花子", "sato@example.com" }),new ListViewItem(new[] { "3", "鈴木一郎", "suzuki@example.com" })
};
listViewUsers.Items.AddRange(items);
大量の項目を追加するときは、1件ずつ追加するよりもAddRangeを使用したほうが、コードを整理しやすく、描画回数も抑えやすくなります。
4-7. 同じ項目の重複登録を防止する
メールアドレスが重複している行を追加しない例です。
private bool EmailExists(string email){foreach (ListViewItem item in listViewUsers.Items){if (item.SubItems.Count <= 2){continue;} string registeredEmail = item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[2]</span>.Text;if (string.Equals(registeredEmail,email,StringComparison.OrdinalIgnoreCase)){return true;}}return false;
}
追加処理から呼び出します。
private void buttonAdd_Click(object sender, EventArgs e){string name = textBoxName.Text.Trim();string email = textBoxEmail.Text.Trim();if (string.IsNullOrWhiteSpace(name)){MessageBox.Show("名前を入力してください。");return;}if (EmailExists(email)){MessageBox.Show("同じメールアドレスがすでに登録されています。");return;}ListViewItem item = new ListViewItem(new[]{_nextId.ToString(),name,email});listViewUsers.Items.Add(item);_nextId++;
}
重複判定では、大文字と小文字、前後の空白、全角と半角などをどこまで同一として扱うかを決めておくことが重要です。
5. ListViewの項目・行を削除する方法
5-1. Removeで指定した項目を削除する
ListViewItemオブジェクトを指定して削除するには、Items.Removeを使用します。
ListViewItem item = listViewUsers.Items[0];listViewUsers.Items.Remove(item);検索して見つけた項目を削除する場合にも使えます。
foreach (ListViewItem item in listViewUsers.Items){if (item.Text == "1"){listViewUsers.Items.Remove(item);break;}}コレクションをforeachで走査している途中に削除すると列挙状態が変わるため、削除後はbreakでループを終了するか、先に削除対象を取得してから削除してください。
5-2. RemoveAtでインデックスを指定して削除する
インデックスを指定して削除する場合は、RemoveAtを使用します。
listViewUsers.Items.RemoveAt(0);インデックスが範囲外の場合は例外が発生します。事前に件数を確認してください。
int index = 0;if (index >= 0 && index < listViewUsers.Items.Count){listViewUsers.Items.RemoveAt(index);}
5-3. 選択されている行を削除する
単一選択のListViewで、選択中の行を削除する例です。
private void buttonDelete_Click(object sender, EventArgs e){if (listViewUsers.SelectedItems.Count == 0){MessageBox.Show("削除する行を選択してください。");return;}ListViewItem selectedItem =listViewUsers.SelectedItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[0]</span>;listViewUsers.Items.Remove(selectedItem);
}
SelectedItems[0]へアクセスする前に、Countが0でないことを必ず確認します。
5-4. 複数選択された行をまとめて削除する
複数選択された行を削除する場合は、選択項目を配列へコピーしてから削除すると安全です。
private void DeleteSelectedItems(){ListViewItem[] selectedItems =listViewUsers.SelectedItems.Cast<ListViewItem>().ToArray();foreach (ListViewItem item in selectedItems){listViewUsers.Items.Remove(item);}
}
LINQを使わない場合は、後ろから削除する方法もあります。
for (int i = listViewUsers.SelectedItems.Count - 1;i >= 0;i--){listViewUsers.Items.Remove(listViewUsers.SelectedItems[i]);}選択コレクションを先頭から削除すると、削除のたびにコレクションの内容が変わり、一部の行が残ることがあります。配列へコピーするか、後ろから処理してください。
5-5. Items.Clearですべての項目を削除する
すべての行を削除するには、Items.Clearを使用します。
listViewUsers.Items.Clear();この処理は項目だけを削除します。Columnsコレクションに登録されている列は残ります。
列も削除する場合は、次のようにします。
listViewUsers.Items.Clear();listViewUsers.Columns.Clear();5-6. 未選択時の例外を防ぐ削除処理
次のコードは、未選択時に例外が発生します。
listViewUsers.Items.Remove(listViewUsers.SelectedItems[0]);安全に処理するには、Countを確認します。
if (listViewUsers.SelectedItems.Count <= 0){MessageBox.Show("行が選択されていません。");return;}ListViewItem item = listViewUsers.SelectedItems[0];listViewUsers.Items.Remove(item);
単一選択にしている場合でも、必ず1行が選択されているとは限りません。フォームを開いた直後や、選択を解除した状態ではCountが0になります。
5-7. 削除前に確認ダイアログを表示する
誤操作を防ぐため、削除前に確認ダイアログを表示する例です。
private void buttonDelete_Click(object sender, EventArgs e){if (listViewUsers.SelectedItems.Count == 0){MessageBox.Show("削除する行を選択してください。","確認",MessageBoxButtons.OK,MessageBoxIcon.Information); return;}DialogResult result = MessageBox.Show($"{listViewUsers.SelectedItems.Count}件の行を削除しますか?","削除確認",MessageBoxButtons.YesNo,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2);if (result != DialogResult.Yes){return;}ListViewItem[] selectedItems =listViewUsers.SelectedItems.Cast<ListViewItem>().ToArray();foreach (ListViewItem item in selectedItems){listViewUsers.Items.Remove(item);}
}
削除確認では、初期選択を「いいえ」にすると、Enterキーの誤操作による削除を防ぎやすくなります。
6. ListViewの選択行と選択項目を取得する方法
6-1. SelectedItemsで選択された項目を取得する
選択されたListViewItemは、SelectedItemsプロパティから取得できます。
if (listViewUsers.SelectedItems.Count > 0){ListViewItem item =listViewUsers.SelectedItems[0];MessageBox.Show(item.Text);
}
MultiSelectがfalseでも、戻り値はコレクションです。
複数選択時は、foreachで処理できます。
foreach (ListViewItem itemin listViewUsers.SelectedItems){Debug.WriteLine(item.Text);}6-2. SelectedIndicesで選択行のインデックスを取得する
選択された行番号は、SelectedIndicesから取得できます。
if (listViewUsers.SelectedIndices.Count > 0){int index = listViewUsers.SelectedIndices[0];MessageBox.Show($"選択行のインデックスは{index}です。");
}
インデックスは0から始まります。画面上の行番号として1から表示したい場合は、1を加算します。
int rowNumber =listViewUsers.SelectedIndices[0] + 1;複数選択時は、すべてのインデックスを取得できます。
foreach (int indexin listViewUsers.SelectedIndices){Debug.WriteLine(index);}6-3. 選択行の1列目の値を取得する
1列目の値は、ListViewItem.Textから取得できます。
if (listViewUsers.SelectedItems.Count == 0){return;}ListViewItem selectedItem =listViewUsers.SelectedItems[0];
string id = selectedItem.Text;
MessageBox.Show($"ID: {id}");
次のようにSubItems[0]から取得することもできます。
string id =selectedItem.SubItems[0].Text;通常は、1列目であることが分かりやすいTextを使うとよいでしょう。
6-4. SubItemsから2列目以降の値を取得する
2列目以降の値は、SubItemsのインデックスを指定して取得します。
ListViewItem item =listViewUsers.SelectedItems[0];string id = item.Text;string name = item.SubItems[1].Text;string email = item.SubItems[2].Text;
SubItemsが不足していると例外が発生するため、外部データなど列数が不定の場合は件数を確認します。
string name =item.SubItems.Count > 1? item.SubItems[1].Text: string.Empty;string email =item.SubItems.Count > 2? item.SubItems[2].Text: string.Empty;
選択行の全列をループで取得することもできます。
foreach (ListViewItem.ListViewSubItem subItemin item.SubItems){Debug.WriteLine(subItem.Text);}6-5. 複数選択されたすべての行を取得する
複数選択された行の全データを取得する例です。
foreach (ListViewItem itemin listViewUsers.SelectedItems){string id = item.Text;string name =item.SubItems.Count > 1? item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[1]</span>.Text: string.Empty;string email =item.SubItems.Count > 2? item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[2]</span>.Text: string.Empty;Debug.WriteLine($"ID={id}, 名前={name}, メール={email}");
}
取得したデータをオブジェクトへ変換することもできます。
List<UserData> selectedUsers =listViewUsers.SelectedItems.Cast<ListViewItem>().Select(item => new UserData{Id = int.Parse(item.Text),Name = item.SubItems[1].Text,Email = item.SubItems[2].Text}).ToList();使用するクラスは次のように定義します。
public class UserData{public int Id { get; set; }public string Name { get; set; } =string.Empty;public string Email { get; set; } =string.Empty;
}
6-6. SelectedIndexChangedイベントで選択変更を検知する
選択状態が変わったときに処理を実行するには、SelectedIndexChangedイベントを使用します。
private void listViewUsers_SelectedIndexChanged(object sender,EventArgs e){if (listViewUsers.SelectedItems.Count == 0){textBoxName.Clear();textBoxEmail.Clear();return;}ListViewItem item =listViewUsers.SelectedItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[0]</span>;textBoxName.Text =item.SubItems.Count > 1? item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[1]</span>.Text: string.Empty;textBoxEmail.Text =item.SubItems.Count > 2? item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[2]</span>.Text: string.Empty;
}
イベントは、選択解除時にも発生します。そのため、SelectedItems.Countが0の場合の処理が必要です。
6-7. ItemSelectionChangedイベントで変更された項目を判定する
どの項目の選択状態が変化したかを知りたい場合は、ItemSelectionChangedイベントを使用します。
private void listViewUsers_ItemSelectionChanged(object sender,ListViewItemSelectionChangedEventArgs e){string state =e.IsSelected ? "選択" : "選択解除";Debug.WriteLine($"行{e.ItemIndex}が{state}されました。");Debug.WriteLine($"項目: {e.Item.Text}");
}
イベント引数から、次の情報を取得できます。
e.Iteme.ItemIndexe.IsSelected複数選択の状態変化を細かく監視したい場合に便利です。
6-8. ダブルクリックした行のデータを取得する
ダブルクリックされた行を取得するには、DoubleClickイベントを使用できます。
private void listViewUsers_DoubleClick(object sender,EventArgs e){if (listViewUsers.SelectedItems.Count == 0){return;}ListViewItem item =listViewUsers.SelectedItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[0]</span>;string id = item.Text;string name = item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[1]</span>.Text;string email = item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[2]</span>.Text;MessageBox.Show($"ID: {id}\n" +$"名前: {name}\n" +$"メール: {email}");
}
マウス座標からダブルクリックされた項目を厳密に取得する場合は、MouseDoubleClickイベントとGetItemAtを使用します。
private void listViewUsers_MouseDoubleClick(object sender,MouseEventArgs e){ListViewItem? item =listViewUsers.GetItemAt(e.X, e.Y);if (item == null){return;}MessageBox.Show(item.Text);
}
7. ListViewの列幅を調整する方法
7-1. Widthプロパティで列幅を固定する
固定の列幅を指定する場合は、ColumnHeader.Widthプロパティを設定します。
listViewUsers.Columns[0].Width = 60;listViewUsers.Columns[1].Width = 150;listViewUsers.Columns[2].Width = 240;列を追加するときに指定することもできます。
listViewUsers.Columns.Add("ID", 60);listViewUsers.Columns.Add("名前", 150);listViewUsers.Columns.Add("メールアドレス", 240);固定幅は表示が安定する一方、文字列が長いと途中で切れることがあります。
7-2. Widthを-1にして項目の文字幅に自動調整する
Widthへ-1を指定すると、列内の項目の内容に合わせて自動調整できます。
listViewUsers.Columns[0].Width = -1;listViewUsers.Columns[1].Width = -1;listViewUsers.Columns[2].Width = -1;項目追加後に設定するのがポイントです。
listViewUsers.Items.Add(new ListViewItem(new[]{"1","山田太郎","very-long-email-address@example.com"}));listViewUsers.Columns[2].Width = -1;
項目を追加する前に自動調整すると、まだ内容が存在しないため、期待した幅にならないことがあります。
7-3. Widthを-2にして項目と列ヘッダーに自動調整する
Widthへ-2を指定すると、列ヘッダーを基準にした自動調整が行われます。
listViewUsers.Columns[0].Width = -2;listViewUsers.Columns[1].Width = -2;listViewUsers.Columns[2].Width = -2;項目内容とヘッダーの両方を考慮したい場合は、後述するAutoResizeColumnsとColumnHeaderAutoResizeStyleを使用したほうが、意図をコード上で明確にできます。
たとえば、最初に内容へ合わせ、その後ヘッダーより狭い列だけをヘッダー幅へ広げる方法があります。
foreach (ColumnHeader columnin listViewUsers.Columns){column.Width = -1;int contentWidth = column.Width;column.Width = -2;int headerWidth = column.Width;column.Width =Math.Max(contentWidth, headerWidth);
}
7-4. すべての列幅をループで一括調整する
すべての列を項目内容へ合わせる例です。
foreach (ColumnHeader columnin listViewUsers.Columns){column.Width = -1;}列ヘッダーへ合わせる場合は次のようにします。
foreach (ColumnHeader columnin listViewUsers.Columns){column.Width = -2;}インデックスを使う場合は次のように書けます。
for (int i = 0;i < listViewUsers.Columns.Count;i++){listViewUsers.Columns[i].Width = -1;}7-5. ColumnHeaderAutoResizeStyleを使って自動調整する
AutoResizeColumnメソッドを使うと、自動調整の基準を列挙値で指定できます。
項目内容へ合わせる場合は、ColumnContentを指定します。
for (int i = 0;i < listViewUsers.Columns.Count;i++){listViewUsers.AutoResizeColumn(i,ColumnHeaderAutoResizeStyle.ColumnContent);}列ヘッダーへ合わせる場合は、HeaderSizeを指定します。
for (int i = 0;i < listViewUsers.Columns.Count;i++){listViewUsers.AutoResizeColumn(i,ColumnHeaderAutoResizeStyle.HeaderSize);}すべての列をまとめて調整する場合は、AutoResizeColumnsを使用できます。
listViewUsers.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);AutoResizeColumnおよびAutoResizeColumnsでは、列内容またはヘッダーを基準として列幅を調整できます。
内容とヘッダーの大きいほうへ合わせる処理は、次のように実装できます。
private void AutoResizeAllColumns(){for (int i = 0;i < listViewUsers.Columns.Count;i++){listViewUsers.AutoResizeColumn(i,ColumnHeaderAutoResizeStyle.ColumnContent); int contentWidth =listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width;listViewUsers.AutoResizeColumn(i,ColumnHeaderAutoResizeStyle.HeaderSize);int headerWidth =listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width;listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width =Math.Max(contentWidth, headerWidth);}
}
7-6. フォームサイズに合わせて最終列の幅を調整する
ListViewの右端まで最終列を広げたい場合は、ClientSizeから他の列幅を引いて計算します。
private void AdjustLastColumnWidth(){if (listViewUsers.Columns.Count == 0){return;}int usedWidth = 0;for (int i = 0;i < listViewUsers.Columns.Count - 1;i++){usedWidth +=listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width;}int lastIndex =listViewUsers.Columns.Count - 1;int remainingWidth =listViewUsers.ClientSize.Width -usedWidth -SystemInformation.VerticalScrollBarWidth -4;listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[lastIndex]</span>.Width =Math.Max(100, remainingWidth);
}
フォームのResizeイベントから呼び出します。
private void Form1_Resize(object sender,EventArgs e){AdjustLastColumnWidth();}縦スクロールバーが常に表示されるとは限らないため、厳密に調整する場合は、表示状態に応じてスクロールバー幅を差し引くか判断します。
7-7. 列幅をユーザーに変更させない方法
Windows FormsのListViewには、列のユーザーリサイズを直接無効にする専用プロパティがありません。
ColumnWidthChangingイベントで変更を取り消す方法があります。
private int[] _fixedColumnWidths ={60,150,240};private void listViewUsers_ColumnWidthChanging(object sender,ColumnWidthChangingEventArgs e){if (e.ColumnIndex < 0 ||e.ColumnIndex >= _fixedColumnWidths.Length){return;}
e.Cancel = true;e.NewWidth =_fixedColumnWidths<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[e.ColumnIndex]</span>;
}
列の幅をコードで変更するときにもイベントが関係する可能性があるため、必要に応じて制御用フラグを設けます。
private bool _isAdjustingColumnWidth;private void listViewUsers_ColumnWidthChanging(object sender,ColumnWidthChangingEventArgs e){if (_isAdjustingColumnWidth){return;}
e.Cancel = true;e.NewWidth =listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[e.ColumnIndex]</span>.Width;
}
8. ListViewでよく使う便利な機能
8-1. チェックボックス付きの一覧を表示する
各行の先頭にチェックボックスを表示するには、CheckBoxesプロパティをtrueにします。
listViewUsers.CheckBoxes = true;チェック状態を設定するには、ListViewItem.Checkedを使用します。
ListViewItem item =new ListViewItem(new[]{"1","山田太郎","yamada@example.com"});item.Checked = true;
listViewUsers.Items.Add(item);
チェック状態が変わったときは、ItemCheckイベントを利用できます。
private void listViewUsers_ItemCheck(object sender,ItemCheckEventArgs e){Debug.WriteLine($"行{e.Index}: {e.CurrentValue} → {e.NewValue}");}ItemCheckイベント内では、Checkedプロパティがまだ更新前の場合があります。変更後の状態はe.NewValueで判定します。
8-2. チェックされた項目だけを取得する
チェックされた項目は、CheckedItemsから取得できます。
foreach (ListViewItem itemin listViewUsers.CheckedItems){string id = item.Text;string name = item.SubItems[1].Text;Debug.WriteLine($"ID={id}, 名前={name}");
}
チェックされた行があるか確認する場合は、Countを使用します。
if (listViewUsers.CheckedItems.Count == 0){MessageBox.Show("処理対象をチェックしてください。");return;}チェックされた行のインデックスは、CheckedIndicesから取得できます。
foreach (int indexin listViewUsers.CheckedIndices){Debug.WriteLine(index);}8-3. ImageListを使ってアイコンや画像を表示する
ListViewへ画像を表示するには、ImageListを用意します。
ImageList imageList = new ImageList{ImageSize = new Size(16, 16),ColorDepth = ColorDepth.Depth32Bit};imageList.Images.Add("user",Properties.Resources.UserIcon);
listViewUsers.SmallImageList = imageList;
項目へ画像キーを指定します。
ListViewItem item =new ListViewItem(new[]{"1","山田太郎","yamada@example.com"});item.ImageKey = "user";
listViewUsers.Items.Add(item);
画像インデックスを指定することもできます。
item.ImageIndex = 0;Details表示では通常、SmallImageListが使用されます。LargeIcon表示ではLargeImageListを設定します。
listViewUsers.LargeImageList = imageList;listViewUsers.View = View.LargeIcon;8-4. LabelEditで項目名を直接編集できるようにする
LabelEditプロパティをtrueにすると、1列目のラベルを直接編集できます。
listViewUsers.LabelEdit = true;プログラムから編集を開始するには、BeginEditを呼び出します。
if (listViewUsers.SelectedItems.Count > 0){listViewUsers.SelectedItems[0].BeginEdit();}編集後の値を検証する場合は、AfterLabelEditイベントを使用します。
private void listViewUsers_AfterLabelEdit(object sender,LabelEditEventArgs e){if (e.Label == null){return;}if (string.IsNullOrWhiteSpace(e.Label)){e.CancelEdit = true;MessageBox.Show("空の値は登録できません。");}
}
LabelEditで直接編集できるのは、基本的に1列目のラベルです。2列目以降を編集したい場合は、TextBoxを重ねるなどの独自実装か、DataGridViewの使用を検討してください。
8-5. EnsureVisibleで指定した行までスクロールする
指定した項目が見える位置までスクロールするには、EnsureVisibleを使用します。
int index = 50;if (index >= 0 &&index < listViewUsers.Items.Count){listViewUsers.EnsureVisible(index);}
新しい行を追加したあと、最終行までスクロールする例です。
ListViewItem item =listViewUsers.Items.Add(new ListViewItem(new[]{"10","新規ユーザー","new@example.com"}));item.EnsureVisible();
選択状態にもする場合は次のようにします。
item.Selected = true;item.Focused = true;item.EnsureVisible();8-6. 列ヘッダーのクリックで項目を並べ替える
ListViewの列を並べ替えるには、IComparerを実装した比較クラスを作成します。
public sealed class ListViewItemComparer: System.Collections.IComparer{private readonly int _columnIndex;private readonly SortOrder _sortOrder;public ListViewItemComparer(int columnIndex,SortOrder sortOrder){_columnIndex = columnIndex;_sortOrder = sortOrder;}public int Compare(object? x,object? y){if (x is not ListViewItem itemX ||y is not ListViewItem itemY){return 0;}string valueX =GetSubItemText(itemX, _columnIndex);string valueY =GetSubItemText(itemY, _columnIndex);int result =string.Compare(valueX,valueY,StringComparison.CurrentCulture);return _sortOrder == SortOrder.Descending? -result: result;}private static string GetSubItemText(ListViewItem item,int index){return item.SubItems.Count > index? item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[index]</span>.Text: string.Empty;}
}
フォーム側で現在の並べ替え状態を保持します。
private int _sortColumn = -1;private SortOrder _sortOrder =SortOrder.None;
ColumnClickイベントで並べ替えます。
private void listViewUsers_ColumnClick(object sender,ColumnClickEventArgs e){if (_sortColumn == e.Column){_sortOrder =_sortOrder == SortOrder.Ascending? SortOrder.Descending: SortOrder.Ascending;}else{_sortColumn = e.Column;_sortOrder = SortOrder.Ascending;}listViewUsers.ListViewItemSorter =new ListViewItemComparer(_sortColumn,_sortOrder);listViewUsers.Sort();
}
IDなどの数値列を文字列として比較すると、「10」が「2」より前になる場合があります。数値列では、intやdecimalへ変換して比較する実装が必要です。
8-7. ForeColor・BackColor・Fontで行の見た目を変更する
行全体の文字色や背景色を変更できます。
ListViewItem item =new ListViewItem(new[]{"1","山田太郎","yamada@example.com"});item.ForeColor = Color.DarkBlue;item.BackColor = Color.LightYellow;item.Font = new Font(listViewUsers.Font,FontStyle.Bold);
listViewUsers.Items.Add(item);
セルごとに見た目を変更する場合は、UseItemStyleForSubItemsをfalseにします。
item.UseItemStyleForSubItems = false;item.SubItems[1].ForeColor = Color.Red;item.SubItems[2].BackColor = Color.LightCyan;
Fontは破棄が必要なリソースです。多数の行ごとに新しいFontを生成するとリソース消費が増えるため、共通のFontを使い回すか、フォームのDispose時に破棄してください。
8-8. Tagプロパティに任意のオブジェクトを保持する
Tagプロパティには、任意のオブジェクトを保存できます。
UserData user = new UserData{Id = 1,Name = "山田太郎",Email = "yamada@example.com"};ListViewItem item =new ListViewItem(new[]{user.Id.ToString(),user.Name,user.Email});
item.Tag = user;
listViewUsers.Items.Add(item);
選択時に元のオブジェクトを取得します。
if (listViewUsers.SelectedItems.Count == 0){return;}ListViewItem selectedItem =listViewUsers.SelectedItems[0];
if (selectedItem.Tag is UserData user){MessageBox.Show($"ID: {user.Id}\n" +$"名前: {user.Name}\n" +$"メール: {user.Email}");}
画面表示用の文字列からデータを再変換する必要がなくなるため、IDや元データをTagへ保持する方法は実用的です。
9. 大量データをListViewへ効率よく表示する方法
9-1. BeginUpdateとEndUpdateで描画のちらつきを防ぐ
大量の行を1件ずつ追加すると、追加のたびに画面が再描画され、ちらつきや処理速度低下の原因になります。
BeginUpdateとEndUpdateで更新処理を囲むと、途中の再描画を抑制できます。
listViewUsers.BeginUpdate();try{listViewUsers.Items.Clear();
for (int i = 1; i <= 10000; i++){ListViewItem item =new ListViewItem(new[]{i.ToString(),$"ユーザー{i}",$"user{i}@example.com"});listViewUsers.Items.Add(item);}
}finally{listViewUsers.EndUpdate();}
例外が発生してもEndUpdateが呼ばれるように、try-finallyを使用するのが安全です。
9-2. AddRangeで項目追加の処理速度を改善する
先にListViewItemの配列を作り、AddRangeでまとめて追加します。
List<ListViewItem> items =new List<ListViewItem>();for (int i = 1; i <= 10000; i++){items.Add(new ListViewItem(new[]{i.ToString(),$"ユーザー{i}",$"user{i}@example.com"}));}
listViewUsers.BeginUpdate();
try{listViewUsers.Items.Clear();listViewUsers.Items.AddRange(items.ToArray());}finally{listViewUsers.EndUpdate();}
BeginUpdateとAddRangeを組み合わせると、大量追加時の描画負荷を抑えられます。
9-3. VirtualModeで大量の項目を仮想表示する
非常に多くのデータを表示する場合は、VirtualModeを検討します。
VirtualModeでは、すべてのListViewItemをItemsへ保持せず、表示が必要になった項目をRetrieveVirtualItemイベントで提供します。利用時にはVirtualListSizeの設定も必要です。
元データを保持するフィールドを用意します。
private readonly List<UserData> _users =new List<UserData>();初期化します。
private void InitializeVirtualListView(){listViewUsers.View = View.Details;listViewUsers.VirtualMode = true;listViewUsers.Columns.Add("ID", 80);listViewUsers.Columns.Add("名前", 160);listViewUsers.Columns.Add("メールアドレス",260);for (int i = 1; i <= 100000; i++){_users.Add(new UserData{Id = i,Name = $"ユーザー{i}",Email = $"user{i}@example.com"});}listViewUsers.VirtualListSize =_users.Count;
}
RetrieveVirtualItemイベントを処理します。
private void listViewUsers_RetrieveVirtualItem(object sender,RetrieveVirtualItemEventArgs e){UserData user = _users[e.ItemIndex];e.Item = new ListViewItem(new[]{user.Id.ToString(),user.Name,user.Email});
}
VirtualModeではItemsコレクションの使い方が通常モードと異なります。検索、選択、キャッシュなども仮想モード向けの実装が必要になる場合があります。
9-4. UIを固めないために非同期でデータを取得する
データベースやWeb APIからデータを取得する処理は、UIスレッド上で同期実行すると画面が固まります。
データ取得部分を非同期化し、取得後にListViewへ反映します。
private async void buttonLoad_Click(object sender,EventArgs e){buttonLoad.Enabled = false;try{List<UserData> users =await LoadUsersAsync();ListViewItem[] items =users.Select(user =>{ListViewItem item =new ListViewItem(new[]{user.Id.ToString(),user.Name,user.Email});item.Tag = user;return item;}).ToArray();listViewUsers.BeginUpdate();try{listViewUsers.Items.Clear();listViewUsers.Items.AddRange(items);}finally{listViewUsers.EndUpdate();}}catch (Exception ex){MessageBox.Show(ex.Message,"読み込みエラー",MessageBoxButtons.OK,MessageBoxIcon.Error);}finally{buttonLoad.Enabled = true;}
}
サンプル用の非同期メソッドです。
private static async Task<List<UserData>>LoadUsersAsync(){await Task.Delay(1000);return Enumerable.Range(1, 1000).Select(i => new UserData{Id = i,Name = $"ユーザー{i}",Email = $"user{i}@example.com"}).ToList();
}
await後に元のUIコンテキストへ戻る通常のイベントハンドラーであれば、そのままListViewを更新できます。
9-5. 別スレッドからListViewを更新するときの注意点
Windows Formsのコントロールは、原則として作成したUIスレッドから操作します。
別スレッドからListViewへ直接アクセスすると、クロススレッド操作の例外が発生する可能性があります。
InvokeRequiredを使った更新例は次のとおりです。
private void AddItemThreadSafe(UserData user){if (listViewUsers.InvokeRequired){listViewUsers.BeginInvoke(new Action<UserData>(AddItemThreadSafe),user); return;}ListViewItem item =new ListViewItem(new[]{user.Id.ToString(),user.Name,user.Email});item.Tag = user;listViewUsers.Items.Add(item);
}
大量データを1件ずつBeginInvokeすると、UIメッセージが増えて逆に遅くなる場合があります。一定件数をまとめて反映する設計が適しています。
10. ListViewが正しく動かないときの原因と対処法
10-1. 項目を追加しても表示されない
項目を追加しても表示されない場合は、次の点を確認します。
最初に、操作しているListViewが画面上のListViewと同じインスタンスか確認します。
listViewUsers.Items.Add("テスト");ローカル変数として新しいListViewを作成していると、画面には表示されません。
// 画面に配置されたListViewとは別のインスタンスListView listViewUsers = new ListView();listViewUsers.Items.Add("テスト");また、ListViewのサイズ、Visibleプロパティ、親コントロール、DockやAnchorの設定も確認してください。
10-2. 2列目以降のSubItemsが表示されない
最も多い原因は、ViewプロパティがDetailsになっていないことです。
listViewUsers.View = View.Details;次に、必要な数の列が追加されているか確認します。
listViewUsers.Columns.Add("ID", 60);listViewUsers.Columns.Add("名前", 150);listViewUsers.Columns.Add("メールアドレス",240);SubItemsを追加していても、対応する列が存在しなければ表示されません。
ListViewItem item = new ListViewItem("1");item.SubItems.Add("山田太郎");item.SubItems.Add("yamada@example.com");listViewUsers.Items.Add(item);
10-3. 選択行を取得すると例外が発生する
次のコードは、選択されていない状態で実行すると例外になります。
ListViewItem item =listViewUsers.SelectedItems[0];Countを確認してから取得します。
if (listViewUsers.SelectedItems.Count == 0){MessageBox.Show("行を選択してください。");return;}ListViewItem item =listViewUsers.SelectedItems[0];
SubItemsへアクセスするときも件数を確認します。
if (item.SubItems.Count > 2){string email =item.SubItems[2].Text;}10-4. 選択行を削除すると一部の行が残る
選択コレクションを先頭から削除すると、削除のたびにコレクションが変化し、項目を飛ばす場合があります。
次のようなコードは避けます。
for (int i = 0;i < listViewUsers.SelectedItems.Count;i++){listViewUsers.Items.Remove(listViewUsers.SelectedItems[i]);}後ろから削除します。
for (int i =listViewUsers.SelectedItems.Count - 1;i >= 0;i--){listViewUsers.Items.Remove(listViewUsers.SelectedItems[i]);}または、先に配列へコピーします。
ListViewItem[] items =listViewUsers.SelectedItems.Cast<ListViewItem>().ToArray();foreach (ListViewItem item in items){listViewUsers.Items.Remove(item);}
10-5. 列幅の自動調整が期待どおりにならない
列幅の自動調整は、項目を追加したあとに実行します。
listViewUsers.Items.AddRange(items);listViewUsers.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
自動調整後に長い文字列の項目を追加した場合、列幅は自動的に再計算されません。追加後にもう一度調整してください。
listViewUsers.Items.Add(newItem);listViewUsers.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
列ヘッダーのほうが長い場合は、HeaderSizeとの比較が必要です。
10-6. 行全体ではなく1列目だけが選択される
FullRowSelectをtrueにします。
listViewUsers.FullRowSelect = true;また、ViewがDetailsであることを確認します。
listViewUsers.View = View.Details;FullRowSelectは、Details表示で行全体を選択したように見せるための設定です。
10-7. ListViewの表示がちらつく・処理が遅い
大量追加時は、BeginUpdateとEndUpdateを使用します。
listViewUsers.BeginUpdate();try{listViewUsers.Items.AddRange(items);}finally{listViewUsers.EndUpdate();}
そのほか、次の点も確認します。
項目追加のたびに列幅を調整していないか
ループ内でImageやFontを毎回生成していないか
1件ごとにInvokeしていないか
不要なイベント処理が大量に発生していないか
表示件数が多すぎないか
VirtualModeを利用できないか
列幅調整は全件追加後に1回だけ実行するほうが効率的です。
10-8. WPFのListView用コードと混同してエラーになる
Windows Formsでは、ColumnsやSubItemsを直接使用します。
listViewUsers.Columns.Add("名前", 150);ListViewItem item =new ListViewItem("山田太郎");
item.SubItems.Add("yamada@example.com");
WPFでは、GridViewColumnやItemsSourceを使用します。
<ListView ItemsSource="{Binding Users}"><ListView.View><GridView><GridViewColumnHeader="名前"DisplayMemberBinding="{Binding Name}" /></GridView></ListView.View></ListView>エラーが発生した場合は、まずusingディレクティブとプロジェクト種類を確認します。
using System.Windows.Forms;同じ名前の型が存在して曖昧になる場合は、完全修飾名を使用できます。
System.Windows.Forms.ListView listView;11. 項目追加・削除・選択行取得・列幅調整を実装した完成サンプル
11-1. 完成サンプルの画面構成
フォームへ次のコントロールを配置します。
| 種類 | Name | Text |
| Label | labelName | 名前 |
| TextBox | textBoxName | 空欄 |
| Label | labelEmail | メールアドレス |
| TextBox | textBoxEmail | 空欄 |
| Button | buttonAdd | 追加 |
| Button | buttonGet | 選択行を取得 |
| Button | buttonDelete | 選択行を削除 |
| Button | buttonResize | 列幅を調整 |
| ListView | listViewUsers | 空欄 |
ListViewには、ID、名前、メールアドレスの3列をコードから追加します。
デザイナーで列を追加した場合は、初期化コード内のColumns.AddRangeを削除してください。
11-2. ListViewを初期化するコード
フォームのコンストラクターから初期化メソッドを呼び出します。
public Form1(){InitializeComponent();InitializeUserListView();}初期化メソッドです。
private void InitializeUserListView(){listViewUsers.View = View.Details;listViewUsers.FullRowSelect = true;listViewUsers.GridLines = true;listViewUsers.MultiSelect = true;listViewUsers.HideSelection = false;listViewUsers.HeaderStyle =ColumnHeaderStyle.Clickable;listViewUsers.Columns.Clear();listViewUsers.Columns.AddRange(new[]{new ColumnHeader{Name = "columnId",Text = "ID",Width = 60},new ColumnHeader{Name = "columnName",Text = "名前",Width = 160},new ColumnHeader{Name = "columnEmail",Text = "メールアドレス",Width = 260}});
}
HideSelectionをfalseにすると、ListViewがフォーカスを失ったあとも選択状態を確認しやすくなります。
11-3. 入力内容を新しい行として追加するコード
IDを採番するフィールドを用意します。
private int _nextId = 1;追加ボタンの処理です。
private void buttonAdd_Click(object sender,EventArgs e){string name =textBoxName.Text.Trim();string email =textBoxEmail.Text.Trim();if (string.IsNullOrWhiteSpace(name)){MessageBox.Show("名前を入力してください。","入力エラー",MessageBoxButtons.OK,MessageBoxIcon.Warning);textBoxName.Focus();return;}if (ContainsEmail(email)){MessageBox.Show("同じメールアドレスが登録されています。","重複エラー",MessageBoxButtons.OK,MessageBoxIcon.Warning);textBoxEmail.Focus();return;}UserData user = new UserData{Id = _nextId,Name = name,Email = email};ListViewItem item =CreateListViewItem(user);listViewUsers.Items.Add(item);_nextId++;textBoxName.Clear();textBoxEmail.Clear();textBoxName.Focus();
}
ListViewItemを生成する処理です。
private static ListViewItemCreateListViewItem(UserData user){ListViewItem item =new ListViewItem(new[]{user.Id.ToString(),user.Name,user.Email});item.Tag = user;return item;
}
重複判定です。
private bool ContainsEmail(string email){if (string.IsNullOrWhiteSpace(email)){return false;}foreach (ListViewItem itemin listViewUsers.Items){if (item.Tag is not UserData user){continue;}if (string.Equals(user.Email,email,StringComparison.OrdinalIgnoreCase)){return true;}}return false;
}
11-4. 選択行の値を取得するコード
選択行を取得する処理です。
private void buttonGet_Click(object sender,EventArgs e){if (listViewUsers.SelectedItems.Count == 0){MessageBox.Show("取得する行を選択してください。","確認",MessageBoxButtons.OK,MessageBoxIcon.Information); return;}List<string> messages =new List<string>();foreach (ListViewItem itemin listViewUsers.SelectedItems){if (item.Tag is UserData user){messages.Add($"ID: {user.Id}\n" +$"名前: {user.Name}\n" +$"メール: {user.Email}");}else{string id = item.Text;string name =item.SubItems.Count > 1? item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[1]</span>.Text: string.Empty;string email =item.SubItems.Count > 2? item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[2]</span>.Text: string.Empty;messages.Add($"ID: {id}\n" +$"名前: {name}\n" +$"メール: {email}");}}MessageBox.Show(string.Join("\n\n----------------\n\n",messages),"選択行の内容",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
Tagに元オブジェクトを格納しているため、画面上の文字列を再解析せずにデータを取得できます。
11-5. 選択行を削除するコード
複数選択に対応した削除処理です。
private void buttonDelete_Click(object sender,EventArgs e){int selectedCount =listViewUsers.SelectedItems.Count;if (selectedCount == 0){MessageBox.Show("削除する行を選択してください。","確認",MessageBoxButtons.OK,MessageBoxIcon.Information);return;}DialogResult result =MessageBox.Show($"{selectedCount}件の行を削除しますか?","削除確認",MessageBoxButtons.YesNo,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2);if (result != DialogResult.Yes){return;}ListViewItem[] selectedItems =listViewUsers.SelectedItems.Cast<ListViewItem>().ToArray();listViewUsers.BeginUpdate();try{foreach (ListViewItem itemin selectedItems){listViewUsers.Items.Remove(item);}}finally{listViewUsers.EndUpdate();}
}
11-6. 列幅を自動調整するコード
項目内容と列ヘッダーの大きいほうへ合わせます。
private void buttonResize_Click(object sender,EventArgs e){AutoResizeColumns();}自動調整メソッドです。
private void AutoResizeColumns(){for (int i = 0;i < listViewUsers.Columns.Count;i++){listViewUsers.AutoResizeColumn(i,ColumnHeaderAutoResizeStyle.ColumnContent); int contentWidth =listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width;listViewUsers.AutoResizeColumn(i,ColumnHeaderAutoResizeStyle.HeaderSize);int headerWidth =listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width;int width =Math.Max(contentWidth, headerWidth);listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width =Math.Min(width + 8, 500);}
}
最大幅を500ピクセルに制限し、極端に長い文字列によって列が広がりすぎるのを防いでいます。
11-7. Form1クラスの全サンプルコード
以下は、追加、取得、削除、重複防止、列幅調整、ダブルクリック取得をまとめたコードです。
using System;using System.Collections.Generic;using System.Drawing;using System.Linq;using System.Windows.Forms;namespace ListViewSample{public partial class Form1 : Form{private int _nextId = 1;
public Form1(){InitializeComponent();InitializeUserListView();RegisterEvents();}private void InitializeUserListView(){listViewUsers.View = View.Details;listViewUsers.FullRowSelect = true;listViewUsers.GridLines = true;listViewUsers.MultiSelect = true;listViewUsers.HideSelection = false;listViewUsers.HeaderStyle =ColumnHeaderStyle.Clickable;listViewUsers.Columns.Clear();listViewUsers.Columns.AddRange(new[]{new ColumnHeader{Name = "columnId",Text = "ID",Width = 60},new ColumnHeader{Name = "columnName",Text = "名前",Width = 160},new ColumnHeader{Name = "columnEmail",Text = "メールアドレス",Width = 260}});AddInitialData();}private void RegisterEvents(){buttonAdd.Click += buttonAdd_Click;buttonGet.Click += buttonGet_Click;buttonDelete.Click += buttonDelete_Click;buttonResize.Click += buttonResize_Click;listViewUsers.MouseDoubleClick +=listViewUsers_MouseDoubleClick;}private void AddInitialData(){UserData[] users ={new UserData{Id = _nextId++,Name = "山田太郎",Email = "yamada@example.com"},new UserData{Id = _nextId++,Name = "佐藤花子",Email = "sato@example.com"},new UserData{Id = _nextId++,Name = "鈴木一郎",Email = "suzuki@example.com"}};ListViewItem[] items =users.Select(CreateListViewItem).ToArray();listViewUsers.Items.AddRange(items);}private static ListViewItemCreateListViewItem(UserData user){ListViewItem item =new ListViewItem(new[]{user.Id.ToString(),user.Name,user.Email});item.Tag = user;return item;}private void buttonAdd_Click(object? sender,EventArgs e){string name =textBoxName.Text.Trim();string email =textBoxEmail.Text.Trim();if (string.IsNullOrWhiteSpace(name)){MessageBox.Show("名前を入力してください。","入力エラー",MessageBoxButtons.OK,MessageBoxIcon.Warning);textBoxName.Focus();return;}if (ContainsEmail(email)){MessageBox.Show("同じメールアドレスが" +"すでに登録されています。","重複エラー",MessageBoxButtons.OK,MessageBoxIcon.Warning);textBoxEmail.Focus();return;}UserData user = new UserData{Id = _nextId,Name = name,Email = email};listViewUsers.Items.Add(CreateListViewItem(user));_nextId++;textBoxName.Clear();textBoxEmail.Clear();textBoxName.Focus();}private bool ContainsEmail(string email){if (string.IsNullOrWhiteSpace(email)){return false;}foreach (ListViewItem itemin listViewUsers.Items){if (item.Tag is not UserData user){continue;}if (string.Equals(user.Email,email,StringComparison.OrdinalIgnoreCase)){return true;}}return false;}private void buttonGet_Click(object? sender,EventArgs e){if (listViewUsers.SelectedItems.Count == 0){MessageBox.Show("取得する行を選択してください。","確認",MessageBoxButtons.OK,MessageBoxIcon.Information);return;}List<string> messages =new List<string>();foreach (ListViewItem itemin listViewUsers.SelectedItems){if (item.Tag is UserData user){messages.Add($"ID: {user.Id}\n" +$"名前: {user.Name}\n" +$"メール: {user.Email}");}}MessageBox.Show(string.Join("\n\n----------------\n\n",messages),"選択行の内容",MessageBoxButtons.OK,MessageBoxIcon.Information);}private void buttonDelete_Click(object? sender,EventArgs e){int selectedCount =listViewUsers.SelectedItems.Count;if (selectedCount == 0){MessageBox.Show("削除する行を選択してください。","確認",MessageBoxButtons.OK,MessageBoxIcon.Information);return;}DialogResult result =MessageBox.Show($"{selectedCount}件の行を" +"削除しますか?","削除確認",MessageBoxButtons.YesNo,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2);if (result != DialogResult.Yes){return;}ListViewItem[] selectedItems =listViewUsers.SelectedItems.Cast<ListViewItem>().ToArray();listViewUsers.BeginUpdate();try{foreach (ListViewItem itemin selectedItems){listViewUsers.Items.Remove(item);}}finally{listViewUsers.EndUpdate();}}private void buttonResize_Click(object? sender,EventArgs e){AutoResizeColumns();}private void AutoResizeColumns(){for (int i = 0;i < listViewUsers.Columns.Count;i++){listViewUsers.AutoResizeColumn(i,ColumnHeaderAutoResizeStyle.ColumnContent);int contentWidth =listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width;listViewUsers.AutoResizeColumn(i,ColumnHeaderAutoResizeStyle.HeaderSize);int headerWidth =listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width;int width =Math.Max(contentWidth,headerWidth);listViewUsers.Columns<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[i]</span>.Width =Math.Min(width + 8, 500);}}private void listViewUsers_MouseDoubleClick(object? sender,MouseEventArgs e){ListViewItem? item =listViewUsers.GetItemAt(e.X, e.Y);if (item?.Tag is not UserData user){return;}MessageBox.Show($"ID: {user.Id}\n" +$"名前: {user.Name}\n" +$"メール: {user.Email}","ユーザー情報",MessageBoxButtons.OK,MessageBoxIcon.Information);}}public sealed class UserData{public int Id { get; set; }public string Name { get; set; } =string.Empty;public string Email { get; set; } =string.Empty;}
}
11-8. サンプルコードを実行するときの確認ポイント
サンプルを実行する前に、デザイナー上のNameプロパティがコードと一致していることを確認します。
listViewUserstextBoxNametextBoxEmailbuttonAddbuttonGetbuttonDeletebuttonResizeデザイナーでClickイベントをすでに登録している場合、RegisterEventsでも登録すると、1回のクリックでイベントが2回実行されます。
たとえば、デザイナー生成コードに次の登録がある場合です。
buttonAdd.Click += buttonAdd_Click;この場合は、RegisterEventsから同じ登録を削除してください。
また、デザイナーで列を追加済みの場合は、初期化時のColumns.ClearとColumns.AddRangeによって、デザイナー設定が置き換えられます。列の管理方法をコードかデザイナーのどちらかへ統一してください。
12. C#のListViewに関するよくある質問
12-1. ListViewとDataGridViewはどちらを使うべきですか?
一覧を表示して選択することが中心であれば、ListViewが適しています。
セル単位の編集、入力検証、データソースとのバインディング、列ごとの高度な表示制御が必要であれば、DataGridViewが適しています。
具体的には、次のように判断できます。
| 要件 | 推奨 |
| ファイル一覧やアイコン一覧 | ListView |
| 読み取り専用の簡易一覧 | ListView |
| チェック付き処理対象一覧 | ListView |
| データベースの編集画面 | DataGridView |
| セルごとに入力する表 | DataGridView |
| コンボボックス列やボタン列 | DataGridView |
12-2. ListViewにデータバインディングはできますか?
Windows Formsの標準ListViewには、DataGridViewのDataSourceのような一般的なデータバインディング機能は用意されていません。
通常は、データのコレクションからListViewItemを作成してItemsへ追加します。
List<UserData> users =GetUsers();ListViewItem[] items =users.Select(user =>{ListViewItem item =new ListViewItem(new[]{user.Id.ToString(),user.Name,user.Email});
item.Tag = user;return item;}).ToArray();
listViewUsers.Items.AddRange(items);
データ変更を自動的に画面へ反映したい場合は、再描画処理を作成するか、データバインディングに強いDataGridViewを検討してください。
WPFのListViewでは、ItemsSourceを使ったバインディングが一般的です。
12-3. 選択されていない場合はどのように判定しますか?
SelectedItems.CountまたはSelectedIndices.Countが0かどうかを確認します。
if (listViewUsers.SelectedItems.Count == 0){MessageBox.Show("行が選択されていません。");return;}SelectedIndicesを使う場合は次のとおりです。
if (listViewUsers.SelectedIndices.Count == 0){return;}SelectedItems[0]やSelectedIndices[0]へアクセスする前に、必ず件数を確認してください。
12-4. 特定の値を持つ行を検索できますか?
FindItemWithTextメソッドを使用できます。
ListViewItem? item =listViewUsers.FindItemWithText("山田太郎");if (item != null){item.Selected = true;item.Focused = true;item.EnsureVisible();}
完全一致で独自に検索する場合は、ループで比較します。
private ListViewItem? FindByEmail(string email){foreach (ListViewItem itemin listViewUsers.Items){if (item.SubItems.Count <= 2){continue;} if (string.Equals(item.SubItems<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[2]</span>.Text,email,StringComparison.OrdinalIgnoreCase)){return item;}}return null;
}
Tagへ元オブジェクトを保存している場合は、オブジェクトのプロパティから検索できます。
ListViewItem? item =listViewUsers.Items.Cast<ListViewItem>().FirstOrDefault(x =>x.Tag is UserData user &&user.Id == 10);12-5. 行ごとにボタンやコントロールを配置できますか?
標準のWindows Forms ListViewには、各セルへButtonやComboBoxなどのコントロールを直接配置する機能はありません。
独自描画や、スクロール位置に合わせてコントロールを重ねる方法はありますが、実装が複雑になります。
行ごとにボタンを表示したい場合は、次の選択肢が適しています。
DataGridViewのDataGridViewButtonColumnを使う
UserControlを並べる
FlowLayoutPanelを使う
独自コントロールを作成する
単純な操作であれば、ListViewの外側に「編集」「削除」ボタンを配置し、選択行へ処理を行う設計が扱いやすいでしょう。
12-6. ListViewの内容をCSVファイルへ保存できますか?
ItemsとSubItemsを順番に読み取り、CSV形式の文字列を作成すれば保存できます。
using System.Text;private void SaveListViewToCsv(string filePath){StringBuilder builder =new StringBuilder();
string header =string.Join(",",listViewUsers.Columns.Cast<ColumnHeader>().Select(column =>EscapeCsv(column.Text)));builder.AppendLine(header);foreach (ListViewItem itemin listViewUsers.Items){string line =string.Join(",",item.SubItems.Cast<ListViewItem.ListViewSubItem>().Select(subItem =>EscapeCsv(subItem.Text)));builder.AppendLine(line);}File.WriteAllText(filePath,builder.ToString(),new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
}
CSV用のエスケープ処理です。
private static string EscapeCsv(string value){if (value.Contains('"')){value = value.Replace(""", """");}if (value.Contains(',') ||value.Contains('"') ||value.Contains('\r') ||value.Contains('\n')){value = $"\"{value}\"";}return value;
}
保存ダイアログを使用する例です。
private void buttonSave_Click(object sender,EventArgs e){using SaveFileDialog dialog =new SaveFileDialog{Filter ="CSVファイル (.csv)|.csv",FileName = "users.csv",AddExtension = true,DefaultExt = "csv"};if (dialog.ShowDialog() !=DialogResult.OK){return;}SaveListViewToCsv(dialog.FileName);
}
12-7. WPFのListViewでも同じコードを使用できますか?
Windows FormsとWPFではListViewの仕組みが異なるため、同じコードは使用できません。
Windows Formsでは次のように項目を追加します。
ListViewItem item =new ListViewItem("1");item.SubItems.Add("山田太郎");
listViewUsers.Items.Add(item);
WPFでは、通常はデータクラスのコレクションをItemsSourceへ設定します。
public ObservableCollection<UserData> Users{get;} = new ObservableCollection<UserData>();Users.Add(new UserData{Id = 1,Name = "山田太郎",Email = "yamada@example.com"});XAMLではGridViewを定義します。
<ListView ItemsSource="{Binding Users}"><ListView.View><GridView><GridViewColumnHeader="ID"DisplayMemberBinding="{Binding Id}" /><GridViewColumnHeader="名前"DisplayMemberBinding="{Binding Name}" /><GridViewColumnHeader="メールアドレス"DisplayMemberBinding="{Binding Email}" /></GridView></ListView.View></ListView>Windows Forms用の記事を参考にするときは、System.Windows.Forms.ListViewが対象であることを確認してください。
まとめ
C#のWindows FormsでListViewを使用すると、複数列の一覧、チェック付き一覧、アイコン付き一覧などを柔軟に作成できます。
表形式で表示するときは、まず次の基本設定を行います。
listViewUsers.View = View.Details;listViewUsers.FullRowSelect = true;listViewUsers.GridLines = true;複数列の行は、ListViewItemとSubItemsを使って追加します。
ListViewItem item =new ListViewItem("1");item.SubItems.Add("山田太郎");item.SubItems.Add("yamada@example.com");
listViewUsers.Items.Add(item);
選択行を取得するときは、未選択時の例外を防ぐため、SelectedItems.Countを確認します。
if (listViewUsers.SelectedItems.Count > 0){ListViewItem item =listViewUsers.SelectedItems[0];}複数選択された行を削除するときは、選択項目を配列へコピーするか、後ろから削除します。
ListViewItem[] selectedItems =listViewUsers.SelectedItems.Cast<ListViewItem>().ToArray();foreach (ListViewItem item in selectedItems){listViewUsers.Items.Remove(item);}
列幅は、AutoResizeColumnまたはAutoResizeColumnsで調整できます。
listViewUsers.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);大量データを追加するときは、BeginUpdate、EndUpdate、AddRangeを組み合わせ、さらに件数が多い場合はVirtualModeを検討します。
ListViewは、単純な一覧表示から選択・削除・チェック・並べ替えまで幅広く対応できます。ただし、セルの直接編集やデータバインディングを中心とする画面では、DataGridViewのほうが適している場合があります。画面の目的に応じて、ListView、ListBox、DataGridViewを使い分けることが重要です。

