Sunday, 26 November 2006

textRollOverColor in Flex DataGrid part 2

In my last post I presented a simple way to customise the DataGridItemRenderer class so the Flex DataGrid control would use the textRollOverColor and textSelectedColor styles correctly. But I also noted that the solution would not fix the DataGrid header items... well, here's my solution to that problem.

First of all, it's worth noting that there are two main reasons why a custom DataGridItemRender is not adequate for the DataGrid header items. The first reason is that the renderer's validateNow() method is almost never called for header items, even though it also called correctly, and often, for the data items. And the second reason is this: the validateNow() method (both the original version, and my custom version) depends on two DataGrid methods in order to determine which text style to use. Those methods are isItemHighlighted() and isItemSelected(). However, neither of those methods work for header items - they both always return false, making them of no use in determining text color for header items.

So, to correctly use the textRollOverColor and textSelectedColor styles, I created a custom DataGrid control that overrides three mouse handlers: mouseDownHandler(), mouseOverHandler(), and mouseOutHandler(). The overrides are as follows:

override protected function mouseDownHandler(event:MouseEvent):void {
  super.mouseDownHandler(event);
  if (enabled && sortableColumns && isHeaderItem(event.target)) {
    var col:int = listItems[0].indexOf(event.target);
    if (columns[col].sortable && event.target.hasOwnProperty("textColor"))
      event.target.textColor = event.target.getStyle("textSelectedColor");
    }
  }

override protected function mouseOverHandler(event:MouseEvent):void {
  super.mouseOverHandler(event);
  if (enabled && sortableColumns && !event.buttonDown && isHeaderItem(event.target)) {
    var col:int = listItems[0].indexOf(event.target);
    if (columns[col].sortable && event.target.hasOwnProperty("textColor"))
      event.target.textColor = event.target.getStyle("textRollOverColor");
  }
}

override protected function mouseOutHandler(event:MouseEvent):void {
  super.mouseOutHandler(event);
  if (isHeaderItem(event.target) && event.target.hasOwnProperty("textColor"))
    event.target.textColor = event.target.getStyle("color");
}

protected function isHeaderItem(item:Object):Boolean {
  return ( showHeaders && (listItems[0].indexOf(item)>=0) );
}

So now if we put together the custom DataGridItemRenderer from the last post, and a custom DataGrid control with the above overrides, then we have a DataGrid control that uses the textRollOverColor and textSelectedColor styles almost perfectly. I say "almost" because the DataGrid control will still use the base text color when re-ordering columns by dragging, but other than that, it all works as I'd expect it to :)

I've put together a really simple demo app, which you can play with here, and/or view the source.

Paul.

Labels:

Saturday, 25 November 2006

textRollOverColor in Flex DataGrid

According to Adobe's documentation, the Flex DataGrid control supports the textRollOverColor CSS property. But this past week I discovered that that this is not the case :( A quick search revealed that I'm not alone in this discovery, but it seems that no one has proposed a useful solution yet... so I went about creating my own :)

So, why doesn't it work as documented? Well, a quick check revealed that the relevant text colors are supposed to be set in the DataGridItemRenderer's validateNow() method. That method sets the item's explicitColor property, and then calls the super.validateNow() method to update the display. However, the super (UITextField) class' validateNow() method does not update the text format unless the styleChangedFlag property is set - but that flag is not being set. Since the styleChangedFlag property is marked as private, I can only assume that it is meant to be set by the relevant setter functions, but as there is no setter function for the explicitColor property, the styleChangedFlag is not set.

So, what to do about it? Well, I found easiest solution was to write a custom DataGridItemRender that overrides the super class' validateNow() method. The new validateNow() method looks like this:

override public function validateNow():void {
  super.validateNow();
  if (data && parent && !(data as DataGridColumn)) {
    var dg:DataGrid = DataGrid(listData.owner);
    if (dg.isItemHighlighted(listData.uid))
      textColor = getStyle("textRollOverColor");
    else if (dg.isItemSelected(listData.uid))
      textColor = getStyle("textSelectedColor");
    else textColor = getStyle("color");
  }
}

It's pretty straight-forward - based very closely on the DataGridItemRenderer's validateNow() method, but instead of setting the item's explicitColor property, it goes straight ahead and sets the underlying text field's color directly. You might also notice the reference to the textSelectedColor style - it turns out that one was also broken, but this override fixes it too.

Something you won't find in the DataGridItemRenderer's validateNow() method is the reference to the "data as DataGridColumn" clause at the end of the first if statement... I won't go into the details now, but this custom renderer won't help for DataGrid header items, and so that clause prevents this override from doing anything to header items. In short, validateNow() is almost never called for DataGrid header items, so they have to be treated specially.

Well, that's it for now... in my next post I will show how to fix the DataGrid header items (need a custom DataGrid control), and link to a demo app that you can test, and view the source of :)

Paul.

Labels: