はじめに
この記事は昔のブログからの移設記事です。
今のVisual Studio用にリメイクしておりますが、古い情報があった場合は申し訳ありません。
上記承諾いただけましたら続きをどうぞ!
ListViewのインデックス番号取得
問題
C#のFormを作成しているときに、csvを読み込んで、その値をListViewに入れ込むプログラムを作りたくなりました。
csvで出力しているログを解析して可視化したかったのです。
なんでjsonとかxmlとかじゃないの?って言われると困りますが。。。
んで、ListViewにデータを入れて、選択したIndex番号を取得してそのカラムのデータを色々ごちゃごちゃしようと思い、まずは選択したIndex番号を取ろうと思ったのですが、ここであることに気づきました。
SelectedIndexプロパティがない
ListBox等であればインデックス番号を取得する際にはSelectedIndexを使うのですが、ListViewにはそれがありません。
解決方法
ListViewにはSelectedIndexがないのですが、代わりにSelectedItemsの中にIndexプロパティが用意されております。
これでSelectedIndexの代替をするわけですね。
というわけで軽く検証用に書いてみました。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace ListViewTestProject { public partial class MainForm : Form { public MainForm() { InitializeComponent(); lv_main.View = View.Details; lv_main.Columns.Add("Column1"); lv_main.Columns.Add("Column2"); lv_main.Columns.Add("Column3"); ListViewItem lvi; lvi = lv_main.Items.Add("item1-1"); lvi.SubItems.Add("item1-2"); lvi.SubItems.Add("item1-3"); lvi = lv_main.Items.Add("item2-1"); lvi.SubItems.Add("item2-2"); lvi.SubItems.Add("item2-3"); lvi = lv_main.Items.Add("item3-1"); lvi.SubItems.Add("item3-2"); lvi.SubItems.Add("item3-3"); } private void lv_main_SelectedIndexChanged(object sender, EventArgs e) { int idx = 0; idx = lv_main.SelectedItems[0].Index; label1.Text = (idx + 1).ToString() + "番目が選択されました。"; } } }
実行するとこんな感じ。

では実際に選択してみましょう。

おーいい感じ。
ではこのまま2つ目も選択しましょう

デデーン!
例外が発生してアプリケーションが強制的に終了されました。
問題その2
1つ目のインデックスを選択したあと、続けて2つ目を選択すると、なぜか例外エラーを吐きすててアプリがお亡くなりになりました。
ArgumentOutOfRangeException はハンドルされませんでした。
‘0’ の InvalidArgument=Value は ‘index’ に対して有効ではありません。
パラメーター名: index
例外エラーのメッセージを見てみると、どうやらindexというパラメータに0が入ってしまったからのようです。
んで調べてみると、どうやらListViewのインデックスを選択しなおした際、現在選択されている項目数)が一時的に0以下になってしまうそうです。
そのせいで「lv_main.SelectedItems[0].Index;」の部分で挙動がおかしくなってしまったのでしょう。
解決方法その2
原因が分かったので、エラー発生条件をつぶしてしまいましょう。
インデックスを選択している項目数(lv_main.SelectedItems.Count)が0以下になるのがいけないのですから、0以下になる際には処理をしないように条件を書いてあげればOKです。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace ListViewTestProject { public partial class MainForm : Form { public MainForm() { InitializeComponent(); lv_main.View = View.Details; lv_main.Columns.Add("Column1"); lv_main.Columns.Add("Column2"); lv_main.Columns.Add("Column3"); ListViewItem lvi; lvi = lv_main.Items.Add("item1-1"); lvi.SubItems.Add("item1-2"); lvi.SubItems.Add("item1-3"); lvi = lv_main.Items.Add("item2-1"); lvi.SubItems.Add("item2-2"); lvi.SubItems.Add("item2-3"); lvi = lv_main.Items.Add("item3-1"); lvi.SubItems.Add("item3-2"); lvi.SubItems.Add("item3-3"); } private void lv_main_SelectedIndexChanged(object sender, EventArgs e) { if (lv_main.SelectedItems.Count > 0) { int idx = 0; idx = lv_main.SelectedItems[0].Index; label1.Text = (idx + 1).ToString() + "番目が選択されました。"; } } } }
というわけで実行!

無事、2つ目を選択しても正常に処理されました!
ちなみに、try-catchで例外対策してもよかです。
try-cacheで書くとこんな感じ(必要以外の部分は端折ります)
private void lv_main_SelectedIndexChanged(object sender, EventArgs e) { int idx = 0; try { idx = lv_main.SelectedItems[0].Index; label1.Text = (idx + 1).ToString() + "番目が選択されました。"; } catch (ArgumentOutOfRangeException) { label1.Text = "エラー!"; } }
基本的にはtry-cacheでの例外処理はifで処理するよりもリソースを多く使う傾向があるらしいので、簡単なものであればifで例外対策してしまったほうがいいでしょう。
まとめ
というわけでまとめます。
・ListViewを使ってインデックス番号をとるときはSelectedItems[n].Indexを使おう
・でも、例外対策はきっちりとね
以上でした。
それでは!
参考
【C#.NET】ListViewの選択しているインデックスを取得する
※というか、自分の古いブログからの転載です。
コメント