OWA或Messenger样式的信息提示窗口——编写ASP.NET AJAX Extender控件(上):客户端Behavior
前言(废话)
在《我的ASP.NET AJAX控件——PopupNotificationExtender:实现OWA或Messenger样式的信息提示窗口》中,我们在页面中创建了一个类似OWA或Messenger样式的信息提示窗口。当时时间有限,一直没能写出教程,值此空闲期间,加上ASP.NET AJAX正式发布,也应该把这个教程系列写出来了。
编写ASP.NET AJAX Extender控件还是能写出很多东西的,不过我也不能面面俱到,提一些重要之处而以,加上一些我自己的理解。如有错误,还请朋友们不吝指正。一篇文章是写不下了,还是拆开来吧,系列大概有2-3篇的样子,很快就能写完。
注意:原文中提供的下载DLL已经不能与现有的最新ASP.NET AJAX配合使用,不过该控件的功能和使用方法一点都没变,所以如果你没有读过上面提到的这篇文章的话,那么最好先看一下。我将在本系列的结束给出最新版本程序的所有源代码,包括DLL。
ASP.NET AJAX Extender控件简要介绍
ASP.NET AJAX Extender控件就是用来附着于现有的ASP.NET服务器端控件之上,为其提供各种各样的附加功能(主要是客户端的),如果你还没用过/听过/见过,那么到这里(http://ajax.asp.net/ajaxtoolkit/)看看吧。
ASP.NET AJAX Extender控件的实现原理就是靠在服务器端封装好的客户端JavaScript Behavior组件,关于ASP.NET AJAX的客户端Behavior组件,值得讲的东西就多了……
算了还是不讲了,直接开始编写这个客户端Behavior组件吧。
准备工作
首先创建一个空白的JavaScript文件,然后……然后没有了,下一篇文章再把Visual Studio拿出来,现在用不着这么强大的武器。接下来的代码都是写在这个文件中了。
注册命名空间
ASP.NET AJAX把命名空间的概念也引入到了JavaScript中。在刚刚创建的这个JS文件中,我们首先注册一个自己的命名空间(可以随便起名字,这里我叫它Dflying.Ajax)。
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.
Type.registerNamespace('Dflying.Ajax');
定义该客户端Behavior组件的私有属性
私有属性么,就要让类的每个实例都能够各不相同,所以要直接写在function定义中。先写一个这样的声明:
Dflying.Ajax.PopupNotificationBehavior = function(element) {
Dflying.Ajax.PopupNotificationBehavior.initializeBase(this, [element]);
// property members here......
}
Dflying.Ajax.PopupNotificationBehavior.initializeBase(this, [element]);这一句用来调用基类的同名方法(没办法,JavaScript没有内建的支持)。
然后再上面的function定义中添加一系列的私有属性,都有各自的用处,这里不多说了:
// property member values
this._resizeEffectDuration = 0.3;
this._showNotificationDuration = 3;
this._resultLabelID = "";
this._servicePath = "";
this._serviceMethod = "";
this._queryServiceInterval = 60000;
// animiations
this._resizeAnimateShow = null;
this._moveAnimateShow = null;
this._resizeAnimateHide = null;
this._moveAnimateHide = null;
// show notification timer and handler
this._showNotificationTimer = null;
this._showNotificationHandler = null;
// query service timer and handler
this._queryServiceTimer = null;
this._queryServiceHandler = null;
// mouse over & out handlers
this._mouseOverHandler = null;
this._mouseOutHandler = null;
// result label
this._resultLabel = null;
// original height of the binding element
this._originalHeight = 0;
// visibility state of the this notification pane
this._isVisiable = false;
定义该客户端Behavior组件的方法(公有/私有)
方法是可以在实例之间共享的,所以我们接下来再声明一个prototype,方法什么的就放在这里了:
Dflying.Ajax.PopupNotificationBehavior.prototype = {
}
然后是initialize方法,这是ASP.NET AJAX客户端组件必须包含的一个方法,用来初始化该组件(可以认为是构造函数),ASP.NET AJAX客户端运行时将自动调用该方法:
initialize : function() {
// get binding element
var element = this.get_element();
// save original height of the binding element
this._originalHeight = element.offsetHeight;
// set binding element to invisiable
element.style.height = 0;
// call base constructor
Dflying.Ajax.PopupNotificationBehavior.callBaseMethod(this, 'initialize');
// init animations
this._resizeAnimateShow = new AjaxControlToolkit.Animation.ResizeAnimation(
element, this._resizeEffectDuration, 25, null, this._originalHeight, 'px');
this._moveAnimateShow = new AjaxControlToolkit.Animation.MoveAnimation(
element, this._resizeEffectDuration, 25, null, -this._originalHeight, true, 'px');
this._resizeAnimateHide = new AjaxControlToolkit.Animation.ResizeAnimation(
element, this._resizeEffectDuration, 25, null, 0, 'px');
this._moveAnimateHide = new AjaxControlToolkit.Animation.MoveAnimation(
element, this._resizeEffectDuration, 25, null, this._originalHeight, true, 'px');
// mouse over handler
this._mouseOverHandler = Function.createDelegate(this, this._onMouseOver);
$addHandler(element, 'mouseover', this._mouseOverHandler);
// mouse out handler
this._mouseOutHandler = Function.createDelegate(this, this._onMouseOut);
$addHandler(element, 'mouseout', this._mouseOutHandler);
// show notification timer setup
this._showNotificationHandler = Function.createDelegate(this, this._onShowNotificationTimerTick);
this._showNotificationTimer = new Sys.Timer();
this._showNotificationTimer.add_tick(this._showNotificationHandler);
this._showNotificationTimer.set_interval((this._showNotificationDuration + this._resizeEffectDuration) * 1000);
// query service timer setup
this._queryServiceHandler = Function.createDelegate(this, this._onQueryServiceTimerTick);
this._queryServiceTimer = new Sys.Timer();
this._queryServiceTimer.add_tick(this._queryServiceHandler);
this._queryServiceTimer.set_interval(this._queryServiceInterval);
this._queryServiceTimer.set_enabled(true);
},
上面代码中用到了ASP.NET AJAX Control Toolkit中的一些Animation组件,很有意思的东西。还可以看到$addHandler()方法用来为DOM元素添加事件处理函数;Sys.Timer也有用到;以及那个巧妙的Function.createDelegate()方法。
与initialize方法类似的是dispose方法,顾名思义,用来释放该组件中用到的资源:
dispose : function() {
// get binding element
var element = this.get_element();
if (this._mouseOverHandler) {
$removeHandler(element, 'mouseover', this._mouseOverHandler);
this._mouseOverHandler = null;
}
if (this._mouseOutHandler) {
$removeHandler(element, 'mouseout', this._mouseOutHandler);
this._mouseOutHandler = null;
}
if (this._resizeAnimateShow) {
this._resizeAnimateShow.dispose();
this._resizeAnimateShow = null;
}
if (this._moveAnimateShow) {
this._moveAnimateShow.dispose();
this._moveAnimateShow = null;
}
if (this._resizeAnimateHide) {
this._resizeAnimateHide.dispose();
this._resizeAnimateHide = null;
}
if (this._moveAnimateHide) {
this._moveAnimateHide.dispose();
this._moveAnimateHide = null;
}
if (this._showNotificationTimer) {
this._showNotificationTimer.dispose();
this._showNotificationTimer = null;
}
this._showNotificationHandler = null;
if (this._queryServiceTimer) {
this._queryServiceTimer.dispose();
this._queryServiceTimer = null;
}
this._queryServiceHandler = null;
Dflying.Ajax.PopupNotificationBehavior.callBaseMethod(this, 'dispose');
},
接下来是轮询Web Service的那个Timer的tick事件处理函数:
_onQueryServiceTimerTick : function(eventObject) {
if (this._servicePath && this._serviceMethod) {
// Call the helper web service
Sys.Net.WebServiceProxy.invoke(
this.get_ServicePath(),
this.get_ServiceMethod(),
false,
null,
Function.createDelegate(this, this._onQueryServiceMethodComplete)
);
}
},
可以看到其中使用了Sys.Net.WebServiceProxy组件用来访问服务器端的Web Method。关于Sys.Net.WebServiceProxy的详细使用方法,请参考官方文档。
在上面的Sys.Net.WebServiceProxy中,我们指定了一个回调方法(Ajax的精髓阿!)——_onQueryServiceMethodComplete(),下面就是它的实现:
_onQueryServiceMethodComplete : function(sender, eventArgs) {
// get server side result
var result = sender;
if (!this._resultLabel)
{
this._resultLabel = $get(this._resultLabelID);
}
// update message
if (result != null && result != "")
{
this._resultLabel.innerHTML = result;
}
// play show animations
if (!this._isVisiable)
{
this._resizeAnimateShow.play();
if (this.get_VerticalSide() == AjaxControlToolkit.VerticalSide.Bottom)
{
this._moveAnimateShow.play();
}
// start timer
this._showNotificationTimer.set_enabled(true);
this._isVisiable = true;
}
},
当这个Web Method返回信息之后,我们控制页面中的Panel将信息显示出来(以动画的形式)。
Panel显示了一段时间之后,同样要以动画形式将其隐藏,下面是相关Timer的tick事件的处理函数:
_onShowNotificationTimerTick : function() {
// disable timer
this._showNotificationTimer.set_enabled(false);
// play hide animations
if (this._isVisiable)
{
this._resizeAnimateHide.play();
if (this.get_VerticalSide() == AjaxControlToolkit.VerticalSide.Bottom)
{
this._moveAnimateHide.play();
}
this._isVisiable = false;
}
},
当鼠标悬停在该Panel上的时候,Panel永远保持显示状态,鼠标移出之后再隐藏,下面就是这两个事件处理函数:
_onMouseOver : function () {
// stop animations in case of they are playing
this._resizeAnimateShow.stop();
this._resizeAnimateHide.stop();
if (this.get_VerticalSide() == AjaxControlToolkit.VerticalSide.Bottom) {
this._moveAnimateHide.stop();
this._moveAnimateShow.stop();
}
// show notification pane
this.get_element().style.height = this._originalHeight + 'px';
// disable timer
this._showNotificationTimer.set_enabled(false);
},
_onMouseOut : function () {
// start timer again
this._showNotificationTimer.set_enabled(true);
this._isVisiable = true;
},
最后是前面一大堆私有属性的getter和setter,很无聊的东西,不过为了让控件外部能够访问,还是要写出来(谁让你用JavaScript呢?):
get_ResizeEffectDuration : function() {
return this._resizeEffectDuration;
},
set_ResizeEffectDuration : function(value) {
if (this._resizeEffectDuration != value) {
this._resizeEffectDuration = value;
}
},
get_ShowNotificationDuration : function() {
return this._showNotificationDuration;
},
set_ShowNotificationDuration : function(value) {
if (this._showNotificationDuration != value) {
this._showNotificationDuration = value;
if (this._showNotificationTimer) {
this._showNotificationTimer.set_interval((this._showNotificationDuration + this._resizeEffectDuration) * 1000);
}
}
},
get_ResultLabelID : function() {
return this._resultLabelID;
},
set_ResultLabelID : function(value) {
if (this._resultLabelID != value) {
this._resultLabelID = value;
this._resultLabel = $get(this._resultLabelID);
}
},
get_ServicePath : function() {
return this._servicePath;
},
set_ServicePath : function(value) {
if (this._servicePath != value) {
this._servicePath = value;
}
},
get_ServiceMethod : function() {
return this._serviceMethod;
},
set_ServiceMethod : function(value) {
if (this._serviceMethod != value) {
this._serviceMethod = value;
}
},
get_QueryServiceInterval : function() {
return this._queryServiceInterval;
},
set_QueryServiceInterval : function(value) {
if (this._queryServiceInterval != value) {
this._queryServiceInterval = value;
if (this._queryServiceTimer) {
this._queryServiceTimer.set_interval(this._queryServiceInterval);
}
}
}
注意上面每一个setter都进行了必要性检查,避免了不必要的重置。
声明继承关系
最后来上这么一句:
Dflying.Ajax.PopupNotificationBehavior.registerClass('Dflying.Ajax.PopupNotificationBehavior', AjaxControlToolkit.AlwaysVisibleControlBehavior);
让我们的Behavior继承于AJAX Control Toolkit中的AjaxControlToolkit.AlwaysVisibleControlBehavior,于是大功告成!
下载
费尽心机写出来的这个PopupNotificationExtenderBehavior.js在这里下载:https://files.cnblogs.com/dflying/PopupNotificationExtenderBehavior.zip