How to tell when an MXML component has totally finished creation? - apache-flex

An MXML component can be quite complex, containing many nested controls, including asynchronously loaded content such as Image/SWFLoader.
Is there one event I can watch for on my component that will only be raised when every control and sub-component has loaded, including SWFs and Images?

CreationComplete will NOT do the trick if you are talking about loading swf content or anything really external like that. CreationComplete gets fired when the MXML components have been laid out as defined in MXML (IE nested components, buttons, boxes, canvasses, etc.), so content that needs to get loaded externally (an image, a swf) does not count.
What you need to do is keep track of everything that you're waiting for and fire off a custom event once all of those elements have loaded.
One possible hackish way to do it would be to listen for whatever load complete event is relevant for each element, then have them call back to the same function that increments a value equal to the number of components you're waiting for. This means you have to pay more attention if you're modifying it, but it also means you don't have to check a boolean for every element that needs to load (IE "if (image1Loaded && image2Loaded && swfLoaded)" etc.)

The onApplicationComplete event?

The creationComplete event should do the trick - creationComplete is called on the parent component after it is called on the children.
You can get some more info on the component lifecycle in the Adobe docs.

In some complex cases, like when your component is considered "finished" only when some data has been retrieved via HTTP or something like that, custom event is your best bet.

Related

Flex Cairngorm 3 Presentation Model Initializations

ok so I have a TitleWindow that I open up... and I have 6 states defined. I am using the Presentation model pattern for all of my views. I have discovered a frustrating nuance. When I tell my window to go to XXX state, the controls have to initialize since the states in flex use lazy loading. so... my PM code that says myTextArea.text bombs out and say "cannot access..." so as a work around, I made some creationComplete events on my controls to register the control with the PM. So when the state changes, the textarea finally initializes and on creationComplete calls PM.registerTextArea() which sets a reference to it. then in that function I run my code... myTextArea.text.. etc.
This seems like such a ugly hack that I hate it. Is there any way to wait until the entire state in created then call code on the PM? I have tried enterstate... but this event seems to fire before the state controls are ready.
I tried to add a comment, but I guess editing is the only way I can do this...
to everyone: thanks for the great feedback. I am doing something slightly off straight PM. Every view has a viewController (as I call them). Its kinda my own hybrid of a delegate / dataprovider. But this is moot. It's the flex component lifecycle when dealing with states that is the pain. if you change state.. there is no event to say "all my components in this state are ready". only event to say "we changed state". so my code that fires on state change is trying to talk to components that aren't ready yet. So from what I see, there seems to be no design pattern or perfect way to ensure that all components are created in a state unless using creationComplete on every component in the state to register it is ready... if you don't, you get a race condition. Regardless of frameworks or design patterns, this seems to be a root issue.
The textarea is an easy PM fix.. just bind it to the pm value. But there are other times I can't.
Specifically, I am trying to attach video to a display once I get to that state. This is done via addchild. regardless of where I do it.. I need to know that the videoDisplay is done loading before I call addchild. I even tried currentStateChange event since docs say that fires last... but alas.. the components in the state are still initializing. So it seems that creationComplete is my only option. Maybe the only sane way to keep to clean code is to create the entire thing (videodisplay and video) using as once the state is entered. I just hoped the flex framework had events to ehlp me here rather than buiilding everything on the fly in as.
Since your PM has a reference to a visual component (myTextArea), this isn't a completely pure form of a presentation model. It appears to be more of a supervising presenter / controller type of setup.
That being said, the way I would fix your problem would be to use a complete presenter outright ( no knowlege of the view at all ) or use a complete controller ( view implements an interface through which the controller communicates ). The advantage of using a presenter in Flex is that you can create a bindable value such as text or selectedItem, and the view will bind to that variable whenever it comes online so the issues dealing with the lifecycle of Flex components go away.

In Flex/Actionscript, how do I ensure that events all travel down one object tree, regardless of target?

I'm still working on my zoomable node-graph project. I'm currently having problems with what I know must be relatively easy, but have been unable to find a solution to:
I have numerous objects, many of them are stored within other objects (and overlap in physical space). As I zoom into an object, it begins to fade away. At the moment it begins to fade, I load in the child object (or create a child object if one doesn't exist). I want to turn off the parent object's ability to respond to most events. The exception is the scroll wheel, which needs to be sent to both objects simultaneously so that the parent can continue to fade out as I zoom farther in. Try as I might, I can't find a way to tell Flex "hey, for right now dispatch these types of events ONLY to this object." I either end up with event dispatch stack overflows from trying to manually redirect the events, or I get events that don't activate at the correct time or on the correct object. What can I do?
I want to turn off the parent object's
ability to respond to most events.
You can't turn off an objects ability to respond to events.
You can write code to remove all event listeners inside that object; although I suspect this will be a manual process.
You can remove that object from the display list so its event listeners won't trigger on events in their capture or bubbling phases. If this object has children that you want to display this won't work.
You may be able to work something out where the 'child' object calls stopPropogation() and/or stopImmediatePropogation() in it's own event listeners. I believe this could prevent the handlers from firing in the parent, but it may depend how your listeners are set up. I do not believe this will have an effect if you are listening in the capture phrase.
You may be able to write "aware" event handlers that basically say:
if(SomeConditionTrue){ return; }
I'm running out if ideas. But, I'm pretty sure there is no way to universally say "Don't let this component respond to events"

Flex 3 - Force all controls to render at start

When I try to access the hidden TABs of my tab navigator control in action script, it returns a null error. But it works OK if I just activate the control in the user interface once. Obviously the control is not created until I use it. How do I make all the tabs automatically created by default ?
<mx:TabNavigator creationPolicy="all"/>
That should do it. Deferred instanciation is a feature, but sometimes it is a hassle.
The Flex framework is optimizing creation be default (creationPolicy="auto") so if you have a configuration dialog with a lot of tabs, for example, and the most useful tab is the first one, your application does not spend time and memory initializing the tabs that the user never sees.
This makes a lot of difference when dialogs like this never release, and is a good default to go with.
One thing to look at is using a private variable in your dialog/form instead of pushing the data to the control on the hidden page. This style treats the whole form as if it were a component, which it sort of is. To repeat: the MXML form/dialog/canvas is a class, and it can have data and methods in addition to containing other components.
Cheers
On a side note, I've run into the deferred-loading policy in a multi-state application, and circumvented it by forcing all elements to be included and invisible in the initial state. Something to consider, but only as a hack.

Access component id of one mxml from another

I have two mxml files. one is main that is application tag mxml file and another is my mxml component file.
I have a viewstack in my main mxml whose id is, say, "mainViewStack".
Now I want to set selectedChild property of "mainViewStack" from my mxml component file.
But I m getting error:
Error #1009: Cannot access a property or method of a null object reference.
on accessing mainObj.mainViewStack.selectedChild.id where mainObj is the object of main mxml file.
Please help me out.
Thank u.
My guess is that you're trying to access the child before it's created. But that's hard to tell without the code.
Try waiting until the FlexEvent.CREATION_COMPLETE event on the application to access the selected child.
This issue is referred to as "deferred instantiation" and is a product of the Flex Component Lifecycle. If you want an extremely thorough explanation of this concept, this white paper is probably the best I have read.
Essentially Flex creates components as they are needed. Each component has a lifecycle that takes it through stages:
Construction
Addition
Initialization
Invalidation
Validation
Update
Removal
A sub-component isn't going to be accessible until it has passed through the initialization phase. This is the point at which a Flex component will dispatch its CREATION_COMPLETE event, letting you (and the framework) know that it is ready for interaction. Prior to this event, you are going to receive null reference errors when attempting to access the component or its children.
ViewStacks compound this by default by not initializing sub-components until they are called for display. The creationPolicy property of a ViewStack, by default, is set to auto. There are several options for this property, including all. Be aware, however, that this can potentially present severe performance issues, as all of the components inside the stack are going to be initialized immediately regardless of whether or not the user actually even looks at the component.
In your specific case this isn't the problem. The component that contains the view stack hasn't been completely initialized. You need to set the child of the ViewStack in a CREATION_COMPLETE event handler.
or you can give "creationPolicy=all" because Flex only creates the first visible child from the viewstack
You can use this code in your MXML component to change application's ViewStack selected child from other MXML component. However it is not a good practice.
FlexGlobals.topLevelApplication.mainViewStack.selectedChild = FlexGlobals.topLevelApplication.childId
You can use static event dispatcher to dispatch an event from one view and listen that event in an other view.

Flex: Render an unrealized UIComponent to BitmapData?

What is the best way to render to a UIComponent which hasn't been added to the stage? (I'm using UIComponents as renderers for objects, and want to render new copies for image export, filtering, etc.)
Two strategies I've seen/used so far include realizing the component to ensure it calls all the lifecycle methods:
Add the component to Application.application, render with BitmapData.draw(), remove component. This is similar to what I've seen done for printing unrealized components as well.
Add the component to a pop up window, render with BitmapData.draw(), dismiss popup after rendering complete.
I believe both of these just rely on the UI not refreshing while the current thread/event is executing, though (1) could also rely on the component being realized out of view.
Is there a better way?
What I've used in the past with much success is the following:
Create a new instance of your component
Add an event listener for FlexEvent.CREATION_COMPLETE
Set visible=false on the component
Add the component as a child of the main Application
When the component is created, the event listener function will be invoked. The rest of the logic should be put in / invoked from your event listener function
Remove the event listener you added in step #2.
Use ImageSnapshot.captureImage() or captureBitmapData() to capture the visual representation of the component.
Remove the component from the main Application
Process the image data as you need to.
I've used this to snapshot various Flex charting components for use in PDF's generated on the server side. After getting the BitmapData I use the PNGEncoder or JPEGEncoder classes to compress the data, then encode it in Base64 before uploading to the server.
I'm pretty sure you can use the draw() method in BitmapData without having your component on the DisplayList.
For example is use it when I need to modify images I load with the Loader Class. In the init handler I create a BitmapData instance and draw the Bitmap from the loadInfo.content property, then copyPixels() or whatever I need to modify the loaded image
So much of a UIComponent's layout can be tied to it's context. This is especially true for a lot of its derivatives (e.g. HBox) since the fluidity of the layout is tied to it's parent's size and the number of siblings sharing its parents space.
Additionally Flex can be a real pain to get to visually update. Often critical render functions aren't done synchronously ... there are callLater, callLater2 and other hacky approaches that make dealing with the auto-magical layout properties of UIComponents a major headache. Not even calling validateNow or updateDisplayList can guarantee that the layout will be correct on the current frame (instead of a few frames in the future).
I suggest the best thing you can do is not use a UIComponent and try and use a Sprite or other.
Your approach to attach it but make it invisible (alpha = 0, mouseEnabled = false, mouseChildren = false) is decent. You should listen for the FlexEvent.CREATION_COMPLETE callback before you are certain it is properly laid out. Then you can bitmapData.draw it and then remove it from the stage. If you must use UIComponents then I know of no better way.
You can call the lifecycle function manually before using the BitmapData.draw(). Do the following.
createChildren().
commitProperties().
updateDisplayList().
bmd.draw().
The first 2 steps are not 100% necessary, you can put all codes into updateDisplayList(). Because you invoke the function manually, you don't have to worry this is invoked by Flex framework many times.

Resources