Implementing Drag-Drop in ASP.NET AJAX

Several folks requested the source code for the ASP.NET AJAX drag-drop example I alluded to in an earlier blog post. Here are the key parts of it (along with some explanations of how it works) that you can borrow using editor inheritance–I mean, cut-and-paste.

The first thing you must do to implement rich drag-drop scenarios in ASP.NET AJAX is implement the IDragSource interface defined in PreviewDragDrop.js. In my example, the user drags and drops color swatches, so I derived a class from Sys.UI.Behavior and implemented IDragSource in the derived class to create a “drag behavior” that I can attach to DOM elements. The drag behavior’s initialize method registers a handler for mousedown events. The handler initiates a drag-drop operation by calling Sys.Preview.UI.DragDropManager.startDragDrop, which is implemented in PreviewDragDrop.js. It also creates a drag visual by cloning the DOM element being dragged and setting the clone’s opacity to 0.4. Here’s the source code for the drag-behavior class:

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;
}
Custom.UI.ColorDragSourceBehavior.prototype =
{
// IDragSource methods
get_dragDataType: function()
{
return ‘DragDropColor’;
},
getDragData: function(context)
{
return this._color;
},
get_dragMode: function()
{
return Sys.Preview.UI.DragMode.Copy;
},
onDragStart: function()
{
},
onDrag: function()
{
},
onDragEnd: function(canceled)
{
if (this._visual)
this.get_element().parentNode.removeChild(this._visual);
},

// Other methods
initialize: function()
{
Custom.UI.ColorDragSourceBehavior.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;
Custom.UI.ColorDragSourceBehavior.callBaseMethod(this, ‘dispose’);
}
}
Custom.UI.ColorDragSourceBehavior.registerClass(‘Custom.UI.ColorDragSourceBehavior’,
Sys.UI.Behavior, Sys.Preview.UI.IDragSource);();

Converting a DOM element (such as a DIV) into a drag source is now a simple matter of newing up a ColorDragSourceBehavior, attaching it to the DOM element, and calling its initialize method:

var source1 = new Custom.UI.ColorDragSourceBehavior ($get(‘RedDragSource’), ‘red’);
var source2 = new Custom.UI.ColorDragSourceBehavior ($get(‘GreenDragSource’), ‘green’);
var source3 = new Custom.UI.ColorDragSourceBehavior ($get(‘BlueDragSource’), ‘blue’);
source1.initialize();
source2.initialize();
source3.initialize();();

The next step is to implement PreviewDragDrop.js’s IDropTarget interface. To accomplish this, I derived another class from Sys.UI.Behavior and implemented IDropTarget in the derived class. The implementation is pretty straightforward. In my example, the IDropTarget.drop method, which is called when a drop occurs, sets the background color of the DOM element to which the drop-target behavior is attached to the color being dragged (that is, the color encapsulated in the drag source). It also implements “drag highlighting” by setting the drop target’s background color to light gray when a cursor carrying a payload enters the drop target and restoring the original color when the cursor leaves. Here’s the drop-target behavior class:

Custom.UI.ColorDropTargetBehavior = function(element)
{
Custom.UI.ColorDropTargetBehavior.initializeBase(this, [element]);
this._color = null;
}

Custom.UI.ColorDropTargetBehavior.prototype =
{
// IDropTarget methods
get_dropTargetElement: function()
{
return this.get_element();
},
canDrop: function(dragMode, dataType, data)
{
return (dataType == ‘DragDropColor’ && data);
},
drop : function(dragMode, dataType, data)
{
if (dataType == ‘DragDropColor’ && data)
{
this.get_element().style.backgroundColor = data;
}
},
onDragEnterTarget : function(dragMode, dataType, data)
{
if (dataType == ‘DragDropColor’ && data)
{
this._color = this.get_element().style.backgroundColor;
this.get_element().style.backgroundColor = ‘#E0E0E0’;
}
},

onDragLeaveTarget : function(dragMode, dataType, data)
{
if (dataType == ‘DragDropColor’ && data)
{
this.get_element().style.backgroundColor = this._color;
}
},
onDragInTarget : function(dragMode, dataType, data)
{
},

// Other methods
initialize: function()
{
Custom.UI.ColorDropTargetBehavior.callBaseMethod(this, ‘initialize’);
Sys.Preview.UI.DragDropManager.registerDropTarget(this);
},

dispose: function()
{
Sys.Preview.UI.DragDropManager.unregisterDropTarget(this);
Custom.UI.ColorDropTargetBehavior.callBaseMethod(this, ‘dispose’);
}
}
Custom.UI.ColorDropTargetBehavior.registerClass(‘Custom.UI.ColorDropTargetBehavior’,
Sys.UI.Behavior, Sys.Preview.UI.IDropTarget);();

The final task is to create a ColorDropTargetBehavior and attach it to a DOM element. In my example, the drop target is a DIV whose background color can be set by dropping a color swatch into it:

var target = new Custom.UI.ColorDropTargetBehavior ($get(‘DropTarget’));
target.initialize();

The end result is a compelling demo in which users can drag color swatches around the page and change the background color of drop targets with simple drag-drop operations. It still feels weird to me to be deriving classes and implementing interfaces in JavaScript, but you play the hand you’re dealt. I frequently tell audiences that the Microsoft AJAX Library has lots of features that aren’t exposed in the ASP.NET AJAX Extensions and that to get the most out of ASP.NET AJAX, you have to understand what’s on the client side and be willing to program it directly. This is a sterling example of the kind of features you can implement by going under the hood and familiarizing yourself with the client-side framework.

 

Stay Informed

Sign up for the latest blogs, events, and insights.

We deliver solutions that accelerate the value of Azure.
Ready to experience the full power of Microsoft Azure?

Atmosera is thrilled to announce that we have been named GitHub AI Partner of the Year.

X