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

My open source Ajax Start Page www.dropthings.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.

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" 
/>

<cdd:CustomDragDropExtender> 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<String>(
"DragItemClass", 
string.Empty);
  20:             }
  21:             set
  22:             {
  23:                
SetPropertyValue<String>(
"DragItemClass", 
value);
  24:             }
  25:         }
  26:  
  27:         [ExtenderControlProperty]
  28:         
public 
string DragItemHandleClass
  29:         {
  30:             get
  31:             {
  32:                 
return GetPropertyValue<String>(
"DragItemHandleClass", 
string.Empty);
  33:             }
  34:             set
  35:             {
  36:                
SetPropertyValue<String>(
"DragItemHandleClass", 
value);
  37:             }
  38:         }
  39:  
  40:         [ExtenderControlProperty]
  41:         [IDReferenceProperty(
typeof(WebControl))]
  42:         
public 
string DropCueID
  43:         {
  44:             get
  45:             {
  46:                 
return GetPropertyValue<String>(
"DropCueID", 
string.Empty);
  47:             }
  48:             set
  49:             {
  50:                
SetPropertyValue<String>(
"DropCueID", 
value);
  51:             }
  52:         }
  53:  
  54:         [ExtenderControlProperty()]
  55:         [DefaultValue(
"")]
  56:         [ClientPropertyName(
"onDrop")]
  57:         
public 
string OnClientDrop
  58:         {
  59:             get
  60:             {
  61:                 
return GetPropertyValue<String>(
"OnClientDrop", 
string.Empty);
  62:             }
  63:             set
  64:             {
  65:                
SetPropertyValue<String>(
"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.

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<String>(
"DragHandleID", 
string.Empty);
  18:             }
  19:             set
  20:             {
  21:                
SetPropertyValue<String>(
"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 code for the extenders here.

Published Thursday, March 22, 2007 9:03 PM by omar
Filed under:

Comments

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

Friday, March 23, 2007 3:58 AM by Boris

Hi Omar,

excellent post!

Thank you very much!

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

Friday, March 23, 2007 8:50 AM by Geoff

Hey Omar,

 Long time fan and user of page flakes. I am curious when you first started writing your widgets did you look at web parts and why did you decide to not use them? Thanks,

     G

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

Saturday, March 24, 2007 12:10 AM by omar

I looked at Web parts end of 2005. As far as I remember, it was quite inflexible and I was not able to make a quick prototype of Pageflakes out of it easily. Moreover, we require 95% of the features to be on client side with Javascript. So, we need a framework which facilitates that. WebParts only gives a very small part of it like Drag & Drop. I also did not find way to get notification of drag & drop on client side and instead of a postback, how to call web service to transmit the data. Besides the multipage feature at the client side is something not supported in Web parts.

In a summary, Pageflakes requires too much facility on the client side which Web parts don't offer. Although web parts is a very nice framework for most of the common need to personalization UI, but not for Ajax Start Pages which are extreme implementation of Rich Internet Application.

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

Friday, March 30, 2007 4:19 AM by alexeypajitnov@hotmail.com

the drag and drog doesn't work with ASP.NET AJAX V1.0 and AjaxToolkit 1.0.10301.0?

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

Saturday, March 31, 2007 11:20 AM by Andy

Please answer the previous question- I followed the instructions as closely as I could, in AJAX v1.0, and my panels are not being rearranged. I get an odd behavior (but some behavior) with the floating panel extender, but at least it does something. controls inside of a control extended with the CustomDragDropExtender seem to exhibit no behavior at all.

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

Monday, April 02, 2007 10:40 PM by marshallbu

I was able to get this example working with latest AJAX v1.0 and the latest toolkit.  One problem I was experiencing though, is an infinite javascript loop when I drop a "widget" outside of either column.  It seems to be happening in the "getScrollOffset" function somewhere in the toolkit.

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

Wednesday, April 11, 2007 9:30 PM by Luke

Hi Omar - once again, fantastic post. I am getting an error when trying to add CustomFloatingBehavior to each of the widgets in the _initializeDraggableItems function.

It's causing an exception which states that 'type' (ie CustomDragDrop.CustomFloatingBehavior) does not derive from Sys.Component.

Just not sure why it would be doing this - is this an indication that it just can find the script file or could it be something else? The script file is in the same class lib as CustomDragDropBehavior.js and it makes it into this fine.

Any ideas?

Cheers

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

Wednesday, April 11, 2007 9:56 PM by Luke

By the way. add a FloatingBehavior extender by itself does not produce the error, only when you attempt to add floatingbehavior to each widget via the customdragdrop script does it fail.

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

Sunday, April 29, 2007 8:02 AM by AJ

Hi Omar,

Would just like to commend you on your post here and your tutorial on codeproject.com.  I find your posts extremely informative and am definately going to bookmark your blog and check up on it from time to time.

A question I have for you though is whether or not all these Microsoft tools are worth it.  As I'm sure you've seen, these tools are often times buggy or close to what you want, but unable to do what you exactly want.  This results in countless hours of hacky fixes or overriding of code.  Is it really worth it when you can just find open source tools that's faster and works just as well?

The only problem I see with using open source tools is that sometimes they have problems adapting to ie, which microsoft tools program for.  I really get torn on this issue.  I can't tell you how many times I've used microsoft tools only having regretted getting so far into it and finding out that some controls just don't work well with other controls.

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

Monday, April 30, 2007 2:52 PM by Raj

Hello Omar,

I am getting the follwing runtime error when using the CustomDragDropExtender in CustomFloatingBehavior.js file.

Can you give me any pointers on where the problem could.

Error: 'AjaxControlToolkit undefined'

location:

At

CustomDragDrop.CustomFloatingBehavior.registerClass('CustomDragDrop.CustomFloatingBehavior',

//Sys.UI.Behavior,

//Sys.Preview.UI.IDragSource,

AjaxControlToolkit.BehaviorBase,

AjaxControlToolkit.IDragSource,

Sys.IDisposable);

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

Monday, April 30, 2007 9:02 PM by omar

Look like AjaxControlToolkit is missing. Please download and install latest Ajax Control Toolkit.

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

Tuesday, May 01, 2007 8:39 AM by Raj

Omar,

Thank you for the reply.  Actually the AjaxControltoolkit is present.  I can work with the other extenders that come with ACT, no problem.

Strangely, I can make your extender work if I use Sys.UI and Sys.Preview instead of AjaxControlTookit.*

Any ideas.  It must be something small becasue if I use Sys.* instead of AjaxControlToolkit.* everything stars working.  I know I can go with Sys.* but perfrmance is important for me as you pointed out in the next post.

Thanks in advance

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

Saturday, May 05, 2007 11:35 PM by omar

I found this bug. Seems like it has started recently. The problem is my CustomDragDrop extender uses AjaxControlToolkit. Although AjaxControlToolkit is mentioned as required script, but it is not getting initialized before my extender does. But if you use any of the AjaxControlTookit extenders before my CustomDragDrop Extender gets loaded, it works fine.

One example is put a hidden button the page before any of the declaration of CustomDragDrop extender and attach a ConfirmButtonExtender to it.

<asp:Button ID="dummy" runat="server" />

   <ajaxToolkit:ConfirmButtonExtender TargetControlID="dummy" runat="server" ConfirmText="Are you sure you want to do this?" />

This will force AjaxControlToolkit to load and initialize before my extender does.

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

Saturday, May 05, 2007 11:41 PM by omar

Hi AJ,

The problem you mentioned with components not working on all environments is common to both Microsoft and Open Source components. None of them can develop components which serve all specific needs of everyone. They target solving 70-90% scenario. But there are always exceptional scenario when things don't work out of the box. We cannot expect another company or another developer be able to solve my own problems all the time. I will always have to tweak things and make it work as I want. Good thing about open source is I can do that. But with closed source MS components, I can't do that.

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

Monday, May 14, 2007 10:33 AM by Shahar Nechmad

Hi Omar.

Really great post. Thanks for all your time and work you had to put in order to share that with us.

I'm trying to download your code from the link in the bottom of the article, but the link seems to be broken.

Could you please post it again?

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

Monday, May 21, 2007 7:52 PM by Amit

Hi omar,

Could you please give me an example on how to use all th eabove cool stuff in asps page?

Thanks

Amit

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

Monday, May 21, 2007 11:21 PM by omar

Hi Amit,

The beginning of the post shows how to put this extender on an ASPX page and make DIVs drag and drop enabled.

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

Tuesday, May 22, 2007 7:30 PM by Abram

Any chance a sample Database can come with the code. I might be missing something, is the DB generated if not available.

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

Saturday, May 26, 2007 5:26 AM by Prem

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.........

Thanks

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

Saturday, May 26, 2007 7:26 AM by Christian

Hi Omar,

great post. So i want to start with it and try to implement, but I'm getting the following javascript error: Sys.ArgumentUndefinedException: Value cannot be undefined. Parameter name: interfaceTypes[0] Source File: localhost/.../ScriptResource.axd

Line: 687

I'm using the AjaxControlToolkit file (1.0.61214) from your download. First I had the same error like Raj, but with your help i could fix it and got now the error above :-/

Christian

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

Saturday, May 26, 2007 7:29 AM by Christian

@Prem

Do you have a link to CustomDragDrop Project in your Webapplication and at the WebForm a linkstatement like these: <%@ Register Assembly="CustomDragDrop" Namespace="CustomDragDrop" TagPrefix="cdd" %>?

Christian

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

Thursday, May 31, 2007 4:27 AM by Raj Nair

Hello Omar,

I'm also getting same error as christain. Any fixes recently?

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

Thursday, May 31, 2007 4:38 AM by Christian

I got it :-). The important thing was that i had forgot options in scriptmanager. First i start with default once:

<asp:ScriptManager ID="ScriptManager1" runat="server" />

But i got an error (previous comment). So debug a lot in javascript, but nothing. So after hours i start to compare the code line by line... And so you have the following options:

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" LoadScriptsBeforeUI="false" ScriptMode="Release" />

After i append these in my code, it runs perfect... ;-)

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

Thursday, May 31, 2007 5:03 AM by raj nair

Hi Christain,

If you dont mind can u upload a small working sample somewhere. Though I'm not getting any errors now, extender is not working as expcted.

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

Thursday, May 31, 2007 7:26 AM by Christian

Hi Raj,

i try to post some code and a link to a zip file (test project), but it doesn't work. So i publish my comment and a link on my own <a href="www.steinisweb.de/.../a>

Hope it helps.

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

Thursday, May 31, 2007 7:37 AM by Christian

Oups, html doesn't work. So here is the link again www.steinisweb.de/.../PermaLink,guid,a15eefc7-27ea-46b3-8541-791ab9483838.aspx

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

Monday, June 11, 2007 2:47 AM by Mike

Anyone else get this to work?  If so can you upload an example, I tried Christian's above but that only seems to have 1 column with 1 widget box and I can't seem to add anymore without having undesired effects.  so anyone else have an example??

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

Wednesday, June 13, 2007 10:09 AM by Daniel

May i suggest a little "correction" in CustomDragDropBehavior.js?

The _showDropCue() function does a weird behaviour with the cue's div height. This another version of the code fix for me that behaviour:

from:

<code>

_showDropCue : function(data)

   {

       this._repositionDropCue(data);

       this._dropCue.style.display = "block";

       this._dropCue.style.visibility = "visible";

       var bounds = Sys.UI.DomElement.getBounds(data.item);

       if( this._dropCue.style.height == "" )

           this._dropCue.style.height = bounds.height.toString() + "px";

   },

</code>

to:

<code>

_showDropCue : function(data)

   {

var bounds = Sys.UI.DomElement.getBounds(data.item);

       this._dropCue.style.height = bounds.height.toString() + "px";

       this._repositionDropCue(data);

       this._dropCue.style.display = "block";

       this._dropCue.style.visibility = "visible";

   },

</code>

I first calculate the cue's div height and assign it (always) before show it.

Daniel.

Thanks for your great piece of code, Omar. I hope MS take it as example.

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

Sunday, June 17, 2007 2:25 PM by Amit

Very good post!!

Need some help from you in customizing this...I want items not to snap to columns and stick to the screen where I drop them. Also to store the location, I want to store the location (x,y,z coordiantes) and not column and order!!

What is the easiest way to achieve this? Please help.

Regards

Amit

# AJAX Fight Night at Web 2.0 &#038; White Wall Wisdom

Tuesday, June 19, 2007 8:10 AM by AJAX Fight Night at Web 2.0 & White Wall Wisdom

Pingback from  AJAX Fight Night at  Web 2.0 &#038; White Wall Wisdom

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

Tuesday, June 26, 2007 6:30 AM by Packman

I am getting  'this._activeDragVisual is null or undefined error'. Could antbody can help me.

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

Friday, June 29, 2007 7:16 AM by stone

Hello Omar,

I have bilud your open source success, but I am getting the follwing error when clicking pagetab linkbutton or new tab linkbutton on my dropthings website

Can you give me any pointers on where the problem could.

Error: "Value of member 'CurrentPageId' of an object of type 'UserSetting' changed,A member defining the identity of the object cannot be changed. Consider adding a new object with new identity and deleting the existing one instead."

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

Friday, June 29, 2007 7:21 AM by stone

Hello Omar,

I have bilud your open source success on Visual Studio Orcas, but I am getting the follwing error when clicking 'My First Page/My 2nd Page' linkbutton or 'new tab' linkbutton on my dropthings website,

Can you give me any help on where the problem could.

Error: "Value of member 'CurrentPageId' of an object of type 'UserSetting' changed,A member defining the identity of the object cannot be changed. Consider adding a new object with new identity and deleting the existing one instead."

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

Saturday, June 30, 2007 9:35 AM by omar

Please turn off CurrentPageId as primary key from database. You will see a composite key in UserSetting.

Will you be interested to share the Orcas version with the community? Please email me the code if you are interested.

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

Saturday, June 30, 2007 2:45 PM by stone

Hi Omar

Thank you for the reply.

I have sent mail to you. attachment is the Orcas version source

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

Saturday, June 30, 2007 2:50 PM by stone

Hi Omar

  Thank you for the reply.I have send the Orcas version Dashboard source to you by email.

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

Sunday, July 01, 2007 12:53 AM by Greg

You have any thoughts on what would be the best way to store a user's settings as they drag and drop widgets on their "page" so that others see the changes when they navigate to that page? (i'm thinking in terms of a very slimmed down portal) -- Would it be writing the coordinates to a database for each widget/div?

thanks,

-g

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

Monday, July 02, 2007 5:08 AM by Packman

I am getting  'this._activeDragVisual is null or not an object' error. Could antbody can help me.

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

Thursday, July 05, 2007 2:27 AM by stone

Hi Omar

  I'm getting the same error,How can I turn off CurrentPageId as primary key from database.

  follwing changes is correct?

"

USE [Dashboard]

GO

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE TABLE [dbo].[UserSetting](

[UserId] [uniqueidentifier] NOT NULL,

[CurrentPageId] [int] NOT NULL,

CONSTRAINT [PK_UserSetting] PRIMARY KEY CLUSTERED

(

[UserId] ASC,

[CurrentPageId] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

) ON [PRIMARY]

GO

ALTER TABLE [dbo].[UserSetting]  WITH CHECK ADD  CONSTRAINT [FK_UserSetting_aspnet_Users] FOREIGN KEY([UserId])

REFERENCES [dbo].[aspnet_Users] ([UserId])

GO

ALTER TABLE [dbo].[UserSetting] CHECK CONSTRAINT [FK_UserSetting_aspnet_Users]"

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

Thursday, July 05, 2007 10:01 AM by Packman

Though i register the custom CustomDragDrop contol in my page i still get the error 'CustomDragDrop' is 'undifined' while deploying in Win2003 server.

But it work fine on when i deployed it on my development server(XP).

How can i deploy it on Win2003 server.

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

Thursday, July 05, 2007 11:41 AM by stone

on Win2003 server?

My CustomDragDrop work fine on win2003 except "CurrentPageId cann't be changed".  you may rebuild CustomDragDrop with AjaxControlToolkit(version:1.0.10618.0)

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

Thursday, July 12, 2007 2:57 PM by bhavesh

same Error: "Value of member 'CurrentPageId' of an object of type 'UserSetting' changed,A member defining the identity of the object cannot be changed. Consider adding a new object with new identity and deleting the existing one instead."

hey stone!! did the above query worked for you?? I tried, it didnt work for me.

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

Friday, July 13, 2007 6:28 AM by stone

I do not know how to remove the primary key.

As you said, in the UserSetting table,CurrentPageID is already NOT assigned as primary key. If it is already not assigned as primary key then how one can remove primary key from that field?

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

Monday, July 16, 2007 5:28 PM by Sat

Omar, Your Blog is too lengthy, I think, you need to make steps, so that developers can able to understand, I tried almost every thing possible, I got results no more than standard Ajax controls, but thanks for your efforts though and lengthy theory.

Good luck for your next Blog and we are waiting for you.

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

Monday, July 23, 2007 9:13 AM by Hemen

Hi Omar,

its nice work for widget. i have used ur DLL for my application. but i have one problem. when there are more then 3 widgets vertically, i have to scroll down to catch header of widget after 3 levels. so in that case when i click on header, whole widget body is moves up by the amount i have scroll down. so my cursor is not on widget body in this case. some times it moves upward so much that i cant even see the widget. so will u please tell me what and where changes r required. so my widget remains at where my mouse pointer.

Thanx a lot,

Regards,

Hemen.

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

Tuesday, July