views:

4109

answers:

4

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);
            }
        }

    }       

}
+4  A: 

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.

inferis
Thanks! You are my hero today!
Niels Bosma
A: 

I've had some minor problem with this solution, var scrollPosition:Number = tree.verticalScrollPosition; is constantly 0??

Niels Bosma
It depends how many items you have in the tree. Do you have a large tree being displayed and have you actually scrolled when you're trying to force a redraw?
inferis
Yes, even when the Tree is scrolled the verticalScrollPosition is still 0.
Niels Bosma
+1  A: 

You might have another problem: whenever you check an item the tree scrolls to the top and this is just annoying. To solve this problem you should update the TreeCheckBox.as file this way: in function checkHandler: private function checkHandler( event: TreeEvent ): void;

comment the commitProperties(); call.

Now it should work well.

Cheers.

A: 

inferis solution really helped me! Thanks man!