WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

UpdatePanel へのトリガ追加(改版)

by WebSurfer 8. November 2010 21:19

先の記事 UpdatePanel へのトリガ追加 で、AsyncPostBackTrigger をプログラムで追加する話を書きましたが、MSDN ライブラリの AsyncPostBackTrigger クラス の説明によると、それはサポートされていないのだそうです。無知でした。(汗)

MSDN ライブラリの説明に書かれているように、RegisterAsyncPostBackControl メソッド を使って書き換えました。

コードは以下のとおりです。先の記事のサンプルから C# のコードの部分を書き換えただけです。こんなに簡単だったとは、以前の苦労はなんだったんだろうという感じです。(笑)

<%@ 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)
  {
    LinkButton lb = 
      (LinkButton)DetailsView1.FindControl("LinkButton1");
    if (lb != null)
    {
      ScriptManager1.RegisterAsyncPostBackControl(lb);
    }        
  }    

  protected void LinkButton1_Click(object sender, EventArgs e)
  {
    Label1.Text = DateTime.Now.ToString();
    UpdatePanel2.Update();
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title>AsyncPostBackTrigger を動的に設定</title>
</head>
<body>
  <form id="form1" runat="server">
  <asp:ScriptManager ID="ScriptManager1" runat="server">
  </asp:ScriptManager>

  <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
    ConnectionString="<%$ ConnectionStrings:MyDB %>" 
    SelectCommand="SELECT [id], [name], [price] FROM [table]" >
  </asp:SqlDataSource>

  <asp:Button ID="DummyButton" 
    runat="server" 
    style="display: none;" />
  <h2>AsyncPostBackTrigger を動的に設定</h2>
  <h3>DetailsView</h3>
  <asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate>
      <asp:DetailsView ID="DetailsView1" 
        runat="server" 
        AutoGenerateRows="False" 
        DataKeyNames="id" 
        DataSourceID="SqlDataSource1" 
        EnableModelValidation="True" 
        AllowPaging="True" Width="250px">
        <Fields>
          <asp:BoundField DataField="id" 
            HeaderText="id" 
            InsertVisible="False" 
            ReadOnly="True" 
            SortExpression="id" />
          <asp:BoundField DataField="name" 
            HeaderText="name" 
            SortExpression="name" />
          <asp:BoundField DataField="price" 
            HeaderText="price" 
            SortExpression="price" />
          <asp:TemplateField ShowHeader="False">
            <ItemTemplate>
              <asp:LinkButton ID="LinkButton1" 
                runat="server" 
                CausesValidation="False" 
                Text="Show Current Time" 
                OnClick="LinkButton1_Click">
              </asp:LinkButton>
            </ItemTemplate>
          </asp:TemplateField>
        </Fields>
      </asp:DetailsView>            
    </ContentTemplate>
  </asp:UpdatePanel>
  <hr />
  <h3>Time</h3>
  <asp:UpdatePanel ID="UpdatePanel2" 
    runat="server" 
    UpdateMode="Conditional">
    <ContentTemplate>
      <asp:Label ID="Label1" 
        runat="server" 
        Text="[time]" />
    </ContentTemplate>
  </asp:UpdatePanel>
  </form>
</body>
</html>

------------ 2010/3/26 追記 ------------

どうも上記の例は適切ではなかったです。

RegisterAsyncPostBackControl メソッドは「同期ポストバックではなく非同期ポストバックを実行できるように、Web サーバーコントロールをトリガとして登録」するものであって、ある特定の UpdatePanel の Trigger コレクションに AsyncPostPackTrigger を登録するものではないです。従って、部分更新を行うには、LinkButton.Click のイベントハンドラで当該 UpdatePanel の Update メソッドを実行する必要があります。

上記の例では、LinkButton が配置された DetailsView は UpdatePanel1 に含まれているので、RegisterAsyncPostBackControl で非同期ポストバックのトリガとして登録する必要はなく、単純に Click のイベントのハンドラ(LinkButton1_Click メソッド)で UpdatePanel2 の Update メソッドを実行すれば済みました。

これは 先のシナリオ でも同じです。LinkButton が UpdatePanel 内にない場合を例にすべきでした。(汗)

Tags: ,

AJAX

UpdatePanel へのトリガ追加

by WebSurfer 6. November 2010 19:08

2010/11/8 追記

MSDN ライブラリの AsyncPostBackTrigger クラス の説明によると、"AsyncPostBackTrigger コントロールのプログラムによる追加はサポートされていません。" とのことです。というわけで、以下の方法はボツです。(汗) 別の方法で書き換えたものを UpdatePanel へのトリガ追加(改版) にポストしましたのでそちらを見てください。

AsyncPostBackTrigger を動的に設定

ASP.NET AJAX Extensions の UpdatePanel コントロールのトリガに AsyncPostBackTrigger クラスの ControlID として Button などのコントロールを動的に設定する話です。

大体のケースでは静的に設定できると思いますが、UniqueID を設定しなければならない場合は話が別です。

UniqueID を設定する必要があるのは、トリガとなる Button などのコントロールが名前付けコンテナに含まれ、ID とは異なる一意の UniqueID がサーバー側で設定される場合です。

名前付けコンテナとは、例えば、Repeater, DataList, DetailsView, FormView, GridView などやマスタページの ContentPlaceHolder です。

そのような場合に、AsyncPostBackTrigger クラスの ControlID プロパティに(UniqueID ではなく)ID を設定すると、「UpdatePanel 'UpdatePanel1' のトリガ用の ID 'Button1' のコントロールが見つかりませんでした。」というようなエラーが出るはずです。

というわけで本来は UniqueID を設定しなければなりませんが、UniqueID はサーバー側で生成されますので、サーバー側で動的に設定した方が簡単そうです。

ただし、タイミングが問題です。自分が試した限りですが、Page_Load ではうまくいかず、Page_Init でないとダメでした。(Page_Load で設定すると UpdateMode="Conditional" ではスクリプトエラー、デフォルトの Always にすればスクリプトエラーは出ないものの偶数回目のクリックで全画面が更新されてしまうというように、期待した動きにはなりません)

しかしながら、目的の(トリガとする)コントロールの参照が Page_Init では取得できない場合が困ります。例えば DetailesView の Template 内に配置したコントロールを FindControl メソッドで取得しようとしても、Page_Init では取得できません。

で、どうするかと言えば、ちょっとハック的なやり方ですが、Page_Init ではダミーのコントロールを設定し、Page_Load で目的のコントロールを設定す��とうまくいきました。

DetailsView の Template に配置した Button を、AsyncPostBackTrigger クラスの ControlID プロパティに設定するサンプルを以下にアップしておきます。

<%@ 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_Init(object sender, EventArgs e)
  {
    // ここでは LinkButton1 取得できない。以下は常に null。
    // それはよしとして、不可解なのは、以下の一文を入れて
    // おくと LinkButton1 クリックで、DetailsView が消えて
    // しまうこと。何故???
    //LinkButton lb =
    //  (LinkButton)DetailsView1.FindControl("LinkButton1");

    // この時点でダミーでもいいから以下のように Trigger を追
    // 加しておかないとうまくいかない。これなしでページロード
    // でトリガを追加すると、LinkButton1 クリックでスクリプト
    // エラーとなる。
    AsyncPostBackTrigger trigger = new AsyncPostBackTrigger();
    trigger.ControlID = DummyButton.ID;
    UpdatePanel2.Triggers.Add(trigger);
  }

  protected void Page_Load(object sender, EventArgs e)
  {
    LinkButton lb = 
      (LinkButton)DetailsView1.FindControl("LinkButton1");
    if (lb != null)
    {
      AsyncPostBackTrigger trigger = new AsyncPostBackTrigger();

      // DetailsView に配置したボタンは UniqueID を持つ。
      // それを ControlID に設定しないとエラーになる。
      trigger.ControlID = lb.UniqueID;
      UpdatePanel2.Triggers.Add(trigger);
    }        
  }    

  protected void LinkButton1_Click(object sender, EventArgs e)
  {
    Label1.Text = DateTime.Now.ToString();
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>AsyncPostBackTrigger を動的に設定</title>
</head>
<body>
  <form id="form1" runat="server">
  <asp:ScriptManager ID="ScriptManager1" runat="server">
  </asp:ScriptManager>

  <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
    ConnectionString="<%$ ConnectionStrings:MyDB %>" 
    SelectCommand="SELECT [id], [name], [price] FROM [table]" >
  </asp:SqlDataSource>

  <asp:Button ID="DummyButton" 
    runat="server" 
    style="display: none;" />
  <h2>AsyncPostBackTrigger を動的に設定</h2>
  <h3>DetailsView</h3>
  <asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate>
      <asp:DetailsView ID="DetailsView1" 
        runat="server" 
        AutoGenerateRows="False" 
        DataKeyNames="id" 
        DataSourceID="SqlDataSource1" 
        EnableModelValidation="True" 
        AllowPaging="True" 
        Width="250px">
        <Fields>
          <asp:BoundField DataField="id" 
            HeaderText="id" 
            InsertVisible="False" 
            ReadOnly="True" 
            SortExpression="id" />
          <asp:BoundField DataField="name" 
            HeaderText="name" 
            SortExpression="name" />
          <asp:BoundField DataField="price" 
            HeaderText="price" 
            SortExpression="price" />
          <asp:TemplateField ShowHeader="False">
            <ItemTemplate>
              <asp:LinkButton ID="LinkButton1" 
                runat="server" 
                CausesValidation="False" 
                Text="Show Current Time" 
                OnClick="LinkButton1_Click">
              </asp:LinkButton>
            </ItemTemplate>
          </asp:TemplateField>
        </Fields>
      </asp:DetailsView>            
    </ContentTemplate>
  </asp:UpdatePanel>
  <hr />
  <h3>Time</h3>
  <asp:UpdatePanel ID="UpdatePanel2" 
    runat="server" 
    UpdateMode="Conditional">
    <ContentTemplate>
      <asp:Label ID="Label1" 
        runat="server" 
        Text="[time]" />
    </ContentTemplate>
  </asp:UpdatePanel>
  </form>
</body>
</html>

Tags: , ,

AJAX

ACT の TabContainer

by WebSurfer 5. November 2010 21:04
ACT の TabContainer と Calendar の問題

AJAX Control Toolkit の TabContainer や Calendar を使用する際、head 要素内にコードブロック(即ち、<% ... %>)を定義すると例外がスローされるという問題と回避策の紹介です。なお、この問題は .NET 4 でも同様に発生します。

Calendar や TabContainer は AjaxControlToolkit.ScriptObjectBuilder クラスの RegisterCssReferences メソッドを起動し、head タグ内に HTML link 要素(CSS ファイルの定義)を追加しようとします。その時に head タグ内にコードブロックがあると、画像に示したように例外をスローします。

この問題を再現する具体的なコード例を下に載せておきます。この例のように JavaScript を定義する際に ClientID を取得するため、コードブロックを埋め込むケースはよくあるのではないでしょうか。

回避策は書くまでもないですが、コードブロックを head タグの外に移動するだけです。

なお、'<%=TextBox1.ClientID%>' の代わりに、実際の ClientID をプログラマがハードコーディングするのは避けたほうがよさそうです。何故なら、コントロールを配置する場所によって Client ID は変化しますし、コードを変更しなくても将来の ASP.NET の仕様の変更で変わるかもしれませんので。

<%@ Page Language="C#" %>
<%@ Register Assembly="AjaxControlToolkit" 
    Namespace="AjaxControlToolkit" 
    TagPrefix="asp" %>

<!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>
    <script type="text/javascript">   
    <!--
        function ShowText() {
            var txtbx = $get('<%=TextBox1.ClientID%>');
            txtbx.value = 'これはテストです。';
        }
    //-->
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ToolkitScriptManager ID="ToolkitScriptManager1" 
            runat="server">
        </asp:ToolkitScriptManager>
        <asp:TextBox ID="TextBox1" 
            runat="server" 
            Text="Test" />
        <asp:Button ID="Button1" 
            runat="server" 
            Text="Button" 
            OnClientClick="ShowText(); return false;" />
        <asp:TabContainer ID="TabContainer1" 
            runat="server" 
            ActiveTabIndex="0" 
            Height="160px" 
            Width="360px">
            <asp:TabPanel runat="server" 
                HeaderText="TabPanel1" 
                ID="TabPanel1">
                <ContentTemplate>ABCDE</ContentTemplate>
            </asp:TabPanel>
            <asp:TabPanel ID="TabPanel2" 
                runat="server" 
                HeaderText="TabPanel2">
                <ContentTemplate>FGHIJ</ContentTemplate>
            </asp:TabPanel>
        </asp:TabContainer>
    </div>
    </form>
</body>
</html>

Tags: , ,

AJAX

About this blog

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

Calendar

<<  May 2024  >>
MoTuWeThFrSaSu
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar