Pushing dynamic skin parts into controlBarContent

Problem
You want to add some controls inside on an Application or Panel’s controlBar area, and you only know their number at runtime. You also want the ability to skin them. For simplicity’s sake the assumption is that the controls are of the same type.

Solution
1. Put controls in the controlBar area
You use the Application/TitleWindow/Panel.controlBarContent property to define the controls that appear in the control bar area. Event though this is done in mxml most of the times, the same can be achieved with actionscript.

//Application/Panel/TitleWindow/Other custom component
Panel.controlBarContent = [ctrl1Ref, ctrl2Ref,…];

2. Ability to skin controls:
Best way to skin the controls is to have them be declared as skin parts on the parent component. Since their number is known only at runtime and they will be created later on after the skin has been created, the skin parts will be dynamic.

In more detail:
Create a custom component which extends from Application/Panel/TitleWindow (whatever suits your needs better). They are the components that support the addition of a control bar group out of the box. Declare your runtime controls as dynamic skin parts. Upon new data arrival create an array with your controls and assign it to the controlBarContent property. Also, create a custom skin class (MXML) that fulfills the contract of a skin for an Application/Panel/TitleWindow component and set it as the skinClass property of your custom class. Within the custom skin, define the dynamic skin parts inside the Declarations tags.

Discussion
I created a custom component called EnhancedPanel that extends Panel. The component will be able to display a variable number of buttons based on some data which will be passed it at run time, and held inside a property called _customData, typed as Array. On new data, the buttons are recreated based on this piece of data. For simplicity sake the custom data array holds only string values used to display the button’s label. Since data can change many times during the application’s lifetime, the already existing buttons need to be destroyed before creating new ones.

Declare the dynamic skin part:

/**
* An optional dymanic skin part that defines the actionButton
*/
[SkinPart(required="false", type="spark.components.Button")]
public var actionButton:IFactory;

//... inside skin
<fx:Declarations>
	<fx:Component id="actionButton">
		<s:Button/>
	</fx:Component>
</fx:Declarations>

Recreate the buttons based on data:

private var _customData:Array;

public function set customData(value:Array):void
{
	destroyActionButtons();

	_customData = value;

	createActionButtons();
}

First any existing button is removed, new data is assigned and then the buttons are created and added. The code works with the assumption that the data array is valid hence no validation code present. For removing dynamic skin parts flex exposes a protected methods inside SkinnableComponent removeDynamicPartInstance():

protected function removeDynamicPartInstance(partName:String, instance:Object):void

To acess existing skin part instances one employs the use of another 2 methods from SkinnableComponent numDynamicParts() and getDynamicPartAt():

protected function numDynamicParts(partName:String):int
protected function getDynamicPartAt(partName:String, index:int):Object

protected function destroyActionButtons():void
{
	var buttonInstance:Button;
	var count:int = numDynamicParts("actionButton");
	while (count > 0)
	{
		buttonInstance = getDynamicPartAt("actionButton", 0) as Button;
		removeDynamicPartInstance("actionButton", buttonInstance);
		buttonInstance = null;
		count--;
	}
}

For creating an instance of a dynamic part adding new skin parts another method from SkinnableComponent createDynamicPartInstance() is used. The new buttons instances are pushed into a temporary array which will end up assigned to the controlBarContent property of the Panel.

protected function createDynamicPartInstance(partName:String):Object

 * @private
 * Create the action buttons 
 */
protected function createActionButtons():void
{
	var buttonInstance:Button;
	var count:int = _customData.length;
	if (count)
	{
		var i:uint;
		var actionButtons:Array = [];
		for (i = 0; i < count; ++i)
		{
			if (_customData[i])
			{
				buttonInstance = createDynamicPartInstance("actionButton") as Button;
				buttonInstance.label = _customData[i];
				actionButtons.push(buttonInstance);
			}
		}

		super.controlBarContent = actionButtons;
	}
}

Hooking/unhooking up behaviors to the buttons is similar to the static skin parts way, by overriding partAdded() respectively partRemoved(), which are called automatically by flex after adding/removing a skin part.

/**
 * @private
 * @override
 */
override protected function partAdded(partName:String, instance:Object):void
{
	super.partAdded(partName, instance);

	if (partName == "actionButton")
	{
		Button(instance).addEventListener(MouseEvent.CLICK, actionButton_clickHandler);
	}
}

/**
 * @private
 * @override
 */
override protected function partRemoved(partName:String, instance:Object):void
{
	super.partRemoved(partName, instance);

	if (partName == "actionButton")
	{
		Button(instance).removeEventListener(MouseEvent.CLICK, actionButton_clickHandler);
	}
}

/**
 * @private
 * @param event
 */
protected function actionButton_clickHandler(event:MouseEvent):void
{
	trace("EnhancedPanel.actionButton_clickHandler(event) stub");
}

As you probably noticed when adding the buttons to the display list:


super.controlBarContent = actionButtons;

I assigned it to the property of the base panel class. That is because I wanted to restrict access to the controlBarContent property from outside of this class. To do that you use the [Exclude] metatag (hide it from flash builder) and override  the getter/setter to do nothing and return null value.

[Exclude(kind="property", name="controlBarContent")]
public class EnhancedPanel extends Panel

//...
override public function set controlBarContent(value:Array):void {}
override public function get controlBarContent():Array {return null;}

Sample below + .FXP PROJECT DOWNLOAD.


Get Adobe Flash player

Leave a Reply

Your email address will not be published. Required fields are marked *

*