WinGrid パフォーマンスの最適化

0 ユーザーが評価
この投稿には確認済みの回答があります。 0 返信 | 1 サポーター

トップ 10 投稿者 
男性
投稿 12
IG Employee
セドリック 投稿済み: 2010/11/25 8:19

元記事 (英語): Mike Saltzmann, http://news.infragistics.com/forums/t/15306.aspx

この記事では、UltraWinGrid コントロールを使用する場合のパフォーマンスを向上する一般的なプログラミング ガイドラインおよびトラブルシューティングのヒントを示します。ここで紹介する方法がすべてのアプリケーションに当てはまるとは限りませんが、開発時よくある問題を例として取り上げているので是非お役立てください。

パフォーマンス

1. 描画

グリッドやその他のコントロールの描画がコントロールのパフォーマンスに影響することが多々あります。ランタイムにコード内のコントロールで複数の処理が実行されており、それぞれがコントロールを全体的にまたは部分的に無効化する原因となる場合にパフォーマンスに影響する可能性が高くなります。

たとえば、グリッドの各行をループしてセルの値を更新するコードを想定します。

 

        private void ultraButton1_Click(object sender, EventArgs e)

        {

            foreach (UltraGridRow row in this.ultraGrid1.Rows)

            {

                row.Cells["Price"].Value = (double)row.Cells["Price"].Value + 1.0;

            }

        }

セルが更新されるたびに、グリッドの一部が無効化され、何度も画面上で再描画されます。このような場合、ループの開始前に描画を無効にし、すべての操作が完了した後に有効に戻します。この処理を行うには、BeginUpdate および EndUpdate メソッドを使用します。

 

        private void ultraButton1_Click(object sender, EventArgs e)

        {

            this.ultraGrid1.BeginUpdate();

 

            try

            {

                foreach (UltraGridRow row in this.ultraGrid1.Rows)

                {

                    row.Cells["Price"].Value = (double)row.Cells["Price"].Value + 1.0;

                }

            }

            finally

            {

                this.ultraGrid1.EndUpdate();

            }

        }

上記のコードでは try...finally ブロックを使用します。これを使用することによって、問題があっても、グリッドの描画が必ず有効に戻ります。

また、グリッドの DataSource を直接変更すると同様な問題が発生します。通常のデータソースは変更する場合にグリッドへ通知し、描画しない場合もグリッドで内部処理を実行します。この内部処理をオフにすることによって、パフォーマンスを向上することが可能です。これには SuspendRowSynchronization/ResumeRowSynchronization メソッドを使用します。これらのメソッドは、BeginUpdate と EndUpdate によってブロック内で常に呼び出す必要があります。

例:

 

        private void ultraButton1_Click(object sender, EventArgs e)

        {

            this.ultraGrid1.BeginUpdate();

            this.ultraGrid1.SuspendRowSynchronization();

 

            try

           

                // グリッドがバインドされる DataTable を取得します// これはデータ ソースが DataTable で

                // あることを前提とします

                DataTable dt = (DataTable)this.ultraGrid1.DataSource;

 

                foreach (DataRow row in dt.Rows)

                {

                    row["Price"] = (double)row["Price"] + 1.0;

                }

            }

            finally

            {

                this.ultraGrid1.ResumeRowSynchronization();

                this.ultraGrid1.EndUpdate();

            }

        }

 

2. CellDisplayStyle プロパティ

デフォルトで、グリッドのすべてのセルはエディターを使用して値を表示および編集します。エディターは多くの能力と柔軟性を持っており、valuelist、カラーピッカー、日付ドロップダウン、マスキング、チェックボックス、ボタンおよびその他機能を提供します。しかし、これらすべての機能のためにオーバーヘッドが余分にかかり、描画をより複雑にしています。アプリケーションがランタイムにどのような機能を必要とし、またどのような機能を必要としないのかをグリッドは知ることができないため、デフォルトですべての機能が有効となります。列で使用しない機能がある場合、CellDisplayType プロパティを設定して、これらの機能をオフにできます。

CellDisplayProperty および他のオプションにの詳細については、インフラジスティックス オンライン ヘルプ. を参照してください。

3. ValueLists

グリッドのスピード落とすもう 1 つのよくある原因として、ValueLists (ValueList、BindableValueList、UltraCombo、UltraComboEditor および UltraDropDown を含む) の不適切な使用が考えられます。ValueList はセルでドロップダウン リストを提供できます。特に DataValue/DisplayValue 機能が使用され、グリッドが値をユーザー フレンドリーな表示テキストに変換する必要がある場合、グリッドはリストを頻繁に検索する必要があることを覚えておいてください。

データ型の一致を確認 - ValueList の DataValues 値のデータ型と異なる場合、パフォーマンスに影響があります。まず、グリッドはあるデータ型からその他のデータ型へ値を変換します。次に、この変換によって例外が発生することがあります。この例外はグリッドでキャッチされ処理されますが、処理しても例外が発生することによってパフォーマンスに大きな影響があります。そのため、ValueList の DataValues がグリッドの値と同じデータ型であることを確認することが重要です。

リストにすべてのグリッド セル値が存在することを確認する - グリッド セル値が ValueList に存在していないことが ValueList によくある問題です。例えば、グリッドには 1..100 範囲の数字値を含む列があり、同じ列に 1..100 範囲の数字値を含む ValueList をアタッチします。これでいいのですが、描画するセルごとにリスト内の一致する項目を見つけるために、グリッドは平均 50 回の比較を実行することが必要となります。しかし今度は列のすべてのセルが 0 の値で開始することを想定します。この場合、項目がリストにないことを決定するために、グリッドは各セルにある 100 個の項目をすべて検索する必要があります。0 の値を ValueList に追加することにより、この問題を多少とも軽減することができます。

バイナリ検索 - UltraDropDown はバイナリ検索ができます。バイナリ検索を実行するために、DropDown はリストに DisplayValues の内部の並べ替え済みリストを保持する必要があります。これは、リストをビルドするために初めてドロップダウンが使用された時に、小さいパフォーマンスヒットがあることを意味します。ただし、それ以降のすべての検索はリニア検索よりもはるかに速くなります。したがって、UltraDropDown の使用は小さい初期のヒットを犠牲として大幅にアプリケーションのパフォーマンスを高めることができます。注:UltraDropDown には DropDownSearchMethod があります。デフォルトは Binary ですが、バイナリ検索を使用しない場合は Linear を使用することができます。

EditAreaDisplayStyle - セルに UltraDropDown または UltraCombo をアタッチし、項目を選択すると、項目の外観がグリッドセルにコピーされます。例えば、ドロップダウンでセルに画像が提供されると、対する項目が選択されたときにこの画像がセルに表示されます。これを処理するには、DropDown はリストで一番大きな画像を検索し、グリッド セルにこの画像に必要なスペースを保持します。この検索によって、グリッドの描画のパフォーマンスに影響があります。UltraDropDown の 2006 Volume 1 から EditAreaDisplayStyle プロパティが追加されました。このプロパティを DisplayText に設定することによって、選択した項目に基づく画像を表示する必要がないことを報告します。特にドロップダウンが画像を使用しない場合は、このプロパティを使用することを推奨します。

4. 再帰

UltraWinGrid はデータ ソースをドリルダウンして、すべてのレベルのために CurrencyManager を作成します。グリッドが再帰的データソースにバインドされる場合、これはグリッドがデフォルトで 100 レベルの CurrencyManagers を作成することを意味します。MaxBandDepth プロパティのデフォルト値は 100 です。

データ ソースの各レベルの深さは対数スケールの CurrencyManagers の数を増やします。したがって最初のレベルのグリッドが必要とする CurrencyManager はひとつだけです。 2 番目のレベルは最初のレベルの行ごとに CurrencyManager を 1 つ必要とします。深さの第 3 のレベルでは 2 番目のレベルの各行に CurrencyManager が 1 つ必要になります。これはすぐに膨大なメモリーを使用することになる可能性があるため、グリッドが CurrencyManager の現在の位置と現在の行を同期することが非常に困難になります。

この問題は、以下の 2 つ解決法があります。

1 つ目は MaxBandDepth を適切なレベル数に設定することです。なぜならデータを 100 レベル下の深さまでドリルダウンすることが必要なエンドユーザーは非常に少ないと思われるからです。そこまで達するまでにどのレベルにいるのかわからなくなってしまうでしょう。通常 5 から 8 の間の値がデータを表示するのに最適なレベルであることと、グリッドのパフォーマンスも落ちることはありません。

2 つ目として、SyncWithCurrencyManager を false に設定できます。これにより、CurrencyManager の現在の位置と現在の行を同期しないようにグリッドに指示します。これは、グリッドと同じデータソースにバインドされたその他のコントロールがアプリケーションにある場合、グリッドの現在の行を変更しても CurrencyManager を配置しないため、その他のコントロールを更新しないことを意味します。しかし多くの場合これは必要ではありません。

5. BindingSource の使用

Visual Studio 2005 (.Net Framework CLR 2.0) の .Net BindingManager にいくつかの変更があります。特定の条件下で DataTable または DataSet に直接バインドされるとグリッドでパフォーマンスの問題が発生する場合があります。

BindingSource でグリッドのデータ ソースをラップするとこれらの問題は修正されます。

6. 例外

上記に簡単に述べたように(3. ValueLists)、キャッチされた例外が処理された場合も、スローされた例外がパフォーマンスに大きい影響を与える場合があります。したがって、すべてのランタイムの例外でブレイクするように Visual Studio IDE を設定し、発生する可能性がある例外を公開するとよいでしょう。例外が発生した場合、例外がどこで発生したかによって例外を回避する方法を決定できます。

 

メモリ使用率

以下は、グリッドによるメモリの使用を削減する方法について紹介します。 

1. 必要ないセルを参照しない - GetCellValue の使用 

グリッドではすべてのデータ行に UltraGridCell オブジェクトを作成せずに、必要な時だけセルを作成します。そのため、できるだけセルへアクセスしないことを推奨します。例えば、合計を計算するには、グリッドの IntializeRow イベントを使用します。

例:

 

        private void ultraGrid1_InitializeRow(object sender, InitializeRowEventArgs e)

        {

            e.Row.Cells["Total"].Value = (double)e.Row.Cells["Quantity"].Value * (double)e.Row.Cells["Price"].Value;

        }

以下のコードは、グリッドの各行の 3 つのセルを参照し、不要なオブジェクトを多く作成します。直接セル オブジェクトの代わりにセル値を処理するメソッドを使用することによって、不要なオブジェクトの作成をしないで済みます。

例:

 

        private void ultraGrid1_InitializeRow(object sender, InitializeRowEventArgs e)

        {

            UltraGridColumn quantityColumn = e.Row.Band.Columns["Quantity"];

            UltraGridColumn priceColumn = e.Row.Band.Columns["Price"];

            e.Row.Cells["Total"].Value = (double)e.Row.GetCellValue(quantityColumn) * (double)e.Row.GetCellValue(priceColumn);

        }

GetCellValue メソッドによって、下記の例では各行の 2 つのセルを作成せず、メモリ利用を削減しました。 

2. 外観の再利用

InitalizeRow イベントでは、セルの Value を設定してセルに外観を適用することがよくあります。セルに外観を適用するには、UltraGridCell への参照の取得が必要なので、これは回避できません。複数のセルに同じオブジェクトを使用することよって、使用するメモリを削減することができます。例えば、セルの ForeColor を負の数では赤に、正の数では黒に変更したい場合を見てみましょう。以下のようなコードになります。 

 

        private void ultraGrid1_InitializeRow(object sender, InitializeRowEventArgs e)

        {

            // 合計列の値を取得します

            UltraGridColumn totalColumn = e.Row.Band.Columns["Total"];

            double total = (double)e.Row.GetCellValue(totalColumn);

 

            if (total < 0)

                e.Row.Cells["Total"].Appearance.ForeColor = Color.Red;

            else

                e.Row.Cells["Total"].Appearance.ForeColor = Color.Black;

        }

上記のコードではすべての行にセルを作成します。セルが Appearance を持つには、セルの作成が必要です。ここでより大きな問題となるのは、セルの Appearance プロパティの作成も遅延されることです。つまり、各行に各セルを作成するだけではなく、各行に対して新しい Appearance オブジェクトも作成します。また、使用可能な色は 2 色だけの場合も、多くの同一の外観オブジェクトを作成することになります。

この問題を解決するために、先に Appearance を作成し、必要に応じて既存の Appearance を使用します。

 

        private void ultraGrid1_InitializeLayout(object sender, Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs e)

        {

            // 負の値の外観を作成します

            Infragistics.Win.Appearance negativeAppearace = e.Layout.Appearances.Add("Negative");

            negativeAppearace.ForeColor = Color.Red;

 

            // 正の値の外観を作成します

            Infragistics.Win.Appearance positiveAppearace = e.Layout.Appearances.Add("Positive");

            positiveAppearace.ForeColor = Color.Black;

        }

 

        private void ultraGrid1_InitializeRow(object sender, InitializeRowEventArgs e)

        {

            UltraGrid grid = (UltraGrid)sender;           

 

            // 合計列の値を取得します

            UltraGridColumn totalColumn = e.Row.Band.Columns["Total"];

            double total = (double)e.Row.GetCellValue(totalColumn);

 

            if (total < 0)

                // グリッドの Appearances コレクションで既に定義された Negative Appearance を使用します

                e.Row.Cells["Total"].Appearance = grid.DisplayLayout.Appearances["Negative"];

            else

                // グリッドの Appearances コレクションで既に定義された Positive Appearance を使用します

                e.Row.Cells["Total"].Appearance = grid.DisplayLayout.Appearances["Positive"];

        }

これにより、複数のオブジェクトの Appearance の同時変更が可能になります。たとえば、正値には緑の代わりに黒を使用したい場合は、ランタイムに Appearance オブジェクトの ForeColor を設定すると、この外観を利用するすべてのオブジェクトが自動的に更新されます。

 

ページ 1 / 1 (1 項目) | RSS
Infragistics Japan
インフラジスティックス ジャパン