WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

データバインド式

by WebSurfer 17. August 2010 23:44

<%# 文字と %> 文字で囲んで用いるデータバインド式を使用すると、ページで DataBind メソッドが呼び出されたときに、サーバーコントロールプロパティとデータソースの間にバインディングが作成されます。

Eval や Bind メソッドを TextBox や Label コントロールの Text プロパティに以下のように設定して使うことが多いと思います。

Text='<%# Eval("Name") %>'

上記のような単純な使い方なら間違うことはなさそうですが、複雑な文字列を組み立てる場合は、別にそのためのメソッドを定義して用いるのがよさそうです。

例えば、以下のようにメソッドで文字列を組み立てて、

string Description(string s, int n)
{
  return String.Format("項目 {0} の数量は {1} です。", s, n);
}

それを以下のように設定できます。

Text='<%# Description((string)Eval("Name"), (int)Eval("Qty")) %>'

ただし、メソッドを使うまでもない簡単な文字列なら、例えば以下のようにする方がよいと思います。

Text='<%# "Name:" + Eval("Name") %>'

なお、以下のようにするのは、データバインド式を含めた全体が文字列として解釈されてしまうので、うまくいきません。

Text='Name:<%# Eval("Name") %>'

ありそうな間違いは、ImageUrl や NavigateUrl に、例えば、以下のように設定するケースでしょうか。

ImageUrl='~/Images/<%# Eval("Name") %>'

この結果は以下のようになってしまいます。

src="Images/%3C%25#%20Eval(%22Name%22)%20%25%3E"

(注)ImageUrl の場合は UrlEncode されるので、< や % などが %3C と %25 になります。一方、NavigateUrl の方は何故か UrlEncode されません。

Tags: ,

ASP.NET

PIVOT の使用

by WebSurfer 7. August 2010 18:18

SQL Server の PIVOT 関係演算子を利用してデータをまとめ、クロス集計レポートを生成し、GridView に表示する例です。

ほとんどの処置はストアドプロシージャで行うので、ASP.NET というよりは SQL Server の機能になりますが。

ここの例では、Microsoft が提供しているサンプルデータベース Northwind の Orders テーブル使用しました。

Orders テーブルは、個々の注文の ID (OrderID, int)、注文した顧客の ID (CustomerID, nchar(5))、注文を取り扱った従業員の ID (EmployeeID, int)、注文を受けた年月日 (OrderDate, datetime) などのデータを約 800 レコード持っています。

DropDownList に顧客名一覧を表示し、その中から顧客を選択すると、その注文を取り扱った従業員と取り扱った注文数を、注文年月ごとに表示するようにしました。以下のような感じです。

PIVOT によるクロス集計レポート

上記のクロス集計を行うためのストアドプロシージャは以下の通りです。

ALTER PROCEDURE dbo.StoredProcedure1
(
@CustomerID nchar(5)
)
AS
DECLARE @sqlstr nvarchar(max), @sqldata nvarchar(max), @employee nvarchar(50); 
DECLARE cur CURSOR FOR 

SELECT DISTINCT EmployeeID 
  FROM Orders 
  WHERE EmployeeID IS NOT NULL AND [CustomerID]=@CustomerID 
  ORDER BY EmployeeID; 

SET @sqldata = N''; 
SET @sqlstr = N'SELECT Year, Month '; 

OPEN cur; 
FETCH NEXT FROM cur INTO @employee; 
WHILE (@@FETCH_STATUS <> - 1) 
  BEGIN 
    IF LEN(@sqldata) > 0 
      BEGIN
        SET @sqldata = @sqldata + N',' 
      END; 
    SET @sqldata = @sqldata + N'[' + @employee + N']'
    SET @sqlstr = @sqlstr + N',[' + @employee + N'] AS [Emp-' + @employee + N']'; 
    FETCH NEXT FROM cur INTO @employee; 
  END; 
CLOSE cur;
DEALLOCATE cur; 

SET @sqlstr = @sqlstr +
  N' FROM 
     (
       SELECT DATEPART(mm, OrderDate) AS Month, DATEPART(yyyy, OrderDate) AS Year, EmployeeID 
       FROM Orders 
       WHERE CustomerID=@ID
     ) AS p 
     PIVOT 
     (
       COUNT(EmployeeID) 
       FOR EmployeeID 
       IN '; 
SET @sqlstr = @sqlstr + N'(' + @sqldata + N')) AS pvt'; 
EXECUTE sp_executesql @sqlstr, N'@ID nchar(5)', @CustomerID

顧客一覧を DropDownList に、クロス集計結果を GridView に表示するための ASP.NET のコードは以下の通りです。

<%@ Page Language="C#" %>

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

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title>無題のページ</title>
</head>
<body>
  <form id="form2" runat="server">
  <div>
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>"             
      SelectCommand="
        SELECT DISTINCT Orders.CustomerID, Customers.CompanyName 
        FROM Orders 
        INNER JOIN Customers 
        ON Orders.CustomerID = Customers.CustomerID">
    </asp:SqlDataSource>

    <asp:DropDownList ID="DropDownList1" 
      runat="server" 
      DataSourceID="SqlDataSource1" 
      DataTextField="CompanyName" 
      DataValueField="CustomerID" 
      AutoPostBack="True">
    </asp:DropDownList>
        
    <asp:SqlDataSource ID="SqlDataSource2" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand="StoredProcedure1"
      SelectCommandType="StoredProcedure">
      <SelectParameters>
        <asp:ControlParameter 
          ControlID="DropDownList1" 
          Name="CustomerID" 
          PropertyName="SelectedValue" 
          Type="String" />
      </SelectParameters>        
    </asp:SqlDataSource>

    <asp:GridView ID="GridView1" 
      runat="server" 
      DataSourceID="SqlDataSource2">
    </asp:GridView>
  </div>
  </form>
</body>
</html>

Tags: ,

SQL Server

GridView でクリックされたセル位置の取得

by WebSurfer 7. August 2010 15:04

ユーザーがブラウザ上で GridView のセルをクリックすると、ポストバックされて、サーバ側のプログラムでクリックされたセルの位置を取得する方法です。

それだけでは面白くない(?)ので、データソースの列が動的に変化するとし、ヘッダを含めて LinkButton または Button で表示し、ページングを有効にするというシナリオです。

以下の画像のような感じです。これはページャーの 3 をクリックして 3 ページに移動し、ヘッダーを含めて上から 2 行目の 10000 をクリックした結果です。

GridView でクリックされたセルの位置の取得

アプリは以下のようにして作成します。こ���なシナリオは普通ないので、役に立たないと言われるかもしれませんが(苦笑)

ページング

ページングのためのコードを自力で書くのはかなり大変です。ページングの機能を持つデータソースコントロールとデータバインドコントロールを組み合わせて使うのが現実的と思います。

ここでは、データソースコントロールに ObjectDataSource を使用し、DataTable を作成するメソッドと GridView(データバインドコントロール)の間を仲介してやることにします。

GridView の列の設定

データソースの列が動的に変化するという前提があります。

テキストで表示するなら、AutoGenerateColumns を true にすれば自動的に必要な列を生成してくれますが、LinkButton または Button を使って表示するという要件があるのでそうはいきません。

AutoGenerateColumns="False" としておき、Page.Load のイベントハンドラで DataTable から列情報を取得し、その情報をもとに ButtonField を初期化して、GridView に設定してやるという操作が必要です。

各行へのデータバインドは、ButtonField.DataTextField プロパティに DataColumn.Caption を設定しておけば、ObjectDataSource と GridView が自動で行ってくれます。

セル位置の設定・取得

クリックされたセルの列・行位置は、GridView.RowCommand イベントハンドラで、引数の GridViewCommandEventArgs オブジェクトの CommandName, CommandArgument プロパティから取得します。

列番号の取得には CommandName を利用します。そのためには、事前に CommandName に列番号を設定しておく必要があります。上記の「GridView の列の設定」の際に DataColumn.Ordinal プロパティから DataTable 内での列位置を取得し、ButtonField.CommandName プロパティに設定します。

行番号の取得には CommandArgument を使用します。CommandArgument にはデフォルトで行番号が設定されるので、事前の設定は不要です。

取得できる行番号は、ページングした場合、例えば1ページ目の n 行と、2ページ目の n 行とで同じになりますので注意してください。

ヘッダー行の変更

ここまでの処置では、まだヘッダーが普通のテキストのままです。これを LinkButton または Button で表示するようにします。

それには、GridView.RowCreated イベントのハンドラで、ヘッダーの Cell の内容を LinkButton または Button に書き換えてやります。

サンプルコード


<%@ 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">
  protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
  {
    if (e.Row.RowType == DataControlRowType.Header)
    {
      TableCellCollection cells = e.Row.Cells;
      foreach (TableCell cell in cells)
      {
        string text = cell.Text;
        cell.Controls.Clear();
        //LinkButton button = new LinkButton();  // ハイパーリンク表示
        Button button = new Button();          // ボタン表示                
        button.Text = text;
        button.CommandArgument = "-1";
        button.CommandName = cells.GetCellIndex(cell).ToString();
        cell.Controls.Add(button);
      }
    }
  }

  protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
  {
    // ページャーがクリックされた場合
    if (e.CommandName == "Page")
    {
      Label1.Text = String.Empty;
    }
    else
    {
      Label1.Text = "Page: " + GridView1.PageIndex.ToString() + 
        ", Command Argument: " + e.CommandArgument.ToString() + 
        ", Command Name: " + e.CommandName;
    }
  }

  protected void Page_Load(object sender, EventArgs e)
  {
    if (!Page.IsPostBack)
    {
      DataView view = (DataView)ObjectDataSource1.Select();
      DataTable table = view.Table;
      foreach (DataColumn column in table.Columns)
      {
        ButtonField field = new ButtonField();
        //field.ButtonType = ButtonType.Link;     // ハイパーリンク表示
        field.ButtonType = ButtonType.Button;   // ボタン表示                
        field.HeaderText = column.Caption;
        field.DataTextField = column.Caption;
        field.CommandName = column.Ordinal.ToString();
        GridView1.Columns.Add(field);
      }
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <h3>方法2: ButtonField を利用</h3>
    <asp:ObjectDataSource ID="ObjectDataSource1" 
      runat="server" 
      SelectMethod="CreateDataTable2" 
      TypeName="PagingTest">
    </asp:ObjectDataSource>
    <asp:GridView ID="GridView1" 
      runat="server" 
      AllowPaging="True" 
      DataSourceID="ObjectDataSource1" 
      onrowcreated="GridView1_RowCreated" 
      onrowcommand="GridView1_RowCommand"
      PageSize="5" 
      AutoGenerateColumns="False" 
      EnableViewState="True">
    </asp:GridView>
    <asp:Label ID="Label1" runat="server"></asp:Label>
  </div>
  </form>
</body>
</html>

ついでに、ObjectDataSource の TypeName, SelectMethod が参照しているクラス/メソッドもアップしておきます。


using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

public class PagingTest
{
  public static DataTable CreateDataTable2()
  {
    DataTable wktbl = new DataTable();
    DataRow wkrow;
        
    wktbl.Columns.Add("y/x");
    wktbl.Columns.Add("x1.min/.max");
    wktbl.Columns.Add("x2.min/.max");
    wktbl.Columns.Add("x3.min/.max");
    wktbl.Columns.Add("x4.min/.max");

    for (int i = 0; i < 20; i++)
    {
      wkrow = wktbl.NewRow();
      wkrow["y/x"] = "y1.min/.max";
      wkrow["x1.min/.max"] = "10000";
      wkrow["x2.min/.max"] = "20000";
      wkrow["x3.min/.max"] = "30000";
      wkrow["x4.min/.max"] = "40000";
      wktbl.Rows.Add(wkrow);
    }

    return wktbl;
  }
}

Tags: ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  January 2021  >>
MoTuWeThFrSaSu
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567

View posts in large calendar