Example: Creating style properties

When you create a component, you might want to create a style property so that component users can configure it by using styles. For example, you create a component, named StyledRectangle, that uses a gradient fill pattern to define its color, as the following example shows:

The StyledRectangle component uses a gradient fill pattern to define its color.

This gradient is defined by two colors that you set by using a new style property called fillColors. The fillColors style takes an array of two colors that component users can set. The StyledRectangle.as class defines default colors for the fillColors style, but you can also set them, as the following example shows:

<?xml version="1.0"?> 
<!-- skinstyle\MainRectWithFillStyles.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:MyComp="myComponents.*">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>
 
    <!-- Set style by using a CSS type selector. -->
    <fx:Style>
        @namespace "myComponents.*";
        StyledRectangle {fillColors: #FF00FF, #00FFFF}
    </fx:Style>

    <!-- By default, use the style defined by the CSS type selector. -->
    <MyComp:StyledRectangle id="mySR1"/>

    <!-- By default, use the style defined by the CSS type selector. -->
    <MyComp:StyledRectangle id="mySR2"/>

    <!-- Change the default style by using the setStyle() method. -->
    <mx:Button label="Set gradient" 
        click="mySR2.setStyle('fillColors', [0x000000, 0xFFFFFF]);"/>

    <!-- Set fillColors in MXML. -->
    <MyComp:StyledRectangle id="mySR3" fillColors="[0x00FF00, 0xFFFFFF]"/>

</s:Application>

The executing SWF file for the previous example is shown below:

In this example, the CSS type selector for the StyledRectangle component sets the initial values of the fillColors property to #FF00FF and #00FFFF. For the second StyledRectangle components, you use the click event of a Button control to change the fillColor style by using the setStyle() method. The third component sets the style property by using an MXML tag attribute.

Defining a style property

You define a style property for a component in the class definition.

  1. Insert the [Style] metadata tag that defines the style before the class definition.

    You insert the [Style] metadata tag before the class definition to define the MXML tag attribute for a style property. If you omit the [Style] metadata tag, the MXML compiler issues a syntax error when you try to set the property as an MXML tag attribute.

    The [Style] metadata tag has the following syntax:

    [Style(name="style_name"[,property="value",...])]

    For more information, see Metadata tags in custom components.

  2. Override the styleChanged() method to detect changes to the property.

  3. Override updateDisplayList() method to incorporate the style into the component display.

  4. Define a static initializer to set the default value of the style property.

    For more information, see Setting default style values.

The following code example defines the StyledRectangle component and the fillColors style. It also defines a second style property named alphas that you can use to set the alpha values of the fill:

package myComponents 
{
    // skinstyle/myComponents/StyledRectangle.as
    import mx.core.UIComponent;
    import mx.styles.CSSStyleDeclaration;
    import mx.styles.StyleManager;
    import flash.display.GradientType;
    import mx.core.FlexGlobals;

    // Insert the [Style] metadata tag to define the name, type 
    // and other information about the style property for the 
    // MXML compiler.
    [Style(name="fillColors",type="Array",format="Color",inherit="no")]
    [Style(name="alphas",type="Array",format="Number",inherit="no")]

    public class StyledRectangle extends UIComponent
    {    
        // Define a static variable.
        private static var classConstructed:Boolean = classConstruct();
    
        // Define a static method.
        private static function classConstruct():Boolean {
            if (!FlexGlobals.topLevelApplication.styleManager.getStyleDeclaration("myComponents.StyledRectangle"))
            {
                // If there is no CSS definition for StyledRectangle, 
                // then create one and set the default value.
                var myRectStyles:CSSStyleDeclaration = new CSSStyleDeclaration();
                myRectStyles.defaultFactory = function():void
                {
                    this.fillColors = [0xFF0000, 0x0000FF];
                    this.alphas = [0.5, 0.5];
                }
                FlexGlobals.topLevelApplication.styleManager.setStyleDeclaration("myComponents.StyledRectangle", myRectStyles, true);

            }
            return true;
        }
    
        // Constructor  
        public function StyledRectangle() {
            super();    
        }           
    
        // Define a default size of 100 x 100 pixels.
        override protected function measure():void {
            super.measure();

            measuredWidth = measuredMinWidth = 100;
            measuredHeight = measuredMinHeight = 100;
        }

        // Define the flag to indicate that a style property changed.
        private var bStypePropChanged:Boolean = true;

        // Define the variable to hold the current gradient fill colors.
        private var fillColorsData:Array;

        // Define the variable to hold the current alpha values.
        private var alphasData:Array;

        // Define the variable for additional control on the fill.
        // You can create a style property for this as well.
        private var ratios:Array = [0x00, 0xFF];
        
        // Override the styleChanged() method to detect changes in your new style.
        override public function styleChanged(styleProp:String):void {

            super.styleChanged(styleProp);

            // Check to see if style changed. 
            if (styleProp=="fillColors" || styleProp=="alphas") 
            {
                bStypePropChanged=true; 
                invalidateDisplayList();
                return;
            }
        }
    
    
        // Override updateDisplayList() to update the component 
        // based on the style setting.
        override protected function updateDisplayList(unscaledWidth:Number,
                unscaledHeight:Number):void {
            super.updateDisplayList(unscaledWidth, unscaledHeight);

            // Check to see if style changed. 
            if (bStypePropChanged==true) 
            {
                // Redraw gradient fill only if style changed.
                fillColorsData=getStyle("fillColors");
                alphasData=getStyle("alphas");
                
                graphics.beginGradientFill(GradientType.LINEAR, 
                    fillColorsData, alphasData, ratios);  
                graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
                
                bStypePropChanged=false;
            }
        }
    }
}

Setting default style values

One of the issues that you have to decide when you create a style property for your component is how to set its default value. Setting a default value for a style property is not as simple as calling the setStyle() method in the component’s constructor; you must take into consideration how Flex processes styles, and the order of precedence of styles.

When Flex compiles your application, Flex first examines any style definitions in the <fx:Style> tag, before it creates any components. Therefore, if you call setStyle() from within the component’s constructor, which occurs after processing the <fx:Style> tag, you set the style property on each instance of the component; this overrides any conflicting CSS declarations in the <fx:Style> tag.

The easiest way to set a default value for a style property is to define a static initializer in your component. A static initializer is executed once, the first time Flex creates an instance of a component. In Defining a style property, you defined a static initializer, by using the classConstructed variable and the classConstruct() method, as part of the StyledRectangle.as class.

The classConstruct() method is invoked the first time Flex creates a StyledRectangle component. This method determines whether a style definition for the StyledRectangle class already exists, defined by using the <fx:Style> tag. If no style is defined, the classConstruct() method creates one, and sets the default value for the style property.

Therefore, if you omit the <fx:Style> tag from your application, the style definition is created by the classConstruct() method, as the following example shows:

<?xml version="1.0"?> 
<!-- skinstyle\MainRectNoStyles.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:MyComp="myComponents.*">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>
 
    <MyComp:StyledRectangle/>
    
</s:Application>

The executing SWF file for the previous example is shown below:

If you include the <fx:Style> tag, the <fx:Style> tag creates the default style definition, as the following example shows:

<?xml version="1.0"?> 
<!-- skinstyle\MainRectCSSStyles.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:MyComp="myComponents.*">
 
    <fx:Style>
        @namespace "myComponents.*";
        StyledRectangle {fillColors: #FF00FF, #00FFFF}
    </fx:Style>

    <MyComp:StyledRectangle/>
    
</s:Application>

The executing SWF file for the previous example is shown below: