はじめに
この記事は昔のブログからの移設記事です。
今の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の選択しているインデックスを取得する
※というか、自分の古いブログからの転載です。
コメント