讲座内容
这次我选择的讲座内容是最近在TechEd 2006 Europe中Shawn Burke的讲座“ASP.NET AJAX Control Toolkit Unleashed: Creating Rich Client-Side Controls and Components”。Shawn Burke是微软.NET Developer Platform总监。这个讲座的PPT和演示代码的下载地址已经由他本人
公布在他的Blog上 (
本地下载 )。另外,在MSDN's Showtime上也已经有了
这个讲座的完整视频 。
此次讲座的内容主要是对于ASP.NET AJAX Control Toolkit进行简单的介绍,展示了Extender控件是如何帮助ASP.NET开发人员简单地将丰富的用户体验集成到他们的Web应用程序中。在这次讲座里将看到应该如何在您的应用中使用ASP.NET AJAX Control Toolkit中的组件,并且了解开发人员是如何方便地开发一个APS.NET AJAX Extender的。
此次讲座分为两部分:“ASP.NET AJAX Control Toolkit介绍和使用”以及“开发一个Extender控件”。本文将对于该讲座的第二部分进行讲述,并且对其第二个演示的剩余部分进行分析。
讲座演示
请注意,在Shawn提供的压缩包内有三个空文件夹:Demo_Base、Demo_Step_2和Demo_Step_3,现在它们在演示时也是有内容的,我已经根据讲座视频设法将它们恢复到了演示时的样子,请点击这里 下载。
Step 2:Consuming other behaviors / Handling events / Coding Client-Side Logic
我们可以发现,Extender在客户端生成了代码,能够构造Behavior对象,也正确设置了Behavior的属性。在大多数情况下,下面要做就是可能会使用Toolkit中其他一些组件的功能。这也是我们在早期版本Toolkit中的一个挑战:如何才能使各种组件能够组合使用?结合各种组件的功能能够使UI变得更加有趣,我们也能获得更灵活的控制能力。实现这个功能的一个难点就是,这些组件都是JavaScript。每个组件都有自己的JavaScript文件,他们会被编译在程序集中。在使用Extender时,Tookit会知道应该使用哪些JavaScript文件,并将它们引入到页面中。
那么我们该如何引入别的Component的JavaScript文件呢?如果确定它们的脚本被正确加载了?在服务器端有一个CustomAttribute:RequiredScriptAttribute 。它的作用就是告诉服务器:“嘿,你们必须在我加载之前加载到页面中”,这样在客户端就能出现能够正确运行的脚本类库了。在客户端,我们能够使用$create 方法创建一个组件。我们一会儿就来看一下应该如何使用。
现在我们会使用另外两个组件,它们原本定义在Atlas中,但是在ASP.NET Beta版本中被移除了。不过它们被转移到了开源的Toolkit中,这样开发人员就能继续使用它们了。他们就是HoverExtender和PopupExtender。我们先在客户端Behavor的构造函数中添加这两个组件和一些其它的变量声明。如下:
在构造函数中声明变量 FontSize.FontSizeBehavior = function (element) {
...
// the behaviors we create
//
this ._popupBehavior = null ;
this ._hoverBehavior = null ;
// Handler delegates
//
this ._hoverHandler = null ;
this ._unhoverHandler = null ;
this ._incrementHandler = null ;
this ._decrementHandler = null ;
// state bits
//
this ._fontSize = 0;
this ._popupVisible = false ;
this ._fontUnit = "px ";
}
然后我们需要在Initialize方法中使用这两个Behavior。首先我们使用HoverBehavior,添加如下代码:
设置HoverBehavior // setup the hover behavior
//
if (this ._hoverCssClass && this ._sizePanelElement) {
this ._hoverHandler = Function .createDelegate(this , this ._onTargetHover);
this ._unhoverHandler = Function .createDelegate(this , this ._onTargetUnhover);
this ._hoverBehavior = $create( AjaxControlToolkit.HoverBehavior, {unhoverDelay:200, hoverElement: this ._sizePanelElement}, null , null , this .get_element());
this ._hoverBehavior.add_hover(this ._hoverHandler);
this ._hoverBehavior.add_unhover(this ._unhoverHandler);
}
HoverBehavior的hover事件会在鼠标移到某个元素上被触发,我们可以使用它在UI上显示一个良好的鼠标移进/移出效果。首先我们会建立两个用于触发事件的handler:_hoverHandler和_unhoverHandler。然后使用$create方法创建一个HoverBehavior并且与当前Behavior的元素绑定起来。$create方法会使ASP.NET AJAX跟踪这个对象,例如可以在合适的时候销毁它。在$create的第二个参数中会指定一些属性,unhoverDelay表示鼠标移开在多少毫秒后会触发unhover事件,hoverElement指定了另一个对象,以避免在移动到这个元素后unhover事件被触发。最后一个参数就是Behavior需要绑定的元素,通过get_element方法可以获得当前的元素,比如在使用WatermarkExtender时就会得到那个文本框,这里就是我们希望改变字体大小的Panel。最后将handler注册给hover和unhover事件。
接着我们使用PopupBehavior,代码如下:
设置PopupBehavior // setup the popup behavior
//
if (this ._sizePanelElement) {
this ._popupBehavior = $create( AjaxControlToolkit.PopupBehavior,
{id: this .get_id() + "_Popup ", // make the ID derive from our ID for uniqueness
positioningMode: AjaxControlToolkit.PositioningMode.TopRight,
parentElement: this .get_element()},
null , null , this ._sizePanelElement);
}
PopupBehavior的作用是帮助我们显示和隐藏某个UI。这里的代码和上面的比较相似,我们通过当前的Panel的客户端ID来获得PopupBehavior的ID,这样保证了这个ID的唯一性。positioningMode指定了元素在弹出时相对于parentElement的位置。
然后我们增加一些我们需要的Handler。代码如下:
Hover & Unhover Handler _onTargetHover : function (eventArgs) {
var e = this .get_element();
Sys.UI.DomElement.addCssClass(e, this ._hoverCssClass);
if (this ._popupBehavior && !this ._popupVisible) {
this ._popupVisible = true ;
// call show to make the the popup visible
//
this ._popupBehavior.show();
}
},
_onTargetUnhover : function (eventArgs) {
var e = this .get_element();
Sys.UI.DomElement.removeCssClass(e, this ._hoverCssClass);
if (this ._popupBehavior && this ._popupVisible) {
this ._popupVisible = false ;
this ._popupBehavior.hide();
}
},
在Hover事件被触发时,也就是用户将鼠标移动到了Panel上,我们会调用PopupBehavior的show方法,用以显示它的UI。在Unhover事件触发时,则调用hide方法使UI隐藏。
我们现在编译代码,然后刷新页面。哎,出错了。如图:
这里出错的原因是因为HoverBehavior没有定义。因此我会回到Extender的代码,添加RequiredScriptAttribute:
添加RequiredScriptAttribute [Designer(typeof (FontSizeDesigner))]
[ClientScriptResource("FontSize.FontSizeBehavior ", "FontSize.FontSizeBehavior.js ")]
[TargetControlType(typeof (Control))]
[RequiredScript(typeof (PopupExtender))]
[RequiredScript(typeof (HoverExtender))]
public class FontSizeExtender : ExtenderControlBase
{
...
}
在这里我们传入PopupExtender和HoverExtender的类型,这使Toolkit在使用当前Behavior时保证了PopupBehavior和HoverBehavior的脚本已经被加载了。我们重新编译代码,打开页面。没有发现异常。
当我们将鼠标移动至上方的Panel时,可以发现含有按钮的Panel显示在了右上角。如图:
可以发现,当我们把鼠标移动到右上角的按钮时,它们也不会消失,这个就是HoverBehavior的hoverElement属性的作用。当鼠标移到其它地方时,右上角的按钮就消失了。而PopupBehavior的作用就是显示和隐藏制定的元素。
现在我们需要来处理按钮的功能了。我们回到Behavior的代码,首先在initialize方法内添加一些简单的代码。如下:
添加按钮的客户端事件 initialize : function () {
FontSize.FontSizeBehavior.callBaseMethod(this , 'initialize');
...
// set up our handlers for the +/- behavior
//
if (this ._incrementElement) {
this ._incrementHandler = Function .createDelegate(this , this ._onIncrementFontSize);
$addHandler(this ._incrementElement, "click ", this ._incrementHandler);
}
if (this ._decrementElement) {
this ._decrementHandler = Function .createDelegate(this , this ._onDecrementFontSize);
$addHandler(this ._decrementElement, "click ", this ._decrementHandler);
}
},
就像我们之前所提到的那样,ASP.NET AJAX为了跨浏览器,提供了一些方法让我们进行统一的操作。在这里,我们使用了$addHandler方法,它为任意浏览器中都提供了相同的注册客户端事件功能。比如我们使用了$addHandler方法为增大字体的按钮注册了click事件。如果您写JavaScript代码的话,使用的是“onclick”,请注意这里只是“click”,因为ASP.NET AJAX需要在多个浏览器中工作。$addHandler方法的第一个参数则是需要为它添加事件的HTML元素,在这里就是“增大”按钮。最后一个则是我们需要注册给click事件的方法。
接着我们需要添加一些方法。首先我们在这里要添加的是一个辅助方法_setFontSize,如下:
辅助方法_setFontSize _setFontSize : function (size) {
// helper function for setting the font size
//
this ._fontSize = size;
// append the units.
//
this .get_element().style.fontSize = size + this ._fontUnit;
},
由于用户可能以不同的单位来设置字体,例如px或者pt,因此我们提供了_fontUnit。在这里get_element()方法获得了当前Behavior所在的元素,也就是需要改变字体大小的Panel。然后就以最普通不过的方法设置其字体。
然后我们再定义增大和减小字体的方法,如下:
增大/减小字体的方法 _onDecrementFontSize : function (eventArgs) {
var fontSize = this ._getCurrentFontSize();
if (fontSize > this .get_MinFontSize()) {
fontSize -= this ._fontSizeIncrement;
}
this ._setFontSize(fontSize);
eventArgs.preventDefault();
},
_onIncrementFontSize : function (eventArgs) {
var fontSize = this ._getCurrentFontSize();
if (fontSize < this .get_MaxFontSize()) {
fontSize += this ._fontSizeIncrement;
}
this ._setFontSize(fontSize);
eventArgs.preventDefault();
},
这是两个非常简单的Handler,在点击两个按钮时会调用它们。在这两个方法里,首先使用_getCurrentFontSize方法得到当前的字体大小,然后改变目标元素的字体大小。需要注意的是,我们这里还调用了eventArgs的preventDefault方法。这里又带来了一个跨浏览器的问题。如何在跨浏览器的情况下组织一个事件被继续传播(propagation)?例如我们需要在相应一个Label的click事件后阻止默认的响应方式。在IE您会使用“return false”这样的作法,而在FireFox里您则会使用“e.returnValue = false”,而使用ASP.NET AJAX话您只需要使用同一种方法。
下面则是_getCurrentFontSize方法。这是一个巧妙的方法,可以用来“测量”出某个元素当前字体大小,在这里就是当前Panel的文字大小。如下(代码较长,就不完整贴出了,朋友们可以看一下附件里的代码):
_getCurrentFontSize方法 _getCurrentFontSize : function () {
// this function figures out the current font size of the element.
//
...
return this ._fontSize;
},
然后就需要为页面上其它的Panel添加相应的UI和Extender了。如下:
页面中其余的Extender和UI的定义 < fontSize :FontSizeExtender ID ="FontSizeExtender1" runat ="server"
SizePanelControlID ="extenderUI1" TargetControlID ="pnlLeftColumn"
DecreaseSizeControlID ="btnDec1" IncreaseSizeControlID ="btnInc1"
PanelHoverCssClass ="panelHover" />
< asp :Panel ID ="extenderUI1" runat ="server" CssClass ="popup" >
< asp :Button ID ="btnDec1" runat ="server" Text ="-" />
< asp :Button ID ="btnInc1" runat ="server" Text ="+" />
</ asp :Panel >
< fontSize :FontSizeExtender ID ="FontSizeExtender2" runat ="server"
SizePanelControlID ="extenderUI2" TargetControlID ="pnlMiddleColumn"
DecreaseSizeControlID ="btnDec2" IncreaseSizeControlID ="btnInc2"
PanelHoverCssClass ="panelHover" />
< asp :Panel ID ="extenderUI2" runat ="server" CssClass ="popup" >
< asp :Button ID ="btnDec2" runat ="server" Text ="-" />
< asp :Button ID ="btnInc2" runat ="server" Text ="+" />
</ asp :Panel >
< fontSize :FontSizeExtender ID ="FontSizeExtender3" runat ="server"
SizePanelControlID ="extenderUI3" TargetControlID ="pnlRightColumn"
DecreaseSizeControlID ="btnDec3" IncreaseSizeControlID ="btnInc3"
PanelHoverCssClass ="panelHover" />
< asp :Panel ID ="extenderUI3" runat ="server" CssClass ="popup" >
< asp :Button ID ="btnDec3" runat ="server" Text ="-" />
< asp :Button ID ="btnInc3" runat ="server" Text ="+" />
</ asp :Panel >
编译,重新打开页面。可以发现,现在点击增大/减小按钮字体已经能够变化了(大家可以打开Demo_Step_3的页面察看一下效果)。可能大家会想一个问题,能不能在Panel之间共享一组控制按钮?在这里是不行的,因为一组按钮会被绑定多个Behavior,在click被触发时也会调用多个事件,这样您会发现所有的Panel字体一起变大或者变小了。所以我们在页面中使用了重复的按钮。
Step 3:Saving Client State / Adding Animation Support
可能大家已经发现了,在页面右下方有一个按钮,当我们点击这个按钮时,页面进行了一个PostBack。这里有个问题,页面上字体大小的改变在PostBack之后失效了,我们需要解决这个问题。
ASP.NET AJAX Control Toolkit提供了一系列的高级特性,其中一点就是提供了Client State机制可以在服务器端和客户端保存和传递状态。另外就像我之前提到的,Toolkit中提供了一套非常灵活的动画效果。我们来看一下应该如何使用它们。
如果要使用Client State机制,在服务器端我们要为Extender添加一个构造函数,将EnableClientState属性设为true。如下:
构造函数 public FontSizeExtender() {
EnableClientState = true ;
}
然后需要添加客户端的脚本,您可以直接使用辅助方法,在客户端_setFontSize方法里添加如下代码:
使用客户端set_ClientState方法保存ClientState值 _setFontSize : function(size) {
// helper function for setting the font size
...
// set the clientState
//
this .set_ClientState(size + ": " + this ._fontUnit);
},
您可以使用set_ClientState方法设置一个字符串,在页面PostBack之后在服务器端就可以得到这个值。更加重要的是,在PostBack之后,服务器段也会重新设置ClientState的值。在这里我们设定ClientState的值为当前的字体大小+冒号+当前使用的单位。
当然,我们还必须保证在组件被构造时我们能够重新取回这个值。我们在客户端initialize方法内添加如下代码:
使用客户端get_ClientState方法重新获得ClientState值 initialize : function () {
FontSize.FontSizeBehavior.callBaseMethod(this , 'initialize');
...
// check client state
//
var clientState = this .get_ClientState();
if (clientState) {
var stateItems = clientState.split(": ");
if (stateItems.length ) {
this ._fontUnit = stateItems[1];
this ._setFontSize(parseInt (stateItems[0]));
}
}
},
通过另外一个辅助方法get_ClientState我们可以得到ClientState的值。由于initialize方法会在组件被初始化时被调用,因此组件会在一开始重新获得字体大小和单位等状态。这就是我们在PostBack之间保存字体状态的方法。
编译,重新打开页面,点击按钮改变某个Panel的字体大小。再点击下方按钮,可以发现在PostBack之后字体大小被正确恢复了。
最后我们来为UI的显示和隐藏添加动画效果,首先修改_onTargetHover方法。如下:
添加了动画效果的_onTargetHover方法 _onTargetHover : function (eventArgs) {
var e = this .get_element();
Sys.UI.DomElement.addCssClass(e, this ._hoverCssClass);
if (this ._popupBehavior && !this ._popupVisible) {
this ._popupVisible = true ;
this ._stopAnimations();
// call show to make the the popup visible
//
this ._popupBehavior.show();
// now animate it's opacity
//
var anim = $create(
AjaxControlToolkit.Animation.FadeInAnimation,
{target: this ._sizePanelElement,
duration: .25, minimumOpacity: 0, maximumOpacity:.75});
// we cache the running animation so we can cancel it if we need to hide.
//
this ._sizePanelElement._runningAnimation = anim;
var handler = Function .createDelegate(this ,
function () {
this ._sizePanelElement._runningAnimation = null ;
});
anim.add_ended(handler);
anim.play();
}
},
动画效果也是组件,也能够通过$create方法创建。同样我们还需要修改_onTargetUnhover方法,如下:
添加了动画效果的_onTargetUnhover方法 _onTargetUnhover : function (eventArgs) {
var e = this .get_element();
Sys.UI.DomElement.removeCssClass(e, this ._hoverCssClass);
if (this ._popupBehavior && this ._popupVisible) {
this ._popupVisible = false ;
// make sure another animation isn't already running.
//
this ._stopAnimations();
var anim = $create(
AjaxControlToolkit.Animation.FadeOutAnimation,
{target: this ._sizePanelElement, duration: .15,
minimumOpacity: 0, maximumOpacity:.75});
this ._sizePanelElement._runningAnimation = anim;
var handler = Function .createDelegate(this ,
function () {
// clear out our state.
//
this ._sizePanelElement._runningAnimation = null ;
this ._popupVisible = false ;
this ._popupBehavior.hide();
});
anim.add_ended(handler);
anim.play();
}
},
我们还需要添加一个_stopAnimations方法。如下:
_stopAnimations方法 _stopAnimations : function() {
// stop any running animation
//
if (this ._sizePanelElement && this ._sizePanelElement._runningAnimation) {
this ._sizePanelElement._runningAnimation.stop();
this ._sizePanelElement._runningAnimation = null ;
}
},
自然也别忘了给服务器端的Extender添加所需的CustomAttribute:
添加RequiredScriptAttribute以引入动画脚本 [RequiredScript(typeof (AnimationScripts))]
编译,重新打开页面。现在可以看到,页面上的按钮的显示和隐藏就会产生美妙的淡入淡出效果了(大家可以打开页面观察一下效果)。
我们现在还在招募志愿的开发人员。我们计划最终会在ASP.NET AJAX Control Tookit中有超过50个组件。最终我们会把这个项目完全交付给社区,这是我们的长期计划。而且随着ASP.NET AJAX的改进,Toolkit也会相应的进步。
本文出自 “赵劼 ” 博客,转载请与作者联系!
本文出自 51CTO.COM技术博客