getting the parent value in Flex - apache-flex

private function editForm():void {
var event:DepManagementEvent = new DepManagementEvent("Edit Page",true);
var navi:String;
event.navi = deleteDataGrid.selectedItem
dispatchEvent(event);
}
This function is in item renderer, i need the parent datagrid id to be called here...

DataGrid(this.owner) or DataGrid(this.listData.owner) would give you the parent data grid. I don't know the exact reasons, but I heard that the second one is the preferred way of doing it.

Some helpful information here:
http://www.adobe.com/devnet/flex/articles/itemrenderers_pt2_04.html

Related

How to keep a list from scrolling on dataProvider refresh/update/change?

I have a simple list and a background refresh protocol.
When the list is scrolled down, the refresh scrolls it back to the top. I want to stop this.
I have tried catching the COLLECTION_CHANGE event and
validateNow(); // try to get the component to reset to the new data
list.ensureIndexIsVisible(previousIndex); // actually, I search for the previous data id in the IList, but that's not important
This fails because the list resets itself after the change (in DataGroup.commitProperties).
I hate to use a Timer, ENTER_FRAME, or callLater(), but I cannot seem to figure out a way.
The only other alternatives I can see is sub-classing the List so it can catch the dataProviderChanged event the DataGroup in the skin is throwing.
Any ideas?
Actually MUCH better solution to this is to extend DataGroup. You need to override this.
All the solutions here create a flicker as the scrollbar gets resetted to 0 and the it's set back to the previous value. That looks wrong. This solution works without any flicker and the best of all, you just change DataGroup to FixedDataGroup in your code and it works, no other changes in code are needed ;).
Enjoy guys.
public class FixedDataGroup extends spark.components.DataGroup
{
private var _dataProviderChanged:Boolean;
private var _lastScrollPosition:Number = 0;
public function FixedDataGroup()
{
super();
}
override public function set dataProvider(value:IList):void
{
if ( this.dataProvider != null && value != this.dataProvider )
{
dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, onDataProviderChanged);
}
super.dataProvider = value;
if ( value != null )
{
value.addEventListener(CollectionEvent.COLLECTION_CHANGE, onDataProviderChanged);
}
}
override protected function commitProperties():void
{
var lastScrollPosition:Number = _lastScrollPosition;
super.commitProperties();
if ( _dataProviderChanged )
{
verticalScrollPosition = lastScrollPosition;
}
}
private function onDataProviderChanged(e:CollectionEvent):void
{
_dataProviderChanged = true;
invalidateProperties();
}
override public function set verticalScrollPosition(value:Number):void
{
super.verticalScrollPosition = value;
_lastScrollPosition = value;
}
}
I ll try to explain my approach...If you are still unsure let me know and I ll give you the source code as well.
1) Create a variable to store the current scroll position of the viewport.
2) Add Event listener for Event.CHANGE and MouseEvent.MOUSE_WHEEL on the scroller and update the variable created in step 1 with the current scroll position;
3) Add a event listener on your viewport for FlexEvent.UpdateComplete and set the scroll position to the variable stored.
In a nutshell, what we are doing is to have the scroll position stored in variable every time user interacts with it and when our viewport is updated (due to dataprovider change) we just set the scroll position we have stored previously in the variable.
I have faced this problem before and solved it by using a data proxy pattern with a matcher. Write a matcher for your collection that supports your list by updating only changed objects and by updating only attributes for existing objects. The goal is to avoid creation of new objects when your data source refreshes.
When you have new data for the list (after a refresh), loop through your list of new data objects, copying attributes from these objects into the objects in the collection supporting your list. Typically you will match the objects based on id. Any objects in the new list that did not exist in the old one get added. Your scroll position will normally not change and any selections are usually kept.
Here is an example.
for each(newObject:Object in newArrayValues){
var found:Boolean = false;
for each(oldObject:Object in oldArrayValues){
if(oldObject.id == newObject.id){
found = true;
oldObject.myAttribute = newObject.myAttribute;
oldObject.myAttribute2 = newObject.myAttribute2;
}
}
if(!found){
oldArrayValues.addItem(newObject);
}
}
My solution for this problem was targeting a specific situation, but it has the advantage of being very simple so perhaps you can draw something that fits your needs from it. Since I don't know exactly what issue you're trying to solve I'll give you a description of mine:
I had a List that was progressively loading data from the server. When the user scrolled down and the next batch of items would be added to the dataprovider, the scrollposition would jump back to the start.
The solution for this was as simple as stopping the propagation of the COLLECTION_CHANGE event so that the List wouldn't catch it.
myDataProvider.addEventListener(
CollectionEvent.COLLECTION_CHANGE, preventRefresh
);
private function preventRefresh(event:CollectionEvent):void {
event.stopImmediatePropagation();
}
You have to know that this effectively prevents a redraw of the List component, hence any added items would not be shown. This was not an issue for me since the items would be added at the end of the List (outside the viewport) and when the user would scroll, the List would automatically be redrawn and the new items would be displayed. Perhaps in your situation you can force the redraw if need be.
When all items had been loaded I could then remove the event listener and return to the normal behavior of the List component.

Flex Spark List scroll to bottom when new data is added

I have a Spark List (spark.components.List) backed by an ArrayCollection for its dataProvider. The List has a vertical scrollbar when there's too many rows to display. What I want is when a new row is added to the List for it to scroll to the bottom to show that new row.
I've tried calling List's ensureIndexIsVisible from a listener on the ArrayCollection. This doesn't work because the List hasn't yet fully rendered the new row. It will either scroll to the second from the last row, or throw the exception:
Error: invalidIndex
at spark.layouts.supportClasses::LinearLayoutVector/start()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\supportClasses\LinearLayoutVector.as:893]
at spark.layouts.supportClasses::LinearLayoutVector/getBounds()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\supportClasses\LinearLayoutVector.as:1117]
at spark.layouts::VerticalLayout/getElementBounds()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\VerticalLayout.as:852]
at spark.layouts.supportClasses::LayoutBase/http://www.adobe.com/2006/flex/mx/internal::getScrollPositionDeltaToElementHelper()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\supportClasses\LayoutBase.as:1367]
at spark.layouts.supportClasses::LayoutBase/getScrollPositionDeltaToElement()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\supportClasses\LayoutBase.as:1348]
at spark.components::List/ensureIndexIsVisible()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\components\List.as:2105]
I've made sure that my listener is added to the ArrayCollection after setting List's dataProvider. The hope is that I'd be calling ensureIndexIsVisible after the List got a chance to process the new row. My guess is that the List doesn't render the row until some redraw event that occurs later (after I've called ensureIndexIsVisible).
I've also tried specifying a VerticalLayout and setting its verticalScrollPosition to an overly large number (like 99999). This has the same problem - it scrolls to the second from the last row.
So, is there a way to scroll to a newly added row in a Spark List? The only I could find on the Internet is this poorly formatted flexcoders thread.
Instead of extending List in order to override updateDisplayList, I added a listener to the FlexEvent.UPDATE_COMPLETE on my List component and it works just fine for me.
My solution was to extend List and override updateDisplayList, which works:
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// call ensureIndexIsVisible here
}
See discussion on Adobe's forums.
I found that when ensureIndexIsVisible is run directly after an insert to a list dataProvider it does not accurately pull the position of the last item in the list. This looks to be because the binding has not had a chance to update the list. The fastest solution for me was to run a callLater on ensureIndexIsVisible.
_acGuestBookMessages.addItemAt(obj, _acGuestBookMessages.length);
var arr:Array = [_acGuestBookMessages.length-1];
callLater(lstGuestBookMessages.ensureIndexIsVisible, arr);
Want to scroll to bottom of an updating spark list and
ensureIndexIsVisible is too much undocumented trouble?
I got this to work by dealing with the physical height of dataGroup property on List...
function newMessage(...)
{
messages.addItemAt(msg, messages.length);
messages.refresh();
scrollToBottom();
}
function scrollToBottom():void
{
messagesList.addEventListener("updateComplete", scrollUpdate);
}
private function scrollUpdate(e:Event):void
{
messagesList.removeEventListener("updateComplete", scrollUpdate);
messagesList.dataGroup.verticalScrollPosition = messagesList.dataGroup.contentHeight - messagesList.dataGroup.height;
}
if(itemsList && itemsList.scroller &&
itemsList.scroller.verticalScrollBar &&
itemsList.scroller.verticalScrollBar.visible){
itemsList.scroller.verticalScrollBar.value =
itemsList.scroller.verticalScrollBar.maximum;
}
Was having the same problem, this works for me -
if(chatCollection.length > 0)
{
chatList.validateNow();
chatList.ensureIndexIsVisible(chatCollection.length - 1);
}

Flex 4 lifecycle and drawing stuff manually

I have a component which inherrits Group. I made a property called dataSource:ArrayList. I wish to draw a Line for each of the entries.
When the 'function set dataSource' -method is invoked I do the following (simplified):
var newLine:Line = new Line();
newLine.stroke = new SolidColorStroke();
newLine.xFrom = 0;
newLine.yFrom = 0;
newLine.xTo = 0;
newLine.yTo = height;
this.addElement(newLine);
The line doesn't stretch itself to the very bottom of the parent. I'm guessing I'm messing up life cycle, but I'm not finding flex life cycle particular easy to understand, so I'm not sure how to go about this.
If you don't want to interact with the line as an object on the display list, I'd simply draw it in updateDisplayList() using the Graphics api, and call invalidateDisplayList() from set dataSource()
The "right" way is slightly more verbose ;-)
private var dataSourceValid = true;
public function set dataSource(value:FooData):void {
_dataSource = foo;
dataSourceValid = false;
invalidateProperties();
}
override protected function commitProperties():void {
if (!dataSourceValid)
commitDataSource();
// Do it later in case we've invalidated something
// belonging to Flex while validating our stuff
super.commitProperties();
}
protected function commitDataSource():void {
// Do whatever we need to with our datasource,
// including adding or removing child elements.
// ...
// If we also need to re-draw something, then
// invalidateDisplayList();
dataSourceValid = true;
}
(All code typed in TextMate, so it's probably full of spelling errors and doesn't compile, but you get the idea)
I agree with you, it probably has to do with the component not being properly measured yet when you create your lines. You could try overriding updateDisplayList and setting the height of the lines you created to be the height parameter supplied to the updateDisplayList method. Don't create the lines in updateDisplayList since it can get called multiple times during the component life cycle. Regarding the life cycle in general, here's a link to a chart I've found helpful in the past: http://danorlando.com/?p=122 Hope that helps.
It isn't entirely clear what you're looking to do, but by putting a dataSource property on Group it appears as if you're trying to re-invent DataGroup. Perhaps you should consider just using the latter instead, along with a custom ItemRenderer within which you could draw a line?

Add Child immediately in Flex 3

I'm having a problem similar to FLEX: dialog not display immediately . Code follows:
private function saveBitmap(event:ContextMenuEvent):void
{
loadingScreen.visible = true;
loadingScreen.appLoadingText.text = "Preparing bitmap...";
addChild(loadingScreen);
validateNow();
var bmpd:BitmapData = new BitmapData(canv.width, canv.height);
bmpd.draw(canv);
var fr:FileReference = new FileReference();
fr.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, removeLoadingScreen);
fr.addEventListener(Event.CANCEL, removeLoadingScreen);
var png:PNGEncoder = new PNGEncoder();
var iba:ByteArray = png.encode(bmpd);
fr.save(iba, "export.png");
}
Basically, bmpd.draw and/or png.encode are dog slow, so I'd like to have a nice "please hold while we prepare your png" dialog to appear. I can't use callLater() because of the FileReference.
And just for good measure, the loading screen appears at the same time the save dialog appears from the call to fr.save().
Any ideas?
Cheers!
You're adding the child in this function. Are you doing any other work to the loadingScreen, such as sizing it? Or positioning it? Most commonly this is done in updateDisplayList(). What container are you using?
Are you sure the Z-order of your two children is correct? You can swap children with the swapChildren method

Flex: Expand AdvancedDataGrid Tree Column programmatically

Does anyone know how to programmatically expand the nodes of an AdvancedDataGrid tree column in Flex? If I was using a tree I would use something like this:
dataGrid.expandItem(treeNodeObject, true);
But I don't seem to have access to this property in the AdvancedDataGrid.
AdvancedDataGrid has an expandItem() method too:
http://livedocs.adobe.com/flex/3/langref/mx/controls/AdvancedDataGrid.html#expandItem()
Copy the sample found at the aforementioned url and call this function:
private function openMe():void
{
var obj:Object = gc.getRoot();
var temp:Object = ListCollectionView(obj).getItemAt(0);
myADG.expandItem(temp,true);
}
You could also open nodes by iterating through the dataProvider using a cursor. Here is how I open all nodes at a specified level:
private var dataCursor:IHierarchicalCollectionViewCursor;
override public function set dataProvider(value:Object):void
{
super.dataProvider = value;
/* The dataProvider property has not been updated at this point, so call
commitProperties() so that the HierarchicalData value is available. */
super.commitProperties();
if (dataProvider is HierarchicalCollectionView)
dataCursor = dataProvider.createCursor();
}
public function setOpenNodes(numLevels:int = 1):void
{
dataCursor.seek(CursorBookmark.FIRST);
while (!dataCursor.afterLast)
{
if (dataCursor.currentDepth < numLevels)
dataProvider.openNode(dataCursor.current);
else
dataProvider.closeNode(dataCursor.current);
dataCursor.moveNext();
}
dataCursor.seek(CursorBookmark.FIRST, verticalScrollPosition);
// Refresh the data provider to properly display the newly opened nodes
dataProvider.refresh();
}
Would like to add here that the AdvancedDataGrid, in spite of having an expandAll() method, has a property called displayItemsExpanded, which set to true will expand all the nodes.
For expanding particular children, the expandChildrenOf() and expandItem() methods can be used, as can be verified from the links given above.

Resources