Introduction to Drag And Drop with Atlas
Quick overview
The Atlas framework provides cross-browser support for drag&drop operations. Basically, to create a UI with drag&drop capabilities we need
- draggable items and drop targets. Draggable items are controls that can be moved around the page, while drop targets are controls that act as "containers" for draggable items. The Atlas framework allows to define draggable controls by implementing the IDragSource interface. A drop target is, instead, a class that implements the IDropTarget interface. It is also possible to create a class that implements both the IDragSource and the IDropTarget interfaces: we'll have a control that acts both as a draggable item and a drop target (an example of such control is a "floating" control).
- a DragDropManager, which is an object with global scope that is instantiated at runtime. This object can be accessed through the reference Web.UI.DragDropManager and is generally used to start dragging operations and to register drop targets. From there on, the DragDropManager handles all the drag&drop operations through calls to IDragSource and IDropTarget methods.
So, a very basic flow needed to create a drag&drop UI could be
- create draggable items by implementing the IDragSource interface. The class that implements this interface is also responsible to call the Web.UI.DragDropManager.startDragDrop() method to start the dragging operation (tipically, this is done in an event handler for the mousedown event of the control's element). Each draggable item has its own dataType, i.e. an identifier that allows to group draggable items (the predefined dataType is HTML);
- create drop targets by implementing the IDropTarget interface. A class that implements this interface is responsible to register the drop target by invoking the Web.UI.DragDropManager.registerDropTarget() method. Each drop target has a list of acceptedDataTypes, that specifies which "types" of draggable items can be dropped on that target.
In summary, a drag&dop operation is tipically started by an IDragSource object with the call to the startDragDrop() method of the DragDropManager. Then, the operation is handled by the DragDropManager, through calls to the IDragSource methods of the item being dragged, and IDropTarget methods of the registered drop target(s).
Builtin classes for Drag&Drop
Before going into the drag&drop engine details (I'll talk about the IDragSource and IDropTarget interfaces in another post) it's better to look at some builtin controls and behaviors provided by the Atlas framework to perform drag&drop operations. Obviously, all these classes implement the IDragSource, IDropTarget interface, or both.
- The DragDropList behavior allows to add drag&drop capabilities to list of controls. One tipical use is to add this behavior to a ListView control.
- The DraggableListItem behavior allows to define a draggable item in a DragDropList. Usually it is added as an ItemTemplate's behavior inside a ListView, to make list items draggable.
- The DataSourceDropTarget behavior is used to add dropped data to a DataSource control.
- The FloatingBehavior can be added to a control to obtain a floating control.
The example
In the example, we'll build a simple page with draggable content (you could give a look to Start or Windows Live page for more complex examples of this kind of layout and functionality) by using the DragDropList and DraggableListItem behaviors. Another example using the DragDropList and other drag&drop examples can be found in Wilco's website. You can found the full code for this example at the end of this post.
First of all, let's create the page with static layout (think of it as a "screenshot" of the final dynamic layout). Then, we'll add dynamic behaviors to controls with some Atlas markup. I've created two zones that are supposed to act as drop zones for draggable content. The draggable content is represented by panels with ASP.NET controls inside them. <!-- Left Area -->
<div id="leftArea" class="list1">
<div id="content1" class="item">
<div id="content1Handle" class="itemHandle">Content 1</div>
<div class="itemContent">
<asp:Login ID="myLogin" runat="server"
CssClass="centered"></asp:Login>
</div>
</div>
<div id="content2" class="item">
<div id="content2Handle" class="itemHandle">Content 2</div>
<div class="itemContent">
<asp:TextBox ID="myTextBox" runat="server"></asp:TextBox>
<asp:Button ID="myButton" runat="server"
Text="ClickMe" />
</div>
</div>
</div>
<!-- Right Area -->
<div id="rightArea" class="list2">
<div id="content3" class="item">
<div id="content3Handle" class="itemHandle">Content 3</div>
<div class="itemContent">
<asp:Calendar ID="myCalendar" runat="server"
CssClass="centered"></asp:Calendar>
</div>
</div>
</div>
As you can see, I've created two drop zones and three content panels inside them, obtaining the static layout for the page.
Now, we need some HTML to represent the "highlighted" content area when a panel is dragged over (the dropCueTemplate), and some HTML to display when all the panels have been moved away from a drop zone (the emptyTemplate). <!-- Hide template elements -->
<div class="templates">
<!-- DropCue Template -->
<div id="dropCueTemplate" class="dropCue"></div>
<!-- Empty Template -->
<div id="emptyTemplate" class="emptyList">Drop content here.</div>
</div>
Ok, we are ready to make this a dynamic page by adding Atlas markup. The two drop zones will become two Atlas controls with DragDropList behavior. The code for the left drop area is <!-- Left Area -->
<control id="leftArea">
<behaviors>
<dragDropList dataType="HTML"
acceptedDataTypes="'HTML'"
dragMode="Move"
direction="Vertical">
<dropCueTemplate>
<template layoutElement="dropCueTemplate" />
</dropCueTemplate>
<emptyTemplate>
<template layoutElement="emptyTemplate" />
</emptyTemplate>
</dragDropList>
</behaviors>
</control>
while the code for the right area is similar. In the above code, we have wrapped the "leftArea" <div> element in an Atlas control, and added a DragDropList behavior to it. This DragDropList holds data of type "HTML", accepts data of type "HTML", has a Vertical orientation (the directon can be Horizontal or Vertical) and the drag operation is done by moving the whole item (the other option is dragMode="Copy", that drags a copy of the item). Inside the <dragDropList> element we have specified, by providing their IDs, which DOM elements to use as the dropCueTemplate and the emptyTemplate.
Let's see the markup for one of the content panels (the code for the other panels is similar): <!-- Draggable items -->
<control id="content1">
<behaviors>
<draggableListItem handle="content1Handle" />
</behaviors>
</control>
Again, the DOM element "content1" becomes an Atlas control with a draggableListItem behavior. This behavior allows the content panel to be dragged. The handle attribute contains the ID of an element that acts as the "grip" for the drag operation. I've set the handle to be the header of each content panel.
Finally, it's time to run the example. Here's the full source:
DragDropExample.aspx <%@ 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 id="Head1" runat="server">
<title>DragAndDrop UI Example</title>
<atlas:ScriptManager ID="scriptManager" runat="server">
<Scripts>
<atlas:ScriptReference ScriptName="AtlasUIDragDrop" />
</Scripts>
</atlas:ScriptManager>
<style type="text/css">
body{font-family:Verdana;font-size:11px;}
.main{position:relative;width:710px;height:540px;margin:auto;}
.list1{position:absolute;left:0px;height:520px;width:340px;
padding:10px 5px 10px 10px;}
.list2{position:absolute;right:0px;height:520px;width:340px;
padding:10px 10px 10px 5px;}
.item{background:#fff;margin-bottom:5px;background:#fff;}
.itemContent{padding:5px;text-align:center;}
.itemHandle{height:15px;background:#e5ecf9;overflow:hidden;
border-top:solid 1px #3366cc;font-weight:bold;
cursor:move;}
.dropCue{border:dashed 1px #ff0000;margin-bottom:5px;}
.emptyList{font-weight:bold;text-align:center;}
.centered{margin:auto;}
.templates{visibility:hidden;}
</style>
</head>
<body>
<form id="form1" runat="server">
<div class="main">
<!-- Left Area -->
<div id="leftArea" class="list1">
<div id="content1" class="item">
<div id="content1Handle" class="itemHandle">Content 1</div>
<div class="itemContent">
<asp:Login ID="myLogin" runat="server"
CssClass="centered"></asp:Login>
</div>
</div>
<div id="content2" class="item">
<div id="content2Handle" class="itemHandle">Content 2</div>
<div class="itemContent">
<asp:TextBox ID="myTextBox" runat="server"></asp:TextBox>
<asp:Button ID="myButton" runat="server"
Text="ClickMe" />
</div>
</div>
</div>
<!-- Right Area -->
<div id="rightArea" class="list2">
<div id="content3" class="item">
<div id="content3Handle" class="itemHandle">Content 3</div>
<div class="itemContent">
<asp:Calendar ID="myCalendar" runat="server"
CssClass="centered"></asp:Calendar>
</div>
</div>
</div>
<!-- Hide template elements -->
<div class="templates">
<!-- DropCue Template -->
<div id="dropCueTemplate" class="dropCue"></div>
<!-- Empty Template -->
<div id="emptyTemplate" class="emptyList">Drop content here.</div>
</div>
</div>
</form>
<script type="text/xml-script">
<page>
<components>
<!-- Left Area -->
<control id="leftArea">
<behaviors>
<dragDropList dataType="HTML"
acceptedDataTypes="'HTML'"
dragMode="Move"
direction="Vertical">
<dropCueTemplate>
<template layoutElement="dropCueTemplate" />
</dropCueTemplate>
<emptyTemplate>
<template layoutElement="emptyTemplate" />
</emptyTemplate>
</dragDropList>
</behaviors>
</control>
<!-- Right Area -->
<control id="rightArea">
<behaviors>
<dragDropList dataType="HTML"
acceptedDataTypes="'HTML'"
dragMode="Move"
direction="Vertical">
<dropCueTemplate>
<template layoutElement="dropCueTemplate" />
</dropCueTemplate>
<emptyTemplate>
<template layoutElement="emptyTemplate" />
</emptyTemplate>
</dragDropList>
</behaviors>
</control>
<!-- Draggable items -->
<control id="content1">
<behaviors>
<draggableListItem handle="content1Handle" />
</behaviors>
</control>
<control id="content2">
<behaviors>
<draggableListItem handle="content2Handle" />
</behaviors>
</control>
<control id="content3">
<behaviors>
<draggableListItem handle="content3Handle" />
</behaviors>
</control>
</components>
</page>
</script>
</body>
</html>
UPDATES:
04/04/2006 - Updated the code to the March CTP.