6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ListView(.Net)を仮想モードで高速化する

Last updated at Posted at 2021-06-25

ListViewの高速化

.NetListViewに大量のデータを表示しようとすると時間が掛かってしまいます。
そこでいくつかある高速化の手段の1つとして、仮想化モードを紹介します。

仮想モードのすごいところ

仮想モードではすべてをデータを描画するのではなく、見えているものだけを描画します。
なので、非常に軽量です。

どのくらい遅いのか(動画)

軽い対策のものを載せていきます。

  • 1行ずつAdd()
  • BeginUpdate()とEndUpdate()を導入
  • AddRange()を導入

1行ずつAdd()

これは対策なしです。
ただひたすら1行ずつAdd()していく
遅すぎるので、表示する行数は10000にしています。(他は100000です。)
約5.73秒でした。

foreach (ListViewItem listViewItem in this.listViewItemList)
{
    this.listView1.Items.Add(listViewItem);
}

add.gif

BeginUpdate()EndUpdate()を導入

描画を止めておく関数BeginUpdate()EndUpdate()を導入する
件数は100000件です。
約3.71秒でした。

this.listView1.BeginUpdate();
foreach (ListViewItem listViewItem in this.listViewItemList)
{
    this.listView1.Items.Add(listViewItem);
}
this.listView1.EndUpdate();

begin-end.gif

AddRange()に置き換え

データ追加をAddRange()でまるごとに追加します。
件数は100000件です。
約2.57秒でした。

this.listView1.BeginUpdate();
this.listView1.Items.AddRange(this.listViewItemList.ToArray());
this.listView1.EndUpdate();

addrange.gif

仮想モードだとどのくらい早いのか(動画)

ezgif-1-f822828ecd41.gif

できなくなること

仮想モードを使用することで、できなくなることがあります。
これは選択されている項目がわからなくなるということです。
仮想モードがオンlistView1.SelectedItemsがアクセスできなるなります。
しかし選択されたインデックスlistView1.SelectedIndicesは利用可能なので、そちらをうまく使いましょう。

仮想モードでもできること

データの更新, 選択された項目のデータ取得, Insert, Removeです。

以下のコードで実装されているので、躓いた際には参考にしてください。

仮想モードの実装コード

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;

namespace TestTask
{
    public partial class Form1 : Form
    {
        List<ListViewItem> listViewItemList;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // ListViewに仮想モードでデータを更新するイベントを追加
            this.listView1.RetrieveVirtualItem += new RetrieveVirtualItemEventHandler(listView1_RetrieveVirtualItem);
            // 仮想モードをオンに
            this.listView1.VirtualMode = true;
        }

        private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
        {
            e.Item = this.listViewItemList[e.ItemIndex];
        }

        private void data1_Click(object sender, EventArgs e)
        {
            this.setDate("Hello");
            this.start();
        }

        private void data2_Click(object sender, EventArgs e)
        {
            this.setDate("こんにちは");
            this.start();
        }

        private void start()
        {
            // 1回仮想モードに表示されるデータ数を0にすることで、別のデータを入れたときに全て更新されるようになる
            this.listView1.VirtualListSize = 0;
            this.listView1.VirtualListSize = this.listViewItemList.Count;
        }

        private void listView1_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListView.SelectedIndexCollection selectedIndices = this.listView1.SelectedIndices;

            foreach(int index in selectedIndices)
            {
                string text = this.listView1.Items[index].Text;
                this.label1.Text = "選択したのは" + text + "です";
            }
        }

        private void insert_Click(object sender, EventArgs e)
        {
            // 追加
            ListViewItem listViewItem = new ListViewItem();
            listViewItem.Text = "追加された項目";

            this.listViewItemList.Insert(10, listViewItem);

            // 仮想モードの表示する数が一つ増えたので更新
            this.listView1.VirtualListSize = this.listViewItemList.Count;
            this.label1.Text = "10番目にInsertしました";
        }

        private void remove_Click(object sender, EventArgs e)
        {
            // 選択された項目を削除
            ListView.SelectedIndexCollection selectedIndices = this.listView1.SelectedIndices;

            foreach (int index in selectedIndices)
            {
                ListViewItem deleteItem = this.listView1.Items[index];
                this.listViewItemList.Remove(deleteItem);
                this.label1.Text = index + "番目の[" + deleteItem.Text + "]が削除されました";

            }

            // 仮想モードの表示する数が一つ減ったので更新
            this.listView1.VirtualListSize = this.listViewItemList.Count;
        }
        private void setDate(string greeting)
        {
            Stopwatch stopWatch = new Stopwatch();

            stopWatch.Start();

            List<ListViewItem> listViewItemList = new List<ListViewItem>();

            for (int i = 0; i < 100000; i++)
            {
                ListViewItem listViewItem = new ListViewItem();
                listViewItem.Text = greeting + i.ToString();
                listViewItemList.Add(listViewItem);
            }
            this.listViewItemList = listViewItemList;

            stopWatch.Stop();

            string time = stopWatch.Elapsed.TotalSeconds.ToString();
            this.label1.Text = "データ生成に" + time + "秒掛かりました。";
        }
    }
}
6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?