我谈到了使用AjaxControlTookit中控件的关键是客户端的各Behavior组件。微软官方推出的示例都太过于重视演示效果,而忽略了实际使用中的问题——市场需要吧,要让技术看上去吸引人,这么做无可厚非。但是作为一个实际开发产品的程序员,如果要灵活地使用AjaxControlTookit中的组件,则必须了解该如何在客户端操作各Behvaior对象的各个成员(属性、方法和事件等)。
在《分》文中,我举了一个操作ModalPopupBehavir对象的例子,这样显示和隐藏模态窗口的功能就局限于在服务器端ModalPopupExtender中指定的控件了。但是在这篇文章中,我想着重讲述一下在客户端操作那些组件事件的一些方法和技巧。因为,服务器端那些Extender对于客户端Behavior组件的事件支持并没有我们期望得那么良好。

Extender组件对于Behavior属性的支持

为什么AjaxControlToolkit中的控件使用起来那么方便?因为我们有了那么多的Extender。有了丰富的Extender控件,开发人员就可以在服务器端进行编程——例如设置各种属性,最终Extender会在页面中写入一些脚本用于创建客户端的Behavior对象。例如如果我们要使用ModalPopupExtender,最简单的情况下我们只需要在Extender中指定TargetControlID和PopupControlID就可以了,如下:
<asp:Button ID="Button1" runat="server" Text="Button" />
<asp:Panel ID="Panel1" runat="server" Height="50px" Width="125px">Hello World!
</asp:Panel>
<ajaxToolkit:ModalPopupExtender runat="server" TargetControlID="Button1" PopupControlID="Panel1" />

一行多余的代码页不要写,Extender会自动在页面上引入所需的脚本文件,并生成如下的客户端代码(格式有所改变):
Sys.Application.add_init(function()
{$create(AjaxControlToolkit.ModalPopupBehavior,{"PopupControlID":"Panel1","dynamicServicePath" : "/Default.aspx","id":"ctl02"},null,null,$get("Button1"));
});

于是,在客户端的init阶段就会创建客户端的对象(init阶段在window.onload事件执行之后才会开始,因此如果页面上的资源需要大量时间才能完成加载的话,用户会发现Behavior的效果会很晚才出现——不过我们先不关心这个)。一般来说,页面上有多少个Extender,客户端就会出现多少个类似的语句。在$create方法的第二个参数是一个在页面上用JSON表示的对象,这是需要设置的Behavior对象的属性集合。例如在上面的例子中,ModalPopupBehavior对象的PopupControlID属性会被设置为"Panel"。
有过AjaxControlToolkit中控件开发经验的朋友一定知道,会被输出到客户端作为Behavior属性的那些Extender的属性都被标记了ExtenderControlPropertyAttribute,例如在ModalPopupExtender中就有如下的成员定义:
[ExtenderControlProperty]
public string PopupControlID
{get { ... }set { ... }
}

这样,AjaxControlToolkit中定义的基础类库就会把这个属性的值作为客户端Behavior的值,并将其输出至客户端——控件开发人员和控件使用者双方皆大欢喜。

有人说:我要响应事件

客户端的Behavior除了属性之外还有各种事件——例如CalendarBehavior的shown事件,它会在日历被打开之后触发。利用事件我们就可以在合适的时候执行特定的代码,获得丰富的功能。在服务器端标记属性然后输出至客户端早已众人皆知,那么我们可以在服务器端指定客户端的事件吗?
以前不行,不过现在已经可以了。在上一个版本AjaxControlToolkit中新增了和属性差不多的支持功能。现在AjaxControlToolkit中包含了一个ExtenderControlEventAttribute类,它的作用就是在服务器端标记某个属性,使它与某个客户端的事件对应。这样我们就可以在服务器端设置客户端某个事件的响应方法,我们只需要保证那个方法存在并且逻辑正确即可。
例如,我们这次使用CaledarExtender作为示例:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<ajaxToolkit:CalendarExtender runat="server" TargetControlID="TextBox1"OnClientShown="onShownHandler"/>
<textarea id="TraceConsole" cols="50" rows="10"></textarea>
<script language="javascript" type="text/javascript">function onShownHandler(sender, args){Sys.Debug.trace("Calendar shown.");}
</script>

目前我在服务器端的CalendarExtender中指定了OnClientShown属性,于是在页面上就出现了如下的JavaScript代码:
Sys.Application.add_init(function()
{$create(AjaxControlToolkit.CalendarBehavior,{"id":"ctl02"}, {"shown":onShownHandler}, null, $get("TextBox1"));
});

$create方法的第三个参数是响应事件方法的集合,其中onShownHandler被作为shown事件的响应方法。因此,在每次Calendar被展开之后onShownHandler方法都会被执行,于是在页面上的TextArea中就会显示出“Calendar shown.”的字样。
服务器端OnClientShown属性和客户端shown事件的映射关系是由CalendarExtender中以下代码决定的:
[DefaultValue("")]
[ExtenderControlEvent]
[ClientPropertyName("shown")]
public virtual string OnClientShown
{get { ... }set { ... }
}

ExtenderControlEventAttribute标记表明该属性将会映射到客户端Behavior的某个事件,而ClientPropertyNameAttribute标记则会接受一个参数,表明具体是哪个事件(如果不标记的话,则使用和属性同名的事件)。
一切就是这么简单……

……只可惜并非每个Extender都那么友好

例如,HoverMenuExtender。话说HoverMenuBehavior也有事件,例如showing和shown。可惜的是,由于ExtenderControlEventAttribute类和它的功能出现的比较晚,早期版本中出现的控件都没有这个功能。可能在新发布的AjaxControlToolkit中会慢慢地为它们补充这些功能,不过现在还是要响应Behavior的事件啊,是不?
在《分》文中,我演示了如何在客户端操作单个ModalPopupBehavior,虽然没有使用事件,但是我们既然可以操作那个Behavior对象了,为它添加一个Event Handler难道还不简单吗?不过似乎有朋友结合基于模板反复的控件(例如Repeater)与AjaxControlTookit中控件的时遇到了阻碍——这下子出现了多个Behavior对象,又该如何设置每一个的BehaviorID,又该如何操作每一个Behavior对象呢?要知道,Repeater在页面上最终会反复多少次可能也无法轻易得知(或使用)啊。
例如,我们结合Repeater控件和HoverMenuExtender来使用:
<asp:Repeater ID="Repeater1" runat="server"><ItemTemplate><a id="link" runat="server">Hover on me!</a><asp:Panel runat="server" ID="tooltip" style="display:none;border:solid 1px black; padding:5px; background-color:Yellow; width:100px">Loading...</asp:Panel><ajaxToolkit:HoverMenuExtender runat="server" ID="hoverMenu"TargetControlID="link" PopupControlID="tooltip" PopupPosition="Right" /></ItemTemplate><SeparatorTemplate><hr /></SeparatorTemplate>
</asp:Repeater>

根据上面的代码,当鼠标指向每个<a />元素时,将会出现一个浮动的标签,其中显示Loading字样。 但是如果我们要在每个区域显示出来之后访问一个WebService方法,并将得到的结果显示在浮动的区域内,又该怎么做呢?很显然,我们必须响应每个HoverMenuBehavior对象的shown事件。于是我们可以这样做:
<script language="javascript" type="text/javascript">function onShownHandler(sender, args){HoverMenuService.GetContent(onSuccess, null, sender.get_popupElement());}function onSuccess(result, tooltip){tooltip.innerHTML = result;}
</script>
<asp:Repeater ID="Repeater1" runat="server"><ItemTemplate><a id="link" runat="server">Hover on me!</a><asp:Panel runat="server" ID="tooltip"style="display:none; border:solid 1px black; padding:5px; background-color:Yellow; width:100px;">Loading...</asp:Panel><ajaxToolkit:HoverMenuExtender runat="server" ID="hoverMenu"TargetControlID="link" PopupControlID="tooltip" PopupPosition="Right" /><script language="javascript" type="text/javascript">Sys.Application.add_load(function(sender, args){if (args.get_isPartialLoad()) return;var behaviorId = '<%# (Container.FindControl("hoverMenu") as HoverMenuExtender).BehaviorID %>';$find(behaviorId).add_shown(onShownHandler);})</script></ItemTemplate><SeparatorTemplate><hr /></SeparatorTemplate>
</asp:Repeater>

我这里使用的是相对最容易的做法。我们既然可以在页面上反复DOM元素,自然也可以反复脚本代码。在ItemTemplate中的脚本代码很容易,其关键在于通过每一项的Container来找到其中的HoverMenuExtender,获得其BehaviorID,接着通过BehaviorID找到HoverMenuBehavior对象,并为它们响应shown事件(值得注意的是,添加事件响应方法是在load阶段中进行,因为那些Behavior对象是在init阶段中创建的)。每当鼠标滑过某个<a />元素时,其对应的HoverMenuBehavior对象的shown事件将被触发,onShownHandler方法会被执行,而其第一个参数sender即为当前触发事件的HoverMenuBehavior对象。于是我们调用HoverMenuService的GetContent方法用于获得填充内容。
还是看看最后在页面上生成的代码。有时候对于开发人员来说,直接阅读代码会更容易理解一些东西:
<a id="Repeater1_ctl00_link">Hover on me!</a>
<div id="Repeater1_ctl00_tooltip" style="...">Loading...
</div>
<script language="javascript" type="text/javascript">Sys.Application.add_load(function(sender, args){if (args.get_isPartialLoad()) return;var behaviorId = 'Repeater1_ctl00_hoverMenu';$find(behaviorId).add_shown(onShownHandler);})
</script>
<hr />
<a id="Repeater1_ctl02_link">Hover on me!</a>
<div id="Repeater1_ctl02_tooltip" style="...">Loading...
</div>
<script language="javascript" type="text/javascript">Sys.Application.add_load(function(sender, args){if (args.get_isPartialLoad()) return;var behaviorId = 'Repeater1_ctl02_hoverMenu';$find(behaviorId).add_shown(onShownHandler);})
</script>
...

不知是从AjaxControlToolkit的哪个版本开始,如果不设置Extender的BehaviorID,就会把Extender控件的ClientID作为BehaviorID了。根据Microsoft AJAX Library创建Component的规则,如果Component在创建时不提供ID,那么就不会在Sys.Application的某个集合中保持那个Component对象的引用,也就是说我们无法通过$find方法来获得它了。如果我们不需要在客户端操作那个Component对象,这并没有任何影响。而如果真的需要操作时,也只需在Extender中将BehaviorID设为特定的值即可。目前自动分配BehaviorID的做法,我估计就是为了解决类似Repeater这样的控件中使用Extender的情况,因为在这种情况下,我们不能在模板中指定Extender的BehaviorID,因为这样就会在客户端创建相同ID的Component——这是不允许做法。当然,我们还是有解决方法的,例如我们可以响应Repeater的ItemDataBound事件,然后在事件每次被触发时为当前Extender的BehaviorID设置唯一的值即可。

结论

AjaxControlTookit中控件的关键在于客户端的Behavior对象,Behavior对象大都有丰富的属性,方法和事件。在目前的版本中,为某些Behavior添加Event Handler并非一件很容易的事情。相信今后的AjaxControlToolkit版本中将会为一些较早的Extender补充对于客户端事件的支持,让我们拭目以待吧,只是眼下我们还需要使用一些技巧来实现功能了。不过,我们完全可以自己修改AjaxControlToolkit的代码,又何必等到官方的发布呢?
本文转自 jeffz 51CTO博客,原文链接:http://blog.51cto.com/jeffz/59876,如需转载请自行联系原作者

Tip:在使用AjaxControlTookit的控件时响应事件相关推荐

  1. MFC vc++ 中CTreeContrl如何自定义实现鼠标单击或双击响应事件 ,即重写类似于控件的响应事件或消息

    代码部分摘录自"天上的猩猩的专栏":https://blog.csdn.net/qq_23992597/article/details/51006920#commentsedit ...

  2. MFC动态创建控件并响应事件代码实现过程

    MFC动态创建控件以及响应动态创建的控件的事件的实现方法如下. 1.创建对象     用new进行动态创建一个对象.然后调用Create函数创建窗口,在函数的父窗口参数中传入this或者用AfxGet ...

  3. LibUIDK 学习------CSkinListCtrl控件消息响应事件

    2021年修改了控件继承自CUIWnd,因此对于CSkinListCtrl控件的消息响应如下: ON_NOTIFY(NM_CLICK, IDC_LIST1, OnClickList1)     ON_ ...

  4. 使用控件时提示“未声明标识符“的解决方法

    使用控件时提示"未声明标识符"的解决方法 参考文章: (1)使用控件时提示"未声明标识符"的解决方法 (2)https://www.cnblogs.com/Ro ...

  5. 用了GradientDrawable后,当点击控件时,控件大小发生变化

    android新手:发现一个很奇怪的问题,用了GradientDrawable后,当点击控件时,程序自动使我的一些控件大小保持一致,为什么呢,我就是不想它们保持一致啊 改了好久好久:GradientD ...

  6. 给echarts添加筛选时间控件时,控件不显示,并设置数据库无数据时echarts模块显示暂无数据

    给echarts模块添加时间控件时,控件无效,经检查发现,控件被画布遮盖了,需要用一个form标签包括时间控件,否则时间控件不生效.另外,如果echarts从后台获取到的数据为空,需要提示暂无信息.这 ...

  7. C# 多线程修改控件时遇到:创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke

    一般在多线程调用UI控件时,涉及到跨线程修改UI,需要使用委托,比如如下: this.Invoke((MethodInvoker)delegate{btnRefresh.Enabled = true; ...

  8. 使用WebBrowser控件时在网页元素上绘制文本或其他自定义内容

    第一次在CNBlogs上发Post是提出一个有关使用WebBrowser控件时对SELECT网页元素操作的疑惑,这个问题至今也没有解决,后来有朋友在该Post的评论里询问WebBrowser控件如何在 ...

  9. 如何解决“呈现控件时出错”的问题(转)

    webpart部署后在SPD中打开显示"呈现控件时出错",后查到这篇文章,主要问题是在CreateChildControls中调用了this.page.Header,在设计视图时, ...

最新文章

  1. 最短路合集(Dijkstra、SPFA、Floyd以及路径还原模板)
  2. php nginx日志分析,如何通过NGINX的log日志来分析网站的访问情况,试试这些命令...
  3. emacs .emacs_使用Emacs进行社交并跟踪您的待办事项列表
  4. 月租最便宜的手机卡_有什么价格便宜,月租少,流量多,并且可以从旧套餐转入的手机卡推荐?...
  5. 使用selenium爬取搜狗微信文章
  6. 【C#】C#实现端口扫描器
  7. 实现搜索框(含历史搜索记录)
  8. My_blog个人博客系统
  9. 读季琦《创始人·手记》
  10. 良好的编程习惯有哪些?
  11. html邮件在线制作模板,最全EDM模板美化教程,在电子邮件中使用CSS和HTML
  12. JAVASE面向对象基础
  13. 如何做好项目管理任务分配
  14. bit,B,KB,MB,GB,TB,PB分别是什么单位,换算是什么
  15. 详细的SQL注入相关的命令
  16. 目标检测 YOLOv5 - 如何提高模型的指标,提高精确率,召回率,mAP等
  17. 基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理
  18. Hexo博客搭建之Next主题添加搜索服务
  19. 基于electron的音视频播放器
  20. 海滩上有一堆桃子,五只猴子来分(C语言)

热门文章

  1. JAVA的类名.this
  2. 通俗易懂的rpc原理
  3. 亚信基于AWS构建世界级企业互联网平台
  4. ASP.NET MVC Model绑定(五)
  5. 100%防御ARP***
  6. 荷花用计算机怎么画,怎样画荷花荷叶简笔画
  7. android基础入门布局讲解(2)
  8. 在 .NET Framework Data Provider for Microsoft SQL Server Compact 3.5 中发生错误
  9. introduction of servlet filter
  10. 关于移动安全的一点总结