Monday, August 18, 2008

Drag and Drop in ASP.NET

First, I want to say I am not a JavaScript expert. The last time I used JavaScript was 7 years ago. Recently I started working on a web site which needs to allow user drag and drop items from a list. I did a lot of research and found some very useful articles. They helped me to implement this feature on my site.

These articles are

http://aspalliance.com/1300_Custom_Client_Side_Drag_and_Drop_Behavior_in_ASPNET_AJAX

http://www.codeproject.com/KB/ajax/JigsawPuzzleGame.aspx

http://msdn.microsoft.com/en-us/magazine/cc135985.aspx

Although they are very helpful, my scenario is not exactly the same. I want to be able to drag an item, drop to an area and then drag/drop it again later. I tried to mimic the Jigsaw Puzzle sample. However, since my item is not an image, only a string, I got lost during the re-writing. Finally I figured out my own solution.

Below is the screen shot of my sample. You can download the sample code from here.

clip_image002

User can drag/drop an item from the list on top to the “Drop it here” area. User can also move the items between grids. Finally, user can remove an item by drag/drop it to the Garbage Can area.

Let’s see how we implement this by using ASP.NET AJAX and Javascript:

Before you start, you will need to download the ASP.NET 3.5 Extension Preview:

http://www.microsoft.com/downloads/details.aspx?FamilyId=A9C6BC06-B894-4B11-8300-35BD2F8FC908&displaylang=en

and ASP.NET Future

http://www.asp.net/downloads/futures/default.aspx?wwwaspnetrdirset=1

  • Add reference to “Microsoft.Web.Preview for .NET 3.5” in your project.

  • Add ScriptManager to the page since we are using AJAX

  • Add ScriptReference

<asp:ScriptManager runat="server" ID="ScriptManager1">
<Scripts>
<asp:ScriptReference Name="PreviewScript.js" Assembly="Microsoft.Web.Preview" />
<asp:ScriptReference Name="PreviewDragDrop.js" Assembly="Microsoft.Web.Preview" />
</Scripts>
</asp:ScriptManager>





  • Create a page with the following html elements







<div id="dragItem1">Item 1</div><br />
<div id="dragItem2">Item 2</div><br />
<div id="dragItem3">Item 3</div><br />
<div id="dragItem4">Item 4</div><br />
<div>
<table border="1" id="dragDropZone">
<tr>
<td width="100" height="100" id="target1">Drop it here</td>
<td width="100" height="100" id="target2">Drop it here</td>
<td width="100" height="100" id="target3">Drop it here</td>
<td width="100" height="100" id="target4">Drop it here</td>
</tr>
<tr><td colspan="4" height="100" id="garbageCan">Garbage Can</td></tr>
</table>
</div>










  • Create DragDrop.js. I posted the whole file here. Please see comments for explaination.





Type.registerNamespace('DotNetIdeas.Ajax.UI');

//ItemDragSourceBehavior
DotNetIdeas.Ajax.UI.ItemDragSourceBehavior = function(element, item, parent, target)
{
DotNetIdeas.Ajax.UI.ItemDragSourceBehavior.initializeBase(this, [element]);
this._mouseDownHandler = Function.createDelegate(this,
this.mouseDownHandler);
this._item = item;
this._visual = null;
this._parent = parent; // Parent control which hold the drag items and drop targes
this._target = target; // current target the drag item is on
}

DotNetIdeas.Ajax.UI.ItemDragSourceBehavior.prototype =
{
// IDragSource methods
get_dragDataType: function() {
return 'DragDropItem';
},

getDragData: function(context) {
return this._item;
},

get_dragMode: function() {
return Sys.Preview.UI.DragMode.Move;
},

//The following two functions are for FireFox, so it won't select the text when drag starts
returnFalse: function() { return false; },
returnTrue: function() { return true; },

onDragStart: function() {
document.onmousedown = this.returnFalse;
// Set the leaving target, so we can remove this item from it later
if (this._parent) this._parent.setLeavingTarget(this._target);
},

onDrag: function() { },

onDragEnd: function(canceled) {
// If drag from the outside of the dragdrop control, then there will be a shadow when moving the item,
// so we need to remove it when drag is end
if (this._visual && this.get_element().parentNode)
this.get_element().parentNode.removeChild(this._visual);
document.onmousedown = this.returnTrue;
},

// Other methods
initialize: function() {
DotNetIdeas.Ajax.UI.ItemDragSourceBehavior.callBaseMethod(this,
'initialize');
$addHandler(this.get_element(), 'mousedown',
this._mouseDownHandler)
},

mouseDownHandler: function(ev) {
window._event = ev; // Needed internally by _DragDropManager

this._visual = this.get_element().cloneNode(true);
this._visual.style.opacity = '0.4';
this._visual.style.filter =
'progid:DXImageTransform.Microsoft.BasicImage(opacity=0.4)';
this._visual.style.zIndex = 99999;

this.get_element().parentNode.appendChild(this._visual);
var location =
Sys.UI.DomElement.getLocation(this.get_element());
Sys.UI.DomElement.setLocation(this._visual, location.x,
location.y);

Sys.Preview.UI.DragDropManager.startDragDrop(this,
this._visual, null);

},

dispose: function() {
if (this._mouseDownHandler)
$removeHandler(this.get_element(), 'mousedown',
this._mouseDownHandler);
this._mouseDownHandler = null;
DotNetIdeas.Ajax.UI.ItemDragSourceBehavior.callBaseMethod(this,
'dispose');
}
}

DotNetIdeas.Ajax.UI.ItemDragSourceBehavior.registerClass
('DotNetIdeas.Ajax.UI.ItemDragSourceBehavior', Sys.UI.Behavior,
Sys.Preview.UI.IDragSource);


//ItemDropTargetBehavior
DotNetIdeas.Ajax.UI.ItemDropTargetBehavior = function(element, parent)
{
DotNetIdeas.Ajax.UI.ItemDropTargetBehavior.initializeBase(this, [element]);
this._items = new Array();
this._parent = parent;
this.targetType = "Target";
}

DotNetIdeas.Ajax.UI.ItemDropTargetBehavior.prototype =
{
// IDropTarget methods
get_dropTargetElement: function()
{
return this.get_element();
},

canDrop: function(dragMode, dataType, data)
{
return (dataType == 'DragDropItem' && data);
},

drop: function(dragMode, dataType, data)
{
if (dataType == 'DragDropItem' && data)
{
if (this.targetType != 'GarbageCan')
{
// If the item already exists in this target area, do not add it again.
for(var id in this._items)
{
if (this._items[id] == data) return;
}

// Add new item to item list in this target area
var newId = this._items.length;
this._items[newId] = data;
var itemBuilder = new Sys.StringBuilder();
var targetId = this.get_element().id.toString();

// Re-generate the HTML string for the items
for (var id in this._items) {
itemBuilder.append("<div id='droppedItem_" + targetId + "_" + id.toString() + "'>" );
itemBuilder.append(this._items[id]);
itemBuilder.append("</div>");
}
this.get_element().innerHTML = itemBuilder.toString();

// Create the draggable items
for (var id in this._items) {
var itemId = "droppedItem" + "_" + targetId + "_" + id.toString();
var droppedItem = new DotNetIdeas.Ajax.UI.ItemDragSourceBehavior($get(itemId), $get(itemId).innerHTML, this._parent, this);
droppedItem.initialize();
}
}

// Remove this item from its previous target
var leavingTarget = this._parent.getLeavingTarget();
if (leavingTarget)
{
leavingTarget.removeItem(data);
}
this._parent.setLeavingTarget(null);
}
},

onDragEnterTarget: function(dragMode, dataType, data)
{
// Highlight the drop zone by changing its background
// color to light gray
if (dataType == 'DragDropItem' && data)
{
this._color = this.get_element().style.backgroundColor;
this.get_element().style.backgroundColor = '#E0E0E0';
}
},

onDragLeaveTarget: function(dragMode, dataType, data)
{
// Unhighlight the drop zone by restoring its original
// background color
if (dataType == 'DragDropItem' && data)
{
this.get_element().style.backgroundColor = "White";
}
},

onDragInTarget: function(dragMode, dataType, data) {},

// Other methods
initialize: function()
{
DotNetIdeas.Ajax.UI.ItemDropTargetBehavior.callBaseMethod(this,
'initialize');
Sys.Preview.UI.DragDropManager.registerDropTarget(this);
},

dispose: function()
{
Sys.Preview.UI.DragDropManager.unregisterDropTarget(this);
DotNetIdeas.Ajax.UI.ItemDropTargetBehavior.callBaseMethod(this,
'dispose');
},

// Remove items from target area
removeItem : function(data)
{
// Remove item from the item list
var j = 0;
while (j < this._items.length) {
if (this._items[j] == data) {
this._items.splice(j, 1);
}
else {
j++;
}
}

// Re-generate HTML for the items
var itemBuilder = new Sys.StringBuilder();
var targetId = this.get_element().id.toString();
for (var id in this._items) {
itemBuilder.append("<div id='droppedItem_" + targetId + "_" + id.toString() + "'>" );
itemBuilder.append(this._items[id]);
itemBuilder.append("</div>");

}
this.get_element().innerHTML = itemBuilder.toString();

// Re-create the draggable items
for (var id in this._items) {
var itemId = "droppedItem" + "_" + targetId + "_" + id.toString();
var droppedItem = new DotNetIdeas.Ajax.UI.ItemDragSourceBehavior($get(itemId), $get(itemId).innerHTML);
droppedItem.initialize();
}
}
}

DotNetIdeas.Ajax.UI.ItemDropTargetBehavior.registerClass
('DotNetIdeas.Ajax.UI.ItemDropTargetBehavior', Sys.UI.Behavior,
Sys.Preview.UI.IDropTarget);


//DragDrop Zone. It is used as an container of the drop target areas. It is the parent of all the targets.
//When one item is moved from one target to another. It will call setLeavingTarget() to store the previous target,
//so when the item is drop, it knows to remove it from the previous target.
DotNetIdeas.Ajax.UI.DragDropZone = function(element)
{
DotNetIdeas.Ajax.UI.DragDropZone.initializeBase(this, [element]);
this._leavingTarget = null;
}

DotNetIdeas.Ajax.UI.DragDropZone.prototype =
{
initialize : function()
{
DotNetIdeas.Ajax.UI.DragDropZone.callBaseMethod(this, "initialize");
this._renderControl();
},

dispose : function()
{
DotNetIdeas.Ajax.UI.DragDropZone.callBaseMethod(this, "dispose");
},

_renderControl : function()
{
// Create drag/drop targets
var target1 = new DotNetIdeas.Ajax.UI.ItemDropTargetBehavior($get('target1'), this);
var target2 = new DotNetIdeas.Ajax.UI.ItemDropTargetBehavior($get('target2'), this);
var target3 = new DotNetIdeas.Ajax.UI.ItemDropTargetBehavior($get('target3'), this);
var target4 = new DotNetIdeas.Ajax.UI.ItemDropTargetBehavior($get('target4'), this);
var garbageCan = new DotNetIdeas.Ajax.UI.ItemDropTargetBehavior($get('garbageCan'), this);
target1.initialize();
target2.initialize();
target3.initialize();
target4.initialize();
garbageCan.initialize();
garbageCan.targetType = "GarbageCan";
},

setLeavingTarget : function(leavingTarget)
{
this._leavingTarget = leavingTarget;
},

getLeavingTarget : function()
{
return this._leavingTarget;
}
};

DotNetIdeas.Ajax.UI.DragDropZone.registerClass("DotNetIdeas.Ajax.UI.DragDropZone", Sys.UI.Control);

///////////////////////////////////////////////////////////////////////
// Script registration

Sys.Application.notifyScriptLoaded();









  • Add ScriptReference for DragDrop.js









<asp:ScriptReference Path="~/DragDrop.js" />











  • Add following code to header section





<script type="text/javascript">
function pageLoad() {
var source1 = new DotNetIdeas.Ajax.UI.ItemDragSourceBehavior($get('dragItem1'), $get('dragItem1').innerHTML);
var source2 = new DotNetIdeas.Ajax.UI.ItemDragSourceBehavior($get('dragItem2'), $get('dragItem2').innerHTML);
var source3 = new DotNetIdeas.Ajax.UI.ItemDragSourceBehavior($get('dragItem3'), $get('dragItem3').innerHTML);
var source4 = new DotNetIdeas.Ajax.UI.ItemDragSourceBehavior($get('dragItem4'), $get('dragItem4').innerHTML);
source1.initialize();
source2.initialize();
source3.initialize();
source4.initialize();

var dragDropZone = new DotNetIdeas.Ajax.UI.DragDropZone($get('dragDropZone'));
dragDropZone.initialize();
}
</script>








 








 







No comments:

Post a Comment