There may be another way to accomplish this, but I wanted to have a simple
commanding system in the
Microsoft Ajax Library which worked
outside of the templates.
After a number of false starts and frustration brought about by very limited
documentation, I discovered a reasonable implementation.
I created a script file, “Commanding.js” (in a Scripts folder):
/// <reference name="MicrosoftAjax.js"/>
Type.registerNamespace("WiredPrairie");
WiredPrairie.Commanding = function(element) {
WiredPrairie.Commanding.initializeBase(this, [element]);
}
WiredPrairie.Commanding.prototype = {
initialize: function() {
WiredPrairie.Commanding.callBaseMethod(this, 'initialize');
// Add custom initialization here
},
dispose: function() {
//Add custom dispose actions here
WiredPrairie.Commanding.callBaseMethod(this, 'dispose');
}
}
WiredPrairie.Commanding.registerClass('WiredPrairie.Commanding', Sys.UI.Control);
if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
This is just a basic JavaScript class, with the default implementation provided by
the Ajax Client Control template in Visual Studio 2008. The reason this is needed is
that Controls all support a special event onBubbleEvent that is needed by commands
when they’re raised.
In the body of the HTML, I attached an instance of the “WiredPrairie.Commanding”
class to the body. Next, I attached a JavaScript function to the onbubbleevent
(wpc:onbubbleevent, remember that all attributes need to be all lowercase). Here,
I’ve used the special {{ }} syntax to indicate I want JavaScript code to execute
when the event is raised.
<body xmlns:sys="javascript:Sys"
xmlns:wpc="javascript:WiredPrairie.Commanding"
sys:attach="wpc"
wpc:onbubbleevent="{{onCommand}}" >
<div >
<button sys:command="startRunningCommand" sys:commandargument="iargueaboutit">Run</button>
</div>
</body>
I needed my JavaScript WiredPrairie.Commanding class to load and run at the right
time on the page, so I’ve used the new loader classes in the Ajax library.
Sys.loader.defineScripts(null, [
{
name: "Commanding"
,releaseUrl: "Scripts/Commanding.js"
,debugUrl: "Scripts/Commanding.js"
,dependencies: ["ComponentModel"]
}
]);
First, I needed to declare this new script file and any dependencies. Since I don’t
yet have a minified version, I’ve just specified the same file for the releaseUrl
and the debugUrl. For the dependencies property, I looked through a few source files
to discover that the “ComponentModel” key included Sys.UI.Control, which my class
depends on to be created, so I added that here (hopefully this will be documented at
some point).
Next, I added code to indicate to the loader which scripts were necessary and let it
determine the best way to load them:
Sys.require([Sys.components.dataView, Sys.scripts.jQuery,
Sys.scripts.Commanding]);
Here, you see the “Commanding” key is added to a special namespace, “Sys.scripts.”
In the new onReady event which is raised when the DOM and scripts have been loaded,
I’ve added code to activate the control class I wrote:
Sys.onReady(function() {
Sys.activateElements(document.documentElement);
});
Finally, I wired up that event declared above in the onbubbleevent attribute on the
body element.
function onCommand(sender, args) {
if (typeof (args) !== "undefined") {
var commandName = args.get_commandName();
var commandArgument = args.get_commandArgument();
alert(commandName + " " + commandArgument);
}
}
OK, that’s cool, but you might want to raise a command without it being triggered by
a control event (such as a click). So, I added a simple function to my Commanding
class:
WiredPrairie.Commanding.raiseCommand = function(sender, commandName, commandArgument, commandSource) {
var source = sender || document.body;
Sys.UI.DomElement.raiseBubbleEvent(source, new Sys.CommandEventArgs(commandName, commandArgument, commandSource));
}
var $sendCommand = WiredPrairie.Commanding.raiseCommand;
Because of the way the raiseBubbleEvent works, it does depend on the source being
set to a valid control/element (as raiseBubbleEvent walks through the parent chain
until there aren’t any more parents) – in this case, I’ve defaulted to the body
element.
Finally, an enhancement to the test case above to demonstrate both receiving and
sending a command:
function onCommand(sender, args) {
if (typeof (args) !== "undefined") {
var commandName = args.get_commandName();
var commandArgument = args.get_commandArgument();
switch (commandName) {
case "startRunningCommand":
$sendCommand(null, "alertCommand", new Date().toLocaleTimeString(), null);
break;
case "alertCommand":
alert(commandArgument);
break;
default:
break;
}
}
}
One command just sends another command which displays an alert. It’s simple, but
functional.
Enjoy.