PDA

View Full Version : Rendering HTML in DataGrid cells?


Ledneh
06-07-2006, 05:13 PM
I've been trying to work out methods for using HTML formatting in DataGrid cells. So far, here's what I've come up with from the help docs and Google and what not:


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:DataGrid id="storyGrid" width="400" height="300" variableRowHeight="true" left="20" top="20">
<mx:dataProvider>
<mx:ArrayCollection>
<mx:source>
<mx:Object Price="11.99">
<mx:Artist>
<![CDATA[<b>Pave<i>ment</i></b>]]>
</mx:Artist>
<mx:Album>
&lt;b&gt;Slanted and Enchanted&lt;/b&gt;
</mx:Album>
</mx:Object>
<mx:Object Artist="Pavement" Album="Brighten the Corners" Price="11.99" />
</mx:source>
</mx:ArrayCollection>
</mx:dataProvider>
<mx:columns>
<mx:Array>
<mx:DataGridColumn dataField="Artist">
<mx:itemRenderer>
<mx:Component>
<mx:Text x="0" y="0" width="100%" htmlText="{data.Artist}"/>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn dataField="Album" />
<mx:DataGridColumn dataField="Price" />
</mx:Array>
</mx:columns>
</mx:DataGrid>
</mx:Application>


Now, all this works just fine for the DataGridColumn for which the HTML renderer is defined. But the rest just blast out the tags without rendering them.

To fix that, I COULD copy and paste the item renderer for each column, changing the data property as I go, but that feels repetitious and wasteful. So I wanted to ask, is there any way to specify to the DataGrid, "render all HTML contents in your tags" or, failing that, a way to specify a default itemRenderer for the whole grid that renders HTML?

Thanks!

nirth
06-18-2006, 01:57 PM
my opinion that the easiest way to do it, is creation the class, than is u need more customized renderers you can extend your class, and assign different renderers to the columns.

i've did it like this

// renderers/HTMLRenderer.as
package renderers
{
import mx.controls.Text;
import mx.events.FlexEvent;

public class HTMLRenderer extends Text
{
public function HTMLRenderer ()
{
super();
}

private var _data:Object;

[Bindable("dataChange")]
[Inspectable(environment="none")]
override public function get data():Object
{
return _data;
}
override public function set data(value:Object):void
{
_data = value;

if (listData)
{
htmlText = listData.text;
}
else if (_data != null)
{
if (_data is String)
htmlText = String(_data);
else
htmlText = _data.toString();
}
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
}
}


and the mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:DataGrid itemRenderer="renderers.HTMLRenderer" id="storyGrid" width="400" height="300" variableRowHeight="true" left="20" top="20">
<mx:dataProvider>
<mx:ArrayCollection>
<mx:source>
<mx:Object Price="11.99">
<mx:Artist>
<![CDATA[<b>Pave<i>ment</i></b>]]>
</mx:Artist>
<mx:Album>
&lt;b&gt;Slanted and Enchanted&lt;/b&gt;
</mx:Album>
</mx:Object>
<mx:Object Artist="Pavement" Album="Brighten the Corners" Price="11.99" />
</mx:source>
</mx:ArrayCollection>
</mx:dataProvider>
<mx:columns>
<mx:Array>
<mx:DataGridColumn dataField="Artist" />
<mx:DataGridColumn dataField="Album" />
<mx:DataGridColumn dataField="Price" />
</mx:Array>
</mx:columns>
</mx:DataGrid>
</mx:Application>

sevmusic
06-05-2009, 03:10 PM
I've seen your code on more than one site.. hopefully you'll see this...

Using this code in Flex 3 gives me this error:

1119: Access of possibly undefined property text through a reference with static type mx.controls.listClasses:BaseListData.

And it is referencing this in HTMLRenderer.as

htmlText = listData.text;

I even tried: import mx.controls.listClasses.BaseListData;

still no luck... am I missing something?

Thanks for the help.

_bill
12-18-2009, 04:48 PM
I had trouble extending the Text component directly. I was getting data corruption when cycling through different data sets, so I added a level of abstraction to deal with the Text component indirectly. :eek:

You can add to this code to include style accessor functions and so forth.

package renderers
{
import mx.containers.Canvas;
import mx.controls.Text;
import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;
import mx.events.FlexEvent;

public class HTMLRenderer extends UIComponent implements IDropInListItemRenderer, IListItemRenderer
{
[Bindable( "dataChange" )]

protected var _data:Object;
protected var _listData:BaseListData;
protected var _container:Canvas;
protected var _textControl:Text;
protected var _textFormat:TextFormat;
protected var _styleSheet:StyleSheet;

public function HTMLRenderer()
{
super();
}

/**
* Returns the data value.
* IListItemRenderer method
*
* @returns Object
* @see IListItemRenderer
*/
public function get data():Object
{
return _data;
}

/**
* Commits the received object to the data property, invalidates properties,
* and dispatches a dataChange events.
* IListItemRenderer method
*
* @param value Object
* @see IListItemRenderer
*/
public function set data( value:Object ):void
{
_data = value;
invalidateProperties();
dispatchEvent( new FlexEvent( FlexEvent.DATA_CHANGE ) );
}

/**
* Returns the listData value.
* IDropInListItemRenderer method
*
* @returns BaseListData
* @see IDropInListItemRenderer
*/
public function get listData():BaseListData
{
return _listData;
}

/**
* Commits the listData and invalidates properties.
* IDropInListItemRenderer method
*
* @param value BaseListData
* @see IDropInListItemRenderer
*/
public function set listData( value:BaseListData ):void
{
_listData = value;
invalidateProperties();
}

/**
* Creates all child elements and ensure they are in the display list.
*/
override protected function createChildren():void
{
super.createChildren();

if ( _container == null )
{
_container = new Canvas();
_container.horizontalScrollPolicy = "off";
_container.verticalScrollPolicy = "off";
addChild( _container );

_textControl = new Text();
_container.addChild( _textControl );
}
else if ( !this.numChildren )
{
addChild( _container );
}
}

/**
* Commits all properties and sets the visible HTML text. This is where
* the magic happens. Called by the framework during init and invalidateProperties.
*/
override protected function commitProperties():void
{
super.commitProperties();

// Determine the value of htmlText.
var value:String = "";
var dataField:String = "htmlText"; // default.
try
{
// Set the htmlText to the label, which will appropriately handle labelFunctions
value = DataGridListData( listData ).label;
}
catch ( err:Error )
{
// Ignore error, try to handle another way.
if ( _data != null && _data[ dataField ] != null )
{
if ( _data[ dataField ] is String ) { value = _data[ dataField ]; }
else { value = _data[ dataField ].toString(); }
}
}
// Set the htmlText!
_textControl.htmlText = value;
invalidateSize();
}

/**
* Measure informs the parent object what the preferred size should be.
* This will be ignored if an explicit width and height are set.
* Called by the framework during init and invalidateSize.
*/
override protected function measure():void
{
super.measure();
if ( _textControl != null )
{
measuredWidth = _textControl.width;
measuredHeight = _textControl.height;
}
}

/**
* Position and size child elements and call all drawing API methods.
* Called by the framework during init and invalidateDisplayList.
*/
override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
{
super.updateDisplayList( unscaledWidth, unscaledHeight );
_container.width = unscaledWidth;
_container.height = unscaledHeight;
}
}
}

I hope this helps folks.

_bill
12-18-2009, 04:59 PM
HTML text doesn't always measure its size correctly immediately. :mad: I added an event to the code above that will fire after 1 frame (allowing the HTML text to render) and re-invalidate the size. The event fires once and then removes itself.

Replace the "commitProperties" function above with this code:


/**
* Commits all properties and sets the visible HTML text. This is where
* the magic happens. Called by the framework during init and invalidateProperties.
*/
override protected function commitProperties():void
{
super.commitProperties();

// Determine the value of htmlText.
var value:String = "";
var dataField:String = "htmlText"; // default.
try
{
// Set the htmlText to the label, which will appropriately handle labelFunctions
value = DataGridListData( listData ).label;
}
catch ( err:Error )
{
// Ignore error, try to handle another way.
if ( _data != null && _data[ dataField ] != null )
{
if ( _data[ dataField ] is String ) { value = _data[ dataField ]; }
else { value = _data[ dataField ].toString(); }
}
}
// Set the htmlText!
_textControl.htmlText = value;

// Invalidate size now, then do it again in another frame to ensure rendering accuracy.
invalidateSize();
this.addEventListener( Event.ENTER_FRAME, invalidateSizeOnEnterFrame, false, 0, true );
}

/**
* Invalidates size and removes this function as an event listener for EnterFrame.
*
* @param event Event of type Event.ENTER_FRAME
*/
protected function invalidateSizeOnEnterFrame( event:Event ):void
{
this.removeEventListener( Event.ENTER_FRAME, invalidateSizeOnEnterFrame, false );
invalidateSize();
}