WinFormsのListViewの項目選択時にデータ呼び出しを行う場合のテクニック
前提
- WinFormsでイベントハンドラを使ってアプリケーション作っているような比較的プログラミング初心者向け
- コード記述量少なめ
やりたいこと
WinFormsのListViewの項目選択時に、そのリスト項目を利用したデータを呼び出す処理があるとして、複数項目選択した際には選択項目すべてを利用したデータ呼び出しをしたい。
データの呼び出しはファイルIOが絡むもので、処理の回数は極力減らしたい。
ListView.SelectedIndexChangedイベントの挙動
SelectedIndexChangedイベントは複数項目選択時には選択項目数分の回数発生する。
SelectedIndexChangedのイベントハンドラにデータ呼び出し処理を記述すると項目の数だけ連続で呼び出し処理が行われ時間がかかってしまう。
例えば MultiSelect = true の ListView に項目A, B, C, Dがあったとして、Aが選択されている状態でShiftを押しながらDをクリックすると、
A, Bが選択状態
A, B, Cが選択状態
A, B, C, Dが選択状態
というようにSelectedIndexChangedイベントが3回発生する。
A, B, C, Dを利用したデータ呼び出しが1回行われることを期待して、SelectedIndexChangedイベントハンドラに ListView.SelectedItems を利用したデータ呼び出し処理が書かれていると、
A, B を利用したデータ呼び出し
A, B, C を利用したデータ呼び出し
A, B, C, D を利用したデータ呼び出し
というように3回データ呼び出しが行われてしまう。
データ呼び出し自体時間がかかる処理の場合、ListViewの項目がひとつずつ選択されていく様子が見えてしまう。
これでは呼び出しの完了まで待ち時間がかかるし、見た目の挙動も美しくない。
これを解決したい。
解決方法
複数選択完了後に一度だけデータ呼び出しをするには Timer を活用する。
TimerのTickイベントハンドラでTimerのStopとListView.SelectedItemsを利用したデータ呼び込み処理を行うようにする。
SelectedIndexChangedが連続で発生している間、そのイベント発生間隔より長いインターバルのTimerをSelectedIndexChangedイベントハンドラ内でStop→Startする。
するとSelectedIndexChangedが連続で発生している間はTimerのTickイベントが発生する前にTimerのインターバルがStop→StartでリセットされるためTickイベントが発生せず、最後の選択が終わった後インターバル経過後にTickが発生する。
これにより選択が全て終わってからデータの呼び込みが1度だけ行われるようになる。
コード例
private Timer LoadingDelayTimer = new Timer(){ Interval = 100 };
//DelayTimerのTickイベントハンドラ
private LoadingDelayTimer_Tick(object sender, EventArgs e)
{
LoadingDelayTimer.Stop();
LoadData();
}
//ListViewのSelectedIndexChangedイベントハンドラ
private ListView_SelectedIndexChanged(object sender, EventArgs e)
{
LoadingDelayTimer.Stop();
LoadingDelayTimer.Start();
}
//データ呼び出し処理
private void LoadData()
{
...
}
- LoadingDelayTimerのIntervalはSelectedIndexChangedが連続的に発生する間隔より明らかに長い時間にする
- 逆にLoadingDelayTimerをあまりに長くしすぎると選択が終わってからデータ呼び出しまでその時間分の遅延を体感できてしまう
- そのあたりを加味して短すぎず長すぎずの時間を設定する