Breaking Out of the DateChooser
The Flex framework is amazingly capable. Between renderers, editors, skins, styles and containers, you can produce amazing applications. Sometimes however developers get stuck in the framework; they think they need to use the framework for everything. I used to see this a lot with list-based controls, but of late the target for getting stuck seems to be the DateChooser. Guess what though, the results are in, and you don’t have to use the DateChooser to produce a calendar.
At their core, calendars are pretty basic. They’re a bunch of boxes that form a small grid. Specifically, you’re looking at seven boxes across and up to six boxes deep, for a total of 42 boxes. Each box has a label that represents a two digit number. Sometimes there’s more, like a header, but that’s just gravy after you’ve got the boxes. Here’s the question then, do you really need the DateChooser to put 42 boxes on the screen with Flex?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
layout="absolute"
creationComplete="doCreationComplete( event )"
xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
Application {
background-gradient-colors: #FFFFFF, #FFFFFF;
}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.controls.Label;
public function doCreationComplete( event:Event ):void
{
var lbl:Label = null;
for( var rows:Number = 0; rows < 6; rows++ )
{
for( var cols:Number = 0; cols < 7; cols++ )
{
lbl = new Label();
lbl.text = "99";
lbl.setStyle( "textAlign", "center" );
lbl.x = cols * 19;
lbl.y = rows * 18;
addChild( lbl );
}
}
}
]]>
</mx:Script>
</mx:Application>
To fill these boxes with date values, you don’t need to understand the differences between the Gregorian calendar and the Julian calendar, simply the basics of the Date class. The Date class constructor takes a variety of arguments, not the least of which are values for the year, month and date. You can use that to get to the first day of the month, and then Date.day to determine the day of the week. Since we’ll want to iterate through our 42 boxes evenly, you can rewind to the first day of the week by subtracting Date.day from Date.date.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
layout="absolute"
creationComplete="doCreationComplete( event )"
xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
Application {
background-gradient-colors: #FFFFFF, #FFFFFF;
}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.controls.Label;
public function doCreationComplete( event:Event ):void
{
var cal:Date = new Date();
var lbl:Label = null;
cal.date = 1;
cal.date = cal.date - cal.day;
for( var rows:Number = 0; rows < 6; rows++ )
{
for( var cols:Number = 0; cols < 7; cols++ )
{
lbl = new Label();
lbl.text = cal.date.toString();
lbl.setStyle( "textAlign", "center" );
lbl.x = cols * 19;
lbl.y = rows * 18;
addChild( lbl );
cal.date = cal.date + 1;
}
}
}
]]>
</mx:Script>
</mx:Application>
When it comes time to differentiate today from any other date, you’ll want to get a new Date instance without passing it any values. Guess what? That’s today! While you’re iterating through the values, you can compare to the today value to determine if there’s a match. From there it’s a matter of changing whatever style you want to differentiate today from the other labels. You’ll also want to make sure and set the labels that aren’t today, back to a default. If you want to get really tricky, you can set additional styles based on the month.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
layout="absolute"
creationComplete="doCreationComplete( event )"
xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
Application {
background-gradient-colors: #FFFFFF, #FFFFFF;
}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.controls.Label;
public function doCreationComplete( event:Event ):void
{
var cal:Date = new Date();
var today:Date = new Date();
var lbl:Label = null;
cal.date = 1;
cal.date = cal.date - cal.day;
for( var rows:Number = 0; rows < 6; rows++ )
{
for( var cols:Number = 0; cols < 7; cols++ )
{
lbl = new Label();
lbl.text = cal.date.toString();
lbl.setStyle( "textAlign", "center" );
// Check month
if( today.fullYear == cal.fullYear && today.month == cal.month )
{
// Check date
if( today.date == cal.date )
{
lbl.setStyle( "color", 0xFF0000 );
} else {
lbl.setStyle( "color", 0x0B333C );
}
} else {
lbl.setStyle( "color", 0x999999 );
}
lbl.x = cols * 19;
lbl.y = rows * 18;
addChild( lbl );
cal.date = cal.date + 1;
}
}
}
]]>
</mx:Script>
</mx:Application>
If it’s interactivity you’re after such as navigational elements, then you’ll probably want to a) keep a reference to the date that’s currently being viewed and b) wrap your little labels in a container that can encapsulate functionality and rendering. From there it’s really up to your imagination to determine what that calendar looks like and what it behaves like when the user interacts with it.
The above example is configured to match the date chooser equivalent from the Ext framework. I’m a big fan of Ext, and Jack Slocum and the gang have put a lot of good design into how it works. I specifically like the ability to easily select months and years. With the above algorithms in mind, I set forth to duplicate the experience so I could provide similar functionality for my Flex-based applications. If you want to see how it’s all done, the source code is available for download (sloppy, but hopefully useful to somebody).
To be fair, implementing all of the functionality of the DateChooser would take quite a bit more effort. There’s a price to pay for being more general in functionality. Sometimes breaking outside of the box, and implementing something specific is a better choice than trying to subclass and tweak every single class in a given component. That’s really the takeaway here - Flex is fantastic, but don’t get trapped inside the component framework box. If it seems like a feature is too difficult to implement with what’s been provided, then don’t be afraid to try something new.
January 30th, 2008 at 1:21 pm
Theres a co-incidence. I’m actually writing a calendar/date chooser as we speak
February 14th, 2008 at 5:20 pm
Maybe you don’t have to now
February 21st, 2008 at 8:09 pm
Nice post Kevin. I am looking for a calendar control which displays a yearly/monthly/weekly view just like Outlook does. The closest I have got is the Scheduling Framework at Flexlib but that doesnt support these custom views. Is there anything else available ?
April 29th, 2008 at 5:44 pm
Kevin, thanks for this, you have managed to make a complex scenario understandable (well, almost!).
Did you find a solution Anuj?
May 17th, 2008 at 7:13 am
I am still working on it and will get back here very soon. Thanks for the patience!
Simon G.
May 29th, 2008 at 6:29 pm
Good luck with that Simon, let us know how you get on.