CustomRowColorDataGrid component
In the last 2 projects I've worked on I've needed to change the background row color of items in the datagrid, based on values in the dataProvider.
However the flex DataGrid doesn't allow you to set the row color this way. Turns out in order to do this you need to extend the DataGrid and override the drawRowBackground() method and put your own logic in it. Not fun at all.
So to make it easier for myself, and you, I've abstracted this out in a way that make the new DataGrid re-usable, and I've added it to my growing collection of components that you can download from here.
So the syntax to use this new grid is to write a new function, just like the ArrayCollections filterFunction. And the new DataGrid will call this function for every row it renders in the DataGrid, to get the right background row color.
The function signature needs to be this:
f(item:Object, defaultColor:uint):uint
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:nimer="com.mikenimer.components.datagrid.*">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private function selectTypeColor(item:Object, color:uint):uint
{
if( item['type'] == "Warning" )
{
return 0xFFFF00;
}
else if( item['type'] == "Error" )
{
return 0xFF0033;
}
return color;
}
]]>
</mx:Script>
<nimer:CustomRowColorDataGrid
id="sampleGrid"
rowColorFunction="selectTypeColor"
width="100%" height="100%">
<nimer:dataProvider>
<mx:Array>
<mx:Object type="Information" message="something good worked"/>
<mx:Object type="Information" message="something else worked"/>
<mx:Object type="Information" message="I like flex"/>
<mx:Object type="Error" message="Opps, something didn't work."/>
<mx:Object type="Warning" message="flex has a learning curve"/>
<mx:Object type="Information" message="flex is fun"/>
</mx:Array>
</nimer:dataProvider>
</nimer:CustomRowColorDataGrid>
</mx:Application>

It looks like a nice component, however i'm currently tying to use it with a XML dataprovider and i'm facing troubles !
Indeed the following :
<nimer:CustomRowColorDataGrid rowColorFunction="selectTypeColor" dataProvider="{memos.memos.memo}">
<nimer:columns>
<mx:DataGridColumn headerText="Capital" dataField="@value" width="450"/>
</nimer:columns>
</nimer:CustomRowColorDataGrid>
Blows a nasty exception:
TypeError: Error #1009:
at com.mikenimer.components.datagrid::CustomRowColorDataGrid/com.mikenimer.components.datagrid:CustomRowColorDataGrid::drawRowBackground()
at mx.controls::DataGrid/mx.controls:DataGrid::drawRowBackgrounds()
at mx.controls::DataGrid/mx.controls:DataGrid::updateDisplayList()
at com.mikenimer.components.datagrid::CustomRowColorDataGrid/com.mikenimer.components.datagrid:CustomRowColorDataGrid::updateDisplayList()
at mx.core::UIComponent/validateDisplayList()
at mx.managers::LayoutManager/::validateDisplayList()
at mx.managers::LayoutManager/::doPhasedInstantiation()
at Function/http://adobe.com/AS3/2006/builtin::apply()
at mx.core::UIComponent/::callLaterDispatcher2()
at mx.core::UIComponent/::callLaterDispatcher()
It seems the problem comes from the mx:DataGridColumn but as you can see I can't do much debugging !
If you can help me on this one ...
Regards
Vianney
What I am trying to do is color specific cells at runtime, say, on a click of some item. Is there any way to achieve this?
Also, I am trying to do something else when the mouse rolls over a header cell. The itemRollOver() method does not recognize this as the 0th row, while itemClick() does. Any way to do this too?
Thanks in advance
honestly, I have no idea. I just uploaded a new zip with the source can you grab that and use the debugger to find the bug. The code itself is very simple so it's probably just a question of adding an IS check somewhere.
not sure, the trick is getting the datagrid to re-run the drawRowBackgrounds() again. Perhaps a call to invalidate display list will do it. Or setting the dataprovider to null and back to your dataset will do it too.
no idea about the itemclick and roll over. If you have a simple example I'll take a look. But this might be a better question for FlexCoders.
I checked the source and I can't really find what i'm looking for,
do you think it is possible to use your components with a XML
dataProvider ? I mean have you tried ? And if so could you tell
me how you managed to do it ?
Thanks !
Vianney
Can you post or email me a simple test case and I'll load up the debugger and take a look.
Here is a pretty simple case that fails dramatically:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
pageTitle="Dashboard" xmlns:nimer="com.mikenimer.components.datagrid.*">
<mx:XML xmlns="" id="myxml">
<memos>
<memo>
<done>true</done>
<date>06/09/2006</date>
<subject>Testationnage</subject>
<content>
Blablabla
</content>
</memo>
<memo>
<done>true</done>
<date>06/09/2006</date>
<subject>Testationnage</subject>
<content>
Blablabla
</content>
</memo>
</memos>
</mx:XML>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private function selectTypeColor(item:Object, color:uint):uint
{
trace(item.toString());
if( item.done == "true" )
{
return 0xFFFF00;
}
else if( item.done == "false" )
{
return 0xFF0033;
}
return color;
}
]]>
</mx:Script>
<nimer:CustomRowColorDataGrid id="test" dataProvider="{myxml.memo}" rowColorFunction="selectTypeColor">
<nimer:columns>
<mx:DataGridColumn dataField="done"/>
<mx:DataGridColumn dataField="date"/>
</nimer:columns>
</nimer:CustomRowColorDataGrid>
</mx:Application>
Now it's strange since when i don't mention the rowColorFunction the datagrid populates perfectly...
I hope the mistake is on my side !
Thanks again.
Vianney
Thanks again Mike.
One question about this. When I'm trying to refence some common properties of dataGrid with AS I keep getting compiler errors and am unable to compile ther project in Flex Builder. For example when trying to set:
myGrid.backgroundAlpha="0.15";
1119: Access of possibly undefined property backgroundAlpha through a reference with static type com.mikenimer.components.datagrid:CustomRowColorDataGrid.
If I put the same setting to nimer:CustomRowColorDataGrid, there is no problem.
Can you help on this?
Thanks,
Jari
It should be easy to workaround though. We just need to copy the metadata from the DataGrid into the CustomRowColorDataGrid. I'll give it a try and see if I can update the grid. (But it will be a few days). However in the mean time you can try it to. Grab the [Style...] metadata lines of code and copy them into my CustomRowDataGrid class. Hopefully the as error will go away.
if( dataIndex < (this.dataProvider as ArrayCollection).length )
... to this ...
if( (this.dataProvider as ArrayCollection) != null && dataIndex < (this.dataProvider as ArrayCollection).length )
Thanks for the great component! This really helped me a lot!
I've updated the code/zip with this fix too. (the latest version is 1.0.4).
If anyone's interested I found another nice way to do this (definitly more code to write but it's code I understand hehe).
Here it is http://www.iepl.net/DataGridRowColorSample/DataGri...
Thank you all !
Vianney
not sure if this is specific to your component (which is pretty slick & darned useful btw but if i try to set the scrollbar to the last added item in a datagrid (resultsPlaceHolder) ala:
resultsPlaceHolder.verticalScrollPosition=resultsPlaceHolder.dataProvider.length;
the row that's colored is thrown off by 1 (too high). i'm calculating some stuff off a map & throwing results into a nimer:CustomRowColorDataGrid & highlighting the summary rows. works a treat but if i try scrolling to the bottom w/the snippet above, the colored row is thrown off.
any ideas?
updateComplete="resultsPlaceHolder.verticalScrollPosition = resultsPlaceHolder.maxVerticalScrollPosition + 1;"
where resultsPlaceHolder is a datagrid.
I tried to understand your code and it seems quite sophisticated. i tried therefor to use the Code from Peter Ent.
http://weblogs.macromedia.com/pent/archives/2006/1...
But this does not work with me. i can not access the dataprovider as it is always null according to my Debugger, even when the output clearly shows data.
Can you perhaps try to explain also his approach to me? It seem to be easier, I just can not access th dataprovider, cast it to ArrayCollection and get the items out of it.
As for you having a null dataProvider. Without a running example, there is not much I can do. I'm driving blind ;) Double check your code (case, scope, etc..) If the grid is populated the value can't be null.
private var Hits:Array = new Array();
private function search() : void{
Hits.length = 0;
var myCursor:IViewCursor = dataGrid1.dataProvider.createCursor();
while(!myCursor.afterLast){
if(myCursor.current.name.indexOf(textCriteria.text) > -1 && textCriteria.text.length > 1)
Hits.push(myCursor.current);
myCursor.moveNext();}
if(Hits.length == 0)
Alert.show("No Matching Entries found");
dataGrid1.invalidateDisplayList(); }
private function selectTypeColor(item:Object, color:uint):uint{
if(Hits.indexOf(item) > -1)
return 0xEBEf80 ;
return color;}
In case anyone else needs :)
it works fine however i have noticed that the searches are case sensitive.
is there a way around this?
i have a datagrid with an array as the dataprovider which is called from a database and populatates the datafields name and email address.
how can i use the .toLowerCase on the name datafield even tho the dataprovider is an array?
sorry im still very new to flex2 and AS3 so any help provided would be of great use
i want to know how to assign color to particular cell in datagrid?
how to create custom tree control along with check-boxes ?
for each node there should one check box.
but when I use it,I have a problem, in my application the datagrid's dataprivode get by webservice,and when I use mx:datagrid,I can see the data when it run.
now I use this component,the page is white ,and I can't see other component on my pages.if this component can bind data with webservice?
I have resolved it!
I'm trying to get it to work within my app and running into problems..
I have this in my code:
<comp:columns >
<mx:Array>
<mx:DataGridColumn width="150"
headerText="Employee"
itemRenderer="com.alltel.rapid.aopscheduler.renderers.WSNameCellRenderer" >
</mx:DataGridColumn>
<mx:DataGridColumn width="84" headerText="Sunday" >
<mx:itemRenderer>
<mx:Component>
<mx:Label toolTip="{outerDocument.toolTipString}"
color="{data.sun.isAtAlternateLocation ? 0xFF0000 : 0x000000}"
text="{ data.sun.timespan }" />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
<mx:DataGridColumn width="84" headerText="Monday" >
<mx:itemRenderer>
<mx:Component>
<mx:Label toolTip="{outerDocument.toolTipString}"
color="{data.mon.isAtAlternateLocation ? 0xFF0000 : 0x000000}"
text="{ data.mon.timespan }" />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</mx:Array>
</comp:columns>
when I use the rowColorFunction I get an error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
Will this even work for me? I'm not using the array of Objects as in your example..
Thanks
Could you please adjust the code in the download to reflect the
if( (this.dataProvider as ArrayCollection) != null && dataIndex < (this.dataProvider as ArrayCollection).length )
from Kevin above? Thanks
- List
- HorizontalList
- TileList
- DataGrid, PrintDataGrid
- Tree
- Menu
Do you know of a simple documentation to do this?
Thanks
I am reading through all these and since new to Flex am a bit lost.
My need is to colour datagrid rows at RUNTIME on user selection of certain conditions. I do not seem to understand how I can use a function to react as soon as a user clicks a button for example or selects an item in a List. I want rows in the dgrid be coloured if the data respond to cetain conditions.
Can you give me an example.
thanks to all.
I encountered the same problems as Vianney, Kevin and GM, and was able to fix them by first implementing Kevin's null-check, then by replacing all references to ArrayCollection with ListCollectionView. XMLListCollection and ArrayCollection both extend ListCollectionView. I have a feeling Vianney and GM have been using e4x like myself, which returns an XMLListCollection to the dataProvider, not an ArrayCollection.
Thanks for the tip!
is there a way to further extend this wonderful component to filter search results so that only the returned results are displayed on the datagrid. i have seen this example http://udayms.com/flex/cgrid/UseCustomGrid.html which uses the filtering method, however i find it quite difficult to implement becuase it does not extend the datagrid.
hth,
--nimer
I'm trying to create a custom property editor, very similar to some of the older IDE's made by Borland and Microsoft. Basic premise was to use a DataGrid with the "property" column on the left, and the editable "value" column on the right. I've found how to change the background color of a column (using the backgroundColor property you've used here), however I haven't found a way to assign a CSS class to a particular column (or cell). Ultimately, I'd like to give each cell in the "properties" column a raised effect using this CSS, so that there is some separation between the rows. Have you run across this situation, and if so, how did you go about solving this?
package comps
{
import mx.controls.DataGrid;
import flash.display.Sprite;
public class ColorDataGrid extends DataGrid
{
public function setColor(row:Number, color:Number):void{
drawRowBackground(Sprite(listContent.getChildByName("rowBGs")), row, this.rowInfo[row].y, rowInfo[row].height, color, verticalScrollPosition+row);
}
}
}
However, using the rowColorFunction I added and having it called from the drawRowBackground() method let's you define a different color for each row - based on the actual data of that row at runtime. Which is why I needed to override the drawRowBackground() method.
If you wanted the rows to auto-color based on the data, here's what you would do:
package comps{
import mx.controls.DataGrid;
import flash.display.Sprite;
public class ColorDataGrid extends DataGrid{
private var funct:Function;
public function set func(fun:Function):void{
this.funct=fun;
drawRowBackgrounds();
}
override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint, dataIndex:int):void{
if(funct==null){
super.drawRowBackground(s, rowIndex, y, height, color, dataIndex);
return;
}
var actualColor:Number;
var data:Object=dataProvider.source[rowIndex];
var genColor:Number=funct.call(this,data);
if(genColor==-1) actualColor=color;
else actualColor=genColor;
super.drawRowBackground(s, rowIndex, y, height, actualColor, dataIndex);
}
}
}
This way, the user can define a function that will say what the color should be for a row based on the row's data. This code has also been tested and works perfectly. This is a whole lot simpler than redefining tons of functionality.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:comps="comps.*" creationComplete="start()">
<mx:Script>
<![CDATA[
public function start():void{
cdg.func=colorSettor;
}
public function colorSettor:Function=function(data:Object):Number{
if(data==null) return -1;
if(data.Album=="Slanted and Enchanted") return 0xFF0000;
return -1;
}
]]>
</mx:Script>
<comps:ColorDataGrid id="cdg">
<comps:dataProvider>
<mx:Object Artist="Pavement" Price="11.99" Album="Slanted and Enchanted" SalePrice="true"/>
<mx:Object Artist="Pavement" Price="9.99" Album="Brighten the Corners" SalePrice="false"/>
</comps:dataProvider>
<comps:columns>
<mx:DataGridColumn dataField="Artist"/>
<mx:DataGridColumn dataField="Album"/>
<mx:DataGridColumn dataField="Price"/>
<mx:DataGridColumn dataField="SalePrice"/>
</comps:columns>
</comps:ColorDataGrid>
</mx:Application>
This too has been tested.
Ok I'm confused, how do you think I did it? What "tons of functionality" do you think I wrote? I have my component posted below - looks pretty simple to me. Besides, you're doing the exact same thing I do, so what are you talking about.
Also your countering yourself in your posts. First you say "You didn't need to redefine drawRowBackground" you can do it another way. BUT in your 2nd post shows that you've removed the setColor() function and you have redefined the "drawRowBackground" method and you are using a callback. Just like I do and just like your said not to in your 1st post. Did you change your mind?
Honestly, It's cool if you find a better way to do something, but telling me I did it wrong and then you post the same basic code as me - that's just wrong. Thanks for stealing my code and calling it your own.
Also with your first post you show a setColor() function on the datagrid itself. Which means to use it you need to call it from outside of your component. So outside of your component you need to loop over the array collection and call the function for each item in the dataProvider. This means that you DOUBLED the time it takes to render the datagrid. First the datagrid loops over the dataprovider and then you do again to set the colors. And what happens when the data in the grid is sorted? (this shouldn't work unless you loop over it twice again and call your setColor function for every row again,)
Overriding the drawRowBackgroud() method and using a callback, limits this looping to 1 time. But I see from your 2nd post that you've figured this out.
As you can see here my component also defines a call back function and overrides the drawRowBackground method. Here is the total code for my component. Notice, that it is the same logic your doing in your 2nd post. And I still don't see the "tons" of functionality you keep talking about. please elaborate.
-----------------------------
public class CustomRowColorDataGrid extends DataGrid
{
private var _rowColorFunction:Function;
public function set rowColorFunction(f:Function):void
{
this._rowColorFunction = f;
}
public function get rowColorFunction():Function
{
return this._rowColorFunction;
}
override protected function drawRowBackground(s:Sprite, rowIndex:int,
y:Number, height:Number, color:uint, dataIndex:int):void
{
if( this.rowColorFunction != null )
{
if( (this.dataProvider as ArrayCollection) != null && dataIndex < (this.dataProvider as ArrayCollection).length )
{
var item:Object = (this.dataProvider as ArrayCollection).getItemAt(dataIndex);
color = this.rowColorFunction.call(this, item, color);
}
}
super.drawRowBackground(s, rowIndex, y, height, color, dataIndex);
}
}
-------------------------------
Secondly, when I wrote the first example I was thinking in the mindset of "You wouldn't ever need to change every color value and it is much more efficient to simply call 1 function once than to call it once for each row." Using this mentality, the client for this component would search through the dataProvider once, but the color function would only be called when the color would differ from the default. Later on you mentioned that you wanted each row's color to change based on the data in the row so I showed how you could, in less code, write a component that would do what you wanted.
Thirdly, when I said "tons of functionality" I was mistakened. After I had adapted my code to what you wanted I opened your source code and the little scroll bar on the right side of the window got really tiny (because of all of your comments) and I saw a bunch of imports (Shape, FlexShape, Graphics, AbstractEvent, ArrayCollection, Event) that I hadn't needed so I figured you had redefined the entire display for a DataGrid. To back up this assumption, I saw you had messed with updateDisplayList, which I hadn't needed to do. When I saw you had a pretty big file compared to mine, I didn't want to read through all of your source code. I simply wanted to tell you that there was an easier way.
Still, you did have some extra stuff that simply added to your file size. I just wanted to help.
Anyway, I understand if you think I stole your code because yours and mine do look very similar (although mine dropped some stuff with no functionality loss) and I won't blame you if you don't believe me when I say that I wrote my code myself and saw my file was way smaller than yours and posted it to help you out, because our files are so similar, but I did write my own code.
Lastly, your code includes much more that what you posted. Here's what the source in the zip file linked to at the top of this page says (comments removed):
package com.mikenimer.components.datagrid
{
import mx.controls.DataGrid;
import flash.display.Shape;
import mx.core.FlexShape;
import flash.display.Graphics;
import flash.display.Sprite;
import mx.rpc.events.AbstractEvent;
import mx.collections.ArrayCollection;
import flash.events.Event;
public class CustomRowColorDataGrid extends DataGrid
{
private var _rowColorFunction:Function;
public function CustomRowColorDataGrid()
{
super();
}
public function set rowColorFunction(f:Function):void
{
this._rowColorFunction = f;
}
public function get rowColorFunction():Function
{
return this._rowColorFunction;
}
private var displayWidth:Number;
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (displayWidth != unscaledWidth - viewMetrics.right - viewMetrics.left)
{
displayWidth = unscaledWidth - viewMetrics.right - viewMetrics.left;
}
}
override protected function drawRowBackground(s:Sprite, rowIndex:int,
y:Number, height:Number, color:uint, dataIndex:int):void
{
if( this.rowColorFunction != null )
{
if( dataIndex < (this.dataProvider as ArrayCollection).length )
{
var item:Object = (this.dataProvider as ArrayCollection).getItemAt(dataIndex);
color = this.rowColorFunction.call(this, item, color);
}
}
super.drawRowBackground(s, rowIndex, y, height, color, dataIndex);
}
This is 1 package declaration, 8 imports, 2 functions and 1 private variable more than what you posted. Did your link go to the incorrect version of your component, or did you realize that I was correct and that a lot of the code wasn't doing anything?
But next time you want to publically comment on other peoples code and start comparing line numbers (which is pointless, since it's the functionality that matters) make sure you really understand the code your commenting on. People can get defensive about their code (as I did).
Yea I have some extra imports, left over?s, that don't do anything (and don't matter). And I do have a straight copy of the updateDisplayList() function from mx:DataGrid - but that's because when I wrote this component I was running into one of those famous/annoying private scope conflict with updateDisplayList. That must of been fixed in the latest version of flex - which is why you didn't need it. This is also why I didn't include it - I only posted the code that mattered for this functionality.
Thanks for providing the source. It helped me to modify DG further.
I only wish I had stumbled across this 5 months ago. I search around quite a bit to find this functionality and found only a few partially related implementations. So, of course I had to write my own.
You can see it here: <a href="http://www.macsims.com/blog/?p=20">http://...;
I took basically the same approach that you did in extending DataGrid and overriding drawRowBackground. Because I wanted mine to work with any collection, not just ArrayCollections, I used a cursor to access the collection. There a few other minor differences, but you can look at the code if you are interested.
is it possible to change background color of an individual cell, not the whole row?
Thanks,
Alex.
saved me time!
I'm going to update the blog and my util function with your function, since it is more accurate.
http://www.intronhk.com
http://www.lucky-int.com/
http://www.come-in-china.com/
http://www.interiordesign-office.com
http://www.china-lapelpin-center.com
http://www.zipperpuller.com
http://www.chinahk-logistic.com
http://www.kingfoxcigarette.com
http://hk.geocities.com/thaiboxing2008
http://www.logisticthl.com
http://www.barcodehk.com
http://www.wygou.com
http://www.clothing-wholesales.com/
http://www.96177.com
http://www.terminals-blocks.com
http://www.33529.com
http://www.green-litehk.com
http://www.it529.com
http://www.taihingjewelry.com
http://sg.geocities.com/toner_cartridgeparts/
http://www.internationalremovals-ltd.com/
http://www.interiordesign-office.com/
http://www.leatherhandbagmanufacturer.com
http://www.interiordesignhk.com
http://www.3cmicro.com/
http://www.go-china-travel.com/