Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8901

DataGridViewの縦スクロールの描画パフォーマンスを改善した話

$
0
0

何が書いてあるか?

  • DataGridViewの縦スクロールの描画パフォーマンスを上げた方法
  • 原因の特定方法(大したことではないが)
  • リフレクションで無理やりSetプロパティの制約を回避した話

役に立つのか?

特殊な状況下なので、万人に役にたつかは微妙。
あ、たぶん。役に立たないです。
リフレクション使えば、例外回避して設定できるのかーぐらいかな。
(そもそも、リフレクション使う人は、それぐらい知ってると思うが)

特殊な状況について

DataGridViewを使いスプレッドシートの入出力画面を作成した。
カラムヘッダは独自に直接セル結合を行っていてFixed設定をしている。
※カラムヘッダは非表示(ColumnHeadersVisible = false)
ロウヘッダは行選択をするため残している。(RowHeadersVisible = true)

事象

DataGridViewの縦スクロール時に、上下端からスクロールする場合のスピードが顕著に遅い(カクカクする)

状況調査&原因特定?

DataGridViewはDataSourceを切り替えて使いまわしているが、遅いのは特定の場合で、すべてが遅いわけではない。

データ量の問題かと、表示レイアウトはそのままにDataSourceのみ変更したところ変化が無いことがわかった
⇒ つまり、データ量、DataSouceは関係ない

遅いところと、そうでないところの違いを調べたところ、
RowHeadersVisible の違いがあることがわかり、非表示にしたところパフォーマンスが改善した。

これが問題か?

対応方法について

RowHeadersVisible の設定でのパフォーマンス有無についてネットで調べたが特に有益な情報は得られず。
パフォーマンス改善について、ダブルバッファリングの手法があることがわかった。
以下、対応内容と採用可否である。

  1. RowHeadersの非表示
    時間があれば、独自実装して1カラム目をヘッダーのように扱うことは可能だが、ナイーブな(お察し下さい)実装のため残念ながら採用できない。

  2. ダブルバッファリング
    ネットで多くみかけたのですが、実際に試してみると、
    早くはなるものの、結合セルの部分の再描画が行われる黒くなり使い物にならない。

ここで手詰まりかと思っていたが、天からのひらめき。
RowHeadersVisibleのtrue/falseで、スピードが変わるならColumnHeadersも効果あるのでは?

ということで、第三の方法として

 3. ColumnHeadersの「表示」
本来、非表示であるカラムヘッダを表示してみたところ、パフォーマンスが改善されたのである。

ちょっとの格闘と対応の完全版

結局原因自体は、よくわからんが、カラムヘッダを表示した場合、パフォーマンスが改善された。
ちなみに、検証結果は以下の通り。
ピンポイントの組み合わせで遅かったよ・・・(多くの人は、この事象に出会わないのでは?)

RowHeadersVisibleColumnHeadersVisible結果
truetrueOK
truefalse遅い
falsetrueOK
falsefalseOK

というわけで、ヘッダの高さをゼロにして非表示扱いにしてみよう。
というのが今回のまとめになる。

dgv.ColumnHeadersHeight=0;//これは、うまくいかない

ところが、ゼロを実際に設定してみたところ例外が発生した。

MSDN(DataGridView.ColumnHeadersHeight プロパティ)を見ると3以下は設定できないようだ。

じゃあ、リフレクションを使ってみようと試してみるが、プロパティはあってもフィールドが見つからない。(NonPublicを指定しているのに)
プロパティ(PropertyInfo)のSetValueを使ってみると例外が発生する・・・

と、しばらく格闘していたが、わかってみればなんのことはない。
使っているクラスが、CommonFramework.Forms.DataGridViewでなく、それを継承したクラスだったからだ。
なまじpublicのプロパティが見えるので気づくのに時間がかかった。

というわけで、以下のコードで対応。

// dgv は、DataGridView(を継承したクラス)のインスタンスdgv.GetType().BaseType.GetField("columnHeadersHeight",BindingFlags.Instance|BindingFlags.NonPublic).SetValue(dgv,0);

なるほど。インスタンスからGetTypeするから混乱の元になったのか。
完全に動的に操作する必要ないから以下で大丈夫だった。
※とはいえ、フィールド名 columnHeadersHeight を特定するのが大変だったわけだが。

typeof(DataGridView).GetField("columnHeadersHeight",BindingFlags.Instance|BindingFlags.NonPublic).SetValue(dgv,0);

.Netのバージョンだったり、そもそも継承の有無で役に立つ可能性は、わからないがメモとして残しておく。


Viewing all articles
Browse latest Browse all 8901

Trending Articles