I’ve posted an update to the wwHoverPanel ASP.NET control yesterday. This library consists of a couple of light weight AJAX controls that provide for a few simple AJAX scenarios: Popup windows dynamically populated both through callbacks or external content in a variety of ways as well as a wwMethodCallback control that allows client code to make two-way JSON callbacks to .NET methods on a page, user or custom controls or a custom HTTP handler.
I’ve updated the control to be compatible with the MS Ajax RC so it should now work properly in combination with UpdatePanels. The control complements MS Ajax and it provides features that are not currently available in MS Ajax. For me the most important of which is a consistent mechanism for calling back to custom and user controls. Like MS Ajax the wwMethodCallback functionality allows calling to .NET methods and passing data both ways using JSON. In addition to what MS Ajax provides, this control allows implementing the callback methods on Page (instances), user or custom controls or a custom HTTP handler for best performance.
There have also been a number of updates to the client library including a new DragBehavior client class and a server side DragPanel control. One of the examples – the Data Driven Resource Manager Administration form (make sure you click the little red icon to get to the admin form) - was making extensive use of client side windows to display additional information and the ability to move these windows around has become kind of crucial as they can obstruct window content that should be readable while entering data into the the pop over windows. wwHoverPanel now also has a Draggable property that allows making any hover windows draggable.
DragPanels are nothing new – the original ATLAS libraries had one and now it’s part of the Control Toolkit.
Most of the applications I’ve built around AJAX recently use a similar metaphor of a fairly complex single form interface with additional forms popping on top and having an easy way to display a panel with more input controls is important. If you run the Resource Administration form you can click on the Add or Translate to pop up additional windows to see a couple of popup windows that are draggable.
Anyway, there’s a client side wwDragBehavior which can be used like this to assign any control:
var DragPanel_DragBehavior = new wwDragBehavior("DragPanel","DragPanelHeader");
where the first parameter is the control to be dragged and the second is a drag handle – an area that initiates the drag. The above code is all it takes to make any control draggable.
On the server there’s a new wwDragPanel control that automatically incorporates this functionality and adds the ability to automatically add a close button to the form (again you can see that in the Resource Administration sample)
<ww:wwDragPanel ID="DragPanel" runat="server" Width="400"
DragHandleId="DragPanelHeader"
Closable="true">
<%-- Drag Handle: Make sure to assign Unique ID and set matching DragHandleID --%>
<div runat="server" id="DragPanelHeader" class="gridheader">
Custom Header Text
</div>
<div id="Content" style="padding:10px;">
If you can't say something nice, don't say nothing at all...
</div>
</ww:wwDragPanel>
The drag panel also deals with any shadows (adminstered through the client wwControl class) associated with it.
The wwHoverPanel control now inherits from wwDragPanel so hover panels can now also be made draggable optionally. This works well for static windows filled by wwHoverWindow. I’ve updated the Blog Browser sample to demonstrate this with an IFRAME example. This sample uses an IFRAME popup window to display blog entry content and this window is now draggable.
Incidentally the Blog Browser demo has a little problem with dragging downward because apparently the browser can’t keep up with redrawing the window fast enough at the mouse position. Oddly though this only happens on downward movement. This not a problem in the Resource Admin demo which is just a simple div tag that gets dragged around so I suspect there’s some heavy overhead in dragging the IFRAME around on that particular page.
I’ve been meaning to add this functionality some time ago, but never got around to it until this week. Actually it’s surprisingly easy to implement drag functionality in client code:
function wwDragBehavior(WindowId,DragHandleId,DragOpacity)
{
var Instance = this;
this.isMouseDown = false;
this.dragHandle = $(DragHandleId);
this.window = $(WindowId);
this.windowObj = new wwControl(this.window);
this.dragOpacity = DragOpacity;
if (DragOpacity == null)
this.dragOpacity = .75;
this.deltaX = 0;
this.deltaY = 0;
this.mouseDown = function(event)
{
var evt = new wwEvent(event);
if (evt.source != Instance.dragHandle)
return;
Instance.windowObj.control.style.position = "absolute";
Instance.deltaX = evt.offsetX;
Instance.deltaY = evt.offsetY;
Instance.dragActivate(event);
evt.consume();
}
this.mouseUp = function(event)
{
Instance.dragDeActivate(event);
}
this.mouseMove = function(event)
{
if (Instance.isMouseDown)
Instance.moveToMouse(event);
}
this.moveToMouse = function(event)
{
var Evt = new wwEvent(event);
Instance.windowObj.setLocation(Evt.clientX-Instance.deltaX,Evt.clientY-Instance.deltaY);
}
this.dragActivate = function(event)
{
Instance.moveToMouse(event);
Instance.isMouseDown = true;
Instance.windowObj.setOpacity(Instance.dragOpacity);
wwEvent.addEventListener(document,"mousemove",Instance.mouseMove,false);
wwEvent.addEventListener(document,"mouseup",Instance.mouseUp,false);
wwEvent.addEventListener(document.body,"selectstart",NullFunction,true);
}
this.dragDeActivate = function(event)
{
Instance.moveToMouse(event);
wwEvent.removeEventListener( document,"mousemove",Instance.mouseMove,false);
wwEvent.removeEventListener( document,"mouseup",Instance.mouseUp,false);
wwEvent.removeEventListener(document.body,"selectstart",NullFunction,true);
Instance.isMouseDown = false;
Instance.windowObj.setOpacity(1);
}
this.hide = function()
{
Instance.windowObj.hide();
}
this.show = function()
{
Instance.windowObj.show();
}
this.stop = function()
{
wwEvent.removeEventListener( document,"mousedown",Instance.mouseDown,false );
}
wwEvent.addEventListener( document,"mousedown",Instance.mouseDown,false);
}
There are a couple dependencies on support classes – wwEvent and wwControl – and those can be found in the wwScriptLibrary.js source file. The code mainly deals with hooking up events to the various mouse operations and then moving the control to the move location. The hardest part is figuring out the position and offsets correctly most of which is handled by the helper classes that do this in a browser independent way.
Anyway I disgress...
There have been many other small tweaks and improvements and the client library has been updated with more functionality. There’s a new wwEvent object to handle source object and returning various x/y coordinate values (document, client and offset) as well as cleaner event handler hook up code providing all of this code in a browser independent manner.
The client library is getting larger (which I’m not so thrilled about) but I think the features that have been added are worth the extra overhead. Now you know why I’ve been asking about integrated Gzip support <s> these last few days to feed the script file as a resource compressed. Compressed it goes down to about 8k – uncompressed it’s 32k.
Why all this effort?
Yeah I know there’s nothing unique about this control library. I don’t claim it to be. But I want a library that’s light weight, easy to use both on the client and on the server and that I can actually understand and debug if necessary. It should be self contained and address usage my scenarios closely. And this is the sole reason I put all of this together. It’s been worth the effort for my own work and I hope a few of you have found it useful as well. Because it’s self contained, it’s easy to use without requiring any sort of configuration and that’s really the idea.
I’ve been going back and forth thinking I might move all of this code over to MS Ajax. There would be some advantages for sure – specifically I could ditch much of my client side library code and I could take full advantage of the client side base class library and base object extensions, some of which I’ve duplicated in my own ways. My JavaScript skills are not - uhm – optimal, so it would be nice to have at least some of the system level code taken care of by folks who really know what they’re doing <g>. But I will say the exercise of building this library has helped me tremendously in feeling more comfortable working with JavaScript so that alone is worth the price of admission...