2

Firstly sorry for the really lengthy question. I am trying to implement an Org.Chart using spark DataGrid. The data for the Org.Chart does not change and hence I am hard coding the values. I had to implement this Org.Chart as I need to have floating Nodes and also the connecting lines should be drawn a bit different when compared to using an external component. Please find the following image of which Im trying to achieve:

enter image description here

Here is the idea:

Each cell of the DataGrid can be either connecting lines or an actor. I have a custom component MyOrgChartNode which has two states: chartLinesState and chartActorState. This component is used as ItemRenderer for DataGrid. Here is the code of MyOrgChartNode:

<fx:Script>
    <![CDATA[
        import mx.controls.Alert;

        import scripts.valueObjects.ActorVO;
        import scripts.valueObjects.CellRendererVO;

        [Bindable]
        private var _cellRenderer:CellRendererVO;
        [Bindable]
        private var _lineColor:uint = 0xFF0000;
        [Bindable]
        private var _lineWidth:int = 1;

        override public function prepare(hasBeenRecycled:Boolean):void
        {
            if (data != null)
            {
                this.height = parseInt(data['cellHeight']);

                if (columnIndex == 1 || columnIndex == 3)
                {
                    this.width = 50;
                }

                currentState = (data[column.dataField] as CellRendererVO).stateName;    
            }
        }

        protected function orgChartNode_clickHandler(event:MouseEvent):void
        {
            // TODO Auto-generated method stub

        }

    ]]>
</fx:Script>

<s:states>

    <s:State name="chartLinesState" />

    <s:State name="chartActorState" />

</s:states>

<s:VGroup width="100%" height="100%" includeIn="chartActorState">

    <myOrgChart:OrgChartNode id="orgChartNode" width="100%" height="100%" click="orgChartNode_clickHandler(event)"
                             nodeActor="{(data[column.dataField] as CellRendererVO).nodeActor}" />

</s:VGroup>

<s:VGroup width="100%" height="100%" includeIn="chartLinesState">

    <s:HGroup width="100%" height="100%"
              gap="0" horizontalAlign="center" verticalAlign="middle">

        <s:VGroup id="horizontalLeftLine" height="100%" width="100%"
                  horizontalAlign="center" verticalAlign="middle"
                  visible="{(data[column.dataField] as CellRendererVO).hasHorizontalLeftLine}">

            <mx:HRule width="100%" strokeWidth="{_lineWidth}" strokeColor="{_lineColor}"
                      opaqueBackground="{_lineColor}"/>

        </s:VGroup>

        <s:VGroup height="100%" gap="0" horizontalAlign="center"
                  verticalAlign="middle"
                  includeInLayout="{(data[column.dataField] as CellRendererVO).hasVerticalTopLine || (data[column.dataField] as CellRendererVO).hasVerticalBottomLine}">

            <s:VGroup id="verticalTopLine" height="50%" verticalAlign="middle"
                      visible="{(data[column.dataField] as CellRendererVO).hasVerticalTopLine}">

                <mx:VRule height="100%" strokeWidth="{_lineWidth}" strokeColor="{_lineColor}"
                          opaqueBackground="{_lineColor}"/>

            </s:VGroup>

            <s:VGroup id="verticalBottomLine" height="50%" verticalAlign="middle"
                      visible="{(data[column.dataField] as CellRendererVO).hasVerticalBottomLine}">

                <mx:VRule height="100%" strokeWidth="{_lineWidth}" strokeColor="{_lineColor}"
                          opaqueBackground="{_lineColor}"/>

            </s:VGroup>

        </s:VGroup>

        <s:VGroup id="horizontalRightLine" height="100%" width="100%"
                  horizontalAlign="center" verticalAlign="middle"
                  visible="{(data[column.dataField] as CellRendererVO).hasHorizontalRightLine}">

            <mx:HRule width="100%" strokeWidth="{_lineWidth}" strokeColor="{_lineColor}"
                      opaqueBackground="{_lineColor}"/>

        </s:VGroup>

    </s:HGroup>

</s:VGroup>

In the above code I am using a valueObject CellRendererVO which contains the required information for the display of the cell such as visibility of the different HRules and VRule present. Code for CellRendererVO is here:

package scripts.valueObjects
{
    public class CellRendererVO extends Object
    {
        private var _hasVerticalTopLine:Boolean;
        private var _hasVerticalBottomLine:Boolean;
        private var _hasHorizontalLeftLine:Boolean;
        private var _hasHorizontalRightLine:Boolean;
        private var _nodeActor:ActorVO;
        private var _stateName:String;

        public function CellRendererVO(hasVerticalTopLine:Boolean = false, hasVerticalBottomLine:Boolean = false,
            hasHorizontalLeftLine:Boolean = false, hasHorizontalRightLine:Boolean = false,
            nodeActor:ActorVO = null, stateName:String = "chartLinesState"
            )
        {
            this.hasVerticalTopLine = hasVerticalTopLine;
            this.hasVerticalBottomLine = hasVerticalBottomLine;
            this.hasHorizontalLeftLine = hasHorizontalLeftLine;
            this.hasHorizontalRightLine = hasHorizontalRightLine;
            this.nodeActor = nodeActor;
            this.stateName = stateName;
        }

        public function get hasVerticalTopLine():Boolean
        {
            return _hasVerticalTopLine;
        }

        public function set hasVerticalTopLine(value:Boolean):void
        {
            _hasVerticalTopLine = value;
        }

        public function get hasVerticalBottomLine():Boolean
        {
            return _hasVerticalBottomLine;
        }

        public function set hasVerticalBottomLine(value:Boolean):void
        {
            _hasVerticalBottomLine = value;
        }

        public function get hasHorizontalLeftLine():Boolean
        {
            return _hasHorizontalLeftLine;
        }

        public function set hasHorizontalLeftLine(value:Boolean):void
        {
            _hasHorizontalLeftLine = value;
        }

        public function get hasHorizontalRightLine():Boolean
        {
            return _hasHorizontalRightLine;
        }

        public function set hasHorizontalRightLine(value:Boolean):void
        {
            _hasHorizontalRightLine = value;
        }

        public function get nodeActor():ActorVO
        {
            return _nodeActor;
        }

        public function set nodeActor(value:ActorVO):void
        {
            _nodeActor = value;
        }

        public function get stateName():String
        {
            return _stateName;
        }

        public function set stateName(value:String):void
        {
            _stateName = value;
        }
    }
}

As you can see, there are different boolean variables used to get the connecting lines and there is another valueObject ActorVO which has actor data. Here is the code:

package scripts.valueObjects
{
    public class ActorVO extends Object
    {
        private var _id:int;
        private var _fullName:String;
        private var _imageSource:String;
        private var _beta1:Number;
        private var _beta2:Number;
        private var _beta3:Number;
        private var _nationality:String;
        private var _education:String;
        private var _gender:String;
        private var _displayName:String;
        private var _progress:Number;
        private var _showImage:Boolean;

        public function ActorVO(id:int = -1, fullName:String = "Full Name", imageSource:String = "",
            beta1:Number = -1, beta2:Number = -1, beta3:Number = -1, nationality:String = "India",
            gender:String = "M", progress:Number = 10, education:String = "", showImage:Boolean = true)
        {
            this.id = id;
            this.fullName = fullName;
            this.imageSource = imageSource;
            this.beta1 = beta1;
            this.beta2 = beta2;
            this.beta3 = beta3;
            this.nationality = nationality;
            this.gender = gender;
            this.progress = progress;
            this.education = education;
            this.showImage = showImage;
        }

        public function copyData(actor:ActorVO):void
        {
            this.id = actor.id;
            this.fullName = actor.fullName;
            this.imageSource = actor.imageSource;
            this.beta1 = actor.beta1;
            this.beta2 = actor.beta2;
            this.beta3 = actor.beta3;
            this.nationality = actor.nationality;
            this.gender = actor.gender;
            this.progress = actor.progress;
            this.education = actor.education;
            this.showImage = showImage
        }

        public function get id():int
        {
            return _id;
        }

        public function set id(value:int):void
        {
            _id = value;
        }

        public function get fullName():String
        {
            return _fullName;
        }

        public function set fullName(value:String):void
        {
            _fullName = value;
            displayName = value.substring(value.lastIndexOf(" ") + 1); //return the last name in the string
        }

        public function get imageSource():String
        {
            return _imageSource;
        }

        public function set imageSource(value:String):void
        {
            /**
             * When copyData function is used, value is ./assets/images/people/
             * and hence imageSouce will be prepended with ./assets/images/people
             * hence first check if there exists path
             * */

            _imageSource = "./assets/images/people/" + value;

            if (value.indexOf("./") != -1)
            {
                _imageSource = value;
            }
        }

        public function get beta1():Number
        {
            return _beta1;
        }

        public function set beta1(value:Number):void
        {
            _beta1 = value;
        }

        public function get beta2():Number
        {
            return _beta2;
        }

        public function set beta2(value:Number):void
        {
            _beta2 = value;
        }

        public function get beta3():Number
        {
            return _beta3;
        }

        public function set beta3(value:Number):void
        {
            _beta3 = value;
        }

        public function get nationality():String
        {
            return _nationality;
        }

        public function set nationality(value:String):void
        {
            _nationality = value;
        }

        public function get education():String
        {
            return _education;
        }

        public function set education(value:String):void
        {
            _education = value;
        }

        public function get gender():String
        {
            return _gender;
        }

        public function set gender(value:String):void
        {
            _gender = value;
        }

        public function get displayName():String
        {
            return _displayName;
        }

        public function set displayName(value:String):void
        {
            _displayName = value;
        }

        public function get progress():Number
        {
            return _progress;
        }

        public function set progress(value:Number):void
        {
            _progress = value;
        }

        public function get showImage():Boolean
        {
            return _showImage;
        }

        public function set showImage(value:Boolean):void
        {
            _showImage = value;
        }
    }
}

And here is the DataGrid code from the Application:

<s:DataGrid id="orgChartDG" borderVisible="false" skinClass="skins.OrgChartDataGridSkin"
                dataProvider="{orgChartDP}" variableRowHeight="true">

        <s:columns>

            <s:ArrayList>

                <s:GridColumn dataField="col1" itemRenderer="customComponents.myOrgChart.TestIR" />

                <s:GridColumn dataField="col2" itemRenderer="customComponents.myOrgChart.TestIR" />

                <s:GridColumn dataField="col3" itemRenderer="customComponents.myOrgChart.TestIR" />

                <s:GridColumn dataField="col4" itemRenderer="customComponents.myOrgChart.TestIR" />

                <s:GridColumn dataField="col5" itemRenderer="customComponents.myOrgChart.TestIR" />

                <s:GridColumn dataField="col6" itemRenderer="customComponents.myOrgChart.TestIR" />

                <s:GridColumn dataField="col7" itemRenderer="customComponents.myOrgChart.TestIR" />

                <s:GridColumn dataField="col8" itemRenderer="customComponents.myOrgChart.TestIR" />

                <s:GridColumn dataField="col9" itemRenderer="customComponents.myOrgChart.TestIR" />

                <s:GridColumn dataField="col10" itemRenderer="customComponents.myOrgChart.TestIR" />

            </s:ArrayList>

        </s:columns>

    </s:DataGrid>

The DataProvider for this DataGrid is an ArrayCollection which contains 10 Objects with properties col1 - col10, where each property is of type CellRendererVO.

Here is the output of this implementation:

enter image description here

Now, here is the trouble

I have button Hide Images as seen in the above image. What needs to be done is, when clicked on this button, all the images shown in the Org.Chart must be hidden. But wait, Im just stuck with something else and for now forget about this implementation.

The click handler for this button is as follows:

private function hideImagesBtn_clickHandler(event:Event):void
            {

                orgChartDP.refresh();
            }

But, when clicked on this button, I see a weird behavior. See the output of the Org.Chart when clicked on Hide Images: enter image description here

I am not able to figure it out why it is changing so. As you can see there is only a refresh of ArrayCollection being done. Why is the whole structure of the Org.Chart is getting changed?

I did some research about this and one reason I can think of is only reusable feature of ItemRenderers. But, however this reason still does not apply for change of Org.Chart.

Any kind of help to solve this issue is appreciated also thanks for coming till here.

Thanks, Anji

Anji
  • 725
  • 1
  • 9
  • 27
  • I must admit I haven't entirely read your lengthy question, but why for [insert divinity of choice]'s sake are you trying to cram a flowchart into a datagrid? – RIAstar Jan 31 '12 at 18:02
  • Somewhat more helpful perhaps: have a look at [Kalileo](http://lab.kapit.fr/display/kalileo/Kalileo). There's a free community edition; the commercial licences are fairly expensive though. – RIAstar Jan 31 '12 at 18:17
  • @RIAstar I have updated my question. Thanks for letting me know about `Kalileo`. I would like to understand why it is being plotted so (in my case). I hope this learning might help me understanding flex `ItemRenderer`s in detail. – Anji Feb 01 '12 at 13:57
  • Why are you using the Datagrid for this ? You should have a completely different type of Object for that. The renderers in DG. may not be the best idea in the world for building such an org chart. I know I may sound strange but an org chart is not a datagrid. You should create the component from scratch starting with canvas/uiComponent to avoid refresh issues and performance problems. – Adrian Pirvulescu Feb 03 '12 at 07:59
  • @AdrianPirvulescu I thought I would make use of ItemRenderers and DataGrid for the Column and Row arrangement. If I had started with an UIComponent, how would think i could do this? – Anji Feb 03 '12 at 08:40
  • 1
    @Anji There are some nice org chart samples on google. All you have to do is to study them a little bit, and if you understand the core or flex then you will know that the datagrid is not the best option. Here are some links http://blog.frankula.com/?p=148 and http://coolestflex.blogspot.com/2008/10/organization-chart-orgchart.html – Adrian Pirvulescu Feb 03 '12 at 10:52
  • +1 for Kalikeo. I just got done with a fairly large project using it. – francis Feb 04 '12 at 23:44
  • @AdrianPirvulescu Thanks for the link. But I would like to know why the `ItemRenderer`s are behaving so rather than solving the main problem itself – Anji Feb 05 '12 at 04:01
  • @Anji I could not tell 100% why it is like this. To do this you need to debug your complete code and trace all itemrenderer activity. This may include the fact that you have to override basic core methods like commitProperties or updateDisplayList just to put a trace in there. Also you need to be aware 100% of the columns/rows rendering mechanism inside the datagrid. I suggest you have some investigation on this before so you can know how rows are created/reused and sometimes destroyed. – Adrian Pirvulescu Feb 05 '12 at 12:01
  • (... continuation of prev msg) So in order to display sample grid data then this control is awesome. But I still believe your should used something else for your orgchart if you target performance and large amount of data to be displayed. – Adrian Pirvulescu Feb 05 '12 at 12:06
  • @Anji, now back to your issue. Why do you use dataProvider.refresh() ? Can you try instead to use orgChartDG.invalidateList(); – Adrian Pirvulescu Feb 07 '12 at 13:00
  • @AdrianPirvulescu Yes, I have used that too. But still the same issues. I have started to write my own implementation starting from extending an UIComponent without using DataGrid for this. It seems to work fine. – Anji Feb 07 '12 at 13:03
  • @AdrianPirvulescu Thanks for you prompt response. I will need some time to debug this issue and will let you know if I can find any. – Anji Feb 07 '12 at 13:04

1 Answers1

0

Sorry but i didn't fully understand the problem, perhaps a full project source could help, and explain it better what is happening and what you are expecting to happened.

There is only few tips that I can give you in generally based on the source codes you posted.

You better not work with states there is lots of bugs there, and I would also give a mind about the children creation policy witch some time can also lead for mistakes.

Hope you will find what you are looking for.

Ilya Gazman
  • 31,250
  • 24
  • 137
  • 216