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

posted on 2007-02-28 17:07  Dflying Chen  阅读(5824)  评论(23编辑  收藏  举报