SATOXのシテオク日記

~ふもっふ、ふもふも~

ListViewの仮想表示モード その1

いやね、たいした話ではないのですが、ListView(.NET/C#)のVirtualMode(仮想表示)を使ってみたというネタ。
ListViewにはListViewItemと呼ばれる項目を表示できるわけですが、そのアイテムが大量にある場合、その登録に時間が掛かるわけです。ちょっとぐらい待ってもいいと思うかもしれませんが、よりよい操作感を求めるならばこの待ち時間はなくした方がいいのです。
で、以下に示すのが既にListViewItemがnewされている条件で、VirtualModeとAddRangeで一度に登録した場合の違いを試すC#のプログラム。
デザインソースは割愛されているので適宜作成して貼り付けて下さい。登場人物としては、次の通り。
ListView listViewVirtual;
ListView listViewNormal;
CheckBox checkBoxVirtual;
CheckBox checkBoxNormal;

  public partial class Form1 : Form
  {
    private ListViewItem[] itemsA1 = new ListViewItem[8000];
    private ListViewItem[] itemsB1 = new ListViewItem[1000];
    private ListViewItem[] itemsA2 = new ListViewItem[8000];
    private ListViewItem[] itemsB2 = new ListViewItem[1000];

    public Form1()
    {
      InitializeComponent();
      this.listViewVirtual.VirtualMode = true;
      this.listViewVirtual.VirtualListSize = itemsA1.Length;

      for (Int32 lp = 0; lp < itemsA1.Length; lp++) {
        itemsA1[lp] = new ListViewItem("testA1:" + lp.ToString());
      }
      for (Int32 lp = 0; lp < itemsB1.Length; lp++) {
        itemsB1[lp] = new ListViewItem("testB1:" + lp.ToString());
      }
      for (Int32 lp = 0; lp < itemsA2.Length; lp++) {
        itemsA2[lp] = new ListViewItem("testA2:" + lp.ToString());
      }
      for (Int32 lp = 0; lp < itemsB2.Length; lp++) {
        itemsB2[lp] = new ListViewItem("testB2:" + lp.ToString());
      }
    }

    private void listViewVirtual_RetrieveVirtualItem(object sender,
        RetrieveVirtualItemEventArgs e)
    {
      e.Item = (this.checkBoxVirtual.Checked == false) ? 
         itemsA1[e.ItemIndex] : e.Item = itemsB1[e.ItemIndex];
    }

    private void checkBoxVirtual_CheckedChanged(object sender, EventArgs e)
    {
      this.listViewVirtual.EnsureVisible(0);
      this.listViewVirtual.VirtualListSize = (checkBoxVirtual.Checked == false) ?
         itemsA1.Length : itemsB1.Length;
      this.listViewVirtual.Invalidate();
    }

    private void checkBoxNormal_CheckedChanged(object sender, EventArgs e)
    {
      this.listViewNormal.Items.Clear();
      this.listViewNormal.Items.AddRange((this.checkBoxNormal.Checked == false) ? 
         itemsA2 : itemsB2);
    }
  }

かなりのパフォーマンスの違いが見受けられると思います。
VirtualModeでは、ListViewコントロールがListViewItem表示が必要な段階でRetrieveVirtualItemイベントが発生するので、ユーザは表示したいListViewItemを返すといったあんばい。

■選択アイテムの問題
ここまで遊んでみて、VirtualModeいいじゃんいいじゃんと思っていたのですがここで問題が。
ListView.SelectedItemsが使えません。
よく考えてみたら「そりゃそうだ」といった感じなのですが、VirtualModeのListViewは言わば「表示だけ管理」しているものであって、アイテムの状態までは管理していないのです。
これに対応するためには、VirtualItemsSelectionRangeChangedイベントを利用します。
このイベントにはStartIndex、EndIndex、IsSelectedというプロパティがもらえるので、「内部的に」選択アイテムをコレクションしてあげる必要があるわけです。

  private void listViewVirtual_VirtualItemsSelectionRangeChanged(object sender,
        ListViewVirtualItemsSelectionRangeChangedEventArgs e)
  {
    Console.WriteLine(e.StartIndex.ToString() + "-" + e.EndIndex.ToString() + 
      ", Selected = " + e.IsSelected.ToString());
  }

しかし、試してみると、「全ての範囲+Selected=false」しかイベントが来ません。バグ?どうなってるんだろう。
知ってる方、教えて下さいm(_ _)m。
続き↓

■ListViewの仮想表示モード その2 - SATOXのシテオク日記