Friday, January 14, 2011

Demystifying Drag and Drop in ASP.NET (Part 1)

This is not particularly and fully related to ASP.NET but the main project which I extracted this concept from is an asp.net project, so I have decided to present an asp.net example.

As a requirement for one of my pet projects, I am required to create drag and drop event handler on an scheduler/events calendar. This threw me into the dark side of javascripting, ASP.NET ICallBackEvent interface, post back/viewstate dodging.

I searched the web for an existing solutions that will enable me drag events memo from a date to another date using JavaScript. Most of the approaches that I encountered are either too complex or overly complicated for the simple scenario

Then I stumbled on this particular script at Web Tool kit which blew my mind away, this JavaScript particularly simplifies the cross browsers headache when trying to handle mouse events across browsers. And its simple object oriented approach to handling and delegating event for drag and drop is just fantastic.

Then something happened
I was looking for a solution that will enable me to drag an event from one data point to another. The solution i am talking about above though perfect but it has not fulfilled the entire drag and drop functionality. I can drag and drop, but when droping, the element being dragged should be attached to the object in the drop zones.

For clarity, try the drag drop sample below. Please note that the two drop zones can allow drag gable elements to be dropped upon them, and that you cannot drop items outside of the drop zones. To give a clear comparison then try the example given in the original script, located here , then you can understand the concepts of drop zones and drop targeting.
















Drop Zone 1 Drop Zone 2 Collection Area




Draggable 1




Draggable 2





The above drag and drop simulates the drop zone functionality which allows drag able elements to be dropped into the zones without hassles.

The missing points in original JavaScript, is the fact that drop zones are not recognized. I will take you bit by bit into how I have improved and added new functionality to the original code :

Identifying the container

The container element for this page example is a table with id = 'main'. A container is an HTML element which will contain the drop zones, note, any html which can contain other elements (p, div, table li etc.)element within the body section of an HTML document can be used as a container. This example makes use of a table as the container.


<table id="main" cellpadding="6" cellspacing="6" border="1">
<thead>
<tr class="style1">
<th class="style1" valign="top"> Drop Zone 1 </th>
<th class="style1" valign="top"> Drop Zone 2 </th>
<th class="style1" valign="top"> Collection Area </th>
</tr>
</thead>
<tbody>
<tr>
<td class="zone">

</td>
<td class="zone">


</td>
<td class="zone">
<div id="dragable1" class="element-class">
<h3 class="box-head">Draggable 1 </h3>
</div>

<div id="dragable2" class="element-class">
<h3 class="box-head">Draggable 2 </h3>
</div>
<span id="writtable">

</span>
</td>
</tr>
</tbody>

</table>


Some table cells within the table main's table row have the class name zone identifying them as the drop zones. Any element with the class name = "zone" can contain draggables.

Any element can also be dragged (except td, th etc. ), so far they are registered using the following javascript snippet.


DragHandler.attach(document.getElementById('dragable element'));


Registering/Preparing the Container and Drag able objects
We needed to look up the DOM and hook events on the drag gable's and retain drop zones into a Position object created below.


var zonesArray = new Array();
var index = 0;

window.onload = function () {

var dragable1 = DragHandler.attach(document.getElementById('dragable1'));
var dragable2 = DragHandler.attach(document.getElementById('dragable2'));

var main = document.getElementById('main');

for (var j = 0; j < main.childNodes.length; j++) {

PrepareDropZones(main.childNodes[j], zonesArray, "zone");
}
}


function Position(element) {
this.X = findPosX(element);
this.Y = findPosY(element);
this.Element = element;
this.Width = (this.X + element.offsetWidth);
this.Height = (this.Y + element.offsetHeight);

this.IsInCordinate = function (XCord, YCord) {
if (XCord > this.X && XCord < (this.Width) && YCord > this.Y && YCord < (this.Height)) {
return true;
}

return false;
}
}


The above code is the missing point in the original code, and the part where elements are dropped. As soon as an element is dropped, the mouse coordinates X and Y are checked against the drop zones X + width and Y + height , if the mouse is within coordinate, then the element is dropped. Shown below is the code for checking drop zones when an element is dropped.


// private method. Stop drag process.
_dragEnd: function (e) {
var oElem = DragHandler._oElem;

var x = parseInt(oElem.style.left);
var y = parseInt(oElem.style.top);

oElem.dragEnd(oElem, x, y);

var evt = e || window.event;
var evtTarget = evt.target || evt.srcElement;

for (var i = 0; i < zonesArray.length; i++) {

var dZone = zonesArray[i];
var cursor = getMousePosition(e);

if (dZone.IsInCordinate(cursor.x, cursor.y)) {
dZone.Element.appendChild(oElem);
oElem.style.left = 0;
oElem.style.top = 0;
break;
}
else {
oElem.style.left = DragHandler._beginX;
oElem.style.top = DragHandler._beginY;
}
}

document.onmousemove = null;
document.onmouseup = null;
DragHandler._oElem = null;
oElem = null;
}


Download the source code (VS 2010):

3 comments:

Ahmed Salako said...

Simplifying drag and drop in asp.net using javascript

Alan Hollander said...

We want to populate a database based on where the items are dragged and dropped. Do you think this would be possible with a modification of your code?

Ahmed Salako said...

@Alan, this is possible with ajax. Your draggble and drop zone element would have an Id that already exist in the database, so dropping an item into a drop zone should trigger an ajax call to post both draggable and drop zone back to the server which you will then need to add into a join table.