C# ListViewの使い方完全ガイド|WinForms・WPFでの表示、追加、選択、ソートまで初心者向けに解説
はじめに
C#でデスクトップアプリを作るとき、「一覧画面を作りたい」「複数列のデータを表示したい」「選択した行の情報を取得したい」といった場面はよくあります。そのようなときに使える代表的なコントロールがListViewです。
ListViewは、WinFormsでもWPFでも利用できる一覧表示用のコントロールです。名前だけを見るとListBoxに似ていますが、複数列の表示、画像付きの表示、チェックボックス、選択行の取得、ソート、検索など、より実用的な一覧画面を作るための機能を備えています。
この記事では、C#のListViewについて、WinFormsとWPFの両方を対象に、基本的な使い方から実務でよく使う処理まで初心者向けに解説します。表示、追加、削除、選択、ソート、検索、データバインディング、よくあるエラーまで順番に確認していきましょう。
1. C#のListViewとは?できることと使いどころ
1-1. ListViewの基本概要
ListViewは、複数の項目を一覧形式で表示するためのUIコントロールです。C#では主に次の2つの環境で利用されます。
WinFormsの
System.Windows.Forms.ListViewWPFの
System.Windows.Controls.ListView
WinFormsのListViewは、Windowsエクスプローラーのような一覧表示に近いコントロールです。アイコン表示、詳細表示、リスト表示など、Viewプロパティを切り替えることで見た目を変更できます。
一方、WPFのListViewは、データバインディングと組み合わせて使うことが多く、GridViewを指定することで表形式の一覧を作成できます。WPFでは見た目のカスタマイズ性が高く、DataTemplateを使えば画像やボタンを含む一覧表示も柔軟に作れます。
ListViewでよく行う処理は次のとおりです。
一覧データの表示
複数列の表示
項目の追加・削除・クリア
選択された行の取得
チェックボックス付き一覧の作成
列クリックによるソート
キーワード検索
画像付き表示
データバインディング
C#で業務アプリや管理画面を作る場合、顧客一覧、商品一覧、ファイル一覧、ログ一覧、タスク一覧などでListViewはよく使われます。
1-2. ListBox・DataGridView・DataGridとの違い
C#には一覧表示に使えるコントロールが複数あります。代表的なものがListBox、ListView、DataGridView、DataGridです。
ListBoxは、シンプルなリスト表示に向いています。基本的には1列の項目を表示する用途が中心で、複数列の表形式にはあまり向いていません。単純に名前一覧や選択肢一覧を表示したい場合に便利です。
ListViewは、ListBoxよりも高機能で、複数列、アイコン、チェックボックス、詳細表示などに対応できます。DataGridViewほど本格的な表編集機能はありませんが、見やすい一覧を作るには十分な機能があります。
DataGridViewは、WinFormsで表形式のデータを表示・編集するための強力なコントロールです。セル単位の編集、列ごとの型指定、データベースとの連携などに向いています。Excelのような表を作りたい場合はDataGridViewが適しています。
DataGridは、WPFで表形式のデータ表示・編集を行うためのコントロールです。WPFで編集可能な一覧や業務用の表を作る場合はDataGridがよく使われます。
簡単に整理すると、次のようになります。
| コントロール | 主な用途 | 特徴 |
|---|---|---|
| ListBox | 単純な一覧 | 1列の選択リストに向いている |
| ListView | 見やすい一覧 | 複数列、アイコン、チェックボックスに対応 |
| DataGridView | WinFormsの表編集 | セル編集やデータベース連携に強い |
| DataGrid | WPFの表編集 | バインディングや編集機能に強い |
「表示中心の一覧」ならListView、「編集中心の表」ならDataGridViewやDataGridを選ぶと考えると分かりやすいです。
1-3. WinFormsとWPFにおけるListViewの違い
WinFormsとWPFでは、同じListViewという名前でも使い方が大きく異なります。
WinFormsのListViewでは、ListViewItemを作成し、Items.Add()で項目を追加します。複数列を表示する場合は、Columnsに列を追加し、ListViewItem.SubItemsに2列目以降のデータを入れます。
C#ListViewItem item = new ListViewItem("001");
item.SubItems.Add("山田太郎");
item.SubItems.Add("東京");
listView1.Items.Add(item);
WPFのListViewでは、オブジェクトのコレクションをItemsSourceにバインドして表示するのが一般的です。列表示をしたい場合は、ListView.ViewにGridViewを指定します。
XML<ListView ItemsSource="{Binding Customers}">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="名前" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="住所" DisplayMemberBinding="{Binding Address}" />
</GridView>
</ListView.View>
</ListView>
WinFormsはコードで直接追加する書き方が中心で、WPFはデータバインディング中心と覚えておくと理解しやすいです。
1-4. ListViewが向いている画面・用途の例
ListViewは、次のような画面に向いています。
顧客一覧画面
商品一覧画面
社員一覧画面
ファイル一覧画面
ログ表示画面
タスク管理画面
設定項目一覧
チェックリスト
検索結果一覧
たとえば、顧客管理アプリで「顧客ID」「名前」「電話番号」「住所」を一覧表示したい場合、ListViewを使えば見やすい一覧画面を簡単に作れます。
ただし、ユーザーがセルを直接編集するような表画面や、データベースの大量データを高度に扱う画面では、DataGridViewやDataGridの方が適している場合があります。ListViewはあくまで「一覧表示を見やすく作る」用途で使うとよいでしょう。
2. WinFormsでListViewを使う基本手順
2-1. ListViewをフォームに配置する方法
WinFormsでListViewを使うには、Visual Studioのフォームデザイナーから配置する方法が簡単です。
手順は次のとおりです。
WinFormsプロジェクトを作成する
ツールボックスから
ListViewを選択するフォーム上にドラッグ&ドロップする
プロパティウィンドウで名前や表示形式を設定する
たとえば、ListViewの名前をlistView1にしておくと、コードから次のように操作できます。
C#listView1.View = View.Details;
listView1.FullRowSelect = true;
listView1.GridLines = true;
よく使う基本設定は次のとおりです。
C#private void Form1_Load(object sender, EventArgs e)
{
listView1.View = View.Details;
listView1.FullRowSelect = true;
listView1.GridLines = true;
}
View.Detailsは複数列の表形式で表示するための設定です。ListViewで業務アプリの一覧画面を作る場合、多くはこのDetailsを使います。
FullRowSelectをtrueにすると、行全体を選択できるようになります。GridLinesをtrueにすると、表の罫線が表示され、見やすくなります。
2-2. Viewプロパティの種類と表示形式
WinFormsのListViewには、表示形式を指定するViewプロパティがあります。主な値は次のとおりです。
| Viewの値 | 表示形式 |
|---|---|
| LargeIcon | 大きいアイコン表示 |
| SmallIcon | 小さいアイコン表示 |
| List | リスト表示 |
| Details | 詳細表示 |
| Tile | タイル表示 |
複数列の一覧を表示したい場合は、必ずView.Detailsを指定します。
C#listView1.View = View.Details;
View.Detailsを指定していないと、列を追加しても画面に表示されないことがあります。ListViewで「列が表示されない」というトラブルの多くは、ViewプロパティがDetailsになっていないことが原因です。
2-3. Columnsで列を追加する方法
ListViewに列を表示するには、Columns.Add()を使います。
C#listView1.Columns.Add("ID", 80);
listView1.Columns.Add("名前", 120);
listView1.Columns.Add("住所", 200);
第1引数には列名、第2引数には列幅を指定します。列の配置を指定したい場合は、次のようにHorizontalAlignmentを指定できます。
C#listView1.Columns.Add("ID", 80, HorizontalAlignment.Left);
listView1.Columns.Add("名前", 120, HorizontalAlignment.Left);
listView1.Columns.Add("年齢", 60, HorizontalAlignment.Right);
列を追加する前に既存の列をクリアしたい場合は、次のようにします。
C#listView1.Columns.Clear();
初期化処理では、重複して列が追加されないようにColumns.Clear()を実行してから列を追加すると安全です。
2-4. ListViewItemで行データを追加する方法
ListViewに行を追加するには、ListViewItemを作成してItems.Add()で追加します。
C#ListViewItem item = new ListViewItem("001");
listView1.Items.Add(item);
このコードでは、1列目に001と表示される行が追加されます。
複数の項目を追加する場合は、次のように書けます。
C#listView1.Items.Add(new ListViewItem("001"));
listView1.Items.Add(new ListViewItem("002"));
listView1.Items.Add(new ListViewItem("003"));
ただし、複数列のデータを扱う場合は、1列目をListViewItemのテキストとして指定し、2列目以降をSubItemsとして追加します。
2-5. SubItemsで複数列データを表示する方法
ListViewで複数列を表示するには、SubItemsを使います。
C#ListViewItem item = new ListViewItem("001");
item.SubItems.Add("山田太郎");
item.SubItems.Add("東京");
listView1.Items.Add(item);
この場合、1列目に001、2列目に山田太郎、3列目に東京が表示されます。
初期設定を含めると、次のようになります。
C#private void Form1_Load(object sender, EventArgs e)
{
listView1.View = View.Details;
listView1.FullRowSelect = true;
listView1.GridLines = true;
listView1.Columns.Add("ID", 80);
listView1.Columns.Add("名前", 120);
listView1.Columns.Add("住所", 200);
ListViewItem item = new ListViewItem("001");
item.SubItems.Add("山田太郎");
item.SubItems.Add("東京都");
listView1.Items.Add(item);
}
SubItems[0]は1列目、SubItems[1]は2列目、SubItems[2]は3列目を表します。選択行から列データを取得するときにも、この考え方を使います。
3. WinForms ListViewの表示・追加・削除・クリア
3-1. 初期データをListViewに表示するサンプルコード
WinFormsのListViewに初期データを表示する基本サンプルを見てみましょう。
C#private void Form1_Load(object sender, EventArgs e)
{
// 表示設定
listView1.View = View.Details;
listView1.FullRowSelect = true;
listView1.GridLines = true;
// 列の設定
listView1.Columns.Add("ID", 80);
listView1.Columns.Add("名前", 120);
listView1.Columns.Add("メールアドレス", 200);
// データ追加
AddCustomer("001", "山田太郎", "yamada@example.com");
AddCustomer("002", "佐藤花子", "sato@example.com");
AddCustomer("003", "鈴木一郎", "suzuki@example.com");
}
private void AddCustomer(string id, string name, string email)
{
ListViewItem item = new ListViewItem(id);
item.SubItems.Add(name);
item.SubItems.Add(email);
listView1.Items.Add(item);
}
AddCustomerのように、行追加用のメソッドを作っておくと、初期表示でもボタン追加でも同じ処理を使い回せます。
ListViewにデータを表示するときは、まず列を作成し、その後に行データを追加します。View.Detailsの設定も忘れないようにしましょう。
3-2. ボタンクリックで項目を追加する方法
テキストボックスに入力した内容をListViewに追加する例です。フォームに次のコントロールがあるとします。
txtIdtxtNametxtEmailbtnAddlistView1
ボタンクリックイベントに次のコードを書きます。
C#private void btnAdd_Click(object sender, EventArgs e)
{
string id = txtId.Text;
string name = txtName.Text;
string email = txtEmail.Text;
if (string.IsNullOrWhiteSpace(id) ||
string.IsNullOrWhiteSpace(name) ||
string.IsNullOrWhiteSpace(email))
{
MessageBox.Show("すべての項目を入力してください。");
return;
}
ListViewItem item = new ListViewItem(id);
item.SubItems.Add(name);
item.SubItems.Add(email);
listView1.Items.Add(item);
txtId.Clear();
txtName.Clear();
txtEmail.Clear();
}
入力チェックを入れておくことで、空の行が追加されるのを防げます。追加後にテキストボックスをクリアすると、続けて入力しやすくなります。
3-3. 選択中の項目を削除する方法
ListViewで選択中の行を削除するには、SelectedItemsを使います。
C#private void btnDelete_Click(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count == 0)
{
MessageBox.Show("削除する行を選択してください。");
return;
}
listView1.Items.Remove(listView1.SelectedItems[0]);
}
複数選択が有効な場合は、選択された項目をすべて削除できます。
C#private void btnDelete_Click(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count == 0)
{
MessageBox.Show("削除する行を選択してください。");
return;
}
foreach (ListViewItem item in listView1.SelectedItems)
{
listView1.Items.Remove(item);
}
}
ただし、コレクションを操作しながら列挙すると問題が起きる場合があります。より安全に書くなら、いったん配列に変換します。
C#foreach (ListViewItem item in listView1.SelectedItems.Cast<ListViewItem>().ToArray())
{
listView1.Items.Remove(item);
}
このコードを使う場合は、ファイルの先頭に次のusingが必要です。
C#using System.Linq;
3-4. すべての項目をクリアする方法
ListViewのすべての行を削除するには、Items.Clear()を使います。
C#private void btnClear_Click(object sender, EventArgs e)
{
listView1.Items.Clear();
}
列も含めてすべて削除したい場合は、Columns.Clear()も使います。
C#listView1.Items.Clear();
listView1.Columns.Clear();
通常、一覧データだけを消したい場合はItems.Clear()、列定義から作り直したい場合はColumns.Clear()も使う、と覚えておきましょう。
3-5. 画像付きで表示する方法
WinFormsのListViewでは、ImageListを使って画像付きの項目を表示できます。
フォームにImageListを追加し、画像を登録しておきます。コードで画像を登録する場合は、次のように書けます。
C#private void Form1_Load(object sender, EventArgs e)
{
imageList1.Images.Add("user", Image.FromFile("user.png"));
imageList1.Images.Add("admin", Image.FromFile("admin.png"));
listView1.SmallImageList = imageList1;
listView1.View = View.Details;
listView1.Columns.Add("ID", 80);
listView1.Columns.Add("名前", 120);
listView1.Columns.Add("権限", 100);
ListViewItem item1 = new ListViewItem("001");
item1.ImageKey = "user";
item1.SubItems.Add("山田太郎");
item1.SubItems.Add("一般ユーザー");
listView1.Items.Add(item1);
ListViewItem item2 = new ListViewItem("002");
item2.ImageKey = "admin";
item2.SubItems.Add("佐藤花子");
item2.SubItems.Add("管理者");
listView1.Items.Add(item2);
}
View.Detailsの場合は、主に1列目に小さいアイコンが表示されます。大きなアイコンで表示したい場合は、LargeImageListとView.LargeIconを使います。
C#listView1.LargeImageList = imageList1;
listView1.View = View.LargeIcon;
ファイル一覧やユーザー一覧など、見た目で種類を区別したい場合に画像付きListViewは便利です。
4. WinForms ListViewで項目を選択・取得する方法
4-1. 選択された行を取得する方法
ListViewで選択された行を取得するには、SelectedItemsを使います。
C#private void btnGetSelected_Click(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count == 0)
{
MessageBox.Show("行が選択されていません。");
return;
}
ListViewItem selectedItem = listView1.SelectedItems[0];
MessageBox.Show(selectedItem.Text);
}
selectedItem.Textは1列目の値です。たとえば1列目がIDなら、選択された行のIDを取得できます。
ListViewでは、行が選択されていない状態でSelectedItems[0]にアクセスするとエラーになります。そのため、必ずSelectedItems.Countを確認してから取得しましょう。
4-2. 選択された列データを取得する方法
複数列のデータを取得するには、SubItemsを使います。
C#private void btnGetSelected_Click(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count == 0)
{
MessageBox.Show("行が選択されていません。");
return;
}
ListViewItem item = listView1.SelectedItems[0];
string id = item.SubItems[0].Text;
string name = item.SubItems[1].Text;
string email = item.SubItems[2].Text;
MessageBox.Show($"ID: {id}\n名前: {name}\nメール: {email}");
}
SubItems[0]が1列目、SubItems[1]が2列目、SubItems[2]が3列目です。列数より大きいインデックスを指定するとエラーになるため、列の数とインデックスの対応に注意しましょう。
4-3. 複数選択を有効にする方法
ListViewで複数の行を選択できるようにするには、MultiSelectをtrueにします。
C#listView1.MultiSelect = true;
複数選択された行を取得するには、SelectedItemsをforeachで処理します。
C#private void btnShowSelected_Click(object sender, EventArgs e)
{
foreach (ListViewItem item in listView1.SelectedItems)
{
string id = item.SubItems[0].Text;
string name = item.SubItems[1].Text;
Console.WriteLine($"{id}: {name}");
}
}
複数選択を無効にしたい場合は、MultiSelectをfalseにします。
C#listView1.MultiSelect = false;
業務アプリでは、削除や一括処理を行う画面では複数選択を有効にし、詳細表示や編集画面では単一選択にすることが多いです。
4-4. SelectedIndexChangedイベントの使い方
選択行が変わったタイミングで処理を行いたい場合は、SelectedIndexChangedイベントを使います。
C#private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count == 0)
{
return;
}
ListViewItem item = listView1.SelectedItems[0];
txtId.Text = item.SubItems[0].Text;
txtName.Text = item.SubItems[1].Text;
txtEmail.Text = item.SubItems[2].Text;
}
この例では、ListViewで選択された行の内容をテキストボックスに表示しています。編集画面でよく使うパターンです。
注意点として、SelectedIndexChangedは選択が解除されたときにも発生します。そのため、イベント内では必ずSelectedItems.Countを確認する必要があります。
4-5. チェックボックス付きListViewの使い方
ListViewにチェックボックスを表示するには、CheckBoxesをtrueにします。
C#listView1.CheckBoxes = true;
チェックされた項目を取得するには、CheckedItemsを使います。
C#private void btnGetChecked_Click(object sender, EventArgs e)
{
foreach (ListViewItem item in listView1.CheckedItems)
{
string id = item.SubItems[0].Text;
string name = item.SubItems[1].Text;
MessageBox.Show($"{id}: {name}");
}
}
チェックボックス付きListViewは、複数の対象を選んで一括処理する画面に向いています。
たとえば、次のような用途があります。
複数ユーザーを選んで削除する
複数商品を選んで出力する
複数ファイルを選んで処理する
タスクの完了状態を管理する
選択状態とチェック状態は別物です。行が選択されているかどうかはSelectedItems、チェックされているかどうかはCheckedItemsで取得します。
5. WinForms ListViewでソート・検索・編集を行う方法
5-1. 列クリックで昇順・降順にソートする方法
ListViewで列ヘッダーをクリックしたときにソートするには、ColumnClickイベントとListViewItemSorterを使います。
まず、ソート用のクラスを作成します。
C#using System.Collections;
using System.Windows.Forms;
public class ListViewItemComparer : IComparer
{
private int column;
private SortOrder sortOrder;
public ListViewItemComparer(int column, SortOrder sortOrder)
{
this.column = column;
this.sortOrder = sortOrder;
}
public int Compare(object x, object y)
{
string textX = ((ListViewItem)x).SubItems[column].Text;
string textY = ((ListViewItem)y).SubItems[column].Text;
int result = string.Compare(textX, textY);
if (sortOrder == SortOrder.Descending)
{
result = -result;
}
return result;
}
}
次に、フォーム側で列クリックイベントを実装します。
C#private int sortColumn = -1;
private SortOrder sortOrder = SortOrder.Ascending;
private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
{
if (e.Column == sortColumn)
{
sortOrder = sortOrder == SortOrder.Ascending
? SortOrder.Descending
: SortOrder.Ascending;
}
else
{
sortColumn = e.Column;
sortOrder = SortOrder.Ascending;
}
listView1.ListViewItemSorter = new ListViewItemComparer(e.Column, sortOrder);
listView1.Sort();
}
これで、同じ列をクリックするたびに昇順と降順が切り替わります。
5-2. 数値・日付を正しくソートする方法
文字列としてソートすると、数値や日付の順番がおかしくなることがあります。
たとえば、文字列として比較すると、次のような順番になる場合があります。
1
10
2
これは文字列比較では先頭から比較するためです。数値として正しく並べたい場合は、int.TryParseやdecimal.TryParseを使って数値に変換してから比較します。
C#public int Compare(object x, object y)
{
string textX = ((ListViewItem)x).SubItems[column].Text;
string textY = ((ListViewItem)y).SubItems[column].Text;
int result;
if (decimal.TryParse(textX, out decimal numX) &&
decimal.TryParse(textY, out decimal numY))
{
result = numX.CompareTo(numY);
}
else if (DateTime.TryParse(textX, out DateTime dateX) &&
DateTime.TryParse(textY, out DateTime dateY))
{
result = dateX.CompareTo(dateY);
}
else
{
result = string.Compare(textX, textY);
}
if (sortOrder == SortOrder.Descending)
{
result = -result;
}
return result;
}
このように、数値として解釈できる場合は数値比較、日付として解釈できる場合は日付比較、それ以外は文字列比較にすると実用的です。
金額、数量、年齢、日付、更新日時などをListViewで表示する場合は、文字列ソートのままにしないよう注意しましょう。
5-3. キーワードで項目を検索する方法
ListView内の項目をキーワードで検索するには、各行の列データを確認して一致するものを探します。
C#private void btnSearch_Click(object sender, EventArgs e)
{
string keyword = txtSearch.Text.Trim();
foreach (ListViewItem item in listView1.Items)
{
item.Selected = false;
item.BackColor = Color.White;
bool match = item.SubItems
.Cast<ListViewItem.ListViewSubItem>()
.Any(subItem => subItem.Text.Contains(keyword));
if (!string.IsNullOrEmpty(keyword) && match)
{
item.BackColor = Color.Yellow;
item.Selected = true;
}
}
}
このコードでは、キーワードに一致した行の背景色を黄色にし、選択状態にしています。CastやAnyを使うため、次のusingが必要です。
C#using System.Linq;
using System.Drawing;
検索結果だけを表示したい場合は、元データを別のリストで管理しておき、検索条件に一致するデータだけをListViewに再表示する方法がよく使われます。
C#private List<Customer> customers = new List<Customer>();
private void DisplayCustomers(IEnumerable<Customer> data)
{
listView1.Items.Clear();
foreach (var customer in data)
{
ListViewItem item = new ListViewItem(customer.Id);
item.SubItems.Add(customer.Name);
item.SubItems.Add(customer.Email);
listView1.Items.Add(item);
}
}
5-4. 選択行の内容を編集・更新する方法
選択された行の内容をテキストボックスに表示し、編集後に更新する例です。
まず、選択時にテキストボックスへ値を表示します。
C#private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count == 0)
{
return;
}
ListViewItem item = listView1.SelectedItems[0];
txtId.Text = item.SubItems[0].Text;
txtName.Text = item.SubItems[1].Text;
txtEmail.Text = item.SubItems[2].Text;
}
次に、更新ボタンでListViewの値を書き換えます。
C#private void btnUpdate_Click(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count == 0)
{
MessageBox.Show("更新する行を選択してください。");
return;
}
ListViewItem item = listView1.SelectedItems[0];
item.SubItems[0].Text = txtId.Text;
item.SubItems[1].Text = txtName.Text;
item.SubItems[2].Text = txtEmail.Text;
}
この方法は簡単ですが、実務ではListViewの表示データだけでなく、元データのリストやデータベースも更新する必要があります。ListViewはあくまで画面表示であり、データの本体ではないと考えることが大切です。
5-5. ListViewItemSorterを使ったカスタムソート
ListViewで独自のソートを行う場合は、ListViewItemSorterにIComparerを実装したクラスを設定します。
より実用的なカスタムソートクラスは次のように作れます。
C#using System;
using System.Collections;
using System.Windows.Forms;
public class CustomListViewSorter : IComparer
{
private readonly int columnIndex;
private readonly SortOrder sortOrder;
public CustomListViewSorter(int columnIndex, SortOrder sortOrder)
{
this.columnIndex = columnIndex;
this.sortOrder = sortOrder;
}
public int Compare(object x, object y)
{
ListViewItem itemX = x as ListViewItem;
ListViewItem itemY = y as ListViewItem;
string valueX = itemX.SubItems[columnIndex].Text;
string valueY = itemY.SubItems[columnIndex].Text;
int result = CompareValue(valueX, valueY);
return sortOrder == SortOrder.Descending ? -result : result;
}
private int CompareValue(string valueX, string valueY)
{
if (decimal.TryParse(valueX, out decimal numberX) &&
decimal.TryParse(valueY, out decimal numberY))
{
return numberX.CompareTo(numberY);
}
if (DateTime.TryParse(valueX, out DateTime dateX) &&
DateTime.TryParse(valueY, out DateTime dateY))
{
return dateX.CompareTo(dateY);
}
return string.Compare(valueX, valueY, StringComparison.CurrentCulture);
}
}
列クリックイベントでは次のように使います。
C#private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
{
if (sortColumn == e.Column)
{
sortOrder = sortOrder == SortOrder.Ascending
? SortOrder.Descending
: SortOrder.Ascending;
}
else
{
sortColumn = e.Column;
sortOrder = SortOrder.Ascending;
}
listView1.ListViewItemSorter = new CustomListViewSorter(e.Column, sortOrder);
listView1.Sort();
}
このようにしておくと、文字列、数値、日付が混在する一覧でも自然な順番でソートできます。
6. WPFでListViewを使う基本手順
6-1. XAMLでListViewを配置する方法
WPFでListViewを使うには、XAMLにListViewタグを記述します。
XML<ListView x:Name="listView1" />
シンプルな文字列リストであれば、次のように項目を直接追加できます。
XML<ListView>
<ListViewItem Content="東京" />
<ListViewItem Content="大阪" />
<ListViewItem Content="名古屋" />
</ListView>
ただし、WPFではコードでListViewItemを直接追加するよりも、データクラスを作成し、ItemsSourceにバインドして表示する方法が一般的です。
6-2. GridViewで列を表示する方法
WPFのListViewで複数列を表示するには、GridViewを使います。
XML<ListView x:Name="CustomerListView">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" Width="80" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="名前" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="メール" Width="200" DisplayMemberBinding="{Binding Email}" />
</GridView>
</ListView.View>
</ListView>
GridViewColumnで列を定義し、DisplayMemberBindingで表示するプロパティを指定します。
たとえば、Id、Name、Emailというプロパティを持つクラスを表示する場合に、このXAMLが使えます。
C#public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
WPFでは、表示したいデータをオブジェクトとして用意し、そのプロパティを画面にバインドするのが基本です。
6-3. ItemsSourceでデータをバインドする方法
WPFのListViewにデータを表示するには、ItemsSourceにコレクションを設定します。
C#public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var customers = new List<Customer>
{
new Customer { Id = "001", Name = "山田太郎", Email = "yamada@example.com" },
new Customer { Id = "002", Name = "佐藤花子", Email = "sato@example.com" },
new Customer { Id = "003", Name = "鈴木一郎", Email = "suzuki@example.com" }
};
CustomerListView.ItemsSource = customers;
}
}
このコードでは、CustomerオブジェクトのリストをListViewに表示しています。XAMLのDisplayMemberBindingに指定したプロパティ名と、C#側のクラスのプロパティ名が一致している必要があります。
プロパティ名が間違っていると、ListViewには何も表示されないか、該当する列だけ空白になります。
6-4. ObservableCollectionを使って追加・削除を反映する方法
WPFでは、画面に表示した後でデータを追加・削除する場合、List<T>よりもObservableCollection<T>を使うのが一般的です。
C#using System.Collections.ObjectModel;
public ObservableCollection<Customer> Customers { get; set; }
public MainWindow()
{
InitializeComponent();
Customers = new ObservableCollection<Customer>
{
new Customer { Id = "001", Name = "山田太郎", Email = "yamada@example.com" },
new Customer { Id = "002", Name = "佐藤花子", Email = "sato@example.com" }
};
CustomerListView.ItemsSource = Customers;
}
追加する場合は、次のようにします。
C#private void AddButton_Click(object sender, RoutedEventArgs e)
{
Customers.Add(new Customer
{
Id = "003",
Name = "鈴木一郎",
Email = "suzuki@example.com"
});
}
削除する場合は、選択された項目を取得してRemoveします。
C#private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
if (CustomerListView.SelectedItem is Customer selectedCustomer)
{
Customers.Remove(selectedCustomer);
}
}
ObservableCollectionを使うと、コレクションの追加・削除が自動的に画面へ反映されます。WPFで動的な一覧画面を作る場合は、非常に重要なクラスです。
6-5. SelectedItemで選択データを取得する方法
WPFのListViewで選択されたデータを取得するには、SelectedItemを使います。
C#private void ShowButton_Click(object sender, RoutedEventArgs e)
{
if (CustomerListView.SelectedItem is Customer customer)
{
MessageBox.Show($"{customer.Id} {customer.Name} {customer.Email}");
}
else
{
MessageBox.Show("行が選択されていません。");
}
}
WinFormsではSelectedItems[0]を使いましたが、WPFではバインドしているデータオブジェクトそのものがSelectedItemに入ります。
このため、WPFでは次のように直接プロパティへアクセスできます。
C#Customer customer = (Customer)CustomerListView.SelectedItem;
string name = customer.Name;
ただし、何も選択されていない場合はSelectedItemがnullになります。必ずnullチェック、またはisによる型チェックを行いましょう。
7. WPF ListViewの表示・選択・ソート・検索
7-1. GridViewColumnで複数列を表示する方法
WPFのListViewで表形式の一覧を作る場合、GridViewColumnを使って列を定義します。
XML<ListView x:Name="ProductListView">
<ListView.View>
<GridView>
<GridViewColumn Header="商品ID" Width="80" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="商品名" Width="160" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="価格" Width="100" DisplayMemberBinding="{Binding Price}" />
<GridViewColumn Header="在庫数" Width="80" DisplayMemberBinding="{Binding Stock}" />
</GridView>
</ListView.View>
</ListView>
対応するデータクラスは次のようになります。
C#public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public int Stock { get; set; }
}
データを設定するコードは次のとおりです。
C#ProductListView.ItemsSource = new List<Product>
{
new Product { Id = "P001", Name = "キーボード", Price = 3000, Stock = 12 },
new Product { Id = "P002", Name = "マウス", Price = 1500, Stock = 30 },
new Product { Id = "P003", Name = "モニター", Price = 28000, Stock = 5 }
};
WPFのListViewでは、列に表示する値をDisplayMemberBindingで指定するため、クラスのプロパティ設計が重要です。
7-2. SelectionChangedイベントの使い方
WPFで選択行が変わったときに処理を行うには、SelectionChangedイベントを使います。
XAMLでイベントを指定します。
XML<ListView x:Name="ProductListView" SelectionChanged="ProductListView_SelectionChanged">
<ListView.View>
<GridView>
<GridViewColumn Header="商品ID" Width="80" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="商品名" Width="160" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="価格" Width="100" DisplayMemberBinding="{Binding Price}" />
</GridView>
</ListView.View>
</ListView>
C#側にイベント処理を書きます。
C#private void ProductListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ProductListView.SelectedItem is Product product)
{
ProductNameTextBox.Text = product.Name;
PriceTextBox.Text = product.Price.ToString();
}
}
選択されたデータを詳細欄に表示したり、編集フォームに反映したりする場合に使えます。
WPFでは、MVVMパターンを使う場合、イベントではなくSelectedItemをViewModelのプロパティにバインドする方法もよく使われます。
XML<ListView ItemsSource="{Binding Products}"
SelectedItem="{Binding SelectedProduct}">
</ListView>
このようにすると、選択された商品をViewModel側で扱えます。
7-3. CollectionViewSourceでソートする方法
WPFでListViewのデータをソートするには、CollectionViewSourceを使う方法が便利です。
C#using System.ComponentModel;
using System.Windows.Data;
private void SortByName()
{
ICollectionView view = CollectionViewSource.GetDefaultView(ProductListView.ItemsSource);
view.SortDescriptions.Clear();
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
}
価格で降順ソートしたい場合は、次のようにします。
C#private void SortByPriceDescending()
{
ICollectionView view = CollectionViewSource.GetDefaultView(ProductListView.ItemsSource);
view.SortDescriptions.Clear();
view.SortDescriptions.Add(new SortDescription("Price", ListSortDirection.Descending));
}
SortDescriptionの第1引数にはプロパティ名を指定します。文字列で指定するため、プロパティ名の入力ミスに注意しましょう。
複数条件でソートすることもできます。
C#view.SortDescriptions.Clear();
view.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending));
view.SortDescriptions.Add(new SortDescription("Price", ListSortDirection.Descending));
この例では、カテゴリ順に並べたうえで、同じカテゴリ内では価格の高い順に並びます。
7-4. CollectionViewSourceでフィルタ検索する方法
WPFでListViewの表示内容を検索・絞り込みするには、ICollectionView.Filterを使います。
C#private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(ProductListView.ItemsSource);
string keyword = SearchTextBox.Text.Trim();
view.Filter = obj =>
{
if (obj is Product product)
{
return string.IsNullOrEmpty(keyword) ||
product.Name.Contains(keyword) ||
product.Id.Contains(keyword);
}
return false;
};
}
このコードでは、商品名または商品IDにキーワードが含まれている商品だけを表示します。
検索条件を変えた後に明示的に更新したい場合は、Refresh()を呼び出します。
C#view.Refresh();
価格や在庫数など、数値条件で検索することもできます。
C#view.Filter = obj =>
{
if (obj is Product product)
{
return product.Stock > 0;
}
return false;
};
WPFでは、元のコレクションを直接変更しなくても、ICollectionViewを使って表示上の並べ替えや絞り込みができます。
7-5. DataTemplateで表示をカスタマイズする方法
WPFのListViewは、DataTemplateを使うことで表示を自由にカスタマイズできます。
たとえば、商品名を太字にし、価格を別の行に表示する場合は次のように書けます。
XML<ListView ItemsSource="{Binding Products}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Price, StringFormat={}{0}円}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
画像付きの商品一覧を作ることもできます。
XML<ListView ItemsSource="{Binding Products}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="4">
<Image Source="{Binding ImagePath}" Width="40" Height="40" Margin="0,0,8,0" />
<StackPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Price, StringFormat={}{0}円}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
GridViewColumn内でCellTemplateを使えば、列ごとに表示をカスタマイズできます。
XML<GridViewColumn Header="商品">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
WPFのListViewは、単なる表だけでなく、カード型の一覧や画像付きリストにも向いています。
8. C# ListViewでよくあるエラーと対処法
8-1. データが表示されない原因
ListViewにデータが表示されない場合、まず確認したいのは次の点です。
WinFormsの場合は、Items.Add()でデータを追加しているか確認します。また、複数列表示をしたい場合はView.Detailsが設定されている必要があります。
C#listView1.View = View.Details;
WPFの場合は、ItemsSourceにデータが設定されているか、バインディングのプロパティ名が正しいかを確認します。
XML<GridViewColumn Header="名前" DisplayMemberBinding="{Binding Name}" />
このとき、C#側のクラスにNameプロパティが存在しないと表示されません。
C#public string Name { get; set; }
また、WPFで画面表示後にデータを追加しても反映されない場合、List<T>を使っていることが原因のことがあります。追加・削除を画面に反映したい場合は、ObservableCollection<T>を使いましょう。
8-2. 列が表示されない原因
WinFormsのListViewで列が表示されない場合、ほとんどの場合はViewプロパティがDetailsになっていません。
C#listView1.View = View.Details;
列を追加していても、View.Detailsでなければ表形式の列は表示されません。
また、列幅が小さすぎる場合も表示されていないように見えます。
C#listView1.Columns.Add("名前", 120);
WPFの場合は、ListView.ViewにGridViewが設定されているか確認します。
XML<ListView.View>
<GridView>
<GridViewColumn Header="名前" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
GridViewColumnを定義していない場合、表形式の列は表示されません。
8-3. 選択項目が取得できない原因
WinFormsで選択項目が取得できない場合、次のような原因が考えられます。
行が選択されていない
SelectedItems.Countを確認していないFullRowSelectが無効で選択しにくいListViewにフォーカスが当たっていない
安全に取得するには、次のように書きます。
C#if (listView1.SelectedItems.Count > 0)
{
ListViewItem item = listView1.SelectedItems[0];
}
WPFの場合は、SelectedItemがnullでないか確認します。
C#if (CustomerListView.SelectedItem is Customer customer)
{
MessageBox.Show(customer.Name);
}
選択されていない状態で無理にキャストすると、NullReferenceExceptionの原因になります。
8-4. ソート結果がおかしい原因
ListViewのソート結果がおかしい場合、文字列として比較していることが原因の可能性があります。
たとえば、数値を文字列として並べると次のようになることがあります。
1
10
2
3
数値として正しくソートするには、intやdecimalに変換して比較します。
C#decimal.TryParse(valueX, out decimal numberX);
decimal.TryParse(valueY, out decimal numberY);
日付も同様です。文字列として比較するのではなく、DateTimeに変換して比較します。
C#DateTime.TryParse(valueX, out DateTime dateX);
DateTime.TryParse(valueY, out DateTime dateY);
ソート対象の列に数値、日付、文字列が混在する場合は、比較ロジックを分けることが重要です。
8-5. WPFで画面更新されない原因
WPFのListViewでデータを追加・削除しても画面が更新されない場合、List<T>を使っている可能性があります。
C#public List<Product> Products { get; set; }
List<T>は、要素の追加や削除を画面へ通知しません。画面に自動反映したい場合は、ObservableCollection<T>を使います。
C#public ObservableCollection<Product> Products { get; set; }
また、既存オブジェクトのプロパティ値を変更したときに画面へ反映したい場合は、INotifyPropertyChangedを実装します。
C#using System.ComponentModel;
public class Product : INotifyPropertyChanged
{
private string name;
public string Name
{
get => name;
set
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
WPFでは、コレクションの変更にはObservableCollection、プロパティの変更にはINotifyPropertyChangedが必要と覚えておきましょう。
9. C# ListViewの実践サンプル
9-1. WinFormsで顧客一覧を表示するサンプル
WinFormsで顧客一覧を表示する基本サンプルです。
C#public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
public string Tel { get; set; }
public string Address { get; set; }
}
フォーム側のコードです。
C#private List<Customer> customers = new List<Customer>();
private void Form1_Load(object sender, EventArgs e)
{
listView1.View = View.Details;
listView1.FullRowSelect = true;
listView1.GridLines = true;
listView1.Columns.Add("顧客ID", 80);
listView1.Columns.Add("名前", 120);
listView1.Columns.Add("電話番号", 120);
listView1.Columns.Add("住所", 200);
customers.Add(new Customer { Id = "C001", Name = "山田太郎", Tel = "090-1111-1111", Address = "東京都" });
customers.Add(new Customer { Id = "C002", Name = "佐藤花子", Tel = "090-2222-2222", Address = "大阪府" });
customers.Add(new Customer { Id = "C003", Name = "鈴木一郎", Tel = "090-3333-3333", Address = "愛知県" });
DisplayCustomers(customers);
}
private void DisplayCustomers(IEnumerable<Customer> data)
{
listView1.Items.Clear();
foreach (Customer customer in data)
{
ListViewItem item = new ListViewItem(customer.Id);
item.SubItems.Add(customer.Name);
item.SubItems.Add(customer.Tel);
item.SubItems.Add(customer.Address);
listView1.Items.Add(item);
}
}
このように、元データをList<Customer>で管理し、表示処理をDisplayCustomersにまとめると、検索や再表示がしやすくなります。
9-2. WinFormsで追加・削除・検索・ソートを行うサンプル
追加、削除、検索を行う簡単なサンプルです。
C#private void btnAdd_Click(object sender, EventArgs e)
{
Customer customer = new Customer
{
Id = txtId.Text,
Name = txtName.Text,
Tel = txtTel.Text,
Address = txtAddress.Text
};
customers.Add(customer);
DisplayCustomers(customers);
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count == 0)
{
MessageBox.Show("削除する顧客を選択してください。");
return;
}
string id = listView1.SelectedItems[0].SubItems[0].Text;
Customer customer = customers.FirstOrDefault(c => c.Id == id);
if (customer != null)
{
customers.Remove(customer);
DisplayCustomers(customers);
}
}
private void btnSearch_Click(object sender, EventArgs e)
{
string keyword = txtSearch.Text.Trim();
var result = customers.Where(c =>
c.Id.Contains(keyword) ||
c.Name.Contains(keyword) ||
c.Tel.Contains(keyword) ||
c.Address.Contains(keyword));
DisplayCustomers(result);
}
ソートは、元データを並べ替えて再表示する方法もあります。
C#private void btnSortByName_Click(object sender, EventArgs e)
{
var sorted = customers.OrderBy(c => c.Name).ToList();
DisplayCustomers(sorted);
}
このサンプルでは、ListViewを直接操作するのではなく、元データのcustomersを操作してから再表示しています。実務ではこの考え方が重要です。
9-3. WPFで商品一覧を表示するサンプル
WPFで商品一覧を表示するサンプルです。
まず、商品クラスを作成します。
C#public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public int Stock { get; set; }
}
XAMLにListViewを配置します。
XML<ListView x:Name="ProductListView" Margin="10">
<ListView.View>
<GridView>
<GridViewColumn Header="商品ID" Width="80" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="商品名" Width="160" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="価格" Width="100" DisplayMemberBinding="{Binding Price}" />
<GridViewColumn Header="在庫" Width="80" DisplayMemberBinding="{Binding Stock}" />
</GridView>
</ListView.View>
</ListView>
C#側でデータを設定します。
C#public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var products = new List<Product>
{
new Product { Id = "P001", Name = "キーボード", Price = 3000, Stock = 10 },
new Product { Id = "P002", Name = "マウス", Price = 1500, Stock = 25 },
new Product { Id = "P003", Name = "モニター", Price = 28000, Stock = 5 }
};
ProductListView.ItemsSource = products;
}
}
これで、WPFのListViewに商品一覧を表形式で表示できます。
9-4. WPFでデータバインディングを使った一覧画面を作るサンプル
WPFらしい書き方として、ObservableCollectionとデータバインディングを使ったサンプルを紹介します。
C#using System.Collections.ObjectModel;
using System.Windows;
public partial class MainWindow : Window
{
public ObservableCollection<Product> Products { get; set; }
public MainWindow()
{
InitializeComponent();
Products = new ObservableCollection<Product>
{
new Product { Id = "P001", Name = "キーボード", Price = 3000, Stock = 10 },
new Product { Id = "P002", Name = "マウス", Price = 1500, Stock = 25 }
};
DataContext = this;
}
private void AddButton_Click(object sender, RoutedEventArgs e)
{
Products.Add(new Product
{
Id = "P003",
Name = "モニター",
Price = 28000,
Stock = 5
});
}
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
if (ProductListView.SelectedItem is Product product)
{
Products.Remove(product);
}
}
}
XAMLは次のようになります。
XML<StackPanel Margin="10">
<ListView x:Name="ProductListView" ItemsSource="{Binding Products}" Height="200">
<ListView.View>
<GridView>
<GridViewColumn Header="商品ID" Width="80" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="商品名" Width="160" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="価格" Width="100" DisplayMemberBinding="{Binding Price}" />
<GridViewColumn Header="在庫" Width="80" DisplayMemberBinding="{Binding Stock}" />
</GridView>
</ListView.View>
</ListView>
<Button Content="追加" Click="AddButton_Click" Margin="0,10,0,0" />
<Button Content="削除" Click="DeleteButton_Click" Margin="0,5,0,0" />
</StackPanel>
Productsに追加・削除すると、ListViewの表示も自動で更新されます。WPFで一覧画面を作るなら、まずこの形を覚えるとよいでしょう。
9-5. 実務で使いやすいListView設計のポイント
実務でListViewを使う場合は、次の点を意識すると保守しやすくなります。
まず、ListView自体をデータの保存場所にしないことです。画面の表示データと実際のデータを分け、元データはList<T>やObservableCollection<T>、データベースなどで管理します。
次に、行追加や再表示の処理をメソッド化します。たとえばDisplayCustomersやDisplayProductsのような表示専用メソッドを作ると、検索後や削除後の再表示が簡単になります。
また、IDなどの一意な値を必ず持たせることも大切です。選択行を削除・更新するとき、名前だけで判断すると重複の可能性があります。顧客IDや商品IDなど、一意なキーを使うと安全です。
さらに、件数が多い場合は一度にすべて表示しない工夫も必要です。ページング、検索条件の指定、仮想モード、DataGridViewやDataGridの利用なども検討しましょう。
ListViewは便利ですが、すべての一覧画面に最適とは限りません。編集が多い表ならDataGridViewやDataGrid、単純な選択リストならListBoxというように、用途に応じて使い分けることが重要です。
10. C# ListViewに関するよくある質問
10-1. ListViewとDataGridViewはどちらを使うべき?
表示中心の一覧ならListView、編集中心の表ならDataGridViewを使うのがおすすめです。
ListViewは、顧客一覧やファイル一覧のように、データを見やすく表示して選択する画面に向いています。チェックボックスや画像付き表示も簡単にできます。
一方、DataGridViewは、セル単位の編集、列ごとの入力制御、データベース連携などに向いています。Excelのような表を作りたい場合はDataGridViewの方が便利です。
つまり、ユーザーが行を選択して詳細を見るだけならListView、表の中で直接編集するならDataGridViewを選ぶとよいでしょう。
10-2. ListViewにデータベースの内容を表示できる?
はい、表示できます。ただし、WinFormsのListViewにはDataGridViewのような強力な自動バインディング機能はありません。そのため、データベースから取得した結果をループでListViewItemに変換して追加するのが一般的です。
C#listView1.Items.Clear();
foreach (var customer in customers)
{
ListViewItem item = new ListViewItem(customer.Id);
item.SubItems.Add(customer.Name);
item.SubItems.Add(customer.Email);
listView1.Items.Add(item);
}
WPFの場合は、データベースから取得したリストをObservableCollectionやListに変換し、ItemsSourceに設定できます。
C#CustomerListView.ItemsSource = customers;
データベースの内容を一覧表示することは可能ですが、更新や削除を行う場合は、ListViewだけでなくデータベース側の処理も忘れずに実装する必要があります。
10-3. ListViewの列幅を自動調整できる?
WinFormsのListViewでは、AutoResizeColumnsを使って列幅を自動調整できます。
C#listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
ヘッダー文字列に合わせる場合は、次のようにします。
C#listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
データ追加後に実行すると、内容に合わせて列幅を調整できます。
C#DisplayCustomers(customers);
listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
WPFの場合は、GridViewColumnのWidthを明示的に指定するのが一般的です。自動調整したい場合は、Width="Auto"に近い動きをさせるために幅指定を省略する方法もありますが、実務ではレイアウト崩れを避けるため、ある程度の列幅を指定することが多いです。
10-4. ListViewで行全体を選択するには?
WinFormsのListViewで行全体を選択するには、FullRowSelectをtrueにします。
C#listView1.FullRowSelect = true;
複数列の一覧では、この設定を入れておくと操作しやすくなります。
あわせて、罫線を表示するならGridLinesも設定します。
C#listView1.GridLines = true;
WPFのListViewでは、標準で行単位の選択になります。見た目を調整したい場合は、ItemContainerStyleなどを使って選択時のスタイルをカスタマイズできます。
10-5. 大量データを表示するときの注意点
ListViewに大量データを表示すると、画面表示が遅くなることがあります。数百件程度なら問題ないことが多いですが、数万件以上になるとパフォーマンスに注意が必要です。
WinFormsでは、追加前にBeginUpdate()を呼び、追加後にEndUpdate()を呼ぶと描画の負荷を減らせます。
C#listView1.BeginUpdate();
foreach (var customer in customers)
{
ListViewItem item = new ListViewItem(customer.Id);
item.SubItems.Add(customer.Name);
item.SubItems.Add(customer.Email);
listView1.Items.Add(item);
}
listView1.EndUpdate();
また、一度にすべて表示せず、検索条件を指定して絞り込む、ページングする、必要な件数だけ表示するなどの工夫も有効です。
WPFでは、仮想化が有効になっているか確認し、必要に応じてVirtualizingStackPanelを使います。ただし、複雑なDataTemplateを使うと描画コストが高くなるため、表示内容をシンプルにすることも重要です。
大量データを編集する業務画面では、ListViewではなくDataGridViewやDataGridを検討した方がよい場合もあります。
まとめ
C#のListViewは、一覧画面を作るときに便利なコントロールです。WinFormsではListViewItemとSubItemsを使って行と列を追加し、WPFではItemsSourceやGridViewを使ってデータバインディング中心で表示します。
WinFormsのListViewでは、複数列を表示する場合にView.Detailsを設定することが重要です。列はColumns.Add()、行はListViewItem、2列目以降はSubItemsで追加します。選択行はSelectedItems、チェックされた行はCheckedItemsで取得できます。
WPFのListViewでは、GridViewColumnで列を定義し、ItemsSourceにデータをバインドします。追加や削除を画面に自動反映したい場合は、ObservableCollectionを使います。選択されたデータはSelectedItemから取得できます。
ソートや検索を行う場合、WinFormsではListViewItemSorterや元データの並べ替え、WPFではCollectionViewSourceを使うと実装しやすくなります。数値や日付をソートする場合は、文字列比較ではなく、適切な型に変換して比較することが大切です。
ListViewは、顧客一覧、商品一覧、ファイル一覧、ログ表示、チェックリストなど、さまざまな画面で活用できます。ただし、セル編集が多い表や大量データを扱う画面では、DataGridViewやDataGridの方が適している場合もあります。
C#でListViewを使いこなすには、まず「表示」「追加」「削除」「選択」「ソート」「検索」の基本処理を押さえることが重要です。この記事のサンプルをもとに、自分のアプリに合わせた一覧画面を作成してみてください。

