The beauty of having access to the source code for a framework is that one can reverse-engineer and overload particular classes to change behaviour. I have found this particularly useful when developing data visualisations using the Adobe Data Visualisation library for Flex 3.5. In this case, my client wanted a Marimekko chart to show sales and marketing data with three data dimensions. A Marimekko chart is a variable width stacked column chart. It is a type of tree-map and shares the characteristic of all tree-maps that the area of a data element is proportional to the product of two data values.
There are no Flex components from either Adobe or third-parties which will create a Marimekko chart, so I decided to make my own. I overloaded a ColumnChart class and rendered the data elements within columns by drawing boxes of different widths; I then had to render the category axis labels and data labels for the column width values manually as well. The final result looks like this:

My agreement with the client does not allow me to post complete source code; but I can give a good summary of the process which should make it easy to reproduce this chart.
1) overload the ColumnChart class to make a new MarimekkoChart class
2) create a new class called MariMekkoSeriesItemRenderer that extends UIComponent and implements IDataRenderer:
public class ReportMarimekkoSeriesItemRenderer extends UIComponent implements IDataRenderer
{
private var _data:Object;
public function set data(value:Object):void
{
if (_data == value)
return;
_data = value;
}
public function get data():Object
{
return _data;
}
}
3) hide horizontal axis labels for the chart by setting the labelFunction for the horizontalAxisRenderer to a function that returns an empty string.
4) add ColumnSeries as usual to the MarimekkoChart, but set the item renderer for each series to our new MariMekkoSeriesItemRenderer class:
series.setStyle("itemRenderer", new ClassFactory(ReportMarimekkoSeriesItemRenderer));
5) now, within the MariMekkoSeriesItemRenderer class, we override the rendering to draw a rectangle that is proportional to the data width value. Note that we must offset the drawing in the x-direction to draw from the left hand edge of the chart data canvas. For this, we will need a data source that not only has the column width but a column “zero” coordinate for each data point as well. The custom rendering should be done in an overload of the updateDisplayList() method of the renderer.
6) create data labels by adding a Text component to each renderer, positioning in the middle of the data rendererer and displaying the data value
7) to add our own axis and data labels, we do a bit of sleight-of-hand: for the first data series only, we create axis labels and position them horizontally in the middle of the renderer, but vertically at the bottom of the chart; we then add these labels as children of the chart rather than the renderer. We do the same at the top of the chart to show the width values.
Example code for the MariMekkoSeriesItemRenderer class below. Note that some of the graphics rendering code has been copied from the BoxItemRenderer.as class in the Flex DMV source code.
public class ReportMarimekkoSeriesItemRenderer extends UIComponent implements IDataRenderer
{
private var _data:Object;
private var _label:Text;
private var _axisLabel:Text;
private var _sizeLabel:Text;
public function set data(value:Object):void
{
if (_data == value)
return;
_data = value;
}
public function get data():Object
{
return _data;
}
override protected function createChildren():void
{
super.createChildren();
if (this._label == null)
{
this._label = new Text();
this._label.text = "";
this.addChild(this._label);
}
if (this._axisLabel == null)
{
this._axisLabel = new Text();
this._axisLabel.text = "";
this._axisLabel.visible = false;
}
if (this._sizeLabel == null)
{
this._sizeLabel = new Text();
this._sizeLabel.text = "";
this._sizeLabel.visible = false;
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
var fill:IFill;
var state:String = "";
var newWidth:Number = width;
var newX:Number = this.x;
var size:Number;
var sizeZero:Number;
var series:ReportMarimekkoSeries;
var chart:ReportMarimekkoChart;
if (_data is ChartItem)
{
var size:Number;
var sizeZero:Number;
var dataValue:Number;
// here, retrieve/calculate the column width for this data point
// and also the "zero" point for starting this column
// both these values should be scaled from zero to 1
series = ColumnSeries(ChartItem(_data).element);
chart = MarimekkoChart(series.owner);
// calculate pixel widths and x-position relative to chart
newWidth = size * chart.dataCanvas.width;
newX = sizeZero * chart.dataCanvas.width;
// add a data label
this._label.text = String(dataValue);
if (chart.series.length > 0)
{
if (series == chart.series[0])
{
// for the first series, we add an extra label
// at the x-axis aand a data value label at the top
this._axisLabel.text = axisLabel; // axisLabel from data
if (this._axisLabel.parent != chart)
{
chart.addChild(this._axisLabel);
this._axisLabel.visible = true;
}
this._sizeLabel.text = String(size); // or use another field
if (this._sizeLabel.parent != chart)
{
chart.addChild(this._sizeLabel);
this._sizeLabel.visible = true
}
}
}
}
if(_data is ChartItem && _data.hasOwnProperty('fill'))
{
state = _data.currentState;
fill = _data.fill;
}
else
fill = GraphicsUtilities.fillFromStyle(getStyle('fill'));
var color:uint;
var adjustedRadius:Number = 0;
switch(state)
{
case ChartItem.FOCUSED:
case ChartItem.ROLLOVER:
if(StyleManager.isValidStyleValue(getStyle('itemRollOverColor')))
color = getStyle('itemRollOverColor');
else
color = ColorUtil.adjustBrightness2(GraphicsUtilities.colorFromFill(fill),-20);
fill = new SolidColor(color);
adjustedRadius = getStyle('adjustedRadius');
if(!adjustedRadius)
adjustedRadius = 0;
break;
case ChartItem.DISABLED:
if(StyleManager.isValidStyleValue(getStyle('itemDisabledColor')))
color = getStyle('itemDisabledColor');
else
color = ColorUtil.adjustBrightness2(GraphicsUtilities.colorFromFill(fill),20);
fill = new SolidColor(GraphicsUtilities.colorFromFill(color));
break;
case ChartItem.FOCUSEDSELECTED:
case ChartItem.SELECTED:
if(StyleManager.isValidStyleValue(getStyle('itemSelectionColor')))
color = getStyle('itemSelectionColor');
else
color = ColorUtil.adjustBrightness2(GraphicsUtilities.colorFromFill(fill),-30);
fill = new SolidColor(color);
adjustedRadius = getStyle('adjustedRadius');
if(!adjustedRadius)
adjustedRadius = 0;
break;
}
var stroke:IStroke = getStyle("stroke");
var w:Number = stroke ? stroke.weight / 2 : 0;
var rc:Rectangle = new Rectangle(
-this.x + newX + w - adjustedRadius,
w - adjustedRadius,
newWidth - 2 * w + adjustedRadius * 2,
height - 2 * w + adjustedRadius * 2);
var g:Graphics = graphics;
g.clear();
g.moveTo(rc.left,rc.top);
if (stroke)
stroke.apply(g);
if (fill)
fill.begin(g,rc);
g.lineTo(rc.right,rc.top);
g.lineTo(rc.right,rc.bottom);
g.lineTo(rc.left,rc.bottom);
g.lineTo(rc.left,rc.top);
if (fill) fill.end(g);
// position the data label
this._label.width = this._label.text.length * 7;
this._label.height = 20;
this._label.x = rc.left + rc.width/2 - (this._label.width/2);
this._label.y = rc.top + rc.height/2 - (_label.height/2);
if (chart)
{
// position the axis label
this._axisLabel.width = this._axisLabel.text.length * 7;
this._axisLabel.height = 20;
this._axisLabel.x = newX + rc.width/2 - (this._axisLabel.width/2) + chart.marginLeft;
this._axisLabel.y = chart.height - 40;
// position the width data label
this._sizeLabel.width = this._sizeLabel.text.length * 7;
this._sizeLabel.height = 20;
this._sizeLabel.x = newX + rc.width - (this._sizeLabel.width/2) + chart.marginLeft;
this._sizeLabel.y = -10;
}
}
}
There are a limitation to this chart - I had to turn interactivity off, as I did not have time to fix the mouse events to fire over the correct areas. Further work is needed to get that right.