Posts in Category "Item Renderers"

MX DataGrid, List and VariableRowHeight

This issue seems to come up frequently:  Someone wants to use a renderer that has a flow-based component like mx:Text or the new Spark Label or Spark RichEditableText and wants the rows to be of variable heights.  Trouble ensues.

There is a key aspect of the MX item renderer lifecycle that has to be accounted for in these scenarios, and that is that, before a renderer is measured, the host List or DataGrid will set the explicitWidth of the renderer to the width of the list or the datagrid column.  This is important since the renderer’s measuredHeight is dependent on the width of the flow-based text components.  If this isn’t handled correctly, the renderer may clip text and/or make it impossible to calculate the exact size of the List or DataGrid.

This also implies that using percent width in a renderer won’t help as percentWidth isn’t applied until after measurement.  You have to tie the width of any flow-based component to the explicitWidth of the renderer so that the measuredHeight is reported correctly.  Sometimes that will require overriding the measure() method, but often you can just use data-binding.

Here  is an example of a custom flow-based renderer.  It draws a square in the upper-left corner and then flows text to the right of it onto multiple lines.  I use data-binding in this example to set the width of the mx:Text so it will then report its measuredHeight correctly.  That allows me to figure out the exact height of the DataGrid by calling measureHeightOfItems and factoring in the viewMetrics.

Run Example
Download Source

Coloring Text in MenuBar

Apparently, some folks want the text in a menu bar to change when you rollover or select it to show a menu. I’m not sure why the default menuBarItemRenderer doesn’t respond to textRollOverColor and textSelectionColor, but here are the modifications I made to get it to work. Usual caveats apply.
Download Source
Run Demo

CheckBoxes in DataGrid With CheckBox Header

So many folks want to know how to put a checkbox in the DataGrid header and have it do select-all/deselect-all. Here’s how I would do it, including how to make the checkbox appear tri-state when only some things are selected.
Usual caveats apply.
Run Demo
Download Source

DataGrid With PopUp Editor

Some folks seem to be in need of an itemEditor in the DataGrid that can popup or float over the DataGrid. The DataGrid currently limits the editor to fit within its size and if you need more space than that, you’ll need to popup an editor.
It is tricky to do so because of a bug in DataGrid plus the complexities of focus handlng and how it is normally used to end the edit session.
Here’s a workaround for the bug and my take on how to create popup editors. Usual caveats apply.
Download Source
Run Example

DataGrid ItemEditor with Two Input Fields

Several folks have been asking about how to have a DataGrid ItemEditor with two input fields (say you want to separately edit first name and last name or something like that).
It’s a bit tricky because of some missing pieces in the underlying Flash focus APIs and because of how Focus events work. Here is my take on how to do it. Usual caveats apply (i.e, code is not supported and may have bugs etc).
Download Source
Run Example

Flex 3 DataGrid Footers

I took a few minutes to prove that there is another way to add Footers to DataGrid in Flex 3. The old blog post showed how to add footers to the border. In Flex 3 we added more hooks so you can add other “content areas” to the DataGrid.
I’m short on time so the example isn’t fully fleshed out. It could definitely use some optimization as to when and how often it refreshes. It probably needs masking if horizontal scrollbars are on, but at least it should show up in the right place relative to the horizontal scroll bar. There’s probably bugs as well, so the usual caveats apply, but it should get you started in the right direction. Also, I did not try this with AdvancedDataGrid. It is actually developed by an entirely different team.
Download Source
Run Demo

CheckBox selection in DataGrid and List

Several folks have asked how to use Checkbox renderers to handle multiple selection in a DataGrid or List. There are some solutions out there already, but here’s mine. Usual caveats apply.
Download Source
Run Demo

DataGrid Footers

Ever notice that the DataGrid has headers but no footers? That’s because footers have to be computed if they are totals or averages or whatever and we wanted to leave that for advanced datagrids in a future release.
Lately, due to some other work we’re doing for 3.0, I’ve been wondering whether footers, headers and locked columns and rows should be part of the chrome instead of the main set of cells. If so, it is possible to create footers in 2.0.1 today by subclassing DataGrid and making a special Border that can put renderers in the border.
Here’s an example that computes the average of the price column. Usual caveats apply.
Download source
Run demo
You can see how one could do column headers that way, or add headers to Tree for a TreeDataGrid.
Now, if the renderers are in the chrome, they are not part of the selection model, and they should probably not renderer data from the dataprovider per-se. So I’d be interested in knowing:
1) If you have used lockedColumns or lockedRows today, what have you used them for?
2) Did you want the lockedColumns or lockedRows to be selectable? Editable?
3) Did you do this to get more header rows/columns or to display data from the dataprovider?

More Thinking About Item Renderers

Well, the last post definitely got people thinking. Since then I’ve seen a few of you struggle with images, centering, etc. Again, if you’re in a hurry, take a container and center an Image in it, but just be aware that that is kind of heavy.
This example shows how I would do it in order to optimize on performance. Most drop-in renderers like Image and CheckBox are comprised of child components that actually display the content, text and icons. By overriding the updateDisplayList methods, you can center the content without the cost of wrapping the renderer in a container.
Download Source Code
Run Demo
This example also shows how to use labelFunction to map fields in your data to external or embedded images. The usual caveats and disclaimers apply.
-Alex

Thinking About Item Renderers

Many Flex apps have DataGrids. Many of those DataGrids want to display something other than plain text which requires the use of custom item renderers. I’ve seen lots of questions and complaining about how hard it is to make simple custom item renderers. I didn’t design it to be that hard, so I decided to take some time and make some examples of how I’d do it to serve as a starting point for some of the things you want to do in DataGrids.
I found the difficulty level to be about what I expected, but along the way I reminded myself of somethings you might find useful when making your own item renderers. What follows are those things you should probably know, and then some examples.
Recycling
Custom Item Renderers does take a mental shift from other kinds of UI development in Flex. This is because item renderers need to be completely data-driven, and because they are not mapped to the dataprovider in a one-to-one fashion. A single instance of an item renderer can end up displaying the data of several different items in your data provider. It might display the first item, then the tenth, then the second as you scroll up and down in the DataGrid. We do this for efficiency reasons: if we made a renderer for every item in the data provider it would consume tons of memory. Therefore we only create item renderers that are visible, plus a few others for measurement and buffering. This has the following implications:
You cannot assume the renderer has been freshly created. When it is time to update the renderer, the components might be holding old values from the item it was rendering before.
You cannot assume that there are only as many renderers created as you see on-screen.
You cannot assume that the first renderer created is for the first visible row.
The most common error I’ve seen is in not resetting values. For example, if you have code that makes the text red if the value is below zero, you might write:

if (data.price < 0)
setStyle("color", 0xFF0000);

This is incorrect. It will work the first time the renderer is created since the default color is black and will work if values are greater than 0, and if the value is less than zero. But once the renderer displays a value less than zero and turns the text red, if it is later asked to renderer a different value that is greater than zero, there is no code to turn the color back to black again. What will happen is that the data will look fine until you start scrolling or receiving updates, then the red will start to appear in funny places. The correct way is to write code like this:

if (data.price < 0)
setStyle("color", 0xFF0000);
else
setStyle("color", 0x000000);

That code will reset the color to black if the price is greater than 0.
Performance
When designing custom item renderers, you should keep performance in mind. While it is simple and fast to take a Canvas, HBox or VBox and put a few components in there and have a renderer, keep in mind that containers are really big and slow and if you are going to have lots of them visible in your DataGrid, you might run into performance problems. We often don’t see them because we develop on really fast machines, but you have to consider what kind of hardware your end-user will have. So, I’d discourage the use of any container from mx.containers in a custom item renderer if there’s going to be more than a half-dozen on screen. Unless you are laying out more than three or four widgets in your renderer, it will be much faster to start with UIComponent and just write some logic to layout the components in their correct positions. Containers from mx.containers also factor in deferred instantiation logic, scrollbars, clipping and a bunch of other things that your probably don’t need to use in your renderer.
That’s why you’ll see that all of the examples use ActionScript instead of MXML. Yes, that means that you can’t use FlexBuilder to code the renderer, and yes, some of these examples could be done in MXML, but it is easy in MXML to do things that generate extra code that you may not need. You’ll notice that I do not use databinding in these examples for the same reason. It is actually a bit wasteful (although very convenient) to use binding on something that isn’t going to change or only change once or twice. Instead, I set the dataprovider on the initialize event (and would do so on a result event if I used HTTPService).
Events
One frequent complaint is that, if your renderer dispatches events, it is hard to find a way to listen to those events. This is true to a degree. We sort of figured that 80% of renderers would be passive. I’m now thinking that number is closer to 50%. However, it isn’t really that hard to get events, or even to be able to hook up to those events in MXML. First, you need Flex 2.0.1 or later. While some have suggested that you use event bubbling to get events out of the DataGrid, I think that’s potentially unscalable. If you don’t guarantee that the event has a unique name, there could be some parent container of DataGrid that uses the same event name and you’ll get runtime errors. Instead, simply dispatch an event from the owner as in:

var event:Event = new ListEvent("myCustomEventName");
event.itemRenderer = this;
owner.dispatchEvent(event);

This event is then dispatched from the DataGrid, and has a reference to the renderer (and therefore the data) that was affected. To write a handler in MXML, you simply have to subclass DataGrid and add event metadata as in:

/**
* Broadcast when the user does something in the item renderer
*/
[Event("myCustomEventName")]
/**
*  Subclass of DataGrid that dispatches a custom event.
*/
public class MyDataGrid extends DataGrid ...
...

DataGridColumns
However, I want to introduce another way of handling events, and that is to use custom DataGridColumns. The DataGridColumn can act as a proxy for item renderers allowing maximum reuse by allowing the specification of parameters on the column that the renderers respond to, and by being a place to listen to for events from the columns. You’ll see many of these examples use subclasses of DataGridColumn that are paired with the custom renderer. One example, the ComboBoxHeader example, uses custom events from the DataGridColumn instead of through the DataGrid.
DataProviders and Collections
It is also possible to write custom DataProviders/Collections. You don’t have to work just with Arrays or XMLLists. The example NameValueTable takes a data object and displays it in a DataGrid by using a custom collection to enumerate the properties based on a ordered list of properties for the data object.
The Examples
These examples come with the usual caveats. They are unsupported, might have bugs, might not work in your app. Also note that future releases of Flex will likely have more powerful DataGrid features built-in. However, if you can’t wait, maybe these examples will help.
BackgroundColor in Item Renderers
Background Color Demo
Background Color Source
While you can use a Canvas and the backgroundColor style to do this, it turns out that the default DataGridItemRenderer is based on TextField and TextField can have solid color backgrounds. This example has a custom DataGridColumn that allows customization of the background color and value as to when display the colored background.
BackgroundColor Changes When Data Changes
Blink When Data Changes Demo
Blink When Data Changes Source
This example is a variation of the BackgroundColor example where the background color is only shown briefly if the data in the renderer changes.
Text Color and Styles in Item Renderers
Text Styles Demo
Text Styles Source
This example changes the color of the text and makes the text bold based on criteria in a styleFunction that works like labelFunction. By specifying different styleFunctions you can get the color and other font styles to be different based on the data.
HTML in an Item Renderer
HTML Renderer Demo
HTML Renderer Source
I’ve seen many people use the Text component as a renderer for rendering HTML (the subset of HTML that Flash supports, that is). It turns out that the default DataGridItemRenderer can handle HTML with a few minor tweaks and is lighterweight than Text.
Sub-Object Item Renderers
Sub-Object Demo
Sub-Object Source
The default DataGridColumn only handles properties on the dataProvider items. It doesn’t know how to look “deeper” for properties in the items’ sub-objects. That’s for performance reasons. Normally, we tell you to use labelFunction, which is a fine solution, but this example shows how a custom DataGridColumn and custom item renderer can also take the extra time to look at sub-objects.
Split Columns/Grouped Columns
Split Column Demo
Split Column Source
Several folks want to see a column split in two with a header over the two columns. Again, this can be done via custom DataGridColumns and custom item renderers. This example allows you to specify two custom sub-DataGridColumns that define the renderers for the sub-columns.
Custom Header Renderers (CheckBox)
CheckBox Header Renderer Demo
CheckBox Header Renderer Source
Making custom header renderers is a bit trickier, especially if they are interactive. You might need to disable certain default behavior in the DataGrid. This example shows how to get a CheckBox as the header for the column.
Custom Header Renderers (ComboBox)
ComboBox Header Demo
ComboBox Header Renderer Source
Another classic custom header is a ComboBox that allows you to pick filters for the DataGrid data. This example uses a custom ComboBox that allows specification of separators in the list of items, to mimic the look some people use in Excel. This example dispatches a custom event via the custom DataGridColumn.
List of CheckBoxes
CheckBox List Demo
CheckBox List Source
This doesn’t use DataGrid, but I put it here to show one way to do this. It uses an inline item renderer to modify the data provider directly. It is tempting to use rendererIsEditor for this functionality, but it feels better if you handle the change event directly instead of relying on the editable renderer workflow which doesn’t update the data provider until after you click out of the renderer.
Name Value Table/Custom Collection
Custom Collection Demo
Custom Collection Source
.
This also doesn’t use DataGrid, but is a good example of how to build a custom collection. In theory, a custom collection could insert groupings into the array of email much like Outlook does. Maybe I’ll prove that in an example some day, but future releases of Flex should have this functionality built-in, and there are some good versions from third-parties already. I put up this example to get you to consider using custom collections to solve some of your problems
Someday there will be other examples available as well (maybe).. Hope these help.
-Alex