GridView のフッターに概要情報を表示する (C#)

作成者: Scott Mitchell

PDF のダウンロード

集計情報は、多くの場合、レポートの下部にある集計行に表示されます。 GridView コントロールを使用すると、フッター行を追加し、プログラムによってそのセルに集計データを挿入することができます。 このチュートリアルでは、このフッター行に集計データを表示する方法について説明します。

はじめに

ユーザーが確認したいのは、各製品の価格、在庫単位数、注文単位数、再注文レベルだけでなく、平均価格、在庫単位の合計数といった集計情報も含まれる場合が多いです。 このような集計情報は、多くの場合、レポートの下部にある集計行に表示されます。 GridView コントロールを使用すると、フッター行を追加し、プログラムによってそのセルに集計データを挿入することができます。

このタスクには、次の 3 つの課題が立ちはだかります。

  1. フッター行を表示するために GridView を構成する
  2. 集計データを算出する。つまり、平均価格や在庫単位の合計数をどのように算出するか
  3. 集計データをフッター行の適切なセルに挿入する

このチュートリアルでは、これらの課題をどう克服するかについて説明していきます。 具体的には、カテゴリをドロップダウン リストとして表示し、そこで選択したカテゴリの製品を GridView に一覧表示するページを作成します。 この GridView には、そのカテゴリに含まれる製品の平均価格と、在庫単位数および注文単位数それぞれの合計数を示すフッター行を表示します。

集計情報が GridView のフッター行に表示される

図 1: 集計情報が GridView のフッター行に表示されています (クリックするとフルサイズの画像が表示されます)

このチュートリアルは、カテゴリから製品のマスター/詳細インターフェイスを使用しているため、前段となる「DropDownList コントロールと GridView を使用したマスター/詳細フィルター処理」チュートリアルで説明した概念を前提として構成されています。 もし、以前のチュートリアルをまだ行っていない場合は、このチュートリアルを続ける前にまずはそちらをご覧ください。

手順 1: カテゴリ用 DropDownList と製品 GridView を追加する

GridView のフッターに集計情報を追加する前に、まずはシンプルにマスター/詳細レポートを作成しましょう。 この最初の手順を完了したら、集計データを含める方法について見ていきます。

まず、CustomFormatting フォルダーの SummaryDataInFooter.aspx ページを開きます。 DropDownList コントロールを追加し、IDCategories に設定します。 次に、DropDownList のスマート タグから [データ ソースの選択] リンクをクリックし、CategoriesBLL クラスの GetCategories() メソッドを呼び出す CategoriesDataSource という名前の新しい ObjectDataSource を追加します。

CategoriesDataSource という名前の新しい ObjectDataSource を追加する

図 2: CategoriesDataSource という名前の新しい ObjectDataSource を追加します (クリックするとフルサイズの画像が表示されます)

ObjectDataSource が CategoriesBLL クラスの GetCategories() メソッドを呼び出すように設定する

図 3: ObjectDataSource で CategoriesBLL クラスの GetCategories() メソッドを呼び出します (クリックするとフルサイズの画像が表示されます)

ObjectDataSource を構成すると、DropDownList のデータ ソース構成ウィザードに自動で戻ります。このウィザードでは、表示するデータ フィールドの値と、DropDownList の ListItem の値に対応するデータ フィールドの値を指定する必要があります。 CategoryName フィールドを表示し、CategoryID を値として使用します。

CategoryName フィールドと CategoryID フィールドを ListItem の Text と Value として使用する

図 4: ListItemText および Value として、それぞれ CategoryName フィールドと CategoryID フィールドを使用します (クリックするとフルサイズの画像が表示されます)

この時点で、システム内のカテゴリを一覧表示する DropDownList (Categories) ができました。 次に、選択したカテゴリに含まれる製品を一覧表示する GridView を追加する必要があります。 ただし、続行する前に、DropDownList のスマート タグから [AutoPostBack を有効にする] チェックボックスをオンにします。 「DropDownList コントロールと GridView を使用したマスター/詳細フィルター処理」のチュートリアルの説明にあるように、DropDownList の AutoPostBack プロパティを true に設定すると、DropDownList の値が変更されるたびにページがポストバックされるようになります。 これにより GridView が更新されるため、新しく選択したカテゴリの製品が表示されます。 AutoPostBack プロパティが false (既定値) に設定されていると、カテゴリを変更してもポストバックが発生しないため、製品一覧は更新されません。

DropDownList のスマート タグの [自動ポストバックを有効にする] チェック ボックスをオンにする

図 5: DropDownList のスマート タグの [AutoPostBack を有効にする] チェックボックスをオンにします (クリックするとフルサイズの画像が表示されます)

選択したカテゴリの製品を表示するために、GridView コントロールをページに追加します。 GridView の IDProductsInCategory に設定し、それを ProductsInCategoryDataSource という名前の新しい ObjectDataSource にバインドします。

ProductsInCategoryDataSource という名前の新しい ObjectDataSource を追加する

図 6: ProductsInCategoryDataSource という名前の新しい ObjectDataSource を追加します (クリックするとフルサイズの画像が表示されます)

ProductsBLL クラスの GetProductsByCategoryID(categoryID) メソッドを呼び出すように ObjectDataSource を構成します。

ObjectDataSource が GetProductsByCategoryID(categoryID) メソッドを呼び出すように設定する

図 7: ObjectDataSource で GetProductsByCategoryID(categoryID) メソッドを呼び出します (クリックするとフルサイズの画像が表示されます)

GetProductsByCategoryID(categoryID) メソッドは入力パラメーターを受け取るので、ウィザードの最後の手順ではパラメーター値のソースを指定します。 選択したカテゴリの製品を表示するには、Categories DropDownList からパラメーターをプルします。

[categoryID] パラメーターの値が選択された [データ ソースの構成] ウィンドウを示すスクリーンショット。

図 8: 選択したカテゴリの DropDownList から categoryID パラメーター値を取得します (クリックするとフルサイズの画像が表示されます)

ウィザードを完了した時点では、GridView に各製品プロパティの BoundField が含まれます。 これらの BoundField をクリーンアップして、BoundField ProductNameUnitPriceUnitsInStockUnitsOnOrder のみが表示されるようにしましょう。 残りの BoundField にフィールド レベルの設定を自由に追加できます (UnitPrice を通貨として書式設定するなど)。 これらの変更を行うと、GridView の宣言マークアップは次のようになります。

<asp:GridView ID="ProductsInCategory" runat="server"
    AutoGenerateColumns="False" DataKeyNames="ProductID"
    DataSourceID="ProductsInCategoryDataSource" EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product"
          SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
            HeaderText="Price"
            HtmlEncode="False" SortExpression="UnitPrice">
            <ItemStyle HorizontalAlign="Right" />
        </asp:BoundField>
        <asp:BoundField DataField="UnitsInStock"
         HeaderText="Units In Stock" SortExpression="UnitsInStock">
            <ItemStyle HorizontalAlign="Right" />
        </asp:BoundField>
        <asp:BoundField DataField="UnitsOnOrder"
           HeaderText="Units On Order" SortExpression="UnitsOnOrder">
            <ItemStyle HorizontalAlign="Right" />
        </asp:BoundField>
    </Columns>
</asp:GridView>

この時点で、選択したカテゴリに含まれる製品の名前、単価、在庫単位数、注文単位数を表示する、完全に機能するマスター/詳細レポートができています。

[Beverages] カテゴリに属する製品の GridView レポートを示すスクリーンショット。

図 9: 選択したカテゴリの DropDownList から categoryID パラメーター値を取得します (クリックするとフルサイズの画像が表示されます)

GridView コントロールを使用すると、ヘッダー行とフッター行の両方を表示できます。 これらの行はそれぞれ ShowHeader プロパティと ShowFooter プロパティの値に応じて表示されます。それぞれ、ShowHeader の規定値は trueShowFooter の既定値は false です。 GridView にフッターを含めるには、ShowFooter プロパティを true に設定します。

GridView の [ShowFooter] プロパティを true に設定する

図 10: GridView の ShowFooter プロパティを true に設定します (クリックするとフルサイズの画像が表示されます)

フッター行には、GridView で定義したフィールドごとにセルが表示されます。ただし、これらのセルは既定では空です。 ブラウザーで進行状況を確認してみましょう。 ShowFooter プロパティが true に設定されているため、GridView には空のフッター行が含まれています。

GridView にフッター行が追加された

図 11: GridView にフッター行が含まれるようになりました (クリックするとフルサイズの画像が表示されます)

図 11 のフッター行の背景は白いため、目立っていません。 そこで、背景色を濃い赤色に指定する FooterStyle CSS クラスを Styles.css で作成し、DataWebControls テーマの GridView.skin スキン ファイルを構成して、この CSS クラスを GridView の FooterStyle にある CssClass プロパティに割り当ててみましょう。 スキンとテーマをブラッシュアップする必要がある場合は、ObjectDataSource でデータを表示する方法に関するチュートリアルを参照してください。

まず、次の CSS クラスを Styles.css に追加します。

.FooterStyle
{
    background-color: #a33;
    color: White;
    text-align: right;
}

FooterStyle CSS クラスのスタイルと HeaderStyle クラスのスタイルは似ていますが、HeaderStyle の背景色の方が少し濃く、テキストは太字で表示されています。 さらに、フッター内のテキストは右揃えですが、ヘッダーのテキストは中央揃えになっています。

次に、この CSS クラスを GridView のすべてのフッターに関連付けるには、DataWebControls テーマの GridView.skin ファイルを開き、FooterStyleCssClass プロパティを設定します。 追加後、ファイルのマークアップは次のようになります。

<asp:GridView runat="server" CssClass="DataWebControlStyle">
   <AlternatingRowStyle CssClass="AlternatingRowStyle" />
   <RowStyle CssClass="RowStyle" />
   <HeaderStyle CssClass="HeaderStyle" />
   <FooterStyle CssClass="FooterStyle" />
   <SelectedRowStyle CssClass="SelectedRowStyle" />
</asp:GridView>

以下のスクリーンショットが示すように、この変更によってフッターがより目立つようになりました。

新しい背景色で書式設定された GridView のフッター行の集計データを示すスクリーンショット。

図 12: GridView のフッター行に赤色の背景色が追加されました (クリックするとフルサイズの画像が表示されます)

手順 3: 集計データを算出する

GridView のフッターが表示できるようになったので、次の課題は、集計データの算出方法です。 この集計情報を計算するには、次の 2 つの方法があります。

  1. SQL クエリを使用することで、データベースに追加のクエリを発行し、特定のカテゴリの集計データを計算することができます。 SQL には、どのデータを集計するかを指定できる GROUP BY 句とともに、多くの集計関数が含まれています。 次の SQL クエリを実行すると、必要な情報が返されます。

    SELECT CategoryID, AVG(UnitPrice), SUM(UnitsInStock),
    SUM(UnitsOnOrder)
    FROM Products
    WHERE CategoryID = categoryID
    GROUP BY CategoryID
    

    もちろん、このクエリは SummaryDataInFooter.aspx ページから直接発行するのではなく、ProductsTableAdapterProductsBLL にメソッドを作成して発行します。

  2. データに基づくカスタム書式設定に関するチュートリアルの説明にあるように、GridView にデータを追加するたびに情報を計算するようにします。GridView の RowDataBound イベント ハンドラーは、データバインドされた後、GridView に行が追加されるたびに一度だけ発生します。 このイベントのイベント ハンドラーを作成することで、集計する値の累計を得ることができます。 最後のデータ行が GridView にバインドされると、合計と平均を計算するために必要な情報が得られます。

データベースへのアクセス回数を減らすことができるだけでなく、データ アクセス レイヤーとビジネス ロジック レイヤーに集計機能を実装する手間が省けるので、私は通常 2 番目のアプローチを採用していますが、どちらの方法でも問題ありません。 このチュートリアルでは 2 番目のオプションを使用し、RowDataBound イベント ハンドラーを用いて累計を追跡してみましょう。

デザイナーで GridView を選択し、プロパティ ウィンドウの稲妻アイコンをクリックしてから RowDataBound イベントをダブルクリックして、GridView の RowDataBound イベント ハンドラーを作成します。 これにより、SummaryDataInFooter.aspx ページの分離コード クラスに ProductsInCategory_RowDataBound という名前の新しいイベント ハンドラーが作成されます。

protected void ProductsInCategory_RowDataBound
    (object sender, GridViewRowEventArgs e)
{
}

累計値を保持するには、イベント ハンドラーのスコープ外で変数を定義する必要があります。 次の 4 つのページレベルの変数を作成します。

  • decimal 型の _totalUnitPrice
  • int 型の _totalNonNullUnitPriceCount
  • int 型の _totalUnitsInStock
  • int 型の _totalUnitsOnOrder

次に、RowDataBound イベント ハンドラーで検出されたデータ行ごとに、これら 3 つの変数をインクリメントするコードを記述します。

// Class-scope, running total variables...
decimal _totalUnitPrice = 0m;
int _totalNonNullUnitPriceCount = 0;
int _totalUnitsInStock = 0;
int _totalUnitsOnOrder = 0;
protected void ProductsInCategory_RowDataBound(object sender,
  GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // Reference the ProductsRow via the e.Row.DataItem property
        Northwind.ProductsRow product =
          (Northwind.ProductsRow)
          ((System.Data.DataRowView)e.Row.DataItem).Row;
        // Increment the running totals (if they are not NULL!)
        if (!product.IsUnitPriceNull())
        {
            _totalUnitPrice += product.UnitPrice;
            _totalNonNullUnitPriceCount++;
        }
        if (!product.IsUnitsInStockNull())
            _totalUnitsInStock += product.UnitsInStock;
        if (!product.IsUnitsOnOrderNull())
            _totalUnitsOnOrder += product.UnitsOnOrder;
    }
}

RowDataBound イベント ハンドラーでは、まず、扱っている対象が DataRow であることの確認が行われます。 この確認が終わると、e.RowGridViewRow オブジェクトにバインドされた Northwind.ProductsRow インスタンスが変数 product に格納されます。 次に、累計値を示す変数が、現在の製品の該当値 (データベース NULL 値が含まれていないと想定) でインクリメントされます。 平均値はこれら 2 つの数値の商であるため、実行中の UnitPrice の合計とNULL UnitPrice 以外のレコードの数の両方を追跡します。

集計データの合算を出すことができたので、最後の手順は GridView のフッター行に表示することです。 このタスクも、RowDataBound イベント ハンドラーを使用してプログラムで実行できます。 RowDataBound イベント ハンドラーは、フッター行を含め、GridView にバインドされている "すべての" 行に対して発生することを思い出してください。 そのため、次のコードを使用することで、イベント ハンドラーを拡張し、フッター行にデータを表示することができます。

protected void ProductsInCategory_RowDataBound
    (object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
      ... Increment the running totals ...
    }
    else if (e.Row.RowType == DataControlRowType.Footer)
    {
      ... Display the summary data in the footer ...
    }
}

フッター行が GridView に追加されるのはすべてのデータ行が追加された後になるため、フッターに集計データを表示する準備ができる頃には、累計値の計算は確実に完了しているといえます。 最後の手順では、フッターのセルにこれらの値を設定します。

特定のフッター セルにテキストを表示するには、e.Row.Cells[index].Text = value を使用します。ここで、Cells のインデックスは 0 から始まります。 次のコードは、平均価格 (合計価格を製品数で割った値) を計算し、GridView の適切なフッター セルに在庫単位数と注文単位数の合計と共に表示します。

protected void ProductsInCategory_RowDataBound
    (object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
      ... <i>Increment the running totals</i> ...
    }
    else if (e.Row.RowType == DataControlRowType.Footer)
    {
      // Determine the average UnitPrice
      decimal avgUnitPrice = _totalUnitPrice / (decimal) _totalNonNullUnitPriceCount;
      // Display the summary data in the appropriate cells
      e.Row.Cells[1].Text = "Avg.: " + avgUnitPrice.ToString("c");
      e.Row.Cells[2].Text = "Total: " + _totalUnitsInStock.ToString();
      e.Row.Cells[3].Text = "Total: " + _totalUnitsOnOrder.ToString();
    }
}

図 13 は、このコードが追加された後のレポートを示しています。 ToString("c") により、平均価格の集計情報が通貨のように書式設定されていることに注目してください。

通貨として書式設定された GridView のフッター行の集計データを示すスクリーンショット。

図 13: GridView のフッター行に赤色の背景色が追加されました (クリックするとフルサイズの画像が表示されます)

まとめ

集計データの表示はレポートに一般的に求められるものですが、GridView コントロールを使用すると、このような情報をフッター行に簡単に含めることができます。 フッター行は、GridView の ShowFooter プロパティが true に設定されていると表示され、セル内のテキストは RowDataBound イベント ハンドラーを用いることでプログラムによって設定できます。 集計データの計算は、データベースに対して再度クエリを実行するか、ASP.NET ページの分離コード クラスのコードを使用して集計データをプログラムで計算することによって行うことができます。

このチュートリアルで、GridView、DetailsView、FormView コントロールを使ったカスタム書式設定の説明を終わります。 次回のチュートリアルでは、これら同じコントロールを使用してデータを挿入、更新、削除する方法に関する説明を開始します。

プログラミングに満足!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。