How do I force all Tree itemrenderers to refresh? - apache-flex

I have item renderers in an mx.controls.Tree that I need to refresh on demand.
I have code in the updateDisplayList that fires for only some of the visible nodes no matter what I do. I've tried triggering a change that they should all be listening for; I have tried clearing and resetting the dataProvider and the itemRenderer properties.
private function forceCategoryTreeRefresh(event : Event = null) : void
{
trace("forceCategoryTreeRefresh");
var prevDataProvider : Object = CategoryTree.dataProvider;
CategoryTree.dataProvider = null;
CategoryTree.validateNow();
CategoryTree.dataProvider = prevDataProvider;
var prevItemRenderer : IFactory = CategoryTree.itemRenderer;
CategoryTree.itemRenderer = null;
CategoryTree.itemRenderer = prevItemRenderer as IFactory;
_categoriesChangeDispatcher.dispatchEvent(new Event(Event.CHANGE));
}
The nodes refresh properly when I scroll them into view (e.g. the .data gets set), but I cannot force the ones that already exist to refresh or reset themselves.
Any ideas?

Try calling this function on change event:
private function refreshList(e:Event):void{
listName.invalidateSize();
listName.invalidateList();
listName.invalidateDisplayList();
listName.validateSize(true);
listName.validateNow();
}
for me work quite well

Try
treeView.dataProvider = treeView.dataProvider;
That should update Tree view.

ItemRenderers in Flex are virtualized so there will not always be one ItemRenderer for each Tree node.
However, you can invalidate the nodes to force a refresh. The answer to this question gives an example of how to do that.

Related

Flex Detecting Dragged Data

I have a list that accepts drops. When an item is dropped (DragEvent.DRAG_DROP) I need to collect the data that is dropped, however I have found nothing in the event that helps me do so, event.dragInitiator.selectedItems would work but gives me an error.
Any help would be appreciated.
The data should be in event.dragSource. You'll have to check for the proper format using hasFormat() and retrieve it with dataForFormat(). Here are the docs for DragSource.
Code would be something like this (assuming this is Flex 4):
// In dragDrop handler or dragComplete
if (event.dragSource.hasFormat("itemsByIndex"))
{
var items:Vector.<Object> = event.dragSource.dataForFormat("itemsByIndex") as Vector.<Object>;
// Do stuff with items
}
You can also listen to changing dataProvider.
list.dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE, list_dataProvider_change);
...
protected function list_dataProvider_change(e :CollectionEvent) :void
{
if (e.kind == CollectionEventKind.REMOVE)
trace('list element removed from - index', e.location);
else if (e.kind == CollectionEventKind.ADD)
trace('list element added to - index', e.location);
}

Flex components with dynamic data update

I need to push real time data to a Flex GUI (Data grid), but each time new data is pushed to the grid, it's losing its previous state.
Example:
if I scrolled to the right, after the next update scrolls come back to the default position, that is, left
if I am selecting any row, it's getting unselected just after update.
Is there a way to maintain the state?
I am using Flex 3. I can move to Flex 4 if it helps.
How do you set the data? Do you change the DataGrid dataProvider with a new collection of objects ? Because, from the behavior described by you this may be the case.
The solution would be instead changing the dataProvider of the DG. Each time, you should just update the values in the collection which is assigned as data provider.
For example,
[Bindable]
var myDataCollection:ArrayCollection = new ArrayCollection([0,1,2,3,4]);
// Handle creation complete.
private function onCreationComplete():void
{
initDG();
}
// Init DG data provider just once.
private function initDG(data:ArraCollection):void
{
myDG.dataProvider = data;
}
private function updateDG_Method_1(row:int, value:int):void
{
var data:ArrayCollection = myDG.dataProvider as ArrayCollection;
if(data && data.length > row)
{
data[row] = value;
}
// We can force refresh if not not done automatically.
myDG.invalidateList();
myDG.validateNow();
}
private function updateDG_Method_2(row:int, value:int):void
{
if(myDataCollection && myDataCollection.length > row)
{
myDataCollection[row] = value;
}
// We can force refresh if not not done automatically.
myDG.invalidateList();
myDG.validateNow();
}
Please ignore/correct any typo .. since I did not test this :))
Good luck!

CheckBox header renderer with HierarchicalCollectionView

I've gotten a checkbox header renderer to work well with flat DPs, but a
hierarchical collection view is another story. On click, I want it to select all
checkboxes in a given column. Here is my code:
var dp:HierarchicalCollectionView = _dataGrid.dataProvider as
HierarchicalCollectionView;
var testDp:GroupingCollection = dp.source as GroupingCollection;
var rawDp:ArrayCollection = testDp.source as ArrayCollection;
for(var i:int=0 ; i < rawDp.length ; i++){
rawDp[i][_dataField] = cb.selected;
}
It selects all checkboxes on the 2nd level of data, but doesn't select the top
level of data. What am I missing here? I can't seem to find it.
Any tips are greatly appreciated. Thank you.
For hierarchical data you have to use a cursor which iterates over all levels of the hierarchical data.
var dp:IHierarchicalCollectionView = _dataGrid.hierarchicalCollectionView;
var cursor:IViewCursor= dp.createCursor();
while (!cursor.afterLast)
{
cursor.current[_dataField] = cb.selected;
cursor.moveNext();
}
Howerver, this works only with nodes that have previously been opened. So either expand all nodes with _dataGrid.expandAll() (you can collapse them afterwards since the nodes only have to be opened once) or iterate your hierarchical data manually:
function setCheckBoxValue(children:ArrayCollection, value:Boolean):void
{
for each (var child:Object in children)
{
if (child.hasOwnProperty("children") && child["children"])
setCheckBoxValue(child["children"], value);
child[_dataField] = value;
}
}
var myDataProvider:HierarchicalData = /* your data provider */;
// Call it like this...
setCheckBoxValue(myDataProvider.source, cb.selected);
Update: To answer your second question...
Create a new CheckBoxColumn which extends AdvancedDataGridColumn. You can use it to preconfigure your headerRenderer and itemRenderer.
In your custom item renderer you get hold of your column like this:grid = AdvancedDataGrid(listData.owner);
column = grid.columns[listData.columnIndex] as CheckBoxColumn;
Do the same in your header renderer.
Whenever the CheckBox value in one of your item renderers changes dispatch a event through your column. Something like: column.dispatchEvent(new Event("checkBoxValueChanged"));
Your header render should add an event listener to the column for the "checkBoxValueChanged" event (or whatever you call it). Whenever that event is fired loop through your data provider and update the headers CheckBox accordingly.
In theory that should work. HTH

How do I programmatically associate a RadioButton with a RadioButtonGroup in ActionScript3?

I have a UI component that, for various reasons, I have to construct programatically. The component is a table of radio buttons grouped by column.
Right now, I'm constructing the column groups like so:
private function createGroupsForItemList(items: XMLList): void {
for each (var item: XML in items) {
var rbGroup: RadioButtonGroup = new RadioButtonGroup();
groups[item.#level.toString()] = rbGroup;
}
}
I'm trying to associate the RadioButton instances with the column groups like so:
private function createValueControl(item: XML): UIComponent {
var control: RadioButton = new RadioButton();
control.label = "";
control.group = groups[item.#level.toString()];
control.addEventListener(Event.SELECT, updateSelection);
return control;
}
I can see in the debugger that the control has an association to the group:
control.group == groups[item.#level.toString()]
However, I can see equally that the group does not know anything about the control:
group.radioButtons.length == 0
I imagine that this is because the setter for group in RadioButton is a dumb setter; all it does is copy to the variable, which doesn't do the magic that groupName does. However, I can't seem to find the value I should use to set the RadioButton.groupName property correctly.
So, in short, I'm stumped on how to get these bits to talk to each other. How do I do this?
-- EDIT --
It turns out that I can have the groups created and associated simply by setting the groupName property, but I can't get at the group to set up a selection listener; the group is NULL immediately after the setting process, which means that the second line below throws the Flex equivalent of an NPE:
control.groupName = groupNameForLevel(item);
control.group.addEventListener(Event.SELECT, updateSelection);
First instinct is that this issue has to do with invalidateDisplayList and when and how that is called. Of course, since issues related to that function are behind a number of Flex's quirks, I may just be scapegoating.
This is not the answer to your question per se, but it seems like it might actually work as an alternate solution.
RadioButtonGroups will initialize based on a IFlexDisplayObject. This means that you can do something like:
var c:HBox = new HBox();
var rbg:RadioButtonGroup = new RadioButtonGroup( c );
// do stuff with rbg.
c.addChild( new RadioButton() );
The problem is that it may not be the most practical answer, but it has the decided benefit of being a workable solution.
Setting groupName should work.
All I can suggest is to step through the group() getter of the RadioButton component and see where exactly it is failing. Are you programmatically creating the group too? If that's the case, maybe it isn't initialized fully yet.

Flex: Memory Problem

I have problem on deleting component which is created on runtime. Please help me.
heres my code in creating component
var oh: ObjectHandles = new ObjectHandles;
oh.x = event.localX-xOff;
oh.y = event.localY-yOff;
Canvas(event.target).addChild(oh);
oh.addEventListener(KeyboardEvent.KEY_DOWN,deleteSel);
oh.width=270;
oh.height=200;
oh.mouseChildren = true;
var vdo:FXVideo = new FXVideo;
vdo.source = "http://thehq.tv/wp-content/uploads/flv/funny-people-trailer.flv";
vdo.percentHeight = 100;
vdo.percentWidth = 100;
oh.addChild(vdo);
code in keyboard delete event
private function deleteSel(event:KeyboardEvent):void
{
if(event.charCode == 127)
{
FXVideo(ObjectHandles(event.target).getChildAt(0)).stop();
delete(ObjectHandles(event.target).getChildAt(0));
ObjectHandles(event.target).removeAllChildren();
ObjectHandles(event.target).parent.removeChild(ObjectHandles(event.target));
delete ObjectHandles(event.target);
}
}
after I delete the Object Handles Component(inside is FxVideo Component) the memory usage is still there. How to remove the memory allocation of component after deletion?
You need to remove the event listener, or you can add the event listener with a weak reference:
oh.addEventListener(KeyboardEvent.KEY_DOWN,deleteSel,false,0,true)
I would not recommend calling delete. Calling removeAllChildren should take care of it. Although, by looking at your code, that probably is not necessary either. Once you remove the event listener it should get cleaned up.
delete only works with dynamic objects and won't have an affect here. I personally recommend explicitly removing the event listener:
event.target.removeEventListener(KeyboardEvent.KEY_DOWN,deleteSel);
as well as using the weak reference suggested by Osman.

Resources