在这三个下载中,Futures 版本在开发人员社区最不受关注。这真让人感到遗憾,因为 Futures 社区技术预览 (CTP) 不仅提供了 ASP.NET AJAX 未来版本可能的样子,同时还包含所有可用于构建当今最前沿的 Web 应用程序的功能。拖放就是一个相关例子。
Futures PreviewDragDrop.js 文件隐含了对基于浏览器的拖放型用户界面提供丰富的支持。它所使用的模型是根据原先的 OLE 拖放模型设计的,其中由拖动源来实现 IDragSource 接口,由放置目标来实现 IDropTarget 接口,同时系统还提供了一个拖放管理器,将拖动源和放置目标连接起来。Futures 拖放管理器是一个 JavaScript 类的实例,名为 Sys.Preview.UI._DragDropManager,它会自动实例化并通过一个名为 Sys.Preview.UI.DragDropManager 的全局变量供使用。
近几个月来,我一直打算编写一个示例,说明如何使用 PreviewDragDrop.js 来实现真正的拖放,其特点是自定义拖动源和自定义放置目标。最后我终于完成了这项工作,结果也非常棒。在这个过程中我学到了很多关于 DragDropManager 的知识,包括如何通过添加对自定义拖动视觉效果的支持来增强它的功能。一旦您熟悉了该模型(并且了解了在 JavaScript 中派生类和实现接口的概念),DragDropManager 便为 Web 用户界面的使用打开一个全新的世界。
运行中的拖放
在深入探讨代码之前,请花一点时间下载本文附带的示例项目,然后快速运行一遍。请用 Visual Studio 的“打开网站”命令打开该示例,然后在浏览器中查看主页 DragDropDemo.aspx。要运行这个示例,必须先安装 ASP.NET AJAX Extensions。您无需下载和安装 Futures CTP,因为它已经在站点的 Bin 文件夹中了。
您将在页面顶部看到五个颜色样本,在页面底部看到一个标记为“Drop It Here(放置于此处)”的空白框(请参见图 1)。颜色样本是拖动源,空白框是放置目标。用鼠标获取其中一个颜色样本,然后将其拖放到下面的框中。当光标进入框中时,请观察框的颜色是如何从白色转变为浅灰色的,这就是所谓的放置目标突出显示功能。现在,将颜色样本放入框中。框会变为与样本相同的颜色。
获取另一个颜色样本,然后在屏幕上四处移动。注意,样本只能被放置在放置目标上,并且光标会指明此时是否可以放置样本。同时还请注意,当拖动一个颜色样本时,有一个样本的半透明再现会随着光标移动,即“拖动视觉效果”,这样您便知道自己拖动的是什么。稍后您将看到,我们编写代码来创建拖动视觉效果,而 ASP.NET AJAX 则完成其余的所有工作,包括让拖动视觉效果与光标一起移动,以及随着每一次鼠标移动更新光标。
驱动本页面的自定义逻辑在一个名为 ColorDragDrop.js 的脚本文件中,该文件位于网站的 Scripts 文件夹中。ColorDragDrop.js 是由 ScriptManager 加载的自定义脚本文件。事实上,在源代码视图中打开 DragDropDemo.aspx 后即可看到 ScriptManager 加载了三个脚本文件:PreviewScript.js,包含本演示使用的一个关键 ASP.NET AJAX 基类;PreviewDragDrop.js,包含 ColorDragDrop.js 使用的拖放支持;以及 ColorDragDrop.js 本身。
DragDropManager
要用 ASP.NET AJAX 构建丰富的拖放型用户界面,第一步是了解 Sys.Preview.UI._DragDropManager 类(以下简称 DragDropManager)。它实际上是根据主机浏览器的类型,将大多数操作委托给 Sys.Preview.UI.IEDragDropManager 或 Sys.Preview.UI.GenericDragDropManager 的一个实例。它的公共接口由十几个方法组成,其中三个在拖放实现中担当了至关重要的角色:
- startDragDrop——启动拖放操作
- registerDropTarget——将对象注册为放置目标
- unregisterDropTarget——注销放置目标
一旦 startDragDrop 方法被调用,DragDropManager 的主要工作就是监视当前的鼠标事件、向光标下的拖动源和放置目标(如果有)发出相关通知,以及改变光标形状以提供可视化指示,表示在当前位置进行放置会发生什么结果。为此,它会针对关键事件(如 mousemove 和 mouseup)注册自己的处理程序。在这些事件处理程序中,它会在被拖动的源上调用 IDragSource 方法,并在下面任一个放置目标上调用 IDropTarget 方法。
例如,当携带负载的光标在放置目标边界内移动时,DragDropManager 会调用拖动源的 get_dragDataType、get_dragMode 以及 getDragData 方法来获取有关负载的信息。然后它会将结果传递给放置目标的 canDrop 方法,以确定拖动源和放置目标是否兼容。一旦发生放置,DragDropManager 会通过调用其 drop 方法来通知放置目标,并再次传入从拖动源获得的数据。
DragDropManager 可以确定光标是否位于放置目标上,因为它针对已注册为放置目标的对象保留了一个引用的内部数组。通过调用 DragDropManager 的 registerDropTarget 方法可以将对象添加到该数组,调用 unregisterDropTarget 可将其删除。注销您所注册的放置目标非常重要,这样 DragDropManager 才能注销它代表那些放置目标注册的相关事件处理程序。不这么做会导致内存泄漏。
DragDropManager 是连接拖动源和放置目标的粘合剂。没有它,您将需要编写大量代码才能使拖放型用户界面正常工作,当这些界面需要在各种浏览器中工作时尤为如此。
构建拖动源
在开始拖动之前必须先有要拖动的对象,这就是拖动源。对于 ASP.NET AJAX 来说,拖动源只是一个对象,它实现了在 PreviewDragDrop.js 中定义的 IDragSource 接口。图 2 列出了属于该接口的方法。
在 JavaScript 中实现接口可能会让人觉得奇怪,因为 JavaScript 并不显式支持接口。因此,它也不显式支持类、继承、命名空间以及面向对象编程 (OOP) 的其他原则。但是,ASP.NET AJAX 的客户端部分(即 Microsoft® AJAX Library)是一个完全以 JavaScript 实现的类库。它采用流行的 JavaScript 编程技术来虚设 OOP,您可以用其创建自己的类,甚至可以在这些类中实现接口并从一个类派生另一个类。
ColorDragDrop.js 包含一对 JavaScript 类,名为 ColorDragSourceBehavior 和 ColorDropTargetBehavior。前者实现 IDragSource,两者都属于名为 Custom.UI 的命名空间。
要创建 Microsoft AJAX Library 风格的 JavaScript 类,首先需要定义与该类同名的函数。此函数将作为类构造函数:
Custom.UI.ColorDragSourceBehavior = function(element, color) { Custom.UI.ColorDragSourceBehavior.initializeBase(this, [element]); this._mouseDownHandler = Function.createDelegate(this, this.mouseDownHandler); this._color = color; this._visual = null; }
然后,可以使用 JavaScript 原型属性来定义要包含在该类的每个实例中的方法:
Custom.UI.ColorDragSourceBehavior.prototype = { // Methods here }
JavaScript 不支持强类型化或类型反射,因此 Microsoft AJAX Library 帮您实现了这一点。Type 类的方法(在 MicrosoftAjax.js 中,是 ASP.NET AJAX 核心的一部分),如 registerClass 和 registerNamespace,允许您注册自己构建的类,并将它们分配给命名空间。之后,您可以使用诸如 getTypeName(由 Microsoft AJAX Library 添加到 JavaScript 的内置 Object 类型)等方法来反射您所注册的类型。
Type 类提供的另一个基本基础结构部分是从派生类的相应方法调用基类方法的机制。您可以从类构造函数调用 initializeBase 以调用基类的构造函数,以及从派生类中的方法覆盖调用 callBaseMethod 以调用基类中的等效方法。
图 3 中显示的 ColorDragSourceBehavior 类使用了所有这些工具,将一个普通的 DOM 元素转换为拖动源所需的逻辑封装到一个易于使用的类中。从对 registerClass 的调用可以看到,ColorDragSourceBehavior 派生自 IDragSource 接口和名为 Sys.UI.Behavior 的 ASP.NET AJAX 基类(在 MicrosoftAjax.js 中定义)。前者使 ColorDragSourceBehavior 可以作为拖动源使用;后者使 ColorDragSourceBehavior 的实例附加到 DOM 元素,以便修改其行为。
ColorDragSourceBehavior 实现的大多数方法(get_dragDataType、getDragData 等)是 IDragSource 接口的成员。但 ColorDragSourceBehavior 实现了并非来自 IDragSource 的一些方法。例如,Initialize 方法为 mousedown 事件注册处理程序,这样拖动源通过调用 DragDropManager.startDragDrop 开始拖放操作。dispose 方法可注销事件处理程序,以避免内存泄漏。
IDragSource 方法本身十分简单。get_dragDataType 会返回字符串“DragDropColor”以标识 ColorDragSourceBehavior 提供给放置目标的数据类型——在本例中为颜色数据。getDragData 会返回一个字符串,定义拖动源所代表的颜色。get_dragMode 会返回 Sys.Preview.UI.DragMode.Copy,指出如果此时进行放置,拖动源将是被复制而不是被移动。(另一个选择是 Sys.Preview.UI.DragMode.Move,它与 Sys.Preview.UI.DragMode.Copy 一起在 PreviewDragDrop.js 中定义。)
ColorDragSourceBehavior 中需要注意的另一点是如何处理拖动视觉效果。mousedown 事件处理程序会通过对 DragDropManager 调用 startDragDrop 来启动拖放操作。然而,在调用 startDragDrop 之前,该处理程序会调用 cloneNode 来克隆拖动源所代表的 DOM 元素。然后,它会将克隆节点的透明度设置为 40%,将节点插入浏览器 DOM,并将新节点的引用传递给 DragDropManager,作为 startDragDrop 的一个参数。接着由 DragDropManager 来负责拖动视觉效果,即动态显示它的移动以使其随光标移动。
构建放置目标
ColorDragDrop.js(包含在代码下载中)中的 ColorDropTargetBehavior 类封装了可将 DOM 元素转换为放置目标的逻辑。它派生自 Sys.UI.Behavior,因此可以附加到 DOM 元素,它同时实现了 IDropTarget 接口,因此可作为放置目标使用。图 4 列出了 IDropTarget 的成员。
ColorDropTargetBehavior 的 onDragEnterTarget 和 onDragLeaveTarget 方法可以实现先前演示的放置目标突出显示。当携带负载的光标进入放置目标后,DragDropManager 就会调用 onDragEnterTarget。如果负载属于正确的类型 (DragDropColor),onDragEnterTarget 就会将目标的当前背景色保存在一个字段中,并将该背景色变为浅灰色。如果光标离开放置目标并仍携带负载,则 DragDropManager 会调用放置目标的 onDragLeaveTarget 方法。ColorDropTargetBehavior 对该方法的实现会将放置目标还原为其最初的背景色。
每当携带负载的光标在放置目标上移动时,DragDropManager 就会调用放置目标的 canDrop 方法以确定目标是否可以接受放置操作。ColorDropTargetBehavior.canDrop 检查负载类型,如果它是 DragDropColor 则返回 true,否则返回 false。此过程使 DragDropManager 类可以通过光标向用户提供必要的可视化反馈。
发生放置操作时会调用放置目标的 drop 方法。ColorDropTargetBehavior.drop 方法从拖动源中提取颜色并相应地设置自己的背景色(实际上是作为放置目标的 DOM 元素的颜色)。
传递到 drop 方法的 dragMode 参数指定拖动模式,即移动或复制。最后将由放置目标根据拖动模式来处理浏览器的 DOM。DragDropDemo.aspx 仅支持复制模式;因此放置目标会通过将其自身的背景色设置为拖动源中包含的颜色以达到“复制”这个拖动源的目的。如果同时还支持移动模式,放置目标将需要在成功完成放置后检查拖动模式,如果 dragMode 已设置为 Sys.Preview.UI.DragMode.Move,则需要删除(或重定向)与该拖动源相关的 DOM 元素。
创建拖动源和放置目标
一旦实现了 ColorDragSourceBehavior 和 ColorDropTargetBehavior,构建包含拖动源和放置目标的页面就非常容易了。图 5 显示了 DragDropDemo.aspx 中的相关标记和代码。
作为拖动源的颜色样本无非是一些背景色设置为红色、黄色、绿色等的 DIV。JavaScript pageLoad 函数会实例化五个 ColorDragSourceBehavior 对象并将每个对象分配给一个 DIV,从而将 DIV 转换为拖动源。调用拖动源的初始化方法使每个拖动源都有机会注册自己的 mousedown 事件处理程序。
放置目标也是 DIV。一个语句会实例化一个 ColorDropTargetBehavior 对象,并将其与 DIV 关联,从而将 DIV 转换为放置目标。另一个语句会调用 ColorDropTargetBehavior 的 initialize 方法,这样它可以在 DragDropManager 中注册为放置目标。
ColorDragSourceBehavior 和 ColorDropTargetBehavior 专门用于启用要拖放的颜色。您可以构建类似它们的类来支持其他拖放情形。例如,如果您要构建一个照片共享站点,并希望允许用户通过在文件夹间拖动照片来对它们进行管理。您可以用 DIV 代表文件夹,并编写一个 PhotoDropTargetBehavior 类,将 DIV 转换为放置目标。您可以相应地创建一个 PhotoDragSourceBehavior 类,将 DOM 图像元素转为可拖动的照片。虽然您构建的这些类都非常特定于应用程序,但它们都遵循本专栏中展示的拖动源和放置目标类所例举的同一模式。
拖放以外的其他功能
ASP.NET AJAX 是一个非常棒的工具,可向 Web 页面添加 AJAX 的奇妙功能,而 ASP.NET AJAX Futures 版本则向核心平台添加了一些非常有用的增强功能。它对拖放型用户界面的支持只是其中一个例子;另外它还包含了动画、客户端数据绑定以及其他简便的 DOM 抽象等等。不论这些功能是否会在某一天使之成为核心,它们现在都可供您使用。请考虑在您的下一个 ASP.NET AJAX 应用程序中使用这些功能吧,它们会让您的应用程序变得更为出色。
如需其他有关如何使用 Futures PreviewGlitz.js 文件中的类向 Web 页面添加有趣动画的示例,请参阅我的博客文章“Creating Sophisticated Animations with the Microsoft AJAX Library”(利用 Microsoft AJAX Library 创建复杂的动画),网址为:wintellect.com/cs/blogs/jprosise/archive/2007/04/04/Creating-Sophisticated-Animations-with-the-Microsoft-AJAX-Library.aspx。