WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

GridView, ListView に合計表示

by WebSurfer 2010年11月7日 19:04
ListView に合計を表示

GirdView や ListView で、ある列の合計金額を計算して、フッターなどに表示したいというケースが時々あります。備忘録として、その例を書いておきます。

GridView は「行」(GridViewRow クラス)で構成されているのに対して、ListView は「項目」(ListViewItem クラス)で構成されているという違いがありますが、基本的な方法は行/項目にデータがバインドされるときのイベントを利用して、値を取得して合計していくという操作は同じだと思います。

GridView, ListView どちらの場合も、データソースコントロールが取得してバインドするデータが DataTable の場合(デフォルト)は、DataItem プロパティを使って DataRowView を取得できますので、それから各行/項目の値を取得するのがよさそうです。

合計した結果を書き込むところが、ちょっと違います。

GridView では、フッターでも RowDataBound イベントが発生します。そのイベントハンドラでデータ行かフッター行かが判定でき、フッター行の場合に合計をフッターに書き込むことができます。

ShowFooter="True" として、その中の TableCell の Text プロパティに書き込む例は以下の通りです。

decimal total = 0m;
    
protected void GridView1_RowDataBound(object sender, 
  GridViewRowEventArgs e)
{        
  if (e.Row.RowType == DataControlRowType.DataRow)
  {
    DataRowView drv = (DataRowView)e.Row.DataItem;
    total = total + (decimal)drv["Freight"];
  }
  else if (e.Row.RowType == DataControlRowType.Footer)
  {
    e.Row.Cells[1].Text = "Freight Total";
    e.Row.Cells[2].Text = String.Format("${0:N2}", total);
  }        
}

ListView では、合計の取得は ItemDataBound イベントハンドラで可能ですが、GridView の時のようにフッター行に合計結果を書き込むことはできません。

LayoutTemplate にフッターの行を追加して Label を配置し、その Text プロパティに書き込むことになります。そのタイミングは、ListView.DataBound イベントがよさそうです。

上の画像を出力した ListView のコードを以下にアップしておきます。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  decimal total = 0m;
    
  protected void ListView1_ItemDataBound(object sender, 
      ListViewItemEventArgs e)
  {
    if (e.Item.ItemType == ListViewItemType.DataItem)
    {
      ListViewDataItem lvdi = (ListViewDataItem)e.Item;
      DataRowView drv = (DataRowView)lvdi.DataItem;
      total = total + (decimal)drv["Freight"];
    }
  }

  protected void ListView1_DataBound(object sender, EventArgs e)
  {
    Label label = (Label)ListView1.FindControl("totalLabel");
    label.Text = String.Format("${0:N2}", total);
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Show Total in ListView</title>
  <style type="text/css">
    table.style1
    {
      border-style: solid;
      border-width: 2px;
      border-color: Black;
      text-align: center;
      border-collapse: collapse;
    }
       
    table.style1 th
    {
      border-style: solid;
      border-width: 2px 1px 2px 1px;
      border-color: Black;
      background-color: #6699FF;
      color: #FFFFFF;
    }
        
    table.style1 td
    {
      border-style: solid;
      border-width: 1px;
      border-color: Black;        
    }
        
    .footer
    {
      background-color: #CCFFFF;
    }  
  </style>

</head>
<body>
  <form id="form1" runat="server">
  <div>
    <h3>Alfreds Futterkiste</h3>
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand="SELECT [OrderID], [OrderDate], [Freight] 
        FROM [Orders] 
        WHERE [CustomerID]='ALFKI'">            
    </asp:SqlDataSource>
    <asp:ListView ID="ListView1" 
      runat="server" 
      DataKeyNames="OrderID" 
      DataSourceID="SqlDataSource1" 
      EnableModelValidation="True" 
      OnItemDataBound="ListView1_ItemDataBound" 
      OnDataBound="ListView1_DataBound">
      <ItemTemplate>
        <tr>
          <td>
            <asp:Label ID="OrderIDLabel" 
              runat="server" 
              Text='<%# Eval("OrderID") %>' />
          </td>
          <td>
            <asp:Label ID="OrderDateLabel" 
              runat="server" 
              Text='<%# Eval("OrderDate", "{0:yyyy/MM/dd}") %>' />
          </td>
          <td style="text-align: right;">
            <asp:Label ID="FreightLabel" 
              runat="server" 
              Text='<%# Eval("Freight", "${0:N2}") %>' />
          </td>
        </tr>
      </ItemTemplate>
      <LayoutTemplate>
        <table ID="itemPlaceholderContainer" 
          runat="server" 
          class="style1">
          <tr runat="server">
            <th runat="server">
              OrderID</th>
            <th runat="server">
              OrderDate</th>
            <th runat="server">
              Freight</th>
          </tr>
          <tr ID="itemPlaceholder" runat="server">
          </tr>
          <tr class="footer">
            <td></td>
            <td>Freight Total</td>
            <td style="text-align: right;">
              <asp:Label ID="totalLabel" runat="server" /></td>
          </tr>
        </table>                        
      </LayoutTemplate>
    </asp:ListView>
  </div>
  </form>
</body>
</html>

Tags: ,

ASP.NET

ListView と DropDownList

by WebSurfer 2010年10月31日 19:32

ListView の InsertItemTemplate に DropDownList を配置し、DropDownList.SelectedValue を DB に挿入するにはどのようにすればよいかという話です。ただし、.NET 4 では問題なく、.NET 3.5 に限った話です。

何故かは不明ですが、InsertItemTemplate に DropDownList を配置した場合は、DropDownList の SelectedValue プロパティを以下のように設定してもうまくいきません。「Eval()、XPath()、および Bind() のようなデータバインド メソッドは、データバインドされたコントロールのコンテキストでのみ使用することができます。」というエラーになります。

<InsertItemTemplate>
  <tr style="">
  ・・・中略・・・
    <td>
      <asp:SqlDataSource ID="SqlDataSource1" ・・・
      ・・・中略・・・
      <asp:DropDownList ID="DropDownList1" 
        runat="server"
        DataSourceID="SqlDataSource1" 
        DataTextField="CategoryName" 
        DataValueField="CategoryName"
        AppendDataBoundItems="True"
        SelectedValue='<%# Bind("memo") %>'>
        <asp:ListItem Value="">空白</asp:ListItem>
      </asp:DropDownList>
    </td>
  </tr>
</InsertItemTemplate>

なお、上記と同様な形で EditItemTemplate に配置した場合は問題なく、DB は SelectedValue でちゃんと更新されます。

この問題の回避策は、以下のように SqlDataSource.Inserting イベントハンドラで処置することです。

protected void SqlDataSource1_Inserting(object sender, 
    SqlDataSourceCommandEventArgs e)
{
  DropDownList ddl = 
    (DropDownList)ListView1.InsertItem.FindControl("DropDownList1");
  e.Command.Parameters["@memo"].Value = ddl.SelectedValue;
}

何故、InsertItemTemplate に DropDownList を配置した場合ダメなのかが分からなかったし、もっとスマートな解決策があるのではないか(ひょっとして、とんでもなく間抜けなのことしているのではないか)ということが気になっていましたが、どうやら .NET 3.5 のバグらしいということで、安心(?)しています。(笑)

Tags: ,

ASP.NET

XML ファイルの更新操作

by WebSurfer 2010年9月27日 23:14

XML ファイルをデータソースに使った場合、表示するだけなら XmlDataSource を使えば、ほぼコーディングレスで Web アプリケーションを作成できます。ただし、XmlDataSource には更新機能がないので、必要な場合は自力でコードを書いて更新機能を実装する必要があります。

MSDN ライブラリの XmlDataSource の説明には、GetXmlDocument メソッドを使って XmlDataDocument オブジェクトを取得し、それに変更を加えてから Save するという方法が紹介されていますが、GridView や ListView 上で編集して更新するにはその方法は難しそうです。

それより、XML ファイル操作用のクラス(選択、削除、挿入、更新操作を行うメソッドを実装)を作り、そのクラスを ObjectDataSource を経由 GridView や ListView にバインドして操作するのが簡単そうです。

そのサンプルは MSDN ライブラリ の「GridView で XML ファイルをデータ ソースとして使いレコードを編集する方法」に紹介されています。そのサンプルに削除、挿入機能を加えて、さらに ID も更新できるように拡張したコードを書いておきます。なお、言語はサンプルの VB.NET を C# に変更しました。

まず、XML ファイル操作用クラス(UserInfoTable クラス)の UpdateDataSet メソッドを、ID も更新できるように変更します。具体的には、引数に original_id を追加し、original_id で DataSet の行を検索し、ヒットした行の当該項目を id に書き換えるよう修正します。次に、削除と挿入操作のためのメソッドを追加します。修正、追加後の UserInfoTable クラスは以下のようになります。

using System;
using System.Data;
using System.Web;
using System.ComponentModel;

public class XmlDataSet
{
  public class UserInfoTable : IDisposable
  {
    const string strXmlFile = "~/App_Data/UserInfo.xml";
    private DataSet myDataSet;

    public UserInfoTable()
    {
      myDataSet = new DataSet();
      myDataSet.Locale = 
        System.Globalization.CultureInfo.InvariantCulture;
      string filePath = 
        HttpContext.Current.Server.MapPath(strXmlFile);
      myDataSet.ReadXml(filePath);
    }

    public virtual void Dispose(bool disposing)
    {
      if (disposing)
      {
        myDataSet.Dispose();
      }
    }

    public void Dispose()
    {
      Dispose(true);
      System.GC.SuppressFinalize(this);
    }

    ~UserInfoTable()
    {
      Dispose(false);
    }

    [DataObjectMethod(DataObjectMethodType.Select, true)]
    public DataSet GetDataSet()
    {
      return myDataSet;
    }

    [DataObjectMethod(DataObjectMethodType.Update, true)]
    public void UpdateDataSet(string id, string name, string original_id)
    {
      string strFillter = "ID='" + original_id + "'";
      DataRow[] rows = myDataSet.Tables[0].Select(strFillter);
      if (rows.Length > 0)
      {
        rows[0]["ID"] = id;
        rows[0]["NAME"] = name;
        Save();
      }
    }

    [DataObjectMethod(DataObjectMethodType.Delete, true)]
    public void DeleteItem(string original_id)
    {
      string strFillter = "ID='" + original_id + "'";
      DataRow[] rows = myDataSet.Tables[0].Select(strFillter);
      if (rows.Length > 0)
      {
        rows[0].Delete();
        Save();
      }
    }

    [DataObjectMethod(DataObjectMethodType.Insert, true)]
    public void InsertItem(string id, string name)
    {
      DataRow row = myDataSet.Tables[0].NewRow();
      row["ID"] = id;
      row["NAME"] = name;
      myDataSet.Tables[0].Rows.Add(row);
      Save();
    }

    private void Save()
    {
      string filePath = 
        HttpContext.Current.Server.MapPath(strXmlFile);
      myDataSet.WriteXml(filePath, XmlWriteMode.IgnoreSchema);
    }
  } 
}

新しい aspx ファイルを作成し、それに ObjectDataSource と ListView を配置します。UserInfoTable クラスをベースに、ウィザードで ObjectDataSource と ListView を設定していくと、以下のようなコードになるはずです。

<asp:ObjectDataSource ID="ObjectDataSource1" 
  runat="server" 
  DeleteMethod="DeleteItem" 
  InsertMethod="InsertItem" 
  SelectMethod="GetDataSet" 
  TypeName="XmlDataSet+UserInfoTable" 
  UpdateMethod="UpdateDataSet">
  <DeleteParameters>
    <asp:Parameter Name="original_id" Type="String" />
  </DeleteParameters>
  <InsertParameters>
    <asp:Parameter Name="id" Type="String" />
    <asp:Parameter Name="name" Type="String" />
  </InsertParameters>
  <UpdateParameters>
    <asp:Parameter Name="id" Type="String" />
    <asp:Parameter Name="name" Type="String" />
    <asp:Parameter Name="original_id" Type="String" />
  </UpdateParameters>
</asp:ObjectDataSource>
<asp:ListView ID="ListView1" 
  runat="server" 
  DataSourceID="ObjectDataSource1" 
  EnableModelValidation="True">
</asp:ListView>

しかしなから、このままではうまく動きません。問題点は以下のとおりです

  • ListView に DataKeyNames="id" の定義がないので、id の値が ObjectDataSource に渡されません。
  • ObjectDataSource に OldValuesParameterFormatString="original_{0}" の設定がないので、id の新旧の区別ができません。
  • ListView の中身(Template やその中の TextBox, Button など)は自動生成されません。自力でコードを書く必要があります。
  • ListView に挿入の行を表示するため、InsertItemPosition="LastItem" を追加します。

以上の点を修正したコードは以下のとおりです。

<asp:ObjectDataSource ID="ObjectDataSource1" 
  runat="server"
  OldValuesParameterFormatString="original_{0}"
  DeleteMethod="DeleteItem" 
  InsertMethod="InsertItem" 
  SelectMethod="GetDataSet" 
  TypeName="XmlDataSet+UserInfoTable" 
  UpdateMethod="UpdateDataSet">
  <DeleteParameters>
    <asp:Parameter Name="original_id" Type="String" />
  </DeleteParameters>
  <InsertParameters>
    <asp:Parameter Name="id" Type="String" />
    <asp:Parameter Name="name" Type="String" />
  </InsertParameters>
  <UpdateParameters>
    <asp:Parameter Name="id" Type="String" />
    <asp:Parameter Name="name" Type="String" />
    <asp:Parameter Name="original_id" Type="String" />
  </UpdateParameters>
</asp:ObjectDataSource>
<asp:ListView ID="ListView1" 
  runat="server"
  DataKeyNames="id"
  DataSourceID="ObjectDataSource1"
  InsertItemPosition="LastItem"
  EnableModelValidation="True">
  <ItemTemplate>
    <tr>
      <td>
        <asp:Button ID="DeleteButton" 
          runat="server" 
          CommandName="Delete" 
          Text="削除" />
        <asp:Button ID="EditButton" 
          runat="server" 
          CommandName="Edit" 
          Text="編集" />
      </td>
      <td>
        <asp:Label ID="idLabel" 
          runat="server" 
          Text='<%# Bind("id") %>' />
      </td>
      <td>
        <asp:Label ID="nameLabel" 
          runat="server" 
          Text='<%# Bind("name") %>' />
      </td>
    </tr>
  </ItemTemplate>
  <InsertItemTemplate>
    <tr>
      <td>
        <asp:Button ID="InsertButton" 
          runat="server" 
          CommandName="Insert" 
          Text="挿入" />
        <asp:Button ID="CancelButton" 
          runat="server" 
          CommandName="Cancel" 
          Text="クリア" />
      </td>
      <td>
        <asp:TextBox ID="idTextBox" 
          runat="server" 
          Text='<%# Bind("id") %>' />
      </td>
      <td>
        <asp:TextBox ID="nameTextBox" 
          runat="server" 
          Text='<%# Bind("name") %>' />
      </td>
    </tr>
  </InsertItemTemplate>
  <LayoutTemplate>
    <table id="Table2" runat="server">
      <tr id="Tr1" runat="server">
        <td id="Td1" runat="server">
          <table ID="itemPlaceholderContainer" 
            runat="server">
            <tr id="Tr2" runat="server">
              <th id="Th1" runat="server">
              </th>
              <th id="Th2" runat="server">
                id</th>
              <th id="Th3" runat="server">
                name</th>
            </tr>
            <tr ID="itemPlaceholder" runat="server">
            </tr>
          </table>
        </td>
      </tr>
    </table>
  </LayoutTemplate>
  <EditItemTemplate>
    <tr>
      <td>
        <asp:Button ID="UpdateButton" 
          runat="server" 
          CommandName="Update" 
          Text="更新" />
        <asp:Button ID="CancelButton" 
          runat="server" 
          CommandName="Cancel" 
          Text="キャンセル" />
      </td>
      <td>
        <asp:TextBox ID="idTextBox" 
          runat="server" 
          Text='<%# Bind("id") %>' />
      </td>
      <td>
        <asp:TextBox ID="nameTextBox" 
          runat="server" 
          Text='<%# Bind("name") %>' />
      </td>
    </tr>
  </EditItemTemplate>
</asp:ListView>

Tags: , ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar