Ajax対応コントロール/コンポーネントの開発:Periodic RefreshパターンはじめにPeriodic Refresh(定期リフレッシュ)パターンを使用するAjax対応コンポーネントは、サーバを定期的にポーリングして変更がないかどうかを確認します。この記事では、AjaxNotifierという名前のAjax対応カスタムコントロールの開発を通じて、Periodic Refreshパターンを用いた独自のAjax対応コントロールの開発方法を説明します。 AjaxNotifierは、サーバを定期的にポーリングして、最新の通知がポストされていないかどうかを確認し、その通知を図1のようなポップアップダイアログに表示します。このポップアップダイアログには、通知の送信元と通知の内容が示されます。 図1 ![]() この処理のワークフローを以下に示します。
AjaxNotifierコントロールは、WebControl基本クラスから派生しており、ICallbackEventHandlerインターフェースを実装します。このコントロールは、WebControlクラスの次のメソッドをオーバーライドします。
WebControlからの派生 リスト1に、 リスト1 OnPreRenderメソッド
protected override void OnPreRender(EventArgs e) { DetermineRenderClientScript(); if (renderClientScript) { string js = Page.ClientScript.GetCallbackEventReference( this, "GetNotificationId('"+ClientID+"')", "AjaxNotifierCallback", "'" + ClientID + "'", true); string js2 = "function DoCallback () {" + js + ";}"; Page.ClientScript.RegisterClientScriptResource (typeof(AjaxNotifier), "CustomComponents.AjaxNotifier.js"); Page.ClientScript.RegisterClientScriptBlock(typeof(AjaxNotifier), typeof(AjaxNotifier).FullName + "DoCallback", js2, true); Page.ClientScript.RegisterStartupScript(typeof(AjaxNotifier), typeof(AjaxNotifier).FullName + "WebDoCallback", js,true); } base.OnPreRender(e); }
Page.ClientScript.RegisterClientScriptResource(typeof(AjaxNotifier),
"CustomComponents.AjaxNotifier.js");
埋め込みリソースについては、『Professional ASP.NET 2.0 Server Control and Component Development』(Wrox, July-2006, ISBN: 0-471-79350-7)の第26章「Developing Ajax-Enabled Controls and Components:Client-Side Functionality」で詳しく説明しています。「AjaxNotifier.js」スクリプトファイルには、AjaxNotifierコントロールが使用するJavaScript関数が含まれています。この記事では、このJavaScript関数について詳しく説明します。 2番目のスクリプトブロックには、
string js = Page.ClientScript.GetCallbackEventReference(
this,
"GetNotificationId('"+ClientID+"')",
"AjaxNotifierCallback",
"'" + ClientID + "'", true);
string js2 = "function DoCallback () {" + js + ";}";
3番目のスクリプトブロックには、サーバへの非同期クライアントコールバックを行うJavaScript関数の呼び出しが含まれます。
Page.ClientScript.RegisterStartupScript(typeof(AjaxNotifier),
typeof(AjaxNotifier).FullName + "WebDoCallback", js,true);
リスト2に、AjaxNotifierコントロールの リスト2 AddAttributesToRenderメソッド
protected override void AddAttributesToRender(HtmlTextWriter writer) { base.AddAttributesToRender(writer); if (renderClientScript) { CssStyleCollection col; writer.AddAttribute("notificationId", "0"); if (dialogStyle != null) { col = dialogStyle.GetStyleAttributes(this); writer.AddAttribute("dialogStyle", col.Value); } if (headerStyle != null) { col = headerStyle.GetStyleAttributes(this); writer.AddAttribute("headerStyle", col.Value); } if (itemStyle != null) { col = itemStyle.GetStyleAttributes(this); writer.AddAttribute("itemStyle", col.Value); } if (alternatingItemStyle != null) { col = alternatingItemStyle.GetStyleAttributes(this); writer.AddAttribute("alternatingItemStyle", col.Value); } } } AjaxNotifierがポップアップ表示するダイアログは、ダイアログのリサイズ時にクライアントサイドで行われるすべての処理(例えばレンダリング、移動、リサイズ、フォント調整など)を自ら担当します。従って、このポップアップダイアログはサーバーコントロールではありません。第27章では、このポップアップダイアログのようなクライアントサイドコンポーネントのCSSスタイル属性をAjax対応コントロール自身の最上位プロパティとして公開する方法を説明しています。 AjaxNotifierは、前述した第27章の ICallbackEventHandlerの実装 AjaxNotifierコントロールでは、ICallbackEventHandlerインターフェースを実装することで、ASP.NET 2.0のクライアントコールバックメカニズムを利用してサーバへの非同期クライアントコールバックを行います。リスト3に、 リスト3 ICallbackEventHandlerのメソッドの実装
protected virtual string GetCallbackResult() { return callbackResult; } protected virtual void RaiseCallbackEvent(string eventArgument) { IDataSource ds = (IDataSource)Page.FindControl(DataSourceID); int notificationId = int.Parse(eventArgument); if (notificationId < 0) notificationId = 1; Page.Session["NotificationId"] = notificationId; if (ds != null) { DataSourceView dv = ds.GetView(string.Empty); dv.Select(DataSourceSelectArguments.Empty, SelectCallback); } } AjaxNotifierは、 リスト4 AjaxNotifierコントロールを使ったページ
<%@ Page Language="C#" %>
<%@ Register TagPrefix="custom" Namespace="CustomComponents"
Assembly="CustomComponents" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<form id="form1" runat="server">
<custom:AjaxNotifier runat="server" DataSourceID="MySource"
DialogStyle-BackColor="LightGoldenrodYellow"
DialogStyle-BorderColor="Tan" DialogStyle-BorderWidth="1px"
DialogStyle-CellPadding="2" DialogStyle-CellSpacing="0"
DialogStyle-BorderStyle="Groove" DialogStyle-ForeColor="Black"
DialogStyle-GridLines="None" HeaderStyle-BackColor="Tan"
HeaderStyle-Font-Bold="True"
AlternatingItemStyle-BackColor="PaleGoldenrod" />
<asp:SqlDataSource runat="server" ID="MySource"
ConnectionString="<%$ connectionStrings:
MySqlConnectionString %>"
SelectCommand="Select * From Notifications Where Id > @Id">
<SelectParameters>
<asp:SessionParameter Name="Id"
SessionField="NotificationId"
Type="Int32" />
</SelectParameters>
</asp:SqlDataSource>
</form>
</body>
</html>
リスト4のページでは、AjaxNotifierコントロールをSqlDataSourceコントロールにバインドしています。 Select * From Notifications Where Id > @Id このSQLステートメントには、
<asp:SqlDataSource runat="server" ID="MySource"
ConnectionString="<%$ connectionStrings:MySqlConnectionString %>"
SelectCommand="Select * From Notifications Where Id > @Id">
<SelectParameters>
<asp:SessionParameter Name="Id" SessionField="NotificationId"
Type="Int32" />
</SelectParameters>
</asp:SqlDataSource>
表形式データソースコントロールとポストされたデータとの間でデータをやり取りするための方法は、Sessionオブジェクトだけではありません。この作業を行う他の方法については、『Professional ASP.NET 2.0 Server Control and Component Development』(Wrox, July-2006, ISBN: 0-471-79350-7)の第27章「Developing Ajax-Enabled Controls and Components:Asynchronous Client Callback」で説明しています。リスト3に示したように、 リスト5 SelectCallbackメソッド
private void SelectCallback(IEnumerable data) { using (StringWriter sw = new StringWriter()) { using (XmlWriter xw = XmlWriter.Create(sw)) { xw.WriteStartDocument(); xw.WriteStartElement("notification"); IEnumerator iter = data.GetEnumerator(); if (iter.MoveNext()) { PropertyDescriptorCollection col = TypeDescriptor.GetProperties(iter.Current); foreach (PropertyDescriptor pd in col) { if (pd.Name == "Source") xw.WriteElementString("source", (string)pd.GetValue(iter.Current)); else if (pd.Name == "Notification") xw.WriteElementString("summary", (string)pd.GetValue(iter.Current)); else if (pd.Name == "Id") xw.WriteElementString("id", pd.GetValue(iter.Current).ToString()); } } xw.WriteEndElement(); xw.WriteEndDocument(); } callbackResult = sw.ToString(); } } 従って、XMLドキュメントの構造または書式を決めることも、Ajax対応コントロールの開発者の仕事の1つとなります。リスト6に、AjaxNotifierコントロールがサポートするXMLドキュメントの例を示します。 リスト6 AjaxNotifierがサポートするXMLドキュメントの例
<notification> <id>3</id> <source>John</source> <summary>We'll meet tomorrow morning</summary> </notification> 他のすべてのXMLドキュメントと同じように、このXMLドキュメントも1つのドキュメント要素( リスト5に示したように、 xw.WriteStartDocument(); 次に、 xw.WriteStartDocument(); その後、列挙オブジェクトにアクセスし、取得したデータを一般的な方法で列挙できるようにします。 IEnumerator iter = data.GetEnumerator(); 続いて、PropertyDescriptionCollectionコレクションを取得します。このコレクションには、取得したレコードのデータフィールドごとに1つのPropertyDescriptorオブジェクトが含まれています。
PropertyDescriptorCollection col =
TypeDescriptor.GetProperties(iter.Current);
次に、これらのPropertyDescriptorオブジェクトを反復処理し、 if (pd.Name == "Source") xw.WriteElementString("source", (string)pd.GetValue(iter.Current)); else if (pd.Name == "Notification") xw.WriteElementString("summary", (string)pd.GetValue(iter.Current)); else if (pd.Name == "Id") xw.WriteElementString("id", pd.GetValue(iter.Current).ToString()); GetNotificationId JavaScript関数の再確認ここで、リスト1のコードに戻り、前に説明しなかった部分について見ていきます。リスト7に、該当部分を太字で示します。 リスト7 OnPreRenderメソッドの再確認
protected override void OnPreRender(EventArgs e) { DetermineRenderClientScript(); if (renderClientScript) { ``string js = Page.ClientScript.GetCallbackEventReference( this, "GetNotificationId('"+ClientID+"')", "AjaxNotifierCallback", "'" + ClientID + "'", true); string js2 = "function DoCallback () {" + js + ";}"; Page.ClientScript.RegisterClientScriptResource (typeof(AjaxNotifier), "CustomComponents.AjaxNotifier.js"); Page.ClientScript.RegisterClientScriptBlock(typeof(AjaxNotifier), typeof(AjaxNotifier).FullName + "DoCallback", js2, true); Page.ClientScript.RegisterStartupScript(typeof(AjaxNotifier), typeof(AjaxNotifier).FullName + "WebDoCallback", js,true); } base.OnPreRender(e); } リスト7の太字部分が示すように、 function GetNotificationId(ajaxNotifierId) { var ajaxNotifier = document.getElementById(ajaxNotifierId); return ajaxNotifier.notificationId; } リスト7で見たように、 リスト7の太字部分のコードが示すように、 リスト8 AjaxNotifierCallbackメソッド
function AjaxNotifierCallback(result, context) { var xmlDocument = CreateXmlDocument(); xmlDocument.loadXML(result); var notification = xmlDocument.documentElement; if (notification.childNodes.length > 0) { var notificationId = notification.childNodes[0].text; var ajaxNotifier = document.getElementById(context); if (notificationId != ajaxNotifier.notificationId) { ajaxNotifier.notificationId = notificationId; InitializeDetailsPopup(context); var notificationSource = notification.childNodes[1].text; var notificationSummary = notification.childNodes[2].text; var content = "<r>" + "<td colspan='2'>" + "<p><center><b>Notification</b></center></p>" + "<p><b>From: </b>"+notificationSource+"</p>" + "<p><b>Message:</b><br/>"+notificationSummary+"</p>" + "</td>" + "</r>"; DisplayDetailsPopup (content); } } setTimeout(DoCallback,6000); }
var xmlDocument = CreateXmlDocument();
次に、サーバから受け取ったXMLドキュメントを、XMLストアにロードします。 xmlDocument.loadXML(result); 次に、XMLドキュメントのドキュメント要素にアクセスします。リスト6に示したように、このドキュメント要素は
var notification = xmlDocument.documentElement;
そして、ドキュメント要素の最初の子要素にアクセスします。リスト6に示したように、最初の子要素は
var notificationId = notification.childNodes[0].text;
次に、
var ajaxNotifier = document.getElementById(context);
AjaxNotifierコントロールの親HTML要素には、現在のユーザーに示されている最新の通知のIDを持つ ajaxNotifier.notificationId = notificationId; その後、 InitializeDetailsPopup(context); 次に、 var notificationSource = notification.childNodes[1].text; var notificationSummary = notification.childNodes[2].text; 次に、 var content = "<r>" + "<td colspan='2'>" + "<p><center><b>New Message</b></center></p>" + "<p><b>From: </b>"+notificationSource+"</p>" + "<p><b>Message:</b><br/>"+ notificationSummary+"</p>" + "</td>" + "</r>"; そして、 DisplayDetailsPopup (content); 最後に、 setTimeout(DoCallback,6000); リスト1では、
次のワークフローで、AjaxNotifierの動作を自分自身で確かめてみましょう。
このアプリケーションを最初に起動したときには、AjaxNotifierによってすべての通知が1つ1つ表示されます。これは、AjaxNotifierの現在の実装には、前回のセッションでユーザーに示された最新の通知のIDが格納されていないからです。そのため、アプリケーションを起動するたびに、この通知IDがゼロにリセットされます。この点は、ASP.NET 2.0のProfileオブジェクトに最新の通知のIDを格納することで簡単に修正できます。
この記事は、Shahram Khosravi著『Professional ASP.NET 2.0 Server Control and Component Development』(Wrox, July-2006, ISBN: 0-471-79350-7)、第29章「Developing Ajax-Enabled Controls and Components:More Ajax Patterns」からの抜粋です。
Copyright 2007 by WROX.All rights reserved. この記事の内容を複製する場合は発行元の許可が必要です。 著者紹介Shahram(Shahram)
ASP.NET、Webサービス、.NET テクノロジ、XMLテクノロジ、ADO.NET、C#、3Dコンピュータグラフィック、ヒューマンインターフェイス(HI)の有用性、および設計パターンを専門とするシニアソフトウェアエンジニア兼コンサルタント。業務の傍ら執筆活動も行う。オブジェクト指向分析、設計、プログラミングに関する経験は10年を超えており、.NET Framework、ADO.NET、ASP.NET、およびXMLテクノロジに関する記事を『Dr. Dobb's Journal』、『asp.netPRO』、『Microsoft MSDN Online』など業界最大手の専門誌に多数寄稿している。
|