Filtering the children of a Flex container

Flex Published on January 5, 2010 by Andrea Bresolin Add comments
Live demoSource code (LICENSE)

In this post I’m going to show an utility function that I implemented some time ago. Its purpose is to filter the children of a Flex container. So, for example, let’s say that you want to have all the children of a specific container going deep throughout the entire children hierarchy, but you want to exclude some classes or instances from the search, then this function is for you. I use it to selectively enable or disable some components or to get the value of a specific property for them, but the function is generic enough to be used in many different situations.

The function is called getContainerChildren. Here is its source code:

package com.devahead.utils
{
	import mx.collections.ArrayCollection;
	import mx.core.Container;

	public class MiscUtils
	{
		/**
		 * Filters all the children of a container.
		 *
		 * @param container
		 *         Container of the children that have to be filtered.
		 * @param childrenClasses
		 *         Array of the classes that have to be kept while
		 *         filtering the children. If not specified, the type
		 *         of the children is not considered while filtering.
		 * @param excludedClasses
		 *         Array of the classes that have to be excluded.
		 *         The children must not belong to these types.
		 * @param excludedInstances
		 *         Array of the instances that have to be excluded.
		 *         The children must not be one of these instances.
		 * @param deepSearch
		 *         If true, specifies that the children must be
		 *         searched throughout the entire hierarchy util
		 *         there are no more children available for a
		 *         component. If false, only the children of the
		 *         container are considered and not their children.
		 * @param forceDeepSearchOnExcludedClasses
		 *         If true and also deepSearch is true, then the children
		 *         are searched also within the components that have been
		 *         excluded from the results because their type is among
		 *         the excludedClasses. If false, the children are never
		 *         searched within the excluded components. If a component
		 *         has been excluded because it's in excludedInstances, then
		 *         a deep search is never executed on it, even if it belongs
		 *         to an excluded class and forceDeepSearchOnExcludedClasses
		 *         is true.
		 * @param forceDeepSearchOnExcludedInstances
		 *         If true and also deepSearch is true, then the children
		 *         are searched also within the components that have been
		 *         excluded from the results because they are among
		 *         the excludedInstances. If false, the children are never
		 *         searched within the excluded components.
		 * @param resultArray
		 *         Array used to store the children found in the recursive
		 *         calls. It should be left null when the function is called
		 *         externally (not recursively).
		 * @return Array of the container's children filtered according
		 *         to the specified parameters.
		 */
		public static function getContainerChildren(container: Container,
			childrenClasses: Array = null, excludedClasses: Array = null,
			excludedInstances: Array = null, deepSearch: Boolean = false,
			forceDeepSearchOnExcludedClasses: Boolean = false,
			forceDeepSearchOnExcludedInstances: Boolean = false,
			resultArray: ArrayCollection = null): ArrayCollection
		{
			var componentsArray: ArrayCollection = (resultArray != null ?
				resultArray : new ArrayCollection());

			if (container != null)
			{
				// Check all the container's children
				for (var i: int = 0; i < container.numChildren; i++)
				{
					var currChild: Object = container.getChildAt(i);

					var isCorrectComponent: Boolean = false;

					// Check if the type of the current child is correct
					if (childrenClasses == null || childrenClasses.length == 0)
					{
						isCorrectComponent = true;
					}
					else
					{
						for each (var currClass: Class in childrenClasses)
						{
							if (currChild is currClass)
							{
								isCorrectComponent = true;
								break;
							}
						}
					}

					// Check if the current child is an instance that has to be excluded
					var excludedAsInstance: Boolean = false;

					if (excludedInstances != null && excludedInstances.length > 0)
					{
						for each (var currExcInst: Object in excludedInstances)
						{
							if (currChild == currExcInst)
							{
								isCorrectComponent = false;
								excludedAsInstance = true;
								break;
							}
						}
					}

					// Check if the current child's type has to be excluded
					var excludedAsClass: Boolean = false;

					if (excludedClasses != null && excludedClasses.length > 0)
					{
						for each (var currExcClass: Class in excludedClasses)
						{
							if (currChild is currExcClass)
							{
								isCorrectComponent = false;
								excludedAsClass = true;
								break;
							}
						}
					}

					// Check if I must use a deep search
					var mustGoDeeper: Boolean = (
						(excludedAsInstance && forceDeepSearchOnExcludedInstances) ||
						(!excludedAsInstance && excludedAsClass && forceDeepSearchOnExcludedClasses));

					if (deepSearch && (currChild is Container) &&
						((!excludedAsInstance && !excludedAsClass) || mustGoDeeper))
					{
						// Get all the children of the current child through a recursive call.
						//
						// NOTE: I don't need to care about the result value of the recursive
						//       call because the children found are directly inserted into the
						//       componentsArray variable.
						getContainerChildren(currChild as Container, childrenClasses,
							excludedClasses, excludedInstances, deepSearch,
							forceDeepSearchOnExcludedClasses, forceDeepSearchOnExcludedInstances,
							componentsArray);
					}

					if (isCorrectComponent)
					{
						// Add the child to the result list
						componentsArray.addItem(currChild);
					}
				}
			}

			return componentsArray;
		}
	}
}

The function is recursive, so during every recursion all the children of a container are discovered and they are filtered according to the specified exclusion classes or instances. If we must go deeper to find other components, then another recursion step is executed.

Here is an example application. It shows some different ways to use the filtering function. For each filter, there are a button and a label next to it showing the parameters of the function. When a button is pressed, all the resulting components are written in the text area under the buttons. For each component you can see its ID and the path in the hierarchy to reach it.

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	pageTitle="Filtering the children of a Flex container - devahead BLOG"
	layout="vertical" horizontalAlign="left"
	viewSourceURL="srcview/index.html">

	<mx:Script>
		<![CDATA[
			import mx.core.Container;
			import com.devahead.utils.MiscUtils;
			import mx.core.UIComponent;
			import mx.collections.ArrayCollection;

			protected function printResult(result: ArrayCollection): void
			{
				resultArea.text = "";

				if (result != null && result.length > 0)
				{
					for each (var item: UIComponent in result)
					{
						resultArea.text += item.id + " (" + item.toString() + ")\n";
					}
				}
			}

			protected function getAllTextInput(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, [TextInput], null, null, true));
			}

			protected function getAllComboBox(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, [ComboBox], null, null, true));
			}

			protected function getAllLabel(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, [Label], null, null, true));
			}

			protected function getAllTextInputNotVbox1(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, [TextInput],
					null, [vbox1], true));
			}

			protected function getAllComboBoxNotPanel(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, [ComboBox],
					[Panel], null, true));
			}

			protected function getAllUIComponentNotComboBox(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, [UIComponent],
					[ComboBox], null, true));
			}

			protected function getAllNotVbox1OKChildren(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, null,
					null, [vbox1], true, false, true));
			}

			protected function getAllNotPanelVBoxOKChildren(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, null,
					[Panel, VBox], null, true, true));
			}

			protected function getTextInputOnlyInVbox1(): void
			{
				printResult(MiscUtils.getContainerChildren(vbox1, [TextInput]));
			}

			protected function getTextInputOnlyInPan1(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, [TextInput]));
			}

			protected function getAllPan1(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1));
			}

			protected function getAllPan1DeepSearch(): void
			{
				printResult(MiscUtils.getContainerChildren(pan1, null, null, null, true));
			}
		]]>
	</mx:Script>

	<mx:HBox>
		<mx:Panel id="pan1" title="pan1">
			<mx:VBox id="vbox1" backgroundAlpha="0.2" backgroundColor="0xff0000">
				<mx:TextInput id="text1" text="text1 in vbox1"/>
				<mx:TextInput id="text2" text="text2 in vbox1"/>
				<mx:ComboBox id="combo1" prompt="combo1 in vbox1"/>
			</mx:VBox>

			<mx:TextInput id="text3" text="text3"/>
			<mx:Label id="lab1" text="lab1"/>

			<mx:Panel id="pan2" title="pan2">
				<mx:Label id="lab2" text="lab2 in pan2"/>
				<mx:ComboBox id="combo2" prompt="combo2 in pan2"/>
			</mx:Panel>
		</mx:Panel>

		<mx:Box backgroundColor="0xffffff" backgroundAlpha="0.5" height="100%">
			<mx:Text fontSize="12">
				<mx:htmlText>
					<![CDATA[
<i>getContainerChildren</i>(
  container: <b>Container</b>,
  childrenClasses: <b>Array</b> = null,
  excludedClasses: <b>Array</b> = null,
  excludedInstances: <b>Array</b> = null,
  deepSearch: <b>Boolean</b> = false,
  forceDeepSearchOnExcludedClasses: <b>Boolean</b> = false,
  forceDeepSearchOnExcludedInstances: <b>Boolean</b> = false,
  resultArray: <b>ArrayCollection</b> = null): <b>ArrayCollection</b>
				]]>
				</mx:htmlText>
			</mx:Text>
		</mx:Box>
	</mx:HBox>

	<mx:Spacer height="20"/>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all TextInput" click="{getAllTextInput()}"/>
		<mx:Label text="getContainerChildren(pan1, [TextInput], null, null, true)"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all ComboBox" click="{getAllComboBox()}"/>
		<mx:Label text="getContainerChildren(pan1, [ComboBox], null, null, true)"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all Label" click="{getAllLabel()}"/>
		<mx:Label text="getContainerChildren(pan1, [Label], null, null, true)"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all TextInput but not those in vbox1"
			click="{getAllTextInputNotVbox1()}"/>
		<mx:Label text="getContainerChildren(pan1, [TextInput], null, [vbox1], true)"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all ComboBox but not those children of a Panel"
			click="{getAllComboBoxNotPanel()}"/>
		<mx:Label text="getContainerChildren(pan1, [ComboBox], [Panel], null, true)"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all UIComponent but not the instances of ComboBox"
			click="{getAllUIComponentNotComboBox()}"/>
		<mx:Label text="getContainerChildren(pan1, [UIComponent], [ComboBox], null, true)"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all components, exclude vbox1, but get its children"
			click="{getAllNotVbox1OKChildren()}"/>
		<mx:Label text="getContainerChildren(pan1, null, null, [vbox1], true, false, true)"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all components, exclude Panel and VBox, but get their children"
			click="{getAllNotPanelVBoxOKChildren()}"/>
		<mx:Label text="getContainerChildren(pan1, null, [Panel, VBox], null, true, true)"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get TextInput only if children of vbox1"
			click="{getTextInputOnlyInVbox1()}"/>
		<mx:Label text="getContainerChildren(vbox1, [TextInput])"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get TextInput only if children of pan1"
			click="{getTextInputOnlyInPan1()}"/>
		<mx:Label text="getContainerChildren(pan1, [TextInput])"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all the children of pan1" click="{getAllPan1()}"/>
		<mx:Label text="getContainerChildren(pan1)"/>
	</mx:HBox>

	<mx:HBox verticalAlign="middle">
		<mx:Button label="Get all the children of pan1 (deep search)"
			click="{getAllPan1DeepSearch()}"/>
		<mx:Label text="getContainerChildren(pan1, null, null, null, true)"/>
	</mx:HBox>

	<mx:Spacer height="20"/>

	<mx:Label text="Result:"/>
	<mx:TextArea id="resultArea" width="450" height="200" wordWrap="false"/>
</mx:Application>

I’m not going to explain each different usage because the function is really generic, so it’s up to you to decide if it suits your needs or not. I just hope it helps somebody.

Tags: , , ,

Leave a Reply

WP Theme & Icons by N.Design Studio
©2009-2010 Andrea Bresolin. All rights reserved. - Privacy Policy
Entries RSS Comments RSS