It is possible to get a reference to the parent of the programmatic skin from within the programmatic skin class. You can use this reference to access properties of the parent component or call methods on it.
You can access the parent from the updateDisplayList() method by using the skin's parent property. You cannot access the parent in the skin's constructor because the skin has not yet been added to the parent control. The value of the skin's parent property is set when the parent component calls the addChild() method to add the skin as a child.
When instantiating components with programmatic skins, the order of events is as follows:
To get a reference to the skin's parent, you must cast the skin's parent property to a UIComponent. The skin inherits this read-only property from the IFlexDisplayObject interface. You should also confirm that the parent is a UIComponent by using the is operator, because Flex throws a run-time error if the cast cannot be made.
The following example gets the class name of the parent control and draws the border and fill, depending on the type of component the parent is:
package {
import flash.display.GradientType;
import flash.display.Graphics;
import mx.skins.Border;
import mx.styles.StyleManager;
import mx.utils.ColorUtil;
import mx.skins.halo.HaloColors;
import mx.core.UIComponent;
public class IconSkin extends Border {
public function IconSkin() {
//super();
}
override public function get measuredWidth():Number {
return 14;
}
override public function get measuredHeight():Number {
return 14;
}
override protected function updateDisplayList(w:Number, h:Number):void {
super.updateDisplayList(w, h);
// User-defined styles
var borderColor:uint = getStyle("borderColor");
var fillAlphas:Array = getStyle("fillAlphas");
var fillColors:Array = getStyle("fillColors");
StyleManager.getColorNames(fillColors);
var highlightAlphas:Array = getStyle("highlightAlphas");
var themeColor:uint = getStyle("themeColor");
var r:Number = width / 2;
var upFillColors:Array;
var upFillAlphas:Array;
var disFillColors:Array;
var disFillAlphas:Array;
var g:Graphics = graphics;
g.clear();
var myParent:String;
switch (name) {
case "upIcon": {
upFillColors = [ fillColors[0], fillColors[1] ];
upFillAlphas = [ fillAlphas[0], fillAlphas[1] ];
if (parent is UIComponent) {
myParent = String(UIComponent(parent).className);
}
if (myParent=="RadioButton") {
// RadioButton border
g.beginGradientFill(GradientType.LINEAR,
[ borderColor, 0x000000 ],
[100,100], [0,0xFF],
verticalGradientMatrix(0,0,w,h));
g.drawCircle(r,r,r);
g.drawCircle(r,r,(r-1));
g.endFill();
// RadioButton fill
g.beginGradientFill(GradientType.LINEAR,
upFillColors,
upFillAlphas,
[0,0xFF],
verticalGradientMatrix(1,1,w-2,h-2));
g.drawCircle(r,r,(r-1));
g.endFill();
} else if (myParent=="CheckBox") {
// CheckBox border
drawRoundRect(0,0,w,h,0,
[borderColor, 0x000000], 1,
verticalGradientMatrix(0,0,w,h),
GradientType.LINEAR,
null, {x: 1,y:1,w:w-2,h:h-2,r:0});
// CheckBox fill
drawRoundRect(1, 1, w-2, h-2, 0,
upFillColors, upFillAlphas,
verticalGradientMatrix(1,1,w-2,h-2));
}
// top highlight
drawRoundRect(1, 1, w-2,
(h-2)/2, {tl:r,tr:r,bl:0,br:0},
[0xFFFFFF, 0xFFFFFF],
highlightAlphas,
verticalGradientMatrix(0,0,w-2,(h-2)/2));
}
// Insert other cases such as downIcon and overIcon here.
}
}
}
}
In many cases, you define a programmatic skin that defines style properties, such as the background color of the skin, the border thickness, or the roundness of the corners. You can make these properties styleable so that your users can change their values in a CSS file or with the setStyle() method from inside their Flex applications. You cannot set styles that are defined in programmatic skins by using inline syntax.
To make a custom property styleable, add a call to the getStyle() method in the updateDisplayList() method and specify that property as the method's argument. When Flex renders the skin, it calls getStyle() on that property to find a setting in CSS or on the display list. You can then use the value of the style property when drawing the skin.
You should wrap this call to the getStyle() method in a check to see if the style exists. If the property was not set, the result of the getStyle() method can be unpredictable.
The following example verifies if the property is defined before assigning it a value:
if (getStyle("lineThickness")) {
_lineThickness = getStyle("lineThickness");
}
You must define a default value for the skin's styleable properties. You usually do this in the skin's constructor function. If you do not define a default value, the style property is set to NaN or undefined if the Flex application does not define that style. This can cause a run-time error.
The following example of the MyButtonSkin programmatic skin class defines default values for the _lineThickness and _backgroundFillColor styleable properties in the skin's constructor. It then adds calls to the getStyle() method in the updateDisplayList() method to make these properties styleable:
package { // Use unnamed package if this skin is not in its own package.
// skins/ButtonStylesSkin.as
// Import necessary classes here.
import flash.display.Graphics;
import mx.skins.Border;
import mx.skins.ProgrammaticSkin;
import mx.styles.StyleManager;
public class ButtonStylesSkin extends ProgrammaticSkin {
public var _backgroundFillColor:Number;
public var _lineThickness:Number;
// Constructor.
public function ButtonStylesSkin() {
// Set default values.
_backgroundFillColor = 0xFFFFFF;
_lineThickness=2;
}
override protected function updateDisplayList(w:Number, h:Number):void {
if (getStyle("lineThickness")) {
// Get value of lineThickness style property.
_lineThickness = getStyle("lineThickness");
}
if (getStyle("backgroundFillColor")) {
// Get value of backgroundFillColor style property.
_backgroundFillColor = getStyle("backgroundFillColor");
}
// Draw the box using the new values.
var g:Graphics = graphics;
g.clear();
g.beginFill(_backgroundFillColor,1.0);
g.lineStyle(_lineThickness, 0xFF0000);
g.drawRect(0, 0, w, h);
g.endFill();
g.moveTo(0, 0);
g.lineTo(w, h);
g.moveTo(0, h);
g.lineTo(w, 0);
}
}
} // Close unnamed package.
In your Flex application, you can set the values of styleable properties by using CSS or the setStyle() method.
The following example sets the value of styleable properties on all Button controls with a CSS:
<?xml version="1.0"?>
<!-- skins/ApplyButtonStylesSkin.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="600" height="600">
<mx:Style>
Button {
upSkin:ClassReference('ButtonStylesSkin');
downSkin:ClassReference('ButtonStylesSkin');
overSkin:ClassReference('ButtonStylesSkin');
disabledSkin:ClassReference('ButtonStylesSkin');
lineThickness:4;
backgroundFillColor:#CCCCCC;
}
</mx:Style>
<mx:Script><![CDATA[
public function changeLineThickness(e:Event):void {
var t:int = Number(b1.getStyle("lineThickness"));
if (t == 4) {
b1.setStyle("lineThickness",1);
} else {
b1.setStyle("lineThickness",4);
}
}
]]></mx:Script>
<mx:Button id="b1" label="Change Line Thickness" click="changeLineThickness(event)"/>
<mx:Button id="b2"/>
</mx:Application>
The executing SWF file for the previous example is shown below:
When using the setStyle() method to set the value of a style property in your Flex application, you can set the value of a styleable property on a single component instance (as in the previous example) or on all instances of a component. The following example uses the setStyle() method to set the value of a styleable property on all instances of the control (in this case, all Button controls):
<?xml version="1.0"?>
<!-- skins/ApplyGlobalButtonStylesSkin.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="600" height="600">
<mx:Style>
Button {
upSkin:ClassReference('ButtonStylesSkin');
downSkin:ClassReference('ButtonStylesSkin');
overSkin:ClassReference('ButtonStylesSkin');
disabledSkin:ClassReference('ButtonStylesSkin');
lineThickness:4;
backgroundFillColor:#CCCCCC;
}
</mx:Style>
<mx:Script><![CDATA[
public function changeLineThickness(e:Event):void {
var t:int = Number(b1.getStyle("lineThickness"));
if (t == 4) {
StyleManager.getStyleDeclaration("Button").setStyle("lineThickness", 1);
} else {
StyleManager.getStyleDeclaration("Button").setStyle("lineThickness", 4);
}
}
]]></mx:Script>
<mx:Button id="b1" label="Change Line Thickness" click="changeLineThickness(event)"/>
</mx:Application>
The executing SWF file for the previous example is shown below:
If you do not set the values of these properties using either CSS or the setStyle() method, Flex uses the default values of the properties that you set in the skin's constructor.
To get the value of an existing style property, such as color or fontSize, you do not have to wrap the call to the getStyle() method in a check for the property's existence. This is because Flex creates a CSSStyleDeclaration that defines the default values of all of a component's styles. Preexisting style properties are never undefined. Style properties that you add to a custom skin are not added to this CSSStyleDeclaration because the component does not know that the property is a style property.
Custom skin properties that you define as styleable are noninheritable. So, subclasses or children of that component do not inherit the value of that property.