WebSurfer's Home

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

GridView 内の隠しボタンでポストバック

by WebSurfer 2014年3月26日 17:34

GridView の各データ行に隠し Button を配置し、クライアントスクリプトを使って行クリックで当該行の隠しボタンをクリックすることによりポストバックがかかり、サーバー側の Button.Click イベントでクリックされた行のレコードの ID を取得するサンプルの紹介です。

GirdView 内の隠しボタンでポストバック

先の記事 __doPostBack を使ってはいけません では、GridView の行クリックでポストバックをかけるために GetPostBackEventReference メソッド を使用しました。

この記事のサンプルでは、GetPostBackEventReference メソッドを使う代わりに、隠し Button をクリックすることによりポストバックをかけます。

隠し Button なのでユーザーは直接それをクリックすることはできません。なので、GridView から生成されるデータ行の tr 要素の click イベントに JavaScript のリスナーをアタッチし、そのリスナーによって隠し Button をクリックします。

リスナーの作成および click イベントへのアタッチは、以下のコードのように jQuery を使うと比較的簡単にできます。

気をつけなければならないことは DOM イベントには「バブリング」というものがあることです。(バブリングの詳細については、先の記事 キャプチャリングとバブリング を見てください)

つまり、(1) tr 要素をクリック ⇒ (2) リスナーがクリックイベントを捕捉 ⇒ (3) リスナー内のメソッドが当該 tr 要素内の Button をクリック ⇒ (4) Button の click イベント発生 ⇒ (5) バブリングで tr 要素にイベントが伝播 ⇒ (6) ステップ (2) に戻る・・・と言うように無限ループになってしまうと言うことです。

この記事の例では、無限ループにならないように event.stopPropagation メソッドによりバブリングを止めました。その部分を含めたページ全体のソースコードをこの記事の下の方に記載しています。

また、実際に動かして試すことができるよう 実験室 にアップしました。興味のある方は試してみてください。

<%@ 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">

  // データソース用の DataTable を作成
  protected DataTable CreateDataTable()
  {
    DataTable dt = new DataTable();

    dt.Columns.Add(new DataColumn("ID", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Name", typeof(string)));
    dt.Columns.Add(new DataColumn("Price", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Qty", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Amount", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Remarks", typeof(string)));

    for (int i = 1; i < 6; i++)
    {
      DataRow dr = dt.NewRow();
      dr["ID"] = i;
      dr["Name"] = "Item " + i.ToString();
      dr["Price"] = 123000 * i;
      dr["Qty"] = i;
      dr["Amount"] = 123000 * i * i;
      dr["Remarks"] = "Remarks " + i.ToString();
      dt.Rows.Add(dr);
    }
    return dt;
  }

  protected void Page_Load(object sender, EventArgs e)
  {
    if (!Page.IsPostBack)
    {
      GridView1.DataSource = CreateDataTable();
      GridView1.DataBind();
    }
  }

  protected void Button1_Click(object sender, EventArgs e)
  {
    Label2.Text = 
      "Selected ID: " + ((Button)sender).CommandArgument;
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>WebSurfer's Page - 実験室</title>
  <script src="/scripts/jquery.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
    $(document).ready(function () {
      $("#<%=GridView1.ClientID%> tr.dataRow")
        .click(function () {

        var hiddenbutton = $(this).find("input:submit");

        // バブリングを止めないと無限ループになる。
        hiddenbutton.click(function (event) {
          event.stopPropagation();
        });

        hiddenbutton.click();
      });

      // カーソルを指型にする。
      $("#<%=GridView1.ClientID%> tr.dataRow")
        .css('cursor', 'pointer');
    });
  //]]>
  </script>
  <style type="text/css">
    .style1
    {
      display: none;
    }
    .style2
    {
      cursor: pointer;
    }
  </style>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      RowStyle-CssClass="dataRow">
      <Columns>
        <asp:TemplateField HeaderText="ID">
          <ItemTemplate>
            <asp:Label ID="Label1" 
              runat="server" 
              Text='<%#Eval("ID")%>'>
            </asp:Label>
            <asp:Button ID="Button1" 
              runat="server" 
              Text="Button" 
              OnClick="Button1_Click" 
              CommandArgument='<%#Eval("ID")%>' 
              CssClass="style1" />
          </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="Name" HeaderText="Name" />
        <asp:BoundField DataField="Price" HeaderText="Price" />
        <asp:BoundField DataField="Qty" HeaderText="Qty" />
        <asp:BoundField DataField="Amount" HeaderText="Amount" />
        <asp:BoundField DataField="Remarks" HeaderText="Remarks" />
      </Columns>
    </asp:GridView>
    <asp:Label ID="Label2" runat="server"></asp:Label>
  </div>
  </form>
</body>
</html>

Tags: ,

ASP.NET

window.opener を使って PostBack

by WebSurfer 2010年11月4日 21:47
window.opener を使って PostBack

JavaScript の window.opener を利用すると、例えばページ A から window.open でページ B を開いたとすると、ページ B からページ A にポストバックをかけることが可能です。

先の記事 ModalPopup で編集・更新操作 で書いた、「一覧画面にレコード一覧を表示してユーザーに選択させ、一覧画面は開いたまま別に編集画面を開いてそこでレコードを編集してもらい、編集画面の[更新]ボタンクリックで DB を更新すると共に編集画面は閉じて、更新結果を一覧画面に反映する」というシナリオに使えます。

それを知らなかったので、先の記事では「開いたままの一覧画面に、編集画面による更新結���を反映するのは難しいです(というよりほとんど無理と思います)。」と書きましたが、そんなことはなかったです。(汗)

以下に、備忘録として例を書いておきます。

検証用に、一覧表示のための GridView を持つ親ページ (RecordList.aspx) と、編集・更新のための DetailsView を持つ子ページ (EditAndUpdate.aspx) を用いてアプリを作ってみました。手順は以下のようになります。

  1. まず親ページを要求する。
  2. 親ページは、DB からデータを取得し、GirdView にレコード一覧を表示。
  3. GridView 上のレコードを選択すると、親ページは開いたまま別ウィンドウを開いて子ページを要求。
  4. 子ページは、選択されたレコードを DB から取得し DetailsView に Edit モードで表示。
  5. 子ページでレコードを編集し、ボタンクリックでポストバックして DB を更新。同時に、親ページにポストバックをかけるための JavaScript を追加する。
  6. 子ページが再描画された時、追加した JavaScript が働いて親ページにポストバックをかける。
  7. 親ページはサーバー側で更新後のレコードを取得し GridView に表示する。
  8. 子ページを自動的に閉じる。

コードは以下のとおりです。

RecordList.aspx(親ページ)

<%@ 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_RowDataBound(object sender, GridViewRowEventArgs e)
  {
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
      LinkButton lb = (LinkButton)e.Row.FindControl("LinkButton1");
      DataRowView rowView = (DataRowView)e.Row.DataItem;
      string script = String.Format(
        "javascript:window.open('EditAndUpdate.aspx?customerId={0}', " +
        "null, 'width=400, height=300'); return false;", 
        rowView["CustomerID"]);
      lb.OnClientClick = script;
    }
  }

  protected void Page_Load(object sender, EventArgs e)
  {
      if (Page.IsPostBack)
      {
          GridView1.DataBind();
      }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title>一覧画面</title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <h1>一覧画面</h1>
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand=
        "SELECT [CustomerID], [CompanyName], [ContactName], 
          [ContactTitle], [Phone] 
        FROM [Customers]">
    </asp:SqlDataSource>
    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      DataKeyNames="CustomerID" 
      DataSourceID="SqlDataSource1" 
      EnableModelValidation="True" 
      OnRowDataBound="GridView1_RowDataBound"
      AllowPaging="True">
      <Columns>
        <asp:BoundField DataField="CustomerID" 
          HeaderText="CustomerID" 
          ReadOnly="True" 
          SortExpression="CustomerID" />
        <asp:BoundField DataField="CompanyName" 
          HeaderText="CompanyName" 
          SortExpression="CompanyName" />
        <asp:BoundField DataField="ContactName" 
          HeaderText="ContactName" 
          SortExpression="ContactName" />
        <asp:BoundField DataField="ContactTitle" 
          HeaderText="ContactTitle" 
          SortExpression="ContactTitle" />
        <asp:BoundField DataField="Phone" 
          HeaderText="Phone" 
          SortExpression="Phone" />
        <asp:TemplateField ShowHeader="False">
          <ItemTemplate>
            <asp:LinkButton ID="LinkButton1" 
              runat="server" 
              CausesValidation="False" 
              CommandName="Select" 
              Text="編集">
            </asp:LinkButton>
          </ItemTemplate>
        </asp:TemplateField>
      </Columns>
    </asp:GridView>
  </div>
  </form>
</body>
</html>

EditAndUpdate.aspx(子ページ)

<%@ 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">
  protected void Page_Load(object sender, EventArgs e)
  {
    string customerid = Request.QueryString["customerId"];
    if (!String.IsNullOrEmpty(customerid))
    {
      DetailsView1.DefaultMode = DetailsViewMode.Edit;
      Panel1.Visible = true;
      Panel2.Visible = false;
      Label1.Text = customerid;
    }
    else
    {
      Panel1.Visible = false;
      Panel2.Visible = true;
    }
  }

  protected void  LinkButton1_Click(object sender, EventArgs e)
  {
    DetailsView1.UpdateItem(false);
    string csname = "PostBackSourceWindowScript";
    Type cstype = this.GetType();
    ClientScriptManager cs = Page.ClientScript;
    if (!cs.IsStartupScriptRegistered(cstype, csname))
    {
      String cstext =
      @"window.onload = function() {
        if (!window.opener || window.opener.closed) {
          self.close();
        }
        else {
          window.opener.document.getElementById('form1').submit();
          self.close();
        }
      };";
      cs.RegisterStartupScript(cstype, csname, cstext, true);
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title>編集画面</title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:Panel ID="Panel1" runat="server">
      <h1>編集画面</h1>
      <span>
        選択された Customer ID: <asp:Label ID="Label1" runat="server" />
      </span>

      <asp:SqlDataSource ID="SqlDataSource2" runat="server" 
        ConnectionString="<%$ ConnectionStrings:Northwind %>" 
        SelectCommand=
          "SELECT [CustomerID], [CompanyName], [ContactName], 
             [ContactTitle], [Phone] 
          FROM [Customers] 
          WHERE ([CustomerID] = @CustomerID)" 
        UpdateCommand=
          "UPDATE [Customers] 
          SET [CompanyName]=@CompanyName, [ContactName]=@ContactName, 
            [ContactTitle]=@ContactTitle, [Phone]=@Phone 
          WHERE [CustomerID] = @CustomerID">
        <SelectParameters>
          <asp:ControlParameter ControlID="Label1" 
            Name="CustomerID" 
            PropertyName="Text" 
            Type="String" />
        </SelectParameters>
        <UpdateParameters>
          <asp:Parameter Name="CompanyName" Type="String" />
          <asp:Parameter Name="ContactName" Type="String" />
          <asp:Parameter Name="ContactTitle" Type="String" />
          <asp:Parameter Name="Phone" Type="String" />
          <asp:Parameter Name="CustomerID" Type="String" />
        </UpdateParameters>
      </asp:SqlDataSource>

      <asp:DetailsView ID="DetailsView1" 
        runat="server" 
        AutoGenerateRows="False" 
        DataKeyNames="CustomerID" 
        DataSourceID="SqlDataSource2" 
        EnableModelValidation="True">
        <Fields>
          <asp:BoundField DataField="CustomerID" 
            HeaderText="CustomerID" 
            ReadOnly="True" 
            SortExpression="CustomerID" />
          <asp:BoundField DataField="CompanyName" 
            HeaderText="CompanyName" 
            SortExpression="CompanyName" />
          <asp:BoundField DataField="ContactName" 
            HeaderText="ContactName" 
            SortExpression="ContactName" />
          <asp:BoundField DataField="ContactTitle" 
            HeaderText="ContactTitle" 
            SortExpression="ContactTitle" />
          <asp:BoundField DataField="Phone" 
            HeaderText="Phone" 
            SortExpression="Phone" />
        </Fields>
      </asp:DetailsView>
      <div>
        <asp:LinkButton ID="LinkButton1" 
          runat="server" 
          onclick="LinkButton1_Click" 
          Text="Save" />
        <asp:LinkButton ID="LinkButton2" 
          runat="server"
          CausesValidation="False" 
          Text="Cancel"
          OnClientClick="self.close(); return false;" />
      </div>
    </asp:Panel>
    <asp:Panel ID="Panel2" runat="server">
      <p>クエリ文字列 customerId が指定されていません。</p>
    </asp:Panel>
  </div>
  </form>
</body>
</html>

先の記事 ModalPopup で編集・更新操作 で紹介したアプリと比べると、一覧画面の[編集]ボタンの二度押し防止と、更新された行を GirdView 上でハイライトする操作が実装できていません。解決策がないか考えてみましたが、思いつきません。というわけで、先の記事の例の方がよさそうです。

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