Flex: Updating a Tree control

actionscript-3apache-flex

I've a tree control with checkboxes that uses the control from http://www.sephiroth.it/file_detail.php?id=151#

Somehow I can't get the control to update when I change the dataProvider (i.e. by clicking a checkbox) the only way I can get it to update is to use the scrollbar. How do I force the update? I've tried all possible methods I can figure out? (see update below)

Also how can I reset the Tree (collpasing all nodes, scroll to the top in a large tree)?

package offerta.monkeywrench.components
{

    import offerta.monkeywrench.components.componentClasses.TreeCheckBoxItemRenderer;

    import mx.collections.ArrayCollection;

    import mx.events.TreeEvent;

    public class WatchTree extends TreeCheckBox
    {

        public var idProperty:String;

        public var watchFactory:Function;

        private var _wSet:Boolean = false;

        /* clientId: */

        private var _clientId:String;

        [Bindable]
        public function get clientId():String
        {
            return _clientId;
        }

        public function set clientId(value:String):void
        {
            this._clientId = value;
        }

        /* //clientId */

        /* watching: */

        private var _watching:ArrayCollection;

        [Bindable]
        public function set watching(value:ArrayCollection):void
        {
            this._watching = value;
        }

        public function get watching():ArrayCollection
        {
            return this._watching;    
        }

        /* //watching */

        override public function initialize() :void
        {
            super.initialize();
            addEventListener("itemCheck", onItemCheck, false, 0, true);
        }

        private function isWatching(id:String):Boolean
        {
            for each(var w:Object in this._watching)
            {
                if(w[this.idProperty]==id) return true;
            }
            return false;
        }

        private function onItemCheck(event:TreeEvent):void
        {
            var item:Object = event.item as Object;
            var currentValue:uint = (event.itemRenderer as TreeCheckBoxItemRenderer).checkBox.checkState;
            if(item.children==null)
            {
                currentValue==2 ? addWatch(item.Id) : removeWatch(item.Id);
            }
            else
            {
                for each(var x:Object in item.children)
                {
                    currentValue==2 ? addWatch(x.Id) : removeWatch(x.Id);
                }
            }
            updateParents(item, currentValue);
            updateChilds(item, currentValue);
            this.dataProvider.refresh();
            super.invalidateProperties();
            super.invalidateDisplayList();
            super.updateDisplayList(this.unscaledWidth, this.unscaledHeight);
        }

        private function updateParents(item:Object, value:uint):void
        {
            var checkValue:String = (value == ( 1 << 1 | 2 << 1 ) ? "2" : value == ( 1 << 1 ) ? "1" : "0");
            var parentNode:Object = item.parent;
            if(parentNode)
            {
                for each(var x:Object in parentNode.children)
                {
                    if(x.checked != checkValue)
                    {
                        checkValue = "2"
                    }
                }
                parentNode.checked = checkValue;
                updateParents(parentNode, value);
            }
        }

        private function updateChilds(item:Object, value:uint):void
        {
            var middle:Boolean = (value&2<<1)==(2<<1);

            if(item.children!=null && item.children.length>0&&!middle)
            {
                for each(var x:Object in item.children)
                {
                    x.checked = value == (1<<1|2<<1) ? "2" : value==(1<<1) ? "1" : "0";
                    updateChilds(x, value);
                }
            }
        }

        private function addWatch(id:String):void
        {
            if(isWatching(id)) return;
            this._watching.addItem(this.watchFactory(id, this.clientId));
        }

        private function removeWatch(id:String):void
        {
            for(var i:int=0, n:int=this._watching.length; i<n; ++i)
            {
                if(this._watching[i][this.idProperty]==id)
                {
                    this._watching.removeItemAt(i);
                    return;
                }
            }
        }

        public function update(__watching:ArrayCollection, __clientId:String):void
        {
            clientId = __clientId;
            watching = __watching;
            if(this.dataProvider!=null)
            {
                var ws:ArrayCollection = ArrayCollection(this.dataProvider);
                for each(var group:Object in ws)
                {
                    var count:int = 0;
                    for each(var child:Object in group.children)
                    {
                        if(isWatching(child.Id))
                        {
                            child.checked = "1";
                            count++;
                        }
                    }
                    group.checked = (count==0 ? "0" : (count==group.children.length ? "1" : "2"));
                }
                this._wSet = false;

                var dp:ArrayCollection = ArrayCollection(this.dataProvider);
                dp.refresh();
                super.invalidateProperties();
                super.invalidateDisplayList();
                super.updateDisplayList(this.unscaledWidth, this.unscaledHeight);

                //scroll up the list???

                //collapse? (doesn't work)
                this.expandItem(null, false);
            }
        }

    }       

}

Best Answer

I've found the Tree control a little touchy in Flex. The way I ended up forcing a redraw was to disconnect the dataProvider and reconnect it, then force validation, something a bit like this :

private function forceRedraw(tree:Tree, dataProvider:Object):void
{
    var scrollPosition:Number = tree.verticalScrollPosition;
    var openItems:Object = tree.openItems;
    tree.dataProvider = dataProvider;
    tree.openItems = openItems;
    tree.validateNow();
    tree.verticalScrollPosition = scrollPosition;
}

I guess this incidentally answers the second part of your question since all you'd have to do is null out the openItems collection and set the verticalScrollPosition to 0.

Related Topic