Loading style sheets at run time

You can load style sheets at run time by using the StyleManager. You can access the top-level StyleManager by using the styleManager property of the Application object. These style sheets take the form of SWF files that are dynamically loaded while your application runs.

By loading style sheets at run time, you can load images (for graphical skins), fonts, type and class selectors, and programmatic skins into your application without embedding them at compile time. This lets skins and fonts be partitioned into separate SWF files, away from the main application. As a result, the application’s SWF file size is smaller, which reduces the initial download time.

However, the first time a run-time style sheet is used, it takes longer for the styles and skins to be applied than if you load styles by using the <fx:Style> tag or set the styles inline. This is because Flex must download the necessary CSS-based SWF file while the application is starting up or running.

Loading style sheets at run time is a three-step process:

  1. Write a CSS file for your application.

  2. Compile the CSS file into a SWF file.

  3. Call the styleManager.loadStyleDeclarations() method in your application. This method loads the CSS-based SWF file into your application. When this method executes, Flex loads the new CSSStyleDeclarations into the top-level StyleManager.

You can load multiple style sheets that define the same styles in the same application. After you set a style, subsequent style sheets can overwrite previous ones if they have common selectors. Styles loaded with run-time style sheets do not completely replace compile-time styles, however. They just override them until the run-time style sheets are unloaded. At that point, Flex reverts to the compile-time style settings. Compile-time style settings include any default styles sheets that were loaded at compile time, theme files loaded by using the theme compiler option, and styles set by using the <fx:Style> block inside an MXML file.

You cannot load an uncompiled CSS file into your application at run time. You must compile it into a SWF file before loading it.

Creating a run-time style sheet

To load style sheets at run time, you must first create a style sheet that is compiled into a SWF file. A run-time style sheet is like any other style sheet. It can be a simple style sheet that sets basic style properties, as the following example shows:

/* ../assets/BasicStyles.css */

@namespace s "library://ns.adobe.com/flex/spark";

s|Button {
    fontSize:    24;
    color: #FF9933;
}

s|Label {
    fontSize:    24;
    color: #FF9933;
}

Or the style sheet can be a complex style sheet that embeds programmatic and graphical skins, fonts, and other style properties, and uses type and class selectors, as the following example shows:

/* assets/ComplexStyles.css */

@namespace mx "library://ns.adobe.com/flex/mx";

mx|Application {
    backgroundImage: "greenBackground.gif";
    theme-color: #9DBAEB;
}
mx|Button {
    fontFamily: Tahoma;
    color: #000000;
    fontSize: 11;
    fontWeight: normal;
    text-roll-over-color: #000000; 
    upSkin: Embed(source="orb_up_skin.gif");
    overSkin: Embed(source="orb_over_skin.gif");
    downSkin: Embed(source="orb_down_skin.gif");
}

.noMargins {
    margin-right: 0;
    margin-left: 0;
    margin-top: 0;
    margin-bottom: 0;
    horizontal-gap: 0;
    vertical-gap: 0;
}

To create a new style sheet in Flash Builder, you select File > New > CSS File. Create the CSS file in the project’s main directory or another subdirectory that is not the bin directory. You should not create the CSS file in the bin directory. Flash Builder will compile the SWF file to the bin directory for you.

Compiling the CSS-based SWF file

Before you can load a style sheet at run time, you must compile the style sheet into a SWF file. The style sheet that you compile into a SWF file must use a .css filename extension.

To compile the CSS file into a SWF file, you use the mxmlc command-line compiler or Flash Builder’s compiler. The default result of the compilation is a SWF file with the same name as the CSS file, but with the .swf extension.

The following example produces the BasicStyles.swf file by using the mxmlc command-line compiler:

mxmlc BasicStyles.css

To compile the SWF file with Flash Builder, right-click the CSS file and select Compile CSS to SWF. Flash Builder saves the SWF file in the project’s bin directory. If the original CSS file is in the bin directory, you cannot compile it into a SWF file. You must move it to a different directory before you can compile it.

When you compile your application, the compiler does not perform any compile-time link checking against the CSS-based SWF files used by the application. This means that you are not required to create the SWF file before you compile your main application. This also means that if you mistype the name or location of the SWF file, or if the SWF file does not exist, the application will fail silently. The application will not throw an error at run time.

Loading the style sheets

You load a CSS-based SWF file at run time by using the StyleManager’s loadStyleDeclarations() method. You can access the top-level StyleManager by using the styleManager property of the Application object.

The following example shows loading a style sheet SWF file:

styleManager.loadStyleDeclarations("../assets/MyStyles.swf");

The first parameter of the loadStyleDeclarations() method is the location of the style sheet SWF file to load. The location can be local or remote.

The second parameter is update. You set this to true or false, depending on whether you want the style sheets to immediately update in the application. For more information, see Updating CSS-based SWF files.

The next parameter, trustContent, is optional and obsolete. If you do specify a value, set this to false.

The final two parameters are applicationDomain and securityDomain. These parameters specify the domains into which the style sheet SWF file is loaded. In most cases, you should accept the default values (null) for these parameters. The result is that the style sheet’s SWF file is loaded into child domains of the current domains. For information on when you might use something other than the default for these parameters, see Using run-time style sheets with modules and sub-applications.

The following example loads a style sheet when you click the button:

<?xml version="1.0"?>
<!-- styles/BasicApp.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <fx:Script>
        <![CDATA[
        public function applyRuntimeStyleSheet():void {
            styleManager.loadStyleDeclarations("assets/BasicStyles.swf")
        }
        ]]>
    </fx:Script>    

    <s:VGroup>
        <s:Label text="Click the button to load a new CSS-based SWF file."/>
        <s:Button id="b1" label="Click Me" click="applyRuntimeStyleSheet()"/>
    </s:VGroup>
    
</s:Application>

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

Loading a remote style sheet typically requires a crossdomain.xml file that gives the loading application permission to load the SWF file. You can do without a crossdomain.xml file if your application is in the local-trusted sandbox, but this is usually restricted to SWF files that have been installed as applications on the local machine.

For more information about crossdomain.xml files, see Using cross-domain policy files.

Also, to use remote style sheets, you must compile the loading application with network access (have the use-network compiler property set to true, the default). If you compile and run the application on a local file system, you might not be able to load a remotely accessible SWF file.

The loadStyleDeclarations() method is asynchronous. It returns an instance of the IEventDispatcher class. You can use this object to trigger events based on the success of the style sheet’s loading. You have access to the StyleEvent.PROGRESS, StyleEvent.COMPLETE, and StyleEvent.ERROR events of the loading process.

The following application calls a method when the style sheet finishes loading:

<?xml version="1.0"?>
<!-- styles/StylesEventApp.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="init()">
    
    <fx:Script>
        <![CDATA[
        import mx.events.StyleEvent;
        
        public function init():void {
            var myEvent:IEventDispatcher = styleManager.loadStyleDeclarations("../assets/ACBStyles.swf");
            myEvent.addEventListener(StyleEvent.COMPLETE, getImage);
        }

        private function getImage(event:StyleEvent):void {
            map1.source = acb.getStyle("dottedMap");
        }        
        ]]>
    </fx:Script>    
    
    <mx:ApplicationControlBar id="acb" width="100%">
        <s:Image id="map1"/>
    </mx:ApplicationControlBar>
    
</s:Application>

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

The style sheet used in this example embeds a PNG file:

/* assets/ACBStyles.css */

@namespace mx "library://ns.adobe.com/flex/mx";

mx|ApplicationControlBar {
    borderStyle:     "solid";
    cornerRadius:    10;
    backgroundColor: #FF9933;
    alpha:           1;
    dottedMap:       "beige_dotted_map.png";
}

Updating CSS-based SWF files

You can force an immediate update of all styles in the application when you load a new CSS-based SWF file. You can also delay the update if you want.

The second parameter of the loadStyleDeclarations() method is update. Set the update parameter to true to force an immediate update of the styles. Set it to false to avoid an immediate update of the styles in the application. The styles are updated the next time you call this method or the unloadStyleDeclarations() method with the update property set to true.

Each time you call the loadStyleDeclarations() method with the update parameter set to true, Adobe® Flash Player and Adobe AIR™ reapply all styles to the display list, which can degrade performance. If you load multiple CSS-based SWF files at the same time, you should set the update parameter to false for all but the last call to this method. As a result, Flash Player and AIR apply the styles only once for all new style SWF files rather than once for each new style SWF.

The following example loads three style SWF files, but does not apply them until the third one is loaded:

<?xml version="1.0"?>
<!-- styles/DelayUpdates.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="init()">

     <s:layout>
        <s:VerticalLayout/>
     </s:layout>
        
    <fx:Script> 
        <![CDATA[
        import mx.events.StyleEvent;
        
        public function init():void {
            styleManager.loadStyleDeclarations("assets/ButtonStyles.swf", false);
            var myEvent:IEventDispatcher = 
                styleManager.loadStyleDeclarations("assets/LabelStyles.swf", false);            
            myEvent.addEventListener(StyleEvent.COMPLETE, doUpdate);
        }

        public function doUpdate(event:StyleEvent):void {
            styleManager.loadStyleDeclarations("assets/ACBStyles.swf", true);
        }
        ]]>
    </fx:Script>    
    
    <s:Label text="This is a Label control."/>

    <mx:ApplicationControlBar id="acb" width="100%">
        <s:Button label="Submit"/>
    </mx:ApplicationControlBar>
    
</s:Application>

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

Unloading style sheets at run time

You can unload a style sheet that you loaded at run time. You do this by using the StyleManager’s unloadStyleDeclarations() method. You can access the top-level StyleManager by using the styleManager property of the Application object. The result of this method is that all style properties set by the specified style SWF files are returned to their defaults.

The following example loads and unloads a style SWF when you toggle the check box:

<?xml version="1.0"?>
<!-- styles/UnloadStyleSheets.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">
  
  <s:layout>
     <s:VerticalLayout/>
  </s:layout>

    <fx:Script> 
        <![CDATA[
        public function toggleStyleSheet():void {
            if (cb1.selected == true) {
                styleManager.loadStyleDeclarations("assets/ButtonStyles.swf", true);
                styleManager.loadStyleDeclarations("assets/LabelStyles.swf", true);
            } else {
                styleManager.unloadStyleDeclarations("assets/ButtonStyles.swf", true);
                styleManager.unloadStyleDeclarations("assets/LabelStyles.swf", true);
            }                       
        }
        ]]>
    </fx:Script>    

    <s:Button id="b1" label="Submit"/>
    
    <s:Label id="l1" text="This is a Label control."/>
    
    <s:CheckBox id="cb1" label="Load style sheet" 
        click="toggleStyleSheet()" selected="false"/>
</s:Application>

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

The unloadStyleDeclarations() method takes a second parameter, update. As with loading style sheets, Flash Player and AIR do not reapply the styles (in this case, reapply default styles when the loaded styles are unloaded) if you set the value of the update parameter to false. For more information about the update parameter, see Loading the style sheets.

Using run-time style sheets in custom components

You can use run-time style sheets in custom components. To do this, you generally call the loadStyleDeclaration() method after the component is initialized. If the style sheet contains class selectors, you then apply them by setting the styleName property.

The following example defines style properties and skins in a class selector named specialStyle:

/* ../assets/CustomComponentStyles.css */
.specialStyle {
    fontSize:    24;
    color: #FF9933;
    upSkin: Embed(source="SubmitButtonSkins.swf", symbol="MyUpSkin");
    overSkin: Embed(source="SubmitButtonSkins.swf", symbol="MyOverSkin");
    downSkin: Embed(source="SubmitButtonSkins.swf", symbol="MyDownSkin");
}

The following example custom component loads this style sheet and applies the specialStyle class selector to itself during initialization:

// styles/MyButton.as -->
package {

    import mx.controls.Button;
    import mx.events.*;
    
    public class MyButton extends Button {
   
        public function MyButton() {
            addEventListener(FlexEvent.INITIALIZE, initializeHandler);
        }

         // Gets called when the component has been initialized
        private function initializeHandler(event:FlexEvent):void {
            styleManager.loadStyleDeclarations("assets/CustomComponentStyles.swf");
            this.styleName = "specialStyle";
        }
    }    
}

The following sample application uses this custom button:

<?xml version="1.0"?>
<!-- styles/MyButtonApp.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:custom="*">
  
    <custom:MyButton/>
  
</s:Application>

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

Using theme SWC files as run-time style sheets

If you have an existing theme SWC file, you can use it as a run-time style sheet. To do this, you must extract the CSS file from the theme SWC file. You then compile the style SWF file by passing the remaining SWC file as a library.

The following steps show this process using the command line:

  1. Extract the CSS file from the SWC file using PKZip or similar archiving utility, as the following example shows:

    $ unzip halo.swc defaults.css
  2. (Optional) Rename the CSS file to a meaningful name. This is the name of your style SWF file. The following example renames the defaults.css file to halo.css:

    $ mv defaults.css halo.css
  3. Compile the style SWF file. Add the theme SWC file to the style SWF file by using the include-libraries option, as the following example shows:

    $ mxmlc -include-libraries=halo.swc halo.css

If you have multiple CSS files inside a theme SWC file, you must extract all of them before compiling the style SWF file.

Using run-time style sheets with modules and sub-applications

When loading run-time style sheets with modules and sub-applications, consider the following factors:
  • The application domain into which you load the style SWF file

  • The StyleManager that loads the style SWF file

Selecting the application domain that you load the style SWF file into determines where the classes are loaded. You typically load a style SWF file into the module’s current application domain. You do not typically load it into a child application domain of the current module or application.

The StyleManager that loads the style SWF file is equally important. Because each module and application has its own StyleManager, you can load a style SWF file with the root application’s StyleManager or any module’s StyleManager. Which StyleManager you choose determines which applications or modules in the chain use those styles.

To load the style sheet into the current application domain of the module, set the applicationDomain parameter to ApplicationDomain.currentDomain in the loadStyleDeclarations() method. The following example loads the Style.swf file in the module into the current application domain:

styleManager.loadStyleDeclaration("Style.swf",true,false, ApplicationDomain.currentDomain)

If you leave the applicationDomain parameter blank, then the default is to load the SWF file into a child domain of the main application. The result is a style SWF that is in a sibling application domain of modules and sub-applications.

Each module and the main application have their own instances of type IStyleManager2. If you want a style SWF file to be used by all modules, load it with the main application’s StyleManager. If you want a style SWF file to be used by a module and its children, load the style SWF file with the module’s StyleManager.

If you load the style SWF file into the main application’s StyleManager, then all child modules and sub-applications will merge their styles with it when they are loaded. If you load the style SWF file into a module or sub-application’s StyleManager, then only the children of that module or sub-application will merge their styles with it.

To load a style SWF file with the current module or application’s StyleManager, use the styleManager property. In general, you should not load a style SWF file into a StyleManager of another module or application from one module or application.

Even when loading a style SWF file into the main application, you should specify the current application domain rather than a child application domain to avoid class conflicts. The following example illustrates loading the run-time style sheet into a child application domain and the current application domain of the main application:

In the first approach, the module and the run-time style sheet are loaded into separate child application domains (application domains 2 and 3). Because the style SWF file and the module are in sibling application domains, the classes referenced in the style module (such as skins) might cause class conflicts with the classes in the module.

The second approach loads the module into a child application domain and the style SWF file into the same domain as the main application. In this case, no class conflicts should occur.

The disadvantage of loading a style SWF file into the current application domain is that you cannot unload it, even when the module is unloaded.