views:

62

answers:

1

Hello,

I am currently developing a dynamic LineChart in FLEX 4. I am implementing a Tree control next to my LineChart, which will filter the LineChart dataprovider and lineseries. The tree control has several branches and ultimately 5 children (leaf nodes) at the bottom of the last branch.

I need the leaf node/children to be displayed as checkboxes inside the tree control. As I understand, this will require overrides in the TreeItemRenderer class. This is where I am a little confused on how to implement that.

Currently I can distinguish between leaf and branches using this code, in my main MXML component. I added this because it may be helpful to some beginning FLEX developers, such as myself, who cannot easily find this functionality documented well:

            private function treeClick(e:ListEvent):void {

            _selectedItem = Tree(e.currentTarget).selectedItem;

            if(mainTree.dataDescriptor.isBranch(_selectedItem)) {
                Alert.show('branch click');
            }
            else {
                Alert.show('leaf node click');
            }

        }

I am looking at the TreeItemRenderer override class from the following example here:

In the example, they override the "createChildden" super function to add checkboxes to the tree control.

My question is, can I override the createChildren function directly in my MXML component, and not have to use an entire class file to override this functionality? Must I re-invent the wheel to do this?

Also, how can I distinguish that my treeItem is a leaf node and not a parent, in the override function? I only want to add checkboxes to the leaf nodes, how can I differentiate? The following example adds checkboxes to all branches and leaf nodes, but I want to add checkboxes only to leaf node/children. How would you approach that?

        override protected function createChildren( ): void
        {
            super.createChildren( );
            if( !_checkbox )
            {
                _checkbox = new CheckBoxExtended( );
                _checkbox.allow3StateForUser = false;
                _checkbox.addEventListener( MouseEvent.CLICK, onCheckboxClick );
                addChild( _checkbox );
            }
        }

Here is the XML I am working with:

    <mx:XMLList id="treeData">
    <node label="DAIX">
        <node label="Account 1">
            <node label="Premise 1">
                <node label="Device 1" oid="31" isChecked="false">
                </node>
                <node label="Device 2" oid="32" isChecked="false">
                </node>
            </node>
            <node label="Premise 2">
                <node label="Device 1" oid="41" isChecked="false">
                </node>
                <node label="Device 2" oid="42" isChecked="false">
                </node>                 
            </node>
        </node>
        <node label="Account 2">
            <node label="Premise 1">
                <node label="Device 1" oid="31" isChecked="false">
                </node>
                <node label="Device 2" oid="32" isChecked="false">
                </node>             
            </node>
            <node label="Premise 2">
                <node label="Device 1" oid="31" isChecked="false">
                </node>
                <node label="Device 2" oid="32" isChecked="false">
                </node>                 
            </node>
        </node>
    </node>    
</mx:XMLList>

Here is my tree tag:

<mx:Tree id="mainTree" dataProvider="{treeData}" itemRenderer="TreeCheckBoxItemRenderer" labelField="@label" showRoot="false" width="100%" height="100%" itemClick="treeClick(event)" />
+1  A: 

Here's what I would do: Create your item renderer derived off of TreeItemRender as an MXML component and include your checkbox in the MXML, then override the setter for listData. In your overridden method do something like this:

EDIT: Added surrounding MXML (note that the MXML is pretty much exactly what FB 4 generates by default when creating a new Tree item renderer, and that I haven't tested this in a Tree.)

EDIT2: Added code for moving checked state back and forth between component and data.

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

        override public function set listData(value:BaseListData):void
        {
            super.listData = value
            this.myCheckbox.visible = !(value as TreeListData).hasChildren;
            this.myCheckbox.includeInLayout = !(value as TreeListData).hasChildren;
        }

        override public function set data(value:Object):void {
            super.data = value;

            this.myCheckbox.selected = this.data.isChecked;
        }

        private function onCheckboxChange(e:Event):void {
            this.data.isChecked = this.myCheckbox.selected;
        }
    ]]>
</fx:Script>
<s:states>
    <s:State name="normal" />      
    <s:State name="hovered" />
    <s:State name="selected" />
</s:states>
<s:HGroup left="0" right="0" top="0" bottom="0" verticalAlign="middle">
    <s:Rect id="indentationSpacer" width="{treeListData.indent}" percentHeight="100" alpha="0">
        <s:fill>
            <s:SolidColor color="0xFFFFFF" />
        </s:fill>
    </s:Rect>
    <s:Group id="disclosureGroup">
        <s:BitmapImage source="{treeListData.disclosureIcon}" visible="{treeListData.hasChildren}" />
    </s:Group>
    <s:CheckBox id="myCheckbox" change="onCheckboxChange(event)"/>
    <s:Label id="labelField" text="{treeListData.label}" paddingTop="2"/>
</s:HGroup>

Wade Mueller
^ I am a bit confused as to why createChildren isnt used here in your code?when you say "include your checkbox in the MXML", do you mean the checkbox in design mode/markup? in the Tree control's MXML layout, itself? or do you mean, include it in the createChildren override, dynamically, in the renderer/override AS code? My instinct as a programmer keeps me away from design and layout and wants to overcomplicate things, so I apologize for not quite understanding.
Devtron
I added the surrounding MXML to my example above. I understand your instinct to stay away from MXML and keep things in code, but with item renderers things get tricky because the renderers get recycled. This means that you cannot necessarily rely on things like createChildren to get called when you expect. The only thing that is really reliable is the data and listData setters, that's why it's generally good practice to adjust the state of your renderer in those methods. Hope that helps.
Wade Mueller
This worked like a champ! The only change required to your code was the reference to the namespace: [import mx.controls.listClasses.BaseListData;]. What a beauty!! This rocks and I would give you 1,000 points if I could! Cheers man. I owe ya one.
Devtron
Awesome, glad to hear it!
Wade Mueller
^ How would you set a checkbox's selected property to false?If you try something like this:_selectedItem = Tree(e.currentTarget).selectedItem;_selectedItem.selected=false;it redraws the tree, loses the checkbox and uses the default icons, and creates a new leaf node with "false" as the label text. Maybe I shouldnt be using e.currentTarget, to toggle the checkbox? Add those two lines into a click event and you'll see what I mean! Awesome post regardless tho, wow!
Devtron
Now comes the fun part. To make your checkboxes useful, you need to add a field to the items in your dataProvider to hold a Boolean value, let's name it "isChecked". You can then bind the selected property on your checkbox to the data value, something like this: <s:Checkbox ... selected="{data.isChecked}"/> Then you can just refer to the dataProvider to determine which ones are selected.
Wade Mueller
I edited the original question to add my XMLList (data) and my Tree tag.I am also now using this in the renderer:<s:CheckBox id="myCheckbox" selected="{data.isChecked}" />When the tree loads, all checkboxes are checked. I can uncheck them easily, but then cannot recheck them. Is my data/nodes incorrect? I notice that when I try to recheck an empty checkbox, the checkmark appears briefly, then disappears, like the Tree control is overriding it, or redrawing it empty.
Devtron
Also, I do not understand why the tree loads with the checkboxes selected=true, when the XML element isChecked=false??? I guess it is disregarding my XML?
Devtron
I also noticed that I get warnings at runtime about binding to the "isChecked" property......warning: unable to bind to property 'isChecked' on class 'XML' (class is not an IEventDispatcher)
Devtron
I added code to show how to move the data back and forth between the component. This will eliminate your warning. It will NOT take care of your problem with having them all show as initially checked. This is caused by your xml, because isChecked isn't getting parsed as a boolean, it's probably coming through as a string and getting coerced to a boolean, meaning "false" is read a non-zero string and therefore assumed to mean "true". I would try changing "false" to 0 and see what happens. No guarantees on that though. :)
Wade Mueller
If I set isChecked=0 in my XML, I get an error. I have to use isChecked="0", and I still get the property binding warning. Also, with the updated code (Edit 2), I get worse behaviour of the nodes. If you expand/collapse the "Premise" nodes, it removes the checkboxes, and re-checks all remaining checkboxes. It also puts "false" into a sub-node entry, and changes the icons. It gets really funky. Try it out and see, its weird.
Devtron
Did you remove selected="{data.isChecked}" from your CheckBox? If not, you need to. That should take care of your warning since you are no longer binding to anything, and it would also explain some of your weird behavior.
Wade Mueller