使用Asp.net2.0和Atlas实现拖放操作
【IT168技术文档】
介绍
本文的目的是帮助读者理解如何使用Microsoft的Atlas技术实现HTML对象的拖放操作。Atlas将使开发Ajax程序变得更简单。就象所有的技术一样,要想充分地发挥Atlas的作用,理解Atlas的底层原理是非常重要的。Atlas的卖点之一就是它提供了新的XML标记语法,通过这种新的XML标记,可以使Atlas更容易使用。有了XML标记,开发人员可以在声明中修改他们的代码。然而,有时开发人员需要使用编程的方式修改他们的代码,为了达到这一目的,开发人员就必须理解标记的底层结构,事实上,开发人员是在和JavaScript和一些Microsoft开发的JavaScript库打交道。为了更好地举例说明Atlas声明模型和编程模型的关系,我将举一些这方面的例子,在这些例子中将同时使用声明和编程的方式来完成同样的任务。我将举例说明如何使用AtlasUIDragDrop库文件实现基本的拖放操作,以及如何设置拖放区。
背景
当我写这篇文章时,Atlas仍然是测试版,并且还在继续变化。这些例子只是对四月份的Atlas的CTP版所设计的。更新的Atlas版本可能将影响到这些例子。我会尽量更新这些代码,以使其可以在新版的Atlas中良好地运行。Atlas只能运行在.NET 2.0中。
一、声明拖放
首先要做的是使用Atlas标记将拖放行为加入到div标签中。所谓拖放,就是拖动一个对象,然后将它放到任何你想放到的地方。本文后面的部分将讲解更复杂的拖放操作,如将一个对象拖到指定的目标,然后执行一些动作。要想使用Atlas来开发网页,必须从Microsoft网站下载Microsoft.Web.Atlas.dll文件到bin目录,然后按以下给出的代码设置你的web.config文件。
<system.web>
<pages>
<controls>
<add namespace="Microsoft.Web.UI"
assembly="Microsoft.Web.Atlas" tagPrefix="atlas"/>
<add namespace="Microsoft.Web.UI.Controls"
assembly="Microsoft.Web.Atlas" tagPrefix="atlas"/>
</controls>
</pages>
</system.web>
你需要在.aspx页中加入一个Atlas脚本管理控件,然后使用AtlasUIDrapDrop库文件设置它:
<atlas:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>...
<atlas:ScriptReference ScriptName="AtlasUIDragDrop" />
</Scripts>
</atlas:ScriptManager>
加入你想拖动的div对象,并保证div对象有一个拖动句柄。
<div style="background-color:Red;height:800px;width:600px;">
<div id="draggableDiv"
style="height:100px;width:100px;background-color:Blue;">
<div id="handleBar"
style="height:20px;width:auto;background-color:Green;">
</div>
</div>
</div>
最后,加入标记脚本,以使div可以被拖动:
<script type="text/xml-script">...
<page xmlns:script="http://schemas.microsoft.com/xml-script/2005">
<components>
<control id="draggableDiv">
<behaviors>
<floatingBehavior handle="handleBar"/>
</behaviors>
</control>
</components>
</page>
</script>
如果你照着上面的步骤做,那么你将有一个可以拖动的div标签。这个例子充分说明了使用Atlas的声明模型开发web程序是多么简单和容易。到现在为止,你已经使用了Atlas的声明标记为一个HTML元素添加了拖动的功能。
二、编程拖放
使用编程模型达到同样的效果只需要加一点代码。理解什么时候在自己的aspx页中加入Atlas脚本管理组件是非常重要的,在你将这个组件加入到aspx页上时,实际上就是让aspx页装载Atlas JavaScript库。Atlas库提供了扩展DOM的客户端类,并且提供了编写浏览器风格代码的工具(虽然目前仍存在一些兼容性问题)。这些客房端类还可以为HTML元素添加一些行为(如上面所提到的div标签)。
为了使用编程模型,你必须使用两个javascript函数替换XML标记。第一个函数是一段普通的JavaScript代码,它的作用是向一个HTML元素添加拖放功能。这个函数使用Atlas客户端类完成这些功能:
这个函数有两个参数:第一个参数是被拖动的HTML元素,第二个参数是这个HTML元素的拖动句柄。接下来,你将建立一个新的Atlas客户端行为对象。拖动行为有一个“handle”属性,通过这个属性,你可以将HTML元素的句柄传递进来。然后需要基于你想拖动的控件建立一个新的客户端控件对象。将div标签放进这个Atlas客户端控件,以便为div加入拖动功能。你可以使用“get_behaviors()”方法返回一个行为集合,并且向HTML对象加入可以增加新行为的方法。最后,调用行为对象的“initialize()”方法进行初始化。这个函数在本文中将会经常使用。<script type="text/javascript">
function addFloatingBehavior(ctrl, ctrlHandle){
//create new floating behavior object
var floatingBehavior = new Sys.UI.FloatingBehavior();
//floating behavior class has a Handle property
floatingBehavior.set_handle(ctrlHandle);
//convert object reference to Atlas client control
var dragItem = new Sys.UI.Control(ctrl);
//get behaviors collection from Atlas control
//and add our floating behavior
dragItem.get_behaviors().add(floatingBehavior);
//run the floating behavior's internal javascript
floatingBehavior.initialize();
}
</script>
现在你需要在aspx页装载时调用“addFloatingBehavior”函数。这段代码是这个例子中最核心的部分。事实上,脚本管理并没有简单的建立Atlas JavaScript库的引用,而是将库中的脚本装载到了DOM中。这就意味着这些库在每件事情都做完后,甚至在aspx页被装载后还能被装载。摆在我们面着的问题是,并没有标准的方法在JavaScript库被装载后加入拖动行为;如果我们在这些库被装载之前尝试运行它,将会产生一个没有找到所调用的方法的JavaScript错误。
实际上有很多方法调用“addFloatingBehavior”函数,但是最简单的方法就是使用一个定制的Atlas事件 — “pageLoad()”,这个事件在Atlas脚本库被装载后调用。为了给div标签加上拖动功能,在Atlas脚本被装载之后,必须写如下代码:
除了使用document.getElementById()外,还可以使用Atlas脚本速记符号“$()”来代替document.getElementById()。<script type="text/javascript">
function pageLoad(){
addFloatingBehavior(document.getElementById('draggableDiv'),
document.getElementById('handleBar'));
}
</script>
上面的代码同样实现了使div可拖动的功能。所产生的效果和使用声明模式完全一样。<script type="text/javascript">
function pageLoad(){
addFloatingBehavior($('draggableDiv'),$('handleBar'));
}
</script>
三、动态拖放
既然声明模型比编程模型更简洁,那么你为什么不自己写JavaScript代码来处理Atlas行为呢?如果想动态地加入行为,你可以加入自己的JavaScript代码。声明模型的一个限制是它只能影响在aspx初始化时加入的对象。如果你动态地为aspx页加入对象,那么使用声明模型将无法为这些新加入的对象添加拖动功能。而使用编程模型却可以做到。
为了实现这一功能,我们使用一个可以建立可拖动div对象的方法来替换前面例子中的“pageLoad()”函数。以下的JavaScript函数使用另一个内嵌的div标签建立一个div标签,然后将这个div标签插入到当前页中,最后向这个div标签加入拖动功能。
你只需要在aspx页上放一个按钮,然后在这个按钮中调用“createDraggableDiv()”函数,HTML代码如下:function createDraggableDiv() ...{
var panel= document.createElement("div");
panel.style.height="100px";
panel.style.width="100px";
panel.style.backgroundColor="Blue";
var panelHandle = document.createElement("div");
panelHandle.style.height="20px";
panelHandle.style.width="auto";
panelHandle.style.backgroundColor="Green";
panel.appendChild(panelHandle);
var target = $('containerDiv').appendChild(panel);
addFloatingBehavior(panel, panelHandle);
}
<input type="button" value="Add Floating Div" onclick="createDraggableDiv();" />
<div id="containerDiv" style="background-color:Purple;height:800px;width:600px;"/>
使用这种方法可以在aspx页中加入许多自己想加入的可拖动控件,因此,一旦你理解并灵活使用Atlas的这些模型,你就可以用事实来证明Atlas的强大和弹性。为了更清楚地说明问题,下面给出了动态拖动的完整代码:
<%...@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Imperative Drag and Drop II</title>
<script type="text/javascript">...
function createDraggableDiv() ...{
var panel= document.createElement("div");
panel.style.height="100px";
panel.style.width="100px";
panel.style.backgroundColor="Blue";
var panelHandle = document.createElement("div");
panelHandle.style.height="20px";
panelHandle.style.width="auto";
panelHandle.style.backgroundColor="Green";
panel.appendChild(panelHandle);
var target = $('containerDiv').appendChild(panel);
addFloatingBehavior(panel, panelHandle);
}
function addFloatingBehavior(ctrl, ctrlHandle)...{
var floatingBehavior = new Sys.UI.FloatingBehavior();
floatingBehavior.set_handle(ctrlHandle);
var dragItem = new Sys.UI.Control(ctrl);
dragItem.get_behaviors().add(floatingBehavior);
floatingBehavior.initialize();
}
</script>
</head>
<body>
<form id="form1" runat="server">
<atlas:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>...
<atlas:ScriptReference ScriptName="AtlasUIDragDrop" />
</Scripts>
</atlas:ScriptManager>
<h2>Imperative Drag and Drop Code with javascript:
demonstrate dynamic loading of behaviors</h2>
<input type="button" value="Add Floating Div" onclick="createDraggableDiv();" />
<div id="containerDiv" style="background-color:Purple;height:800px;width:600px;"/>
</form>
</body>
</html>
四、声明拖放区
拖动HTML元素在页面上划来划去,当松开鼠标时,这些HTML元素就被拖到了鼠标所在的位置,这看起来非常过隐。为了使这个功能更有用,当松开鼠标时可以引发一个事件。此外,这个事件的引发应该依赖于HTML放下的位置。换句话说,应该为这些被拖动的HTML对象加入拖放区,可以使用同样的方法将一个HTML div标签变成一个拖动对象。
在下面的例子中,我将展示如何使用Atlas来设置拖放区。默认情况下,Atlas并不支持使用和建立可拖动元素一样的方法来建立拖放区。要想实现这一功能,必须同时使用DrapDropList和DraggableListItem对象,你可以通过它们建立一个排序的拖放列表。如果你想了解更多的信息,可以在网上找到更多的使用DrapDropList的例子。
拖放区的主要缺点是只能对DragDropList中的元素起作用。因此,使用DrapDropList和DraggableListItem元素只能对特定的元素有效。为了实现上述的更为通用的拖放区功能,你需要自己使用JavaScript实现自己的拖放区类。幸运的是,实现这些并不困难。
为了拥有更强大的功能,Atlas为JavaScript增加了一些面向对象扩展,如命名空间、抽象类、接口等。你可以充分利用这些扩展来实现自定义拖放区。如果你看一看AtlasUIDrapDrop.js的源码(可以通过Visual Studio debugger查看源码),你就会发现在这个文件中定义了一些接口,这其中包括一个为Sys.UI.DrapSource定义的接口和一个为Sys.UI.DropTarget定义的接口。事实上,FloatingBehavior和DraggableListItem类实现了Sys.UI.DragSource接口,同时,Sys.UI.DropTarget实现了DrapDropList类。以下是这两个接口的代码:
Sys.UI.IDragSource = function() ...{
this.get_dataType = Function.abstractMethod;
this.get_data = Function.abstractMethod;
this.get_dragMode = Function.abstractMethod;
this.onDragStart = Function.abstractMethod;
this.onDrag = Function.abstractMethod;
this.onDragEnd = Function.abstractMethod;
}
Sys.UI.IDragSource.registerInterface('Sys.UI.IDragSource');
Sys.UI.IDropTarget = function() ...{
this.get_dropTargetElement = Function.abstractMethod;
this.canDrop = Function.abstractMethod;
this.drop = Function.abstractMethod;
this.onDragEnterTarget = Function.abstractMethod;
this.onDragLeaveTarget = Function.abstractMethod;
this.onDragInTarget = Function.abstractMethod;
}
Sys.UI.IDropTarget.registerInterface('Sys.UI.IDropTarget');
你为什么需要实现这些接口,而不是直接编写支持拖放或拖放区功能的类呢?原因是在幕后有一个叫DrapDropManager的类,这个类用于管理拖放操作中的被拖放的元素和拖放区之间的交互,而这个类仅仅能管理实现了IDragSource和IDropTarget接口的类。这个DragDropManager类的register可以使拖放区对每一个拖到这上面的对象进行限制,这一切是通过处理MouseOver事件完成的,通过MouseOver事件,拖动区可以只让一个对象拖到它的上面,而让其它的对象无法办到这一点。这个类完成了大多数的工作,因此,我们无需写更多的代码。首先,建立一个叫DropZoneBehavior.js的JavaScript文件。将它放在scriptLibrary目录中,但实现拖放区功能并不一定要这样做。接下来,将如下代码复制到这个文件中:
Type.registerNamespace('Custom.UI');
![]()
Custom.UI.DropZoneBehavior = function() ...{
![]()
Custom.UI.DropZoneBehavior.initializeBase(this);
this.initialize = function() ...{
Custom.UI.DropZoneBehavior.callBaseMethod(this, 'initialize');
// Register ourselves as a drop target.
Sys.UI.DragDropManager.registerDropTarget(this);
}
this.dispose = function() ...{
Custom.UI.DropZoneBehavior.callBaseMethod(this, 'dispose');
}
this.getDescriptor = function() ...{
var td = Custom.UI.DropZoneBehavior.callBaseMethod(this, 'getDescriptor');
return td;
}
![]()
// IDropTarget members.
this.get_dropTargetElement = function() ...{
return this.control.element;
}
this.drop = function(dragMode, type, data) ...{
alert('dropped');
}
this.canDrop = function(dragMode, dataType) ...{
return true;
}
this.onDragEnterTarget = function(dragMode, type, data) ...{
}
this.onDragLeaveTarget = function(dragMode, type, data) ...{
}
this.onDragInTarget = function(dragMode, type, data) ...{
}
}
Custom.UI.DropZoneBehavior.registerClass('Custom.UI.DropZoneBehavior',
Sys.UI.Behavior, Sys.UI.IDragSource,
Sys.UI.IDropTarget, Sys.IDisposable);
Sys.TypeDescriptor.addType('script', 'DropZoneBehavior',
Custom.UI.DropZoneBehavior);
![]()
我需要解释一下这个类。首先需要注意的是倒数第二行的开头部分“Custom.UI.DropZoneBehavior.registerClass”。它的作用是使用Atlas注册上面定义的DropZoneBehavior类。registerClass方法的第一个参数是类名。第二个参数是基类。后面的其它参数是第一个参数所指向的类所实现的接口。最后一行使这个类在Atlas脚本中可以使用。现在回到第一行,“Type.registerNamespace”方法注册了自定义的命名空间。下一行使用了匿名方法形式定义了一个新的类。我不太熟悉这种JavaScript语法,但这种语法对编写面向对象的JavaScript是非常重要的。在这个匿名方法中,定义了类的Initialize、Dispose和getDescriptor方法,这些方法都是标准的方法,它们都非常简单。在这些方法中唯一要做的就是调用基类同名的方法(就是在倒数第二行所指定的这个基类的方法)。除了这些,你只要做一件事,就是在Initialize方法中使用Sys.UI.DrapDropManager注册拖放对象。
接下来,在这个类中实现了IDropTarget的方法。在这个例子中只实现了两个方法,“this.canDrop”和“this.drop”。“canDrop”方法只返回一个true。当然,可以在这个方法中写更多的代码来实现更有趣的效果,但在我们这个例子中只是想实现拖动一个div标签。“drop”方法的实现也很简单。当一个被拖动的对象被放在拖放区上时,弹出一个对话框,以表示这个事件被成功调用。
也许你应该建立一个新的aspx页来展示一下你的自定义拖放区。上面的例子代码足可以完成这个功能。在Atlas脚本管理器中,除了注册AtlasUIDrapDrop脚本外,还应该注册上面的DropZoneBehavior脚本:
<atlas:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>...
<atlas:ScriptReference ScriptName="AtlasUIDragDrop" />
<atlas:ScriptReference Path="scriptLibrary/DropZoneBehavior.js" />
</Scripts>
</atlas:ScriptManager>
接下来,你应该在HTML body中加一个新的div标签,以便使这个div可以被拖动。
<div style="background-color:Red;height:200px;width:200px;">
<div id="draggableDiv" style="height:100px;width:100px;background-color:Blue;">
<div id="handleBar" style="height:20px;width:auto;background-color:Green;">
</div>
</div>
</div>
![]()
<div id="dropZone" style="background-color:cornflowerblue;height:200px;width:200px;">
Drop Zone
</div>
最后,你需要加一个XML标记,以使div对象可以响应拖放区的事件。XML标记如下:
![]()
<script type="text/xml-script">...
<page xmlns:script="http://schemas.microsoft.com/xml-script/2005">
<components>
<control id="dropZone">
<behaviors>
<DropZoneBehavior/>
</behaviors>
</control>
<control id="draggableDiv">
<behaviors>
<floatingBehavior handle="handleBar"/>
</behaviors>
</control>
</components>
</page>
</script>
