Tuesday, June 10, 2008

CalculatedMeasures in Flex OLAP

I wrote a small sample which shows how a Custom Aggregator can be written and used to simulate a Calculated measure in Flex OLAP. Of-course, this solution may not work for all requirements but can be used atleast in some scenarios. The source is here.

Sunday, June 1, 2008

Here is a GroupingCollection with good performance !

I created a new GroupingCollection2 (by tweaking the default implemenation) which has better performance compared to the GroupingCollection. Please find the swc here.

Replace the GroupingCollection instance in any application with GroupingCollection2 and check the performance. When I tested it with 8K records with 4 grouping fields it was able to group in 5 seconds which seems to be good (in comparsion).

Please note this has not gone through extensive testing. I would be glad to fix any bugs found.

If you are one of those who doesn't have patience to build and test an app, here is the one I built. Here is the source.

If you think it is worth your time drop a note about your findings !!

Wednesday, May 28, 2008

Grouping XML data using GroupingCollection

I was under the impression this doesn't require any sample as it should be very easy to write one. But frequent questions on the forums have resulted in this post.

There is not much to describe so :

<mx:XML id="inputData" >
 <Tulokset>
 <Tulos id="xxx" group="xx" />
 <Tulos id="xxx" group="xx" />
 <Tulos id="xxx" group="xx" />
 </Tulokset>
</mx:XML> 
 
<mx:AdvancedDataGrid creationComplete="gc.refresh()">
 <mx:dataProvider>
  <mx:GroupingCollection source="{inputData.Tulos}" id="gc">
   <mx:Grouping>
    <mx:GroupingField name="@group" />
   </mx:Grouping>
  </mx:GroupingCollection>
 </mx:dataProvider>
 <mx:columns>
  <mx:AdvancedDataGridColumn dataField="@id" />
 </mx:columns>
</mx:AdvancedDataGrid>

The source is here.

Update: The below example shows how to use grouping when the data is in child nodes instead of attributes. This is a bit tricky because GroupingCollection is derived from HierarchicalData which treats child nodes as children by default. This leads to problems when we try to display the data after grouping. The trick is to make use of the childrenField property and point it to something like "undefined".

<mx:XML id="inputData" >
 <Tulokset>
  <Tulos>
     <id>xx1</id>
     <group>xxx</group>
  </Tulos>
  <Tulos>
     <id>xx2</id>
     <group>xxx</group>
  </Tulos>
  <Tulos>
     <id>xx3</id>
     <group>xxx</group>
  </Tulos>
 </Tulokset>
</mx:XML> 
 
<mx:AdvancedDataGrid creationComplete="gc.refresh()">
 <mx:dataProvider>
  <mx:GroupingCollection source="{inputData.Tulos}" id="gc" childrenField="undefined">
   <mx:Grouping>
    <mx:GroupingField name="group"  />
   </mx:Grouping>
  </mx:GroupingCollection>
 </mx:dataProvider>
 <mx:columns>
  <mx:AdvancedDataGridColumn dataField="id" />
 </mx:columns>
</mx:AdvancedDataGrid>

The source is here.

Please note that childrenField is also helpful when we are trying to display XML data with data and children as child nodes. Use it to point to the proper node name which contains the children. In the following example we need to set childrenField="details" to work properly.


<mx:XML id="special">
<rows>
<row>
  <name>A</name>
  <fund>100</fund>
  <details>
   <row>
   <name>a</name>
   <fund>20</fund>
   </row>
   <row>
   <name>b</name>
   <fund>80</fund>
   </row>
  </details>
</row>
<row>
  <name>C</name>
  <fund>200</fund>
  <details>
   <row>
   <name>a</name>
   <fund>80</fund>
   </row>
   <row>
   <name>b</name>
   <fund>80</fund>
   </row>
   <row>
   <name>c</name>
   <fund>40</fund>
   </row>
  </details>
</row>
</rows> 
</mx:XML>

<mx:AdvancedDataGrid >
   <mx:dataProvider>
       <mx:HierarchicalData source="{special.row}" childrenField="details" >
       </mx:HierarchicalData>
   </mx:dataProvider>
 <mx:columns>
  <mx:AdvancedDataGridColumn dataField="name" />
  <mx:AdvancedDataGridColumn dataField="fund" />
 </mx:columns>
</mx:AdvancedDataGrid>

Object/Array and ObjectProxy/ArrayCollection binding problems

Take a look at discussion here.

Many seems to be stumble upon this problem. Very similar to Object not dispatching any event for property value changes Array also doesn't dispatch any event for items added/deleted from it. Due to this when an object or array is used in the dataProvider of a list based control or comboBox etc any updates to the object or array would not get reflected in the UI.

The following sample should make it clear.

The first DG is fed with a ArrayCollection of Objects and the second DG has ObjectProxies. Select a item in the DGs and edit the values displayed in the text inputs. To complete the editing hit "Enter". Notice that in the first DG the values don't get reflected immediately where as in second DG it does.

To support Object based dataProviders the list based controls in Flex make a explicit call to ICollectionView.itemUpdated() to force a change event. This is the reason edits in list based controls get propagated to other list based controls when both of them are using the same colletion as input.

The source is here.

As noted in the forum discussion, when HierarchicalData is built using Objects/Arrays (to represent children) the changes to the Array would not get propagated to ADG. Hence it is necessary to construct the HierarchicalData using ObjectProxies and ArrayCollections.

Thursday, May 15, 2008

Fixing itemRenderer memory leak in AdvancedDataGrid on columns change

Please read the discussion about memory leak here.

I think I found the workaround for this issue. We need to override the set columns method in a extended ADG class and add the code to free the dictionary holding on to the itemRenderers. Here is the code.

<?xml version="1.0" encoding="utf-8"?>
<mx:AdvancedDataGrid xmlns:mx="http://www.adobe.com/2006/mxml">
 <mx:Script>
  <![CDATA[
   override public function set columns(value:Array):void
   {
    super.columns = value;
    itemRendererToFactoryMap = new Dictionary(false);
   }
  ]]>
 </mx:Script>
</mx:AdvancedDataGrid>

Thursday, April 17, 2008

Why do people shy away from giving useful 360 degree feedback?

I know human psychology is one of most complex things in the universe! But still fail to understand why people shy away from giving useful 360 degree feedback even when it is requested?

I can understand the case where feedback provider holds back this thoughts when there is a fear that his or her name would be revealed and it may spoil a good relationship or cause unnecessary trouble in office or life.

But when the feedback is being provided for a good cause why do people shy away from giving a complete feedback? Why do they provide 180 degree feedback which only leads in a direction but doesn't complete the circle leaving the seaker to ponder all over for the correct point?

Take example of ADG performance poll. There are votes for good, ok and bad. Good and OK are understandable because it is kind of known as testing and many in house scenarios has proved that the performance is acceptable (if not great) in many use cases. But BAD was (kind of) unexpected because of so many hours of testing which has gone in.

Bad performance can arise because of many reasons. For example it can be due to bad performance of GroupingCollection as Doug has kindly pointed out. It can be due to complex custom itemRenderers being used. It can be due to a genuine problem in a particular control flow inside ADG. But without a complete feedabck (pointers to the exact problem faced if not samples) it would be impossible to work on anything. It would be similar to searching for a black coat in a dark room or worse for a non existent coat in a dark room.

This also goes along the same lines as my post about software patches. If customers don't make use of available source and public bug base and provide a good feedback even a very good intention/path provided to help customers goes waste.

In this context I don't know whom to blame for the situation as customers we find ourselves in, because we are not making full use of the easy avenues open in front of us to make a product worthy of the money we have paid for. Or is it that customers have accepted the situation and are showing indifference towards demanding what they rightly deserve?

Wednesday, April 9, 2008

Software patches good/bad/ugly?

This topic has been in my mind for quite sometime now. It used to reach the top and remain their for sometime whenever Maxthon poped up "A new version available with fixes would you like to install?" dialog.

In early days when the auto update feature was unheard of I used to think software patches are ugly. One needed to keep visiting the website of the product frequently to check whether a patch was available to fix the problems one is facing with the product.

Once patches became more and more popular and available I used to think them as bad. That was becuase sometimes patches used to have more serious problems than the original product. And once you have applied a patch it was not easy to remove it and a complete uninstall-install cycle was required to get back to square one. The "The update requires a system restart. Restart now?" screen only served to add more fuel to the bad feeling.

Now that auto updates are default and internet is thought as part of your desktop I have beginning to think that patches are good! I think the quality of patches is better now compared to earlier days, may be due to companies having automated testing tools to assure quality.

My latest thought is that "Not having (releasing) patches is really really a bad thing". Pushing a product out of the door having already planned for patches is bad. But at the same time knowing that customers are suffering with a released product, asking them to wait for the next release (which would be more than a year later) is just pathetic. Because every release comes with its own new features and new bugs. Companies don't push out new releases without new features as they won't earn any money with a release (patch) aimed at fixing serious bugs or performance issues in the previous release. The problem with this kind of approach is that the attitude about a bug changes in the following way.

Version 1.0 release : We are releasing a great product which would help our users in a great way by boosting their productivity. We would fix all serious bugs soon.

Customer: Great. Let me use it.

Version 2.0 release : We have lot of new features for you. In this release we have addressed all serious bugs (read it as bugs reported by great parteners and volume customers) in the previous release.

Customer: But what about other serious bugs faced by me?

Company: We would fix all bugs soon.

Customer: They are only fixing bugs for the new features and few more bugs from volume customers.

Version 3.0 release : We have lot of new features. We have revelutionized the workflow. We have rewritten many parts of the code to boost peformance. etc... Of-course now we are open-source !

Customer: Now it is Open-source ? Great! But what about bugs which were in 1.0 release?

Company: Oh! our customers have lived with these bugs for more than 2 years, now they have almost stopped complaining.....they can live with it! If they complain more, let us ask them to upgrade to the new version and follow a new workflow/API for the same feature which we have introduced. And anyway we are open-source let them grab the source and fix the bug themselves. We can't spend our precious development time over fixing those old bugs.

Customer: Where I can find a developer and time to study this piece of open-source product and fix bugs for me? ...?....?

As customers who have paid their hard earned money I think we deserve a better treatment. Don't you think so?

Using Flex OLAP/OLAPDataGrid for doing Pivoting computations

Satish has developed a new PivotComponent using OLAPDataGrid and OLAP which can be used to perform pivot analysis of data.

The sample can be found here. Drag and drop one or more dimensions into the column and row fields. The results would get displayed as soon as the drop happens. Drag and drop more dimensions into column, row or slicer fields. Use the drop down attached to dimension button to pick specific members of the dimension to do slicing. The sample uses OLAPChart and OLAPDataGrid extensions to display the OLAPResult generated.

Description and source

Monday, April 7, 2008

Custom Aggregator sample for Flex OLAP

Here is a sample which shows a percent custom aggregator used for OLAP. The sample shows computation of percentage of values to the total value. The total value is assumed to be known before hand.

The PercentAgg class implements the IOLAPCustomAggregator interface. The constructor takes the total value as input and uses it to compute the percentage of values in the computeEnd and computeObjectEnd functions.

The sample also shows how OLAPAttribute class can be extended to OLAPTotal to replace the "(All)" value with a custom name "Total".

The sample also shows how OlapDataGrid can be created in AS and itemRendererProviders can be supplied to it and values displayed in ODG can be styled. Here is the complete source.

Writing a Custom Aggregator for Flex OLAP

Writing a custom aggregator for OLAP involves implementing the IOLAPCustomAggregator interface. It is easy to write a custom one based on the default ones. The default SUM, AVARAGE, MIN, MAX and COUNT aggregators source can be found in the mx.olap.aggregators directory.

The SUM, MIN, MAX and COUNT aggregators are all simple and acutally doesn't require any special handling as they are linear. The AVARAGE aggregator is special because avarage of avarages wouldn't give the correct result and hence we need to maintain proper information to arrive at the correct result.

The following sample shows how this is achieved.

IOLAPCustomAggregator has 6 functions can they can be divided into two sets.

1. computeBegin,computeLoop and computeEnd. These functions are called to initialize, compute and return the simple aggregation value.

Suppose 1..8 are the input values and they need to avaraged as two different sets 1..5 and 6..8. The sequence of function calls is one computeBegin, one or many calls to computeLoop and one call to computeEnd. 1. computeBegin can be used to prepare for a fresh computation. 2. computeLoop is the place where the acutal computation takes place. This function would get called repeatedly to add new input values to the aggregation. 3. computeEnd is supposed to return the value of the computation. This is called when cube decides that no new input values would get added to this aggregation and it is ready to receive the final value.

2. computeObjectBegin, computeObjectLoop, computeObjectEnd. These functions are called to initialize, compute and return the aggregation of aggregations.

The second set of calls would be, one call to computeObjectBegin, one or many calls to computeObjectLoop (depending on the number of aggregated values) and one call to computeObjectEnd.

As shown in the image these functions are supposed to compute the avarage of avarages. As the first set of functions saved the sum and count values separately the task is easy. We need just compute the sum of both these values from each avarage object.

In computeObjectBegin the first value to start the computation is passed. In computeObjectLoop the the computation is carried forward with additional values. In computeObjectEnd the result of the computation should be returned. Hope this helps!

Sunday, April 6, 2008

AdvancedDataGrid performance poll

Hello AdvancedDataGrid users !

I have added two polls to the end of my blog. One for suggesting in what areas should ADG performance improve. Second one for overall performance of ADG in your projects/apps.

Please spare some time out of your busy schedule and take the polls.

I would urge you take both the polls because just taking one poll many not deliver the exact picture. For example if you say vertical scrolling performance needs to be improved we don't know whether the app is hurt by its performance or it is that it would do better with a performance boost.

Thanks for your time !

Of-course if you would like to send me a sample showing poor performance in a particular/general context please do by sending a mail

Update:I am seeing more number of votes on Bad than expected :(

I would appreciate if you leave a comment as to which particular area of ADG you found the performance to be so poor that it forced you turn it down. I am asking this because votes are also going in favor of Good and OK. It would be of great help if I get a pointer in that direction so that we can study that area of the code more and see what performance improvements can be done. Thanks!

Thursday, March 27, 2008

Documentation about Creating a Automation delegate for custom components in Flex

Documentation and sammple about how to go about creating an automation delegate for custom components in Flex is detailed here

It also has step by step description of delegate creation for the RandomWalk component created by Ely.

Hope it helps!

Wednesday, March 19, 2008

AdvancedDataGrid, Charts and OLAP source gets unziped in FlexBuilder Pro version

What I thought is a known fact seems not so widely known. Hence posting it.

When FlexBuilder Pro version is bought and a valid licensce key is entered the source code for the complete datavisulation.swc would get unzipped and get placed under

<FlexBuilder installation direcotry>sdk/3.0.0/fbpro/...

directory. Which means users would get access to AdvancedDataGrid, Charts and OLAP source code.

It is not open-source but source is available for you to take a look and override functions with more confidence and knowledge of the internal workings. And of-course suggest/complain !

Enjoy !

Thursday, March 13, 2008

Introducing OLAPTimeDimension for OLAP

In this sample though the data contained date information we didn't use it while building the OLAP cube. The reason was simple. The default OLAP implmentation in Flex 3 cannot handle dates in that format. It requires the date to be broken down into its components like year and month. If half year and quarter information is required that needs to be added to the flat data. That is a great pain point.

Here is OLAPTimeDimension to the rescue. It can be introduced as any other dimension in the OLAPCube schema and then configured to return year, half year, quarter, month and day in any combination.

<mx:OLAPCube id="salesCube" >

 <mx:OLAPDimension name="SalesData" >
   <mx:OLAPAttribute name="Company" dataField="company" />
   <mx:OLAPAttribute name="Region" dataField="region" />
   <mx:OLAPAttribute name="Market" dataField="market" />
   <mx:OLAPAttribute name="Product" dataField="product" />
    
   <mx:OLAPHierarchy name="Region-Market-Store" >
    <mx:OLAPLevel attributeName="Company" />
    <mx:OLAPLevel attributeName="Region" />
    <mx:OLAPLevel attributeName="Market" />
    <mx:OLAPLevel attributeName="Product" />
   </mx:OLAPHierarchy>
 </mx:OLAPDimension>
  
 <local:OLAPTimeDimension name="Years" dataField="date" />
  
 <mx:OLAPMeasure name="Revenue" dataField="revenue" />
  
</mx:OLAPCube>

Using this we get the following result where in we can query revenue for different years.

It can be easily extended to query quarters and months by setting includeQuarter and includeMonth to true on OLAPTimeDimension. The source is here.

Simple uses of OLAP

In Flex 3 OLAP APIs were introduced. And here is a sample showing how it can be used in simple data processing.

The sample shows how a companys product revenue over regions and markets can be compared. Click the "Create cube" button to process the input data which looks like this

<row>
  <company>Fiction</company>
  <region>Asia</region>
  <market>Digital publishing</market>
  <product>Frames</product>
  <date>3/31/2005</date>
  <revenue>10</revenue>
</row>

and build the OLAP cube. Clicking on the "Query Revenue with Product by Region" button would display the result in OLAPDataGrid. The query returns the total revenue comparing product by region across all years (2005 and 2006 in the sample data case). The last row and last column display the totals by product and totals by region respectively.

Now different markets can be selected from the ComboBox and revenues from different markets can be compared.

The source can be found here.

Wednesday, March 12, 2008

Creating OLAPCube in AS

In Flex 3 OLAP APIs were introduced and there have been many queries about how to create a OLAPCube in AS instead of defining it in MXML. Here is a sample :

private var salesCube:OLAPCube;

private function createCube():void
{
 salesCube = new OLAPCube();

 var dim1:OLAPDimension = new OLAPDimension("SalesData");

 //add attributes to the dimension
 var attr1:OLAPAttribute = new OLAPAttribute("Region");
 attr1.dataField = "region";

 var attr2:OLAPAttribute = new OLAPAttribute("Market");
 attr2.dataField = "market";

 var attr3:OLAPAttribute = new OLAPAttribute("Store");
 attr3.dataField = "store";

 var attr4:OLAPAttribute = new OLAPAttribute("LineOfBusiness");
 attr4.dataField = "line_of_business";

 var attr5:OLAPAttribute = new OLAPAttribute("Model");
 attr5.dataField = "model";

 dim1.attributes = new ArrayCollection([ attr1, attr2, attr3, attr4, attr5 ]);

 //add a user defined hierarchy   
 var regionHierarchy:OLAPHierarchy = new OLAPHierarchy("Region-Market-Store");

 //define the levels of the hierarchy
 var level1:OLAPLevel = new OLAPLevel();
 level1.attributeName = "Region" ;

 var level2:OLAPLevel = new OLAPLevel();
 level2.attributeName = "Market" ;

 var level3:OLAPLevel = new OLAPLevel();
 level3.attributeName = "Store" ;

 var level4:OLAPLevel = new OLAPLevel();
 level4.attributeName = "LineOfBusiness" ;

 var level5:OLAPLevel = new OLAPLevel();
 level5.attributeName = "Model" ;

  //add levels to the hierarchy
  regionHierarchy.levels = new ArrayCollection([ level1, level2, level3, level4, level5 ]);

  //add hierarchy to the dim
 dim1.hierarchies = new ArrayCollection([ regionHierarchy ]);

 //more dimensions can be defined here

 var measure:OLAPMeasure = new OLAPMeasure("Revenue");
 measure.dataField = "revenue" ;

 //more measures can be defined here

 //add the dimensions and measures to the cube.
 salesCube.elements = [ dim1, measure ];
}
This creates the same OLAPCube as the following in MXML
<mx:OLAPCube id="salesCube" >

 <mx:OLAPDimension name="SalesData" >
   <mx:OLAPAttribute name="Region" dataField="region" />
   <mx:OLAPAttribute name="Market" dataField="market" />
   <mx:OLAPAttribute name="Store" dataField="store" />
   <mx:OLAPAttribute name="LineOfBusiness" dataField="line_of_business" />
   <mx:OLAPAttribute name="Model" dataField="model" />
    
   <mx:OLAPHierarchy name="Region-Market-Store" >
    <mx:OLAPLevel attributeName="Region" />
    <mx:OLAPLevel attributeName="Market" />
    <mx:OLAPLevel attributeName="Store" />
    <mx:OLAPLevel attributeName="LineOfBusiness" />
    <mx:OLAPLevel attributeName="Model" />
   </mx:OLAPHierarchy>
 </mx:OLAPDimension>
  
 <mx:OLAPMeasure name="Revenue" dataField="revenue" />
  
</mx:OLAPCube>

Monday, March 10, 2008

DateChooser with support for backgroundColoring some dates

I got a query recently asking whether it is possible to have the DateChooser highlight certain special dates with a different background color. The query was accompanided by a DateChooser code which achieved this by using the TexField.backgroud and TexField.backgroudColor properties (similar to my other posts for DG row background coloring). But the draw back of the approach was that selection and roll over feedback was not working.

I took a look at DateChooser and CalendarLayout and realized that with the current design/implmentation it was not possible to add the backgroundColor support. The selection and rollOver indicators were lower in z order than date UITextFields.

I modified the CalenderLayout code to have three layers (UIComponents). One for background, second for indicators and third one for the dates. After modifying the addChild* and removeChild calls refer to the appropriate layers, the remaining work was to add "speicalDates" and "specialDatesColor" property and wire them up at appropirate places (commitProperties and updateDisplayList) to achive the following result :

I have randomnly generated the special dates in the above sample to keep the sample live on any day it is viewed!

I have added a alpha value of 0.7 in DateChooserIndicator to make the selection indicator show through the backgroundColor. Feel free to tweak this value to your flavor.

The project source to build the Flex3Ex.swc is here. It contains the modified DateChooser and CalendarLayout classes. I posted the complete Flex project here because Travis pointed out that it is difficult for newbies to understand how to use the component source files. Thanks Travis!

The source for the sample app is here.

The components can be further enhanced to support multiple specialDates (array of array of dates) and multiple specialDatesColors.

Update: I got to implement this. Source is available here.

Update: I have updated the source to support the following:

Now dates can be specified as strings "06/01/2009" (for 1st June 2009) but make sure that it succeeds to pass through Date.parse function and returns a valid Date.

Date ranges can be specified as follows:

{ rangeStart:"06/01/2009", rangeEnd:"06/05/2009" }

Update: There seems to be some problem with library path handling etc due to which a swc is not getting picked up properly in FB. The work around I came up for this is to include the Flex3Ex/src directory in the source path of the project as shown here

Friday, February 29, 2008

GroupingCollection to group mails on Date and Subject

Here is a sample I wrote using GC to group mails according to Date and Subject very similar to Outlook. This sample serves to show the complex ways in which GC can be configures and used.

Subject grouping: The sample uses GroupingField.groupingFunction and GroupingField.compareFunction to achive the Subject grouping even when subject line text is prefixed with RE:, FW: etc. Grouping.groupingObjectFunction is used to supply a custom grouping object MailGroup which has a date field getter function to return the maximum date of the child mail items.

Date grouping: Again groupingFunction is used to form diffrent groups of mails based on the date value.

The "show unread mails" option can be used to remove the read mails from the view. This shows filtering applied to the HierarchicalCollectionView which ADG creates internally when a GroupingCollection (or anything which implements IHierarchicalData) is supplied as the dataProvider.

The "highlight unread mails" option uses the ADG.styleFunction property to hightlight the unread mails by making them bold.

The source is available here

Please note I have a tweak in the sample to keep it ever-green, that is the date values of the mail items are generated at runtime than hard coded.

Monday, February 25, 2008

PrintTileList along the lines of PrintDataGrid.

After seeing this query I thought it should be easy to get it to work. So spent 2 hours (that is little more than expected, isn't it!) and here is the result.

The source is here.

Saturday, February 23, 2008

Drag-drop between two containers instead of list based controls in Flex

I keep seeing many questions on forums about drag-drop of images between two containers or between a control and a container. So I thought a sample may help.

The sample shows a side toolbox from which cursors can be dragged and dropped into the canvas on the right hand side. The code is simple so I won't try to explain the steps in detail here.

The source for this sample is here.

The source for dragging and dropping items between a DataGrid and container can be found here.

Wednesday, February 20, 2008

Alternate row color in DataGrid per DataGridColumn

The idea came from a flexcoders post here. After attempting the previous example of row background colors in ADG I thought it should be easy to achive this which resulted in this post.

I extended the DGColumn and added a "alternatingItemColors" style it so that it would be possible to set it per column.

I extended the DGItemRenderer and used the background and backgroundColor properties to set the items background color. I added logic in a overridden validateNow function to pick up the color from the alternatingItemColors color array.

The drawback of this approach is that the selection feedback gets almost hidden by the background coloring which would not be the case if the drawRowBackground function is overridden.

The result :

The source is available here.

Changing row background color in AdvancedDataGrid

Changing the row backgroundColor in AdvancedDataGrid to highlight few rows is easy (though not possible out of the box).

I have used a different approach than overriding the drawRowBackground function used for DG. The drawback of this approach is that the selection feedback gets almost hidden by the background coloring. If the intention is to highlight a search or hightlight a particular values this should not be a serious problem.

The default itemRenderer ADGItemRenderer extends from UITextField (for perforamnce reasons) which doesn't have a backgroundColor style. Hence it is not possible to do it out of the box. But TextField (parent of UITextField) has background and backgroundColor properties which can be set to change the backgroundColor of the TextField.

I have created a new custom itemRenderer ADGItemRendererEx which derives from ADGItemRenderer. I have added a "rowColor" style to this and use this style value to set the TextFields background color.

By providing a styleFunction to ADG we get control to return any styles we want to be set on the itemRenderer. Any logic to determine a particular rows color can go into this function. The return value is a object with style names as properties and style values as proprety-values. For ex:

return { rowColor:0xFF0000, fontWeight:"bold" };

If function logic decides not to change the styles it can return an empty object.

The following sample shows the result. By typing any string in the text box the particular row containing the searched string is highlighted. User can choose to search for either company column or product column.

The source is available here.

Auto generating DataGrid columns for XML data

When we have dynamic XML data as follows

<employees>

<employee>

<name>Christina Coenraets</name>

<phone>555-219-2270</phone>

<email>ccoenraets@fictitious.com</email>

<active>true</active>

</employee>

<employee>

<name>Louis Freligh</name>

<phone>555-219-2100</phone>

<email>lfreligh@fictitious.com</email>

<active>true</active>

</employee>

</employees>

or in the form of attributes

<employees>

<employee name="Christina Coenraets"

phone="555-219-2270"

email="ccoenraets@fictitious.com"

active="true" />

<employee name="Louis Freligh"

phone="555-219-2100"

email="lfreligh@fictitious.com"

active="true" />

</employees>

and feed into DG or ADG it won't create columns auto-magically as it does for Object data type. This may be because it is difficult to decide whether you want to display the child nodes as columns or attributes of nodes. And may be it was not appropriate to clutter the API with one more flag to do this.

I wrote the following code to get this working.

private function generateCols(input:XMLList, 
 useAttributes:Boolean = false):Array
{
 var e1:XML = input[0];
 var columns:Array = [];
 var children:XMLList ;
 if (useAttributes)
    children = e1.attributes();
 else
    children = e1.children();
 for each(var child:XML in children)
 {
    var col:DataGridColumn = new DataGridColumn();
    col.dataField = useAttributes ? "@" + child.name() : child.name();
    
    var fieldName:String = child.name();
    col.headerText = fieldName.charAt(0).toUpperCase() + fieldName.substr(1);
    columns.push(col);
 }
 return columns;
}

The source is available here

The thread related to this post is here

Monday, January 28, 2008

Using AdvancedDataGrid as a tree control with sorting

The AdvancedDataGrid control can be configured and used as a simple Tree control with sorting support (which is not available in the Tree control).

The following example shows how.

When hierarchical XML (or Object based) data is fed into a HierachicalData (which implements IHierachicalData) and set as dataProvider to ADG it internally converts it into a HierarchicalCollectionView (implementing IHierarchicalCollectionView). The good news is that HierarchicalCollectionView provides the sort and filtering APIs which can be used to sort and filter the data very similar to flat data.

Click on the "Simple sort" button to see how the data gets sorted recursively. It doesn't do a good job as it mixes up parent and leaf items. If you are one those who expects everything according to the alphabetical order you may like it !

Click on the "Custom sort" button to see how the sorting can be customized to show the parent items at the top and leaf items at the bottom. The custom sort function uses the IHierarchicalData.canHaveChildren API to move the parent items up the ladder.

"Remove sort" does the obvious.

The source and data used for example.

Tuesday, January 22, 2008

Extending DGColumn and ADGColumn to handle nested data fields.

I have seen numerous questions regarding how to specify DataGridColumn.dataField when the data is nested (object or XML). Developers attempt to use "datafield1.datafield2.datafield3" to find out that it doesn't work !

The data might look like this :

<mx:XMLList id="testData" xmlns="">

<root>

<item>

<childItem>

<name>India Flex group</name>

<mail>indiafx@gmail.com</mail>

</childItem>

<location>India</location>

</item>

<item>

<childItem>

<name>Singapore Flex group</name>

<mail>singaporefx@gmail.com</mail>

</childItem>

<location>Singapore</location>

</item>

<item>

<childItem>

<name>Sreenivas</name>

<mail>sreenivas.ramaswamy@gmail.com</mail>

</childItem>

<location>Bangalore</location>

</item>

</root>

</mx:XMLList>

The answer has always (most of the time?) been to use DataGrid.labelFunction. I dislike the solution because it requires the developer to do this again and again for every column and every usage. I find it easier to extend the DataGridColumn and override the itemToLabel function to do the needful.

The usage is simple. The Nested and ordinary columns can be mixed or only nested columns can be used as the logic kicks in only when "." is present in dataField value.

<mx:AdvancedDataGrid dataProvider="{testData.item}">

<mx:columns>

<local:AdvancedDataGridColumnNested dataField="childItem.name" headerText="Name" width="150"/>

<local:AdvancedDataGridColumnNested dataField="childItem.mail" headerText="email" width="100"/>

<mx:AdvancedDataGridColumn dataField="location" headerText="Location" />

</mx:columns>

</mx:AdvancedDataGrid>

<mx:DataGrid dataProvider="{testData.item}">

<mx:columns>

<local:DataGridColumnNested dataField="childItem.name" headerText="Name" width="150"/>

<local:DataGridColumnNested dataField="childItem.mail" headerText="email" width="100"/>

<mx:DataGridColumn dataField="location" headerText="Location" />

</mx:columns>

</mx:DataGrid>

The result looks like this:

The data can be nested to any level not just two. The same logic takes care of everything!

Here are the source files for DataGridColumnNested and ADGColumnNested and test app.

Flex talking to a ActiveX (using ExternalInterface)

I keep seeing/getting request to know how Flex can communicate with a ActiveX control/DLL. So thought will post this sample in which calls were being exchanged between a Flex app and a ActiveX C++ DLL. You can find the source code here.

The sample has two portions a flex app called CalledApp and a C++ DLL called CallingDLL. Load the CalledApp project in FlexBuilder and CallingDLL project is Visual Studio 2003 or later. Build the DLL and the Flex app. Run the Flex app and click on the button. When flex app calls the DLL function the DLL calls back the flex app.

The key is to make the ActiveX DLL scriptable in IE. I went through the trouble long back. The C++ code shows how to traverse the browser DOM to get access to the correct player and how to invoke functions exposed by the SWF.

The Flex app uses ExternalInterface to create a instance of the ActiveX and make calls to it.

However the sample doesn't show passing arrays to the ActiveX which requires conversion of JavaScript Arrays to COM arrays. I am sure smart developers out there can figure out this :).

If you are looking for a sample where in the SWF is loaded in the Flash player ActiveX control embedded in a Desktop application it can be found here.

Sunday, January 20, 2008

Extending DateChooser.selectableRange to accept multiple values/ranges

Recently I came across the following requirement: Enable few disjoint dates in a DateChooser for user selection. The default DateChooser.selectableRange does not allow inserting of list of dates. It takes only a range and disables the rest.

I copied and modified the Flex 2 DateChooser control to achive the new functionlity. The swc is available here http://sreenivas.ramaswamy.googlepages.com/FlexEx2.swc.

I used this file to do the testing. I have done basic testing and it seems to work.

Only few dates in Jan, Feb and March of 2008 are enabled and it restircts the navigation to only those months

Currently I have made changes to only Flex 2 DateChooser. I would be happy to fix any bugs found !

Here are the Flex 3 files: DateChooser and CalenderLayout. The project source to build a Flex3Ex SWC which contains the modified code for DateChooser and CalendarLayout can be found here.