カスタム クライアント イベントの作成
更新 : 2007 年 11 月
ASP.NET における AJAX の機能として、完全な複数層のクライアント イベント モデルがあります。Sys.Application クラスには、アプリケーション レベルのイベントが用意されています。Sys.WebForms.PageRequestManager クラスには、部分ページ レンダリングで使用するページの部分に関するイベントが用意されています。各コンポーネント (コントロールや動作など) にも、独自のイベントがあります。これらのイベントの詳細については、「AJAX クライアント ライフ サイクル イベント」を参照してください。
ASP.NET では、クライアント ライフ サイクルにイベントを追加することもできます。Sys.UI.DomEvent クラスを使用すると、HTML ドキュメント オブジェクト モデル (DOM: Document Object Model) イベントをカスタムの ASP.NET AJAX コンポーネントに関連付けることができます。さらに、Sys.EventHandlerList クラスを使用すると、新しい ASP.NET AJAX クライアント イベントを直接作成できます。
DOM イベントへの関連付け
多くの場合、ユーザーが必要とするイベントは、HTML DOM で定義されているイベントに対応しています。たとえば、カスタムの ASP.NET AJAX ボタン コントロールでは、関連付けられている HTML <button> 要素の click イベントを使用できます。DOM ベースのイベントを ASP.NET AJAX アプリケーションの、またはカスタムのコンポーネントに関連付けるには、DomEvent クラスの addHandler メソッドを使用します。使用例は次のとおりです。
Sys.UI.DomEvent.addHandler(element, 'click', this.myClickHandler);
また、$addHandler ショートカットも使用できます。使用例は次のとおりです。
$addHandler(element, 'click', this.myClickHandler);
addHandler メソッドは、element、eventName、および handler の 3 つのパラメータを受け取ります。element パラメータは、イベントを公開する DOM 要素への参照です。eventName パラメータは、DOM イベント自体の名前です。handler パラメータは、イベントの発生時に呼び出す関数への参照です。詳細については、「Sys.UI.DomEvent addHandler メソッド」を参照してください。
DOM イベントのハンドラを削除するには、Sys.UI.DomEvent.removeHandler メソッド、または $removeHandler ショートカットを、addHandler と同じパラメータを指定して呼び出します。
メモ : |
---|
addHandler 関数および removeHandler 関数に渡すイベント名の先頭には "on" を付けないでください。たとえば、"onclick" ではなく "click" を使用します。 |
カスタム イベント ハンドラの追加と削除
カスタムの ASP.NET AJAX コンポーネントのイベントに新しいイベント ハンドラを追加するには、Sys.EventHandlerList クラスの addHandler メソッドを使用します。ASP.NET AJAX イベント モデルのすべてのクライアント イベント、および関連付けられたイベント ハンドラは、専用のディクショナリである EventHandlerList オブジェクトに格納されます。すべてのコンポーネント (現在の Application オブジェクトを含む) には、それぞれの EventHandlerList インスタンスがあります。EventHandlerList オブジェクトに項目を追加すると、該当するコンポーネントに新しいイベントとイベント ハンドラが追加されます。イベントを追加するには、次の構文を使用します。
this.get_events().addHandler(event, handler);
Sys.Component クラスの events プロパティは、そのコンポーネントの EventHandlerList インスタンスを返します。events プロパティは、Sys.UI.Control、Sys.UI.Behavior、および Sys.Application の各クラスに継承されます。event パラメータは、ハンドラの追加先となる新しいイベント、または既存のイベントの名前です。handler パラメータは、イベントの発生時に呼び出す関数への参照です。event パラメータを新しい値に設定すると、新しいイベントがディクショナリに追加されます。
ディクショナリからカスタムのイベントを削除するには、Sys.EventHandlerList クラスの removeHandler メソッドを使用します。このメソッドは、addHandler と同じパラメータを受け取ります。
Microsoft AJAX Library で既に定義されているイベントにハンドラを追加するには、そのイベントの add_ アクセサを使用します。使用例は次のとおりです。
Sys.Application.add_load(myLoadHandler);
カスタム イベントの発生
カスタム イベントを発生させるには、EventHandlerList インスタンスの getHandler メソッドを呼び出します。このとき、パラメータとしてイベント名を指定します。このメソッドは、そのイベントのハンドラであるすべての関数を集約した関数を返します。返された関数を呼び出して、イベントを発生させます。使用例は次のとおりです。
var h = this.get_events().getHandler('myCustomEvent')
if (h) h(this, Sys.EventArgs.Empty);
規則により、イベント ハンドラは sender および eventArgs の 2 つのパラメータを取ります。sender はイベントの対象となるコンポーネントで、通常は this です。eventArgs パラメータは、Sys.EventArgs オブジェクトへの参照です。このオブジェクトには、イベントに渡す情報 (マウスの座標など) を格納できます。sender パラメータと eventArgs パラメータは、getHandler から返された関数のシグネチャが、関連付けられたすべてのハンドラ関数のシグネチャと同じである場合に限り、省略できます。ただし、前の例のようにパラメータを指定することをお勧めします。
例
説明
次の例では、2 つのセクションから成る複数選択テストを 1 つ作成します。セクションに含まれるすべての質問の回答が終了したら、セクションの背景色が変化します。ユーザーがテストの最後にあるボタンをクリックすると、各質問の隣に、回答が正解であるかどうかを示すステータス メッセージが表示されます。
このアプリケーションには、2 つのカスタム コントロールのインスタンスが含まれています。Question コントロールは、HTML <select> 要素に関連付けられており、Section コントロールは、1 つ以上の Question コントロールを含む <div> 要素に関連付けられています。Question コントロールは select イベントを公開しており、このイベントは、Sys.UI.DomEvent インスタンスによって基になる <select> 要素の onChange イベントに関連付けられています。Section コントロールは complete イベントを公開しており、このイベントは、Section インスタンス内のすべての Question コントロールで回答が選択されると、ユーザー定義の関数によって発生します。
コード
次の例では、コンポーネントのインスタンスを作成し、イベントを処理する Default.aspx ページを示します。
<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Custom Events Example</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" >
<Scripts>
<asp:ScriptReference Path="question.js" />
<asp:ScriptReference Path="section.js" />
</Scripts>
</asp:ScriptManager>
<script type="text/javascript">
// Add handler to init event
Sys.Application.add_init(appInitHandler);
function appInitHandler() {
// create components
$create(Demo.Question, {correct: '3'},
{select: onAnswer},null, $get('Question1'));
$create(Demo.Question, {correct: '3'},
{select: onAnswer},null, $get('Question2'));
$create(Demo.Question, {correct: '3'},
{select: onAnswer},null, $get('Question3'));
$create(Demo.Question, {correct: '3'},
{select: onAnswer},null, $get('Question4'));
$create(Demo.Section, null,
{complete: onSectionComplete},null, $get('group1'));
$create(Demo.Section, null,
{complete: onSectionComplete},null, $get('group2'));
}
function onAnswer(question) {
// If all questions in this section answered,
// raise complete event
var section = question.get_element().parentElement;
var questions = section.children;
$get(question.get_id() + 'Status').innerHTML = '';
for (var i=0; i<questions.length; i++) {
if (questions[i].selectedIndex === -1) {
return;
}
}
$find(section.id).raiseComplete();
}
function onSectionComplete(section) {
// Change background color of <div>.
section.get_element().style.backgroundColor = 'yellow';
}
function done() {
// Display correct answers where needed.
var c = Sys.Application.getComponents();
for (var i=0; i<c.length; i++) {
var type = Object.getType(c[i]).getName();
if (type !== 'Demo.Question') continue;
var element = c[i].get_element();
var answer = element.selectedIndex;
var correct = $find(c[i].get_id()).get_correct();
var statusElement = c[i].get_id() + 'Status';
if (answer !== correct) {
$get(statusElement).innerHTML = 'Incorrect. Try again.';
}
else
{
$get(statusElement).innerHTML = 'Correct.';
}
}
}
function resethandler() {
var c = Sys.Application.getComponents();
for (var i=0; i<c.length; i++) {
var type = Object.getType(c[i]).getName();
if (type === 'Demo.Question') {
var element = c[i].get_element();
element.selectedIndex = -1;
var answer = element.selectedIndex;
var statusElement = c[i].get_id() + 'Status';
$get(statusElement).innerHTML = '';
}
else if (type === 'Demo.Section') {
c[i].get_element().style.backgroundColor = 'White';
}
}
}
</script>
<h3>Addition</h3><br />
<div id="Group1">
2 + 2 =
<select id="Question1">
<option>2</option>
<option>22</option>
<option>4</option>
<option>5</option>
</select><span id="Question1Status"></span><br />
2 + 3 =
<select id="Question2" >
<option>3</option>
<option>23</option>
<option>5</option>
<option>6</option>
</select><span id="Question2Status"></span><br />
</div><br /> <br />
<h3>Subtraction</h3><br />
<div id="Group2">
2 - 1 =
<select id="Question3" >
<option>2</option>
<option>0</option>
<option>1</option>
<option>-2</option>
</select><span id="Question3Status"></span><br />
2 - 2 =
<select id="Question4" >
<option>2</option>
<option>-2</option>
<option>0</option>
<option>-4</option>
</select><span id="Question4Status"></span><br />
</div><br /><br />
<input id="Submit1" type="button" value="Check Answers" onclick="done()" />
<input id="Reset1" type="button" value="Start Again" onclick="resethandler()" />
</form>
</body>
</html>
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Custom Events Example</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" >
<Scripts>
<asp:ScriptReference Path="question.js" />
<asp:ScriptReference Path="section.js" />
</Scripts>
</asp:ScriptManager>
<script type="text/javascript">
// Add handler to init event
Sys.Application.add_init(appInitHandler);
function appInitHandler() {
// create components
$create(Demo.Question, {correct: '3'},
{select: onAnswer},null, $get('Question1'));
$create(Demo.Question, {correct: '3'},
{select: onAnswer},null, $get('Question2'));
$create(Demo.Question, {correct: '3'},
{select: onAnswer},null, $get('Question3'));
$create(Demo.Question, {correct: '3'},
{select: onAnswer},null, $get('Question4'));
$create(Demo.Section, null,
{complete: onSectionComplete},null, $get('group1'));
$create(Demo.Section, null,
{complete: onSectionComplete},null, $get('group2'));
}
function onAnswer(question) {
// If all questions in this section answered,
// raise complete event
var section = question.get_element().parentElement;
var questions = section.children;
$get(question.get_id() + 'Status').innerHTML = '';
for (var i=0; i<questions.length; i++) {
if (questions[i].selectedIndex === -1) {
return;
}
}
$find(section.id).raiseComplete();
}
function onSectionComplete(section) {
// Change background color of <div>.
section.get_element().style.backgroundColor = 'yellow';
}
function done() {
// Display correct answers where needed.
var c = Sys.Application.getComponents();
for (var i=0; i<c.length; i++) {
var type = Object.getType(c[i]).getName();
if (type !== 'Demo.Question') continue;
var element = c[i].get_element();
var answer = element.selectedIndex;
var correct = $find(c[i].get_id()).get_correct();
var statusElement = c[i].get_id() + 'Status';
if (answer !== correct) {
$get(statusElement).innerHTML = 'Incorrect. Try again.';
}
else
{
$get(statusElement).innerHTML = 'Correct.';
}
}
}
function resethandler() {
var c = Sys.Application.getComponents();
for (var i=0; i<c.length; i++) {
var type = Object.getType(c[i]).getName();
if (type === 'Demo.Question') {
var element = c[i].get_element();
element.selectedIndex = -1;
var answer = element.selectedIndex;
var statusElement = c[i].get_id() + 'Status';
$get(statusElement).innerHTML = '';
}
else if (type === 'Demo.Section') {
c[i].get_element().style.backgroundColor = 'White';
}
}
}
</script>
<h3>Addition</h3><br />
<div id="Group1">
2 + 2 =
<select id="Question1">
<option>2</option>
<option>22</option>
<option>4</option>
<option>5</option>
</select><span id="Question1Status"></span><br />
2 + 3 =
<select id="Question2" >
<option>3</option>
<option>23</option>
<option>5</option>
<option>6</option>
</select><span id="Question2Status"></span><br />
</div><br /> <br />
<h3>Subtraction</h3><br />
<div id="Group2">
2 - 1 =
<select id="Question3" >
<option>2</option>
<option>0</option>
<option>1</option>
<option>-2</option>
</select><span id="Question3Status"></span><br />
2 - 2 =
<select id="Question4" >
<option>2</option>
<option>-2</option>
<option>0</option>
<option>-4</option>
</select><span id="Question4Status"></span><br />
</div><br /><br />
<input id="Submit1" type="button" value="Check Answers" onclick="done()" />
<input id="Reset1" type="button" value="Start Again" onclick="resethandler()" />
</form>
</body>
</html>
次の例では、Demo.Question コントロールを定義する Question.js ファイルを示します。
Type.registerNamespace("Demo");
// Constructor
Demo.Question = function(element) {
Demo.Question.initializeBase(this, [element]);
// Create a delegate for the select event.
this._selectDelegate = null;
}
Demo.Question.prototype = {
// correct property accessors
get_correct: function() {
return this.get_element().name - 1;
},
set_correct: function(value) {
this.get_element().name = value;
},
// Bind and unbind to select event.
add_select: function(handler) {
this.get_events().addHandler('select', handler);
},
remove_select: function(handler) {
this.get_events().removeHandler('select', handler);
},
// Release resources before control is disposed.
dispose: function() {
var element = this.get_element();
if (this._selectDelegate) {
$clearHandlers(element);
delete this._selectDelegate;
}
Demo.Question.callBaseMethod(this, 'dispose');
},
initialize: function() {
var element = this.get_element();
// Make sure no option is selected.
element.value = "";
// Attach delegate to select event.
if (this._selectDelegate === null) {
this._selectDelegate = Function.createDelegate(this, this._selectHandler);
}
Sys.UI.DomEvent.addHandler(element, 'change', this._selectDelegate);
Demo.Question.callBaseMethod(this, 'initialize');
},
_selectHandler: function(event) {
var h = this.get_events().getHandler('select');
if (h) h(this, Sys.EventArgs.Empty);
}
}
Demo.Question.registerClass('Demo.Question', Sys.UI.Control);
// Since this script is not loaded by System.Web.Handlers.ScriptResourceHandler
// invoke Sys.Application.notifyScriptLoaded to notify ScriptManager
// that this is the end of the script.
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
Type.registerNamespace("Demo");
// Constructor
Demo.Question = function(element) {
Demo.Question.initializeBase(this, [element]);
// Create a delegate for the select event.
this._selectDelegate = null;
}
Demo.Question.prototype = {
// correct property accessors
get_correct: function() {
return this.get_element().name - 1;
},
set_correct: function(value) {
this.get_element().name = value;
},
// Bind and unbind to select event.
add_select: function(handler) {
this.get_events().addHandler('select', handler);
},
remove_select: function(handler) {
this.get_events().removeHandler('select', handler);
},
// Release resources before control is disposed.
dispose: function() {
var element = this.get_element();
if (this._selectDelegate) {
$clearHandlers(element);
delete this._selectDelegate;
}
Demo.Question.callBaseMethod(this, 'dispose');
},
initialize: function() {
var element = this.get_element();
// Make sure no option is selected.
element.value = "";
// Attach delegate to select event.
if (this._selectDelegate === null) {
this._selectDelegate = Function.createDelegate(this, this._selectHandler);
}
Sys.UI.DomEvent.addHandler(element, 'change', this._selectDelegate);
Demo.Question.callBaseMethod(this, 'initialize');
},
_selectHandler: function(event) {
var h = this.get_events().getHandler('select');
if (h) h(this, Sys.EventArgs.Empty);
}
}
Demo.Question.registerClass('Demo.Question', Sys.UI.Control);
// Since this script is not loaded by System.Web.Handlers.ScriptResourceHandler
// invoke Sys.Application.notifyScriptLoaded to notify ScriptManager
// that this is the end of the script.
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
次の例では、Demo.Section コントロールを定義する Section.js ファイルを示します。
Type.registerNamespace("Demo");
// Constructor
Demo.Section = function(element) {
Demo.Section.initializeBase(this, [element]);
}
Demo.Section.prototype = {
// Create add and remove accessors fot the complete event.
add_complete: function(handler) {
this.get_events().addHandler("complete", handler);
},
remove_complete: function(handler) {
this.get_events().removeHandler("complete", handler);
},
// Create a function to raise the complete event.
raiseComplete: function() {
var h = this.get_events().getHandler('complete');
if (h) h(this);
},
// Release resources before control is disposed.
dispose: function() {
var element = this.get_element();
$clearHandlers(element);
Demo.Section.callBaseMethod(this, 'dispose');
}
}
Demo.Section.registerClass('Demo.Section', Sys.UI.Control);
// Since this script is not loaded by System.Web.Handlers.ScriptResourceHandler
// invoke Sys.Application.notifyScriptLoaded to notify ScriptManager
// that this is the end of the script.
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
Type.registerNamespace("Demo");
// Constructor
Demo.Section = function(element) {
Demo.Section.initializeBase(this, [element]);
}
Demo.Section.prototype = {
// Create add and remove accessors fot the complete event.
add_complete: function(handler) {
this.get_events().addHandler("complete", handler);
},
remove_complete: function(handler) {
this.get_events().removeHandler("complete", handler);
},
// Create a function to raise the complete event.
raiseComplete: function() {
var h = this.get_events().getHandler('complete');
if (h) h(this);
},
// Release resources before control is disposed.
dispose: function() {
var element = this.get_element();
$clearHandlers(element);
Demo.Section.callBaseMethod(this, 'dispose');
}
}
Demo.Section.registerClass('Demo.Section', Sys.UI.Control);
// Since this script is not loaded by System.Web.Handlers.ScriptResourceHandler
// invoke Sys.Application.notifyScriptLoaded to notify ScriptManager
// that this is the end of the script.
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();