8 Replies Latest reply on May 27, 2011 9:28 AM by UbuntuPenguin

    Recursively iterating over all child controls

    JoshBeall Level 1

      Hi All,

       

      I'm trying to recursively iterate over all child controls in my Flex 4 application, and I've been doing something like this to get the immediate children of a particular container:

       

      public static function getElements(parent:Object):Vector.<IVisualElement>{
           var result:Vector.<IVisualElement> = new Vector.<IVisualElement>();
           for(var i:int = 0; i < parent.numElements; i++){
                result.push(parent.getElementAt(i));
           }
           return result;
      }
      

       

      I can then call that function recursively.  It works--kinda.  The way it doesn't work is that if I call this object on the application object itself, it will only give me back elements that are visible in the current state.  As I move from one state to another, different elements are returned by my getElements method.

       

      So, is there are way I can find all child elements, whether or not they are visible in the current state?

       

        -Josh

        • 1. Re: Recursively iterating over all child controls
          kevinklin Adobe Employee

          Hi Josh,

           

          The reason that states are preventing you from finding all the child elements is that Flex doesn't create the elements for a given state until you enter that state. A way to force Flex to create all elements regardless of which state its excluded from is to set "itemCreationPolicy" to "immediate" on any component that uses includeIn/excludeFrom. For example: <s:Button excludeFrom="state1" itemCreationPolicy="immediate" />. This will create the elements at application start up.

           

          As a note, if you're using a ViewStack, you'll also need to set ViewStack's creationPolicy="all" for the components in the ViewStack to be instantiated.

           

          More information here: http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf63611-7ffa.html

           

          Let me know if you have any more questions.

           

          -Kevin

          • 2. Re: Recursively iterating over all child controls
            JoshBeall Level 1

            kevinklin wrote:

             

            The reason that states are preventing you from finding all the child elements is that Flex doesn't create the elements for a given state until you enter that state.

            I thought of that, but I don't think that's the culprit because I tried the following scenario (specifically thinking of what you described):

             

            Go to state A, and fill in some information in the textboxes in that state

            Go to state B, and trigger my code to recurse through the control tree.  It finds only the controls visible in state B.

            Go back to state A, and verify that the textboxes still have the values I placed in them.

             

            Unless I misunderstand the way Flex creates the controls, as soon as I went to state A, the controls would have been instantiated and would then have persisted for the life of the Flex application--is that right?  Or are they actually being removed when I go to a different state, and then recreated (and the textbox values set back to where it was when I last was on that state) when I return to that state?

             

              -Josh

            • 3. Re: Recursively iterating over all child controls
              kevinklin Adobe Employee

              Unless I misunderstand the way Flex creates the controls, as soon as I went to state A, the controls would have been instantiated and would then have persisted for the life of the Flex application--is that right?  Or are they actually being removed when I go to a different state, and then recreated (and the textbox values set back to where it was when I last was on that state) when I return to that state?

               

                -Josh

               

              The first statement is correct. Sorry, my comment was confusing. Once you enter a state, all the controls in that state will be instantiated and persist through the app.

               

              Sorry, my original response was lacking. In general, if a control isn't included in the state, it becomes unparented. This means if you're walking through the element tree of the application, the control won't be there. The simple way to reach these controls is to give it an "id" in MXML and keep track of them this way. There isn't a simple way to arbitrarily find all the elements regardless of state.

               

              As an aside, two non-trivial ways to achieve what you want:

              1. Call your method on each state while switching to each state. Make sure to exclude components that exist across states. This is the brute force method =).
              2. Another way is to introspect the states property and the State objects themselves. The State objects have information and pointers to the instances that need to be added or created in each state in their "overrides" property. So, you could combine this introspection method with your getElements() to find all the elements. I'll post a follow-up if you wish to know about how to do this exactly.

               

              -Kevin

              • 4. Re: Recursively iterating over all child controls
                JoshBeall Level 1

                kevinklin wrote:

                 

                Another way is to introspect the states property and the State objects themselves. The State objects have information and pointers to the instances that need to be added or created in each state in their "overrides" property. So, you could combine this introspection method with your getElements() to find all the elements. I'll post a follow-up if you wish to know about how to do this exactly.

                I'm very interested in this.  Could you elaborate?

                 

                Thanks!

                • 5. Re: Recursively iterating over all child controls
                  kevinklin Adobe Employee

                  Hi Josh,

                   

                  So, I've come up with some quick code for you. However, I must add my disclaimers first:

                  1. The code is incomplete in that it excludes children of children that don't use states, but includes children of children that are associated with states of the parent. I'm also definitely not an expert in how our states system works.
                  2. I'm not sure what your use case for all of this is, but my best guess is that it would be better to find a different way to solve your problem than to go the route of introspecting states. It's a long and dirty road =).


                  Other than that, I've attached the code for you. The meat of this is:

                   

                      /**
                       *  Finds all elements in all states that use the states of the specified parent.
                       */
                      public static function getAllElements(parent:UIComponent):Vector.<IVisualElement>
                      {   
                          var container:IVisualElementContainer = parent as IVisualElementContainer;
                          if (!container)
                              return new Vector.<IVisualElement>();
                          
                          var result:Vector.<IVisualElement> = getCurrentElements(container);
                          var states:Array = parent.states;
                          
                          for each (var state:State in parent.states)
                          {
                              for each (var addItems:AddItems in state.overrides)
                              {
                                  var elt:IVisualElement = addItems.items as IVisualElement;
                                  
                                  if (elt && !containsElement(result, elt))
                                      result.push(elt);
                              }
                          }
                          return result;
                      }
                  

                   

                   

                  This method takes the parent container (in my case, the application) and returns a vector of children elements that associate themselves with the states of this parent container. First, it grabs all the elements already created from the parent container. Next, it iterates over each state and then each AddItems of each state. The AddItems object contains the information and pointers to the specific element that will be "added" when the current state changes to that state. By calling addItems.items, we force the creation of the specific element which is returned to us. Now, we can use a simple containsElement() method to check for duplicates and build a list of elements that would be added to each of the states.

                   

                  Again, the code is finding all of the children of the provided parent in all of its parent's states; however, it also finds children of children that may be using the parent's states as well. But if the children of children are not using the parent's states then it will not be found (easily fixable if you recurse). In addition, knowing which elements belong to which container just from introspecting the State object is pretty complicated and involves understanding how addItems.apply() works.

                   

                  Anyway, I hope this helps you. I would be interested in hearing your use case since this code is pretty complicated. I'd like to see if I could help by finding a different approach for your problem.

                   

                  -Kevin

                  • 6. Re: Recursively iterating over all child controls
                    JoshBeall Level 1

                    Hi,

                     

                    We'd like to be able to "on the fly" add instrumentation to some of our apps so that we can gather detailed information about how users are interacting with the app.  My thinking is that if I can recursively iterate over the entire control tree, I could then add event listeners to, say, all textboxes, radiobuttons and dropdownlists in my app.  Every time a user interacts with one of those controls, my app could report to my backend service about that interaction.  I can then piece together precise user interaction data--how much time it took to fill in a certain set of data, how much time was spent on each field, etc.

                     

                    That data will help us figure out where the most heavily used (and/or cumbersome) areas of our app are, and we can focus on those areas for improvement.

                     

                    This kind of data would be especially powerful when combined with user surveys where we solicit direct user feedback.  Then we have both what they're saying about our app, and what they're doing with our app.

                     

                    For various reasons (such as load on our backend service) we want to be able to selectively turn this feature on and off, rather than wiring it into the app so that it is always on for everybody.

                     

                    I'm interested in feedback if you see a better way to go about this.  Thanks!

                     

                      -Josh

                    • 7. Re: Recursively iterating over all child controls
                      kevinklin Adobe Employee

                      Hi Josh,

                       

                      I was thinking about this again the other day and realized that there's a more obvious (and clean) solution. You could wait for the "currentStateChange" event on the Application or top level container and then run through all the children adding your event listeners. Lazily adding the event listeners gives you the advantage of not needing to do this work up front except for the initial state. You can also check if a component is already listening for the event and skip over it.

                       

                      -Kevin

                      • 8. Re: Recursively iterating over all child controls
                        UbuntuPenguin Level 4

                        If you want to monitor user actions and paths in your application you might want to check out the Flex automation stuff.  http://www.adobe.com/devnet/flex/samples/custom_automated.html

                         

                        I haven't looked into it myself, but it came to mind when you mentioned you wanted to know what your users where doing.