Thinking Declaratively with MXML
There’s no mistaking that Flex 2 has been a historic release in the evolution of the framework. With Flex 3 closing in fast, there’s even more great features we can now use in our applications. Sometimes however, it is important to go back a remember some of the smaller features we may be overlooking. On a recent project I was reminded of the beauty of MXML itself, and the simple elegance that is markup.
MXML is one of those fundamentals of Flex that can sometimes catch developers off guard. The key is to remember that for the most part, every MXML tag and attribute corresponds directly to a Flex class and property respectively. When you declare
Note that there are exceptions such as
With that in mind, your assignment is to create a Canvas that has a DropShadowFilter on it at startup. You might think of a few ways to manage this task. The first solution that comes to most peoples minds is to catch the creationComplete event and then create and assign an instance of the filter. The second is to do the same steps, but all inline. These approaches will look something like the following snippet.
<mx:Script>
import flash.filters.DropShadowFilter;
public function doCreationComplete( event:Event ):void
{
myCanvas.filters = new Array( new DropShadowFilter() );
}
</mx:Script>
...
<mx:Canvas id="myCanvas" creationComplete="doCreationComplete( event )" />
Thinking declaratively however, it is important to remember that attributes of a tag are properties of a class. In this respect, why can’t we declare the “filters” property using MXML? Further, why not create an instance of DropShadowFilter declaratively as well. While we’re at it, we can even set all the filter properties declaratively. This approach looks something like the following snippet.
<mx:Canvas>
<mx:filters>
<mx:Array>
<mx:DropShadowFilter blurX="3" blurY="6" color="0x000000" />
</mx:Array>
</mx:filters>
</mx:Canvas>
If after reading these code samples, you find yourself thinking that this is obvious, rest assured that I’ve seen even the best Flex developers jump through hoops to solve a problem in script that might have otherwise been accomplished declaratively. In looking back through some of my own past code, I’ve even seen places where I’ve use the script approach where the declarative approach made more sense.
Okay, so where are some other areas where thinking declaratively can really make a big difference?
Probably the next most common situation where I see script being used where MXML would work better is when developers use the Flex charting. The Flex charts (improved in Flex 3) provide a wealth of flexibility in the appearance. Strokes, fill, axes, series are all classes that can be creatively declaratively just as easily as they can be through script.
<mx:CartesianChart id="chart" width="100%" height="100%">
<mx:horizontalAxis>
<mx:DateTimeAxis
id="dAxis"
displayName="Time"
autoAdjust="{adjust.selected}"
displayLocalTime="{local.selected}" />
</mx:horizontalAxis>
<mx:horizontalAxisRenderer>
<mx:AxisRenderer
minorTickPlacement="outside"
minorTickLength="3">
<mx:minorTickStroke>
<mx:Stroke color="#BBCCDD" weight="1" />
</mx:minorTickStroke>
</mx:AxisRenderer>
</mx:horizontalAxisRenderer>
<mx:dataProvider>{dataSet}</mx:dataProvider>
<mx:series>
<mx:ColumnSeries
displayName="Bannana Sales"
xField="dt"
yField="F0" />
</mx:series>
</mx:CartesianChart>
What is interesting is that it is equally common to find developers having problems going the other way - making the connection between all those tags and the class/properties they represent. As an example, I’ve often been asked how to add columns to a DataGrid from script. This is usually because the developer has disconnected the markup from the class.
<mx:Script>
...
import mx.controls.dataGridClasses.DataGridColumn;
public function doCreationComplete( event:Event ):void
{
var cols:Array = new Array();
var col:DataGridColumn = null;
for( var c:Number = 0; c < 5; c++ )
{
col = new DataGridColumn();
col.headerText = "Stuff " + c;
cols.push( col );
}
grid.columns = cols;
}
...
</mx:Script>
...
<mx:DataGrid id="myGrid" creationComplete="doCreationComplete( event )" />
Finally, I’ll leave you with a somewhat more fringe case, that has application nonetheless. How do you apply a mask to a display object? There’s clearly a mask property, but how does that manifest itself in markup? In the following example, I’ve applied a mask object via MXML.
<mx:Canvas width="100" height="100" mask="{myMask}" />
<mx:Canvas id="myMask" width="100" height="100" borderSkin="skins.MyStarMaskSkin" />
What’s particularly interesting is that the only script is in drawing the border skin of the mask object. There’s been some discussion of the forthcoming MXMLG, which I think really starts to make the declarative side of Flex even more compelling. In the meantime, I encourage you to step back occasionally, and think about what the humble feature of MXML can do for you.