ASP.NET Ajax Extender for multi-column widget drag & drop

I have discontinued this Ajax Extender. I now use jQuery plugins to offer drag & drop, which is much faster, smaller and easier than ASP.NET AJAX stuffs. I suggest you see the latest implementation of it from:

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

My open source Ajax Start Page “http://dropthings.omaralzabir.com/”>dropthings.omaralzabir.com has an ASP.NET

Ajax Extender which provides multi-column drag & drop for
widgets. It allows reordering of widgets on the same column and
also drag & drop between column. It also supports client side
notification so that you can call web service and store the
position of the widgets behind the scene without (async)
postback.

“http://omar.mvps.org/images/ASP.NETAjaxExtenderformulticolumnwidgetd_12D5/image01.png”
width=”574″ alt=””>

I first thought of going for a plain vanilla Javascript based
solution for drag & drop. It requires less code, less
architectural complexity and provides better speed. Another reason
was the high learning curve for making Extenders in proper way in
Asp.net Ajax given that there’s hardly any documentation
available on the web (during the time of writing this blog).
However, writing a proper extender which pushes Asp.net Ajax to the
limit is a very good way to learn under-the-hood secrets of Asp.net
Ajax framework itself. So, the two extenders I will introduce here
will tell you almost everything you need to know about Asp.net Ajax
Extenders.

Ajax Control Toolkit comes with a DragPanel
extender which I could use to provide drag & drop support to
panels. It also has a ReorderList control which I could use to
provide reordering of items in a single list. Widgets are basically
panels that flow vertically in each column. So, it might be
possible that I could create reorder list in each column and use
the DragPanel to drag the widgets. But I could not use
ReorderList because:

  • ReorderList strictly uses Html Table to render its
    items in a column. I have no table inside the columns. Only one
    Panel is there inside an UpdatePanel.
  • ReorderList takes a Drag Handle template and
    creates a drag handle for each item at runtime. I already have drag
    handle created inside a Widget which is the widget header. So, I
    cannot allow ReorderList to create another drag
    handle.
  • I need client side callback on drag & drop so that I can
    make Ajax calls and persist the widget positions. The callback must
    give me the Panel where the widget is dropped, which is dropped and
    at what position.

Next challenge is with the DragPanel extender. The default
implement of Drag & Drop in Ajax Control Toolkit has some
problems:

  • When you start dragging, the item becomes absolutely
    positioned, but when you drop it, it does not become static
    positioned. There’s a small hack needed for restoring the original
    position to “static”.
  • It does not put the dragging item on top of all items. As a
    result, when you start dragging, you see the item being dragged
    below other items which makes the drag get stuck especially when
    there’s an IFRAME.

So, I have made a CustomDragDropExtender and a
CustomFloatingExtender.
CustomDragDropExtender is for the column containers
where widgets are placed. It provides the reordering support. You
can attach this extender to any Panel control.

Here’s how you can attach this extender to any
Panel and make that Panel support drag
& drop of Widgets:

   1:
<
asp:Panel
ID
="LeftPanel"
runat
="server"
class
="widget_holder"
columnNo
="0"
>
   2:
<
div
id
="DropCue1"
class
="widget_dropcue"
>
   3:
 
div
>
   4:
 
asp:Panel
>
   5:
   6:
<
cdd:CustomDragDropExtender
ID
="CustomDragDropExtender1"
   7:
runat
="server"
   8:
TargetControlID
="LeftPanel"
   9:
DragItemClass
="widget"
  10:
DragItemHandleClass
="widget_header"
  11:
DropCueID
="DropCue1"
  12:
OnClientDrop
="onDrop"
/>

offers the
following properties:

  • TargetControlID – ID of the Panel which becomes the Drop
    zone
  • DragItemClass – All child elements inside the Panel
    having this class will become draggable. E.g. Widget DIV has this
    class so that it can become draggable.
  • DragItemHandleClass – Any child element having this class
    inside the draggable elements will become the drag handle for the
    draggable element. E.g. Widget Header area has this class, so it
    acts as the drag handle for the Widget.
  • DropCueID – ID of an element inside the Panel which acts
    as Drop Cue.
  • OnClientDrop – Name of a Javascript function which is
    called when widget is dropped on the Panel.

LeftPanel becomes a widget container which allows widgets to be
dropped on it and reordered. The DragItemClass attribute on the
extender defines the items which can be ordered. This prevents from
non-widget Html Divs from getting ordered. Only the DIVs with the
class “widget” are ordered. Say there are 5 DIVs with the class
named “widget”. It will allow reordering of only these five
divs:

   1:
<
div
id
="LeftPanel"
class
="widget_holder"
>
   2:
<
div
class
="widget"
> ...
 
div
>
   3:
<
div
class
="widget"
> ...
 
div
>
   4:
   5:
<
div
class
="widget"
> ...
 
div
>
   6:
<
div
class
="widget"
> ...
 
div
>
   7:
<
div
class
="widget"
> ...
 
div
>
   8:
   9:
<
div
>This DIV will not move

div
>
  10:
<
div
id
="DropCue1"
class
="widget_dropcue"
>
div
>
  11:

div
>

When a widget is dropped on the panel, the extender fires the
function specified in OnClientDrop. It offers standard
Ajax Events. But unlike basic Ajax events where you have to
programmatically bind to events, you can define property and
specify the function name to call. So, instead of doing this:

   1:
function pageLoad( sender, e ) {
   2:
   3:
var extender1 =
get(‘CustomDragDropExtender1’);
   4:   extender1.add_onDrop( onDrop );
   5:
   6: }

You can do this:

   1:
<
cdd:CustomDragDropExtender
ID
="CustomDragDropExtender1"
   2:
runat
="server"
   3:
OnClientDrop
="onDrop"
/>

When the event is raised, the function named onDrop
gets fired. This is done with the help of some handy library
available in ACT project.

When the event is fired, it sends the container, the widget and
the position of the widget where the widget is dropped.

   1:
function onDrop( sender, e )
   2: {
   3:
var container = e.get_container();
   4:
var item = e.get_droppedItem();
   5:
var position = e.get_position();
   6:
   7:
//alert( String.format( "Container: {0}, Item:
{1}, Position: {2}", container.id, item.id, position ) );
   8:
   9:
var instanceId =
parseInt(item.getAttribute(
"InstanceId"));
  10:
var columnNo =
parseInt(container.getAttribute(
"columnNo"));
  11:
var row = position;
  12:
  13:
WidgetService.MoveWidgetInstance( instanceId, columnNo, row );
  14: }

The widget location is updated by calling the
WidgetService.MoveWidgetInstance.

CustomDragDropExtender has 3 files:

  • CustomDragDropExtender.cs – The server side extender
    implementation
  • CustomDragDropDesigner.cs – Designer class for the
    extender
  • CustomDragDropExtender.js – Client side scriptfor the
    extender

Server side class CustomDragDropExtender.cs has the following
code:

   1: [assembly:
System.Web.UI.WebResource(
"CustomDragDrop.CustomDragDropBehavior.js",

"text/javascript")]
   2:
   3:
namespace CustomDragDrop
   4: {
   5:     [Designer(
typeof(CustomDragDropDesigner))]
   6:     [ClientScriptResource(
"CustomDragDrop.CustomDragDropBehavior",
"CustomDragDrop.CustomDragDropBehavior.js")]
   7:     [TargetControlType(
typeof(WebControl))]
   8:     [RequiredScript(
typeof(CustomFloatingBehaviorScript))]
   9:     [RequiredScript(
typeof(DragDropScripts))]
  10:
public
class CustomDragDropExtender :
ExtenderControlBase
  11:     {
  12:
// TODO: Add your property accessors here.
  13:
//
  14:         [ExtenderControlProperty]
  15:
public
string DragItemClass
  16:         {
  17:             get
  18:             {
  19:
return GetPropertyValue(
"DragItemClass",
string.Empty);
  20:             }
  21:             set
  22:             {
  23:
SetPropertyValue(
"DragItemClass",
value);
  24:             }
  25:         }
  26:
  27:         [ExtenderControlProperty]
  28:
public
string DragItemHandleClass
  29:         {
  30:             get
  31:             {
  32:
return GetPropertyValue(
"DragItemHandleClass",
string.Empty);
  33:             }
  34:             set
  35:             {
  36:
SetPropertyValue(
"DragItemHandleClass",
value);
  37:             }
  38:         }
  39:
  40:         [ExtenderControlProperty]
  41:         [IDReferenceProperty(
typeof(WebControl))]
  42:
public
string DropCueID
  43:         {
  44:             get
  45:             {
  46:
return GetPropertyValue(
"DropCueID",
string.Empty);
  47:             }
  48:             set
  49:             {
  50:
SetPropertyValue(
"DropCueID",
value);
  51:             }
  52:         }
  53:
  54:         [ExtenderControlProperty()]
  55:         [DefaultValue(
"")]
  56:         [ClientPropertyName(
"onDrop")]
  57:
public
string OnClientDrop
  58:         {
  59:             get
  60:             {
  61:
return GetPropertyValue(
"OnClientDrop",
string.Empty);
  62:             }
  63:             set
  64:             {
  65:
SetPropertyValue(
"OnClientDrop",
value);
  66:             }
  67:         }
  68:
  69:     }
  70: }

Most of the code in the extender defines the property. The
important part is the declaration of the class:

   1: [assembly:
System.Web.UI.WebResource(
"CustomDragDrop.CustomDragDropBehavior.js",

"text/javascript")]
   2:
   3:
namespace CustomDragDrop
   4: {
   5:     [Designer(
typeof(CustomDragDropDesigner))]
   6:     [ClientScriptResource(
"CustomDragDrop.CustomDragDropBehavior",
"CustomDragDrop.CustomDragDropBehavior.js")]
   7:     [TargetControlType(
typeof(WebControl))]
   8:     [RequiredScript(
typeof(CustomFloatingBehaviorScript))]
   9:     [RequiredScript(
typeof(DragDropScripts))]
  10:
public
class CustomDragDropExtender :
ExtenderControlBase
  11:     {

The extender class inherits from
ExtenderControlBase defined in ACT project. This base
class has additional features over Ajax runtime provided Extender
base class. It allows you to define RequiredScript
attribute, which makes sure all the required scripts are downloaded
before the extender script is downloaded and initialized. This
extender has dependency over another extender named
CustomFloatingBehavior. It also depends on ACT’s
DragDropManager. So, the RequiredScript
attribute makes sure those are downloaded before this
extender’s script is downloaded. The
ExtenderControlBase is a pretty big class and does a
lot of work for us. It contains default implementations for
discovering all the script files for the extender and rendering
them properly.

The [assembly:System.Web.UI.WebResource] attribute
defines the script file containing the script for extender. The
script file is an embedded resource file.

[ClientScriptResource] attribute defines the
scripts required for the extender. This class is also defined in
ACT. ExtenderControlBase uses this attribute to find out which .js
files are working for the extender and renders them properly.

The challenge is to make the client side javascript for the
extender. On the js file, there’s a Javascript pseudo
class:

   1: Type.registerNamespace(
'CustomDragDrop');
   2:
   3:
CustomDragDrop.CustomDragDropBehavior =
function(element) {
   4:
   5:
CustomDragDrop.CustomDragDropBehavior.initializeBase(
this, [element]);
   6:
   7:
this._DragItemClassValue =
null;
   8:
this._DragItemHandleClassValue =
null;
   9:
this._DropCueIDValue =
null;
  10:
this._dropCue =
null;
  11:
this._floatingBehaviors = [];
  12: }

During initialize, it hooks on the Panel it is attached to and
the drop cue to show while drag & drop is going on over the
Panel:

   1:
CustomDragDrop.CustomDragDropBehavior.prototype = {
   2:
   3:     initialize :
function() {
   4:
// Register ourselves as a drop target.
   5:
AjaxControlToolkit.DragDropManager.registerDropTarget(
this);
   6:
//Sys.Preview.UI.DragDropManager.registerDropTarget(this);
   7:
   8:
// Initialize drag behavior after a while
   9:         window.setTimeout(
Function.createDelegate(
this,
this._initializeDraggableItems ), 3000 );
  10:
  11:
this._dropCue = get(
this.get_DropCueID());
  12:     },

After initializing the DragDropManager and marking the Panel as
a drop target, it starts a timer to discover the dragable items
inside the panel and create Floating behavior for them. Floating
behavior is the one which makes a DIV draggable.

FloatingBehavior makes one DIV freely draggable on the page. But
it does not offer drop functionality. DragDropBehavior offers the
drop functionality which allows a freely moving DIV to rest on a
fixed position.

Discovering and initializing floating behavior for the dragable
items is the challenging work:

   1:
// Find all items with the drag item class and
make each item
   2:
// draggable        
   3: _initializeDraggableItems :
function()
   4: {
   5:
this._clearFloatingBehaviors();
   6:
   7:
var el =
this.get_element();
   8:
   9:
var child = el.firstChild;
  10:
while( child !=
null )
  11:     {
  12:
if( child.className ==
this._DragItemClassValue && child
!=
this._dropCue)
  13:         {
  14:
var handle =
this._findChildByClass(child,
this._DragItemHandleClassValue);
  15:
if( handle )
  16:             {
  17:
var handleId = handle.id;
  18:
var behaviorId = child.id +
"_WidgetFloatingBehavior";
  19:
  20:
// make the item draggable by adding floating
behaviour to it                    
  21:
var floatingBehavior =
create(CustomDragDrop.CustomFloatingBehavior,
  22:                         {
"DragHandleID":handleId,
"id":behaviorId,
"name": behaviorId}, {}, {}, child);
  23:
  24:                 Array.add(
this._floatingBehaviors, floatingBehavior
);
  25:             }
  26:         }
  27:         child = child.nextSibling;
  28:     }
  29: },

Here’s the algorithm:

  • Run through all immediate child elements of the control where
    the extender is attached to
  • If the child item has the class for draggable item, then:
    • Find any element under the child item which has the class for
      Drag handle
    • If such item found, then attach a CustomFloatingBehavior with
      the child item

The _findChildByClass function recursively iterates
through all the child elements and looks for an element which has
the defined class. It’s an expensive function. So, it is
important that the drag handle is very close to the dragable
element.

   1: _findChildByClass :
function(item, className)
   2: {
   3:
// First check all immediate child items
   4:
var child = item.firstChild;
   5:
while( child !=
null )
   6:     {
   7:
if( child.className == className )
return child;
   8:         child = child.nextSibling;
   9:     }
  10:
  11:
// Not found, recursively check all child
items
  12:     child = item.firstChild;
  13:
while( child !=
null )
  14:     {
  15:
var found =
this._findChildByClass( child, className
);
  16:
if( found !=
null )
return found;
  17:         child = child.nextSibling;
  18:     }
  19: },

When user drags an item over the Panel where the extender is
attached to, DragDropManager fires the following
events:

   1: onDragEnterTarget :
function(dragMode, type, data) {
   2:
this._showDropCue(data);
   3: },
   4:
   5: onDragLeaveTarget :
function(dragMode, type, data) {
   6:
this._hideDropCue(data);
   7: },
   8:
   9: onDragInTarget :
function(dragMode, type, data) {
  10:
this._repositionDropCue(data);
  11: },

Here we deal with the drop cue. The challenging work is to find
out the right position for the drop cue.

“http://omar.mvps.org/images/ASP.NETAjaxExtenderformulticolumnwidgetd_12D5/image08.png”
width=”524″ alt=””>

We need to find out where we should show the drop cue based on
where user is dragging the item. The idea is to find out the widget
which is immediately below the dragged item. The item is pushed
down by one position and the drop cue takes its place. While
dragging, the position of the drag item can be found easily. Based
on that, I locate the widget below the drag item:

   1: _findItemAt :
function(x,y, item)
   2:     {
   3:
var el =
this.get_element();
   4:
   5:
var child = el.firstChild;
   6:
while( child !=
null )
   7:         {
   8:
if( child.className ==
this._DragItemClassValue && child
!=
this._dropCue && child != item )
   9:             {
  10:
var pos =
Sys.UI.DomElement.getLocation(child);
  11:
  12:
if( y <= pos.y )
  13:                 {
  14:
return child;
  15:                 }
  16:             }
  17:             child =
child.nextSibling;
  18:         }
  19:
  20:
return
null;
  21:     },

This function returns the widget which is immediately under the
dragged item. Now I add the drop cue immediately above the
widget:

   1: _repositionDropCue :
function(data)
   2:     {
   3:
var location =
Sys.UI.DomElement.getLocation(data.item);
   4:
var nearestChild =
this._findItemAt(location.x, location.y,
data.item);
   5:
   6:
var el =
this.get_element();
   7:
   8:
if(
null == nearestChild )
   9:         {
  10:
if( el.lastChild !=
this._dropCue )
  11:             {
  12:                 el.removeChild(
this._dropCue);
  13:                 el.appendChild(
this._dropCue);
  14:             }
  15:         }
  16:
else
  17:         {
  18:
if( nearestChild.previousSibling !=
this._dropCue )
  19:             {
  20:                 el.removeChild(
this._dropCue);
  21:                 el.insertBefore(
this._dropCue, nearestChild);
  22:             }
  23:         }
  24:     },

One exception to consider here is that there can be no widget
immediately below the dragged item. It happens when user is trying
to drop the widget at the bottom of column. In that case, the drop
cue is shown at the bottom of the column.

When user releases the widget, it drops right on top of drop cue
and the drop cue disappears. After the drop the onDrop
event is raised to notify where the widget is dropped.

   1: _placeItem :
function(data)
   2:     {
   3:
var el =
this.get_element();
   4:
   5:
data.item.parentNode.removeChild( data.item );
   6:         el.insertBefore( data.item,

this._dropCue );
   7:
   8:
// Find the position of the dropped item
   9:
var position = 0;
  10:
var item = el.firstChild;
  11:
while( item != data.item )
  12:         {
  13:
if( item.className ==
this._DragItemClassValue ) position++;
  14:             item =
item.nextSibling;
  15:         }
  16:
this._raiseDropEvent(
/* Container */ el,
/* droped item */ data.item,
/* position */ position );
  17:     }
Generally you can make events in extenders by adding two
functions in the extender:
   1: add_onDrop :
function(handler) {
   2:
this.get_events().addHandler(
"onDrop", handler);
   3: },
   4:
   5: remove_onDrop :
function(handler) {
   6:
this.get_events().removeHandler(
"onDrop", handler);
   7: },
But this does not give you the support for defining the event
listener name in the ASP.NET declaration:
   1:
<
cdd:CustomDragDropExtender
ID
="CustomDragDropExtender1"
   2:
runat
="server"
   3:
OnClientDrop
="onDrop"
/>

The declaration only allows properties. In order to support such
declarative assignment of events, we need to first introduce a
property named OnClientDrop in the extender. Then
during assignment of the property, we need to find out the function
specified there and attach event notification on that function. The
discovery of the function from its name is done by
CommonToolkitScripts.resolveFunction which is
available in ACT project.

   1:
// onDrop property maps to onDrop event
   2:     get_onDrop :
function() {
   3:
return
this.get_events().getHandler(
"onDrop");
   4:     },
   5:
   6:     set_onDrop :
function(value) {
   7:
if (value && (0 <
value.length)) {
   8:
var func =
CommonToolkitScripts.resolveFunction(value);
   9:
if (func) {
  10:
this.add_onDrop(func);
  11:             }
else {
  12:
throw Error.argumentType(
'value',
typeof(value),
'Function',
'resize handler not a function, function name, or
function text.');
  13:             }
  14:         }
  15:     },
Raising the event is same as basic Ajax events:
   1: _raiseEvent :
function( eventName, eventArgs ) {
   2:
var handler =
this.get_events().getHandler(eventName);
   3:
if( handler ) {
   4:
if( !eventArgs ) eventArgs =
Sys.EventArgs.Empty;
   5:             handler(
this, eventArgs);
   6:         }
   7:     },
This is all about the CustomDragDropExtender.
Next challenge is to make the CustomFloatingBehavior.
The server side class is declared as:
   1: [assembly:
System.Web.UI.WebResource(
"CustomDragDrop.CustomFloatingBehavior.js",

"text/javascript")]
   2:
   3:
namespace CustomDragDrop
   4: {
   5:     [Designer(
typeof(CustomFloatingBehaviorDesigner))]
   6:     [ClientScriptResource(
"CustomDragDrop.CustomFloatingBehavior",
"CustomDragDrop.CustomFloatingBehavior.js")]
   7:     [TargetControlType(
typeof(WebControl))]
   8:     [RequiredScript(
typeof(DragDropScripts))]
   9:
public
class CustomFloatingBehaviorExtender :
ExtenderControlBase
  10:     {
  11:         [ExtenderControlProperty]
  12:         [IDReferenceProperty(
typeof(WebControl))]
  13:
public
string DragHandleID
  14:         {
  15:             get
  16:             {
  17:
return GetPropertyValue(
"DragHandleID",
string.Empty);
  18:             }
  19:             set
  20:             {
  21:
SetPropertyValue(
"DragHandleID",
value);
  22:             }
  23:         }
  24:     }
  25: }

There’s only one property –
DragHandleID. Widget’s header works as the drag
handle. So, the header ID is specified here.

This extender has dependency on DragDropManager so
the [RequiredScript(typeof(DragDropScripts))]
attribute is there.

Besides the designer class, there’s one more class which
CustomDragDropExtender need in order to specify its
dependency over this floating behavior:

   1: [ClientScriptResource(
null,
"CustomDragDrop.CustomFloatingBehavior.js")]
   2:
public
static
class CustomFloatingBehaviorScript
   3:   {
   4:   }

This class can be used inside RequiredScript
attribute. It only defines which script file contains the client
side code for the extender.

The client side Javascript is same as
FloatingBehavior that comes with ACT. The only
difference is some hack when drag starts.
DragDropManager does not return the item being dragged
to static position once it makes it absolute. It also does not
increase the zIndex of the item. If the drag item does
not become the top most item, while dragging it goes below other
elements on the page. So, I have made some changes in the
mouseDownHandler of the behavior to add these features:

   1:
function mouseDownHandler(ev) {
   2:     window._event = ev;
   3:
var el =
this.get_element();
   4:
   5:
if (!
this.checkCanDrag(ev.target))
return;
   6:
   7:
// Get the location before making the element
absolute
   8:     _location =
Sys.UI.DomElement.getLocation(el);
   9:
  10:
// Make the element absolute 
  11:     el.style.width = el.offsetWidth
+
"px";
  12:     el.style.height =
el.offsetHeight +
"px";
  13:
Sys.UI.DomElement.setLocation(el, _location.x, _location.y);
  14:
  15:     _dragStartLocation =
Sys.UI.DomElement.getLocation(el);
  16:
  17:     ev.preventDefault();
  18:
  19:
this.startDragDrop(el);
  20:
  21:
// Hack for restoring position to static
  22:     el.originalPosition =
"static";
  23:     el.originalZIndex =
el.style.zIndex;
  24:     el.style.zIndex =
"60000";
  25: }

Setting el.originalPosition = static fixes the bug
in DragDropManager. It incorrectly stores absolute has
the originalPosition when startDragDrop
is called. So, after calling this function, I set it to correct
originalPosition which is “static”.

When drag completes, the original zIndex is restored and left,
top, width and height is cleared. DragDropManager makes the item
position static, but it does not clear the left, top, width and
height attributes. This moves the element away from the place where
it is dropped. This bug is fixed in the onDragEnd event:

   1:
this.onDragEnd =
function(canceled) {
   2:
if (!canceled) {
   3:
var handler =
this.get_events().getHandler(
'move');
   4:
if(handler) {
   5:
var cancelArgs =
new Sys.CancelEventArgs();
   6:             handler(
this, cancelArgs);
   7:             canceled =
cancelArgs.get_cancel();
   8:         }
   9:     }
  10:
  11:
var el =
this.get_element();
  12:     el.style.width =
el.style.height = el.style.left = el.style.top =
"";
  13:     el.style.zIndex =
el.originalZIndex;
  14: }

That’s all folks!

You can get the “http://omar.mvps.org/images/CustomDragDrop.zip”>code for the
extenders here.

27 thoughts on “ASP.NET Ajax Extender for multi-column widget drag & drop”

  1. Pardon me Omar. I'm a novice asp.net developer.

    In your example, WidgetService.MoveWidgetInstance is being called. Do I have to create this service myself? How?

    I don't mean to sound unappreciative but normally, when I find an extender, I add it on my toolbox, drag it to my form, set its properties and it works. As a user of your extender, do we need anything else (e.g. OnDrop) to make it to work?

    I appreicate your help. I think this is great. I just need to learn exactly how to set it up.

  2. Hi omar,

    I downloaded the source code.. but i unable to run that.. the file doesnt contain any aspx page.. have only cs file js file..i am using asp.net ajax 3.5.. can u plz help me… plz send me the working example to my mail id.. it is vidhya@ilink-systems.com

  3. vidhya: firstly you should learn to read the tutorial first, secondly if you had some idea about coding you'd already have worked out it appears to be a class/extender. so there wont be an aspx page. Dude it's constructive critasizm, you'll figure this all out after a few good asp2/ajax books try some “Sams” or “O'rilley” titles!

    Omar: keep up the good works man i'm a big fan of your stuff, its defiantely not for Noobs as i had asked you a similar question as vidhya about 1 year ago and didnt even get a response !! *frowns* — feed your followers man we make who you are!

    Cheers

  4. Hi Christian,

    this is an advanced topic, so a bit hard to grasp. If you have some specific areas that you don't understand, let me know. I will try to clear it up.

    Sorry Vidhya for not getting back to you earlier. I don't always get notification on comment posts.

  5. Let me begin by saying that i love your blog msmvps.com a lot

    now.. back to the post haha

    I cant say that im 100% with what you typed up… care to clear things up for me?

  6. Hi Omar,

    Thank you for your post. I am trying to get the CustomDragDrop extender to work, but I must be missing something.

    Where do I put the CS and JS files that are included in the source code? Do I have to compile them? If so, do I put the DLL in Bin when done?

  7. OK, so I downloaded your code and compiled it (after remarking out the following line from CustomDragDropExtender.cs – [RequiredScript(typeof(FloatingBehaviorScript))], as this line was causing a compilation error) with version 1.0.20229.20821 of the AjaxControlToolkit. I used example aspx page from Mike Puddephat and also included the 2 .js files in it. I have referenced the compiled .dll in my project. This is what I have codewise for my aspx page:

    < %@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>

    < %@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %>

    < %@ Register Assembly="CustomDragDrop" Namespace="CustomDragDrop" TagPrefix="cdd" %>

    < !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "“>http://www.w3.org/…/xhtml11.dtd“>

    “>http://www.w3.org/…/xhtml“>

    ASP.NET AJAX Extender for multicolumn drag & drop

    .rightcol { width: 30%; float: left; padding: 0.5em; border: 1px solid black; margin: 0.5em; }

    .leftcol { width: 30%; float: left; padding: 0.5em; border: 1px solid black; margin: 0.5em; }

    .widget_header { width: 100%; padding-top: 0.1em; padding-bottom: 0.1em; margin: 0em; font-size: 100%; font-weight: normal; cursor: move; background-color: #f0f0f0; }

    .widget { position: relative; left: 0px; top: 0px; margin-bottom: 1em; background-color: white; }

    .widget_dropcue { border: dashed 1px gray; display:none; margin-bottom: 1em; }

    function onDrop(sender, e)

    {

    var container = e.get_container();

    var item = e.get_droppedItem();

    var position = e.get_position();

    var columnNo = parseInt(container.getAttribute("columnNo"));

    alert( String.format( "Container: {0}, Item: {1}, Position: {2}, Column: {3}", container.id, item.id, position, columnNo ) );

    }

    LoadScriptsBeforeUI="false" ScriptMode="Release" />

    Widget Header 1

    Widget Header 2

    DragItemClass="widget" DragItemHandleClass="widget_header" DropCueID="drop_cue_left"

    OnClientDrop="onDrop" />

    Widget Header 3

    Widget Header 4

    DragItemClass="widget" DragItemHandleClass="widget_header" DropCueID="drop_cue_right"

    OnClientDrop="onDrop" />

    As you can see I have added the 'dummy' button and confirm button extender (which functions properly) that should activate the AjaxControlToolkit as suggested in one of your responses. However, it is still not working. I am using MS VS Pro 2005. The page loads and I get the 'crosshairs' indicating that I should be able to move the 'widget' but all that happens is the text highlights. Nothing moves, no dropcue appears. I get the following 2 errors on the page:

    1) Line: 5

    Char: 1

    Error: 'Type' is undefined

    Code: 0

    URL: localhost/.../Default.aspx

    2) Line: 258

    Char: 20

    Error: 'onDrop' is undefined

    Code: 0

    URL: localhost/.../Default.aspx

    Any thoughts as to what I might be doing wrong?

    Thanx in advance!

    Ted

  8. I found my error. I had put the 'onDrop' function in the body and not the head of the page. All is working now – but it does require the 'dummy' button and confirmbuttonextender to run.

    Ted

  9. Omar,

    How do you keep the new placement of the widgets on page refresh? Whenever I refresh/postback the widgets return to their starting positions.

    Thanx in advance,

    Ted

  10. Hi Omar

    I m not able to use this extender.

    I m getting javascript error “CustomDragDrop” is undefined.

    What to do?

    Plz Help it is very urgent………

  11. Hi everyone, before i ask my question, i would like to say that this work is totally impressive so congratulations !

    Configuration : .Net 2.0 – VS2005

    My problem is that i have two simple zones where there is 2 items(widgets) in each.

    I would like to had a button on each item to simple remove them from the dragable zone.

    The buttons i would like to affect an action are named “BtnDelete..”. How can i code a function to remove them ?

    Here is my code :

    —————— CODE ——————-

    < %@ Page Language="C#" AutoEventWireup="true" CodeFile="Second.aspx.cs" Inherits="Second" %>

    < !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "“>http://www.w3.org/…/xhtml11.dtd“>

    “>http://www.w3.org/…/xhtml“>

    Drag and Drop Test

    .rightcol { width: 300px; float: left; padding: 0.5em; border: 1px solid black; margin: 0.5em; }

    .leftcol { width: 300px; float: left; padding: 0.5em; border: 1px solid black; margin: 0.5em; }

    .widget_header { width:auto; padding-top: 0.1em; padding-bottom: 0.1em; margin: 0em; font-size: small; cursor: move; background-color: #f0f0f0; font-family:Arial;}

    .widget { position: relative; left: 0px; top: 0px; margin-bottom: 0.4em; background-color: white; }

    .widget_dropcue { border: dashed 1px red; display:none; margin-bottom: 1em; }

    LoadScriptsBeforeUI=”false” ScriptMode=”Release” />

    Widget Header 1

    Widget Header 2

    DragItemClass=”widget” DragItemHandleClass=”widget_header” DropCueID=”drop_cue_left”

    OnClientDrop=”onDrop” />

    Widget Header 3

    Widget Header 4

    DragItemClass=”widget” DragItemHandleClass=”widget_header” DropCueID=”drop_cue_right”

    OnClientDrop=”onDrop” />

    function onDrop(sender,e)

    {

    var container = e.get_container();

    var item = e.get_droppedItem();

    var position = e.get_position();

    var instanceId = parseInt(item.getAttribute("InstanceId"));

    var columnNo = parseInt(container.getAttribute("columnNo"));

    var row = position;

    try

    {

    WidgetService.MoveWidgetInstance(instanceId, columnNo,row );

    }

    catch(e)

    {

    }

    }

    —————— CODE ——————-

    Thanks you for your help 🙂

  12. Following line from CustomDragDropExtender.cs is giving compiler error. Is it safe to remove this line?

    [RequiredScript(typeof(FloatingBehaviorScript))]

    Error 2 The type or namespace name 'FloatingBehaviorScript' could not be found (are you missing a using directive or an assembly reference?) C:NithiGurusamyProjectsAdvisoryConsoleCustomDragDropCustomDragDropExtender.cs 21 25 CustomDragDrop

  13. Hi Omar,

    Thank you for your post. I am trying to get the CustomDragDrop extender to work, but I must be missing something.

    Where do I put the CS and JS files that are included in the source code? Do I have to compile them? If so, do I put the DLL in Bin when done?

    I m getting javascript error “CustomDragDrop” is undefined.

    What to do?

    Plz Help it is very urgent………

  14. I am using the Customdragdrop extender it works fine with firefox ..but in ms internet explorer I am not able to select the header of widget properly, the selected widget goes to upperside and drops into another div also it is moving anywhere on my dashboard page … Please help me to sole it.

  15. Hi, i'm trying to use this drag drop extender, but i'm rendering mi panels in a listview, Extender for multi-column widget drag & drop works in a listview????? plz help

  16. when i use Extender for multi-column widget drag & drop in a list i get this:

    The TargetControlID of 'CustomDragDropExtender1' is not valid. A control with ID 'thePanelID' could not be found.

  17. i'm getting: Error Creating Control – CustomDragDropExtender2Unable to cast object of type 'Netco.CustomExtenders.CustomDragDrop.CustomDragDropDesigner' to type 'System.ComponentModel.Design.IDesigner'.

    What i'm missing???

  18. Hi,

    Can you let me know how to use WidgetService.MoveWidgetInstance( instanceId, columnNo, row ); from aspx page i have created a .ascx file with name WidgetService where this function is define.

  19. Hello,
    I read your book, and decided to implement the drag and drop portion of it into my CMS. In firefox, everything works great. And I get no javascript errors in firebug.

    In IE7 I can make the first drag and drop, but when I try to do a second I get the error.
    this._activeDragVisual is null or not an object.

    I have spent literally days trying to figure this out, and have tried the various fixes suggested online with no luck. No matter what I try, I get only one drop in IE7 and the second one causes an error. If you know of a fix for this, it would be greatly appreciated.

  20. Although that is a solution that I can learn from, this CMS is already built and it’s huge. I’m simply trying to fix one small issue. That would be a lot of work to overhaul the entire thing. Is there no way to fix that issue?

  21. Hey ,

    Well i am a Student of Computer science, and i am gonna make widget for the first time.

    so i just want to know how to make a widget drag and drop without postback .

    please let me know as soon as possible.

    thanks in advance

Leave a Reply