A question of style

Published by Manfred Karrer on Friday, 8 of January , 2010 at 00:03

If you ever had some strange problems with styles in Flex, you might want to know how styles are internally handled and implemented.
Ok this post will not cover too much of all the stuff going on there, but a bit of the basics how styles are finding their way from the css file to your component.

Lets assume that we have a css file with type selectors and class selectors, a module based application and some custom components which are designed the way that you can use them out of the box and that they can be individually skinned and styled by other developers reusing them. That´s what custom components are for, right?

So the first question is:
How are the styles compiled into your swf file?
Assume that they are defined in an external css file. You know you can compile css files to swf files and load them at runtime or simply compile them into your application at compile time with a simple assignment like this:

[code lang="actionscript3"]
 
[/code]

Here is the content of our simple css:

[code lang="actionscript3"]
Button
{
	color: #990000;
}
.myCustomStyle1
{
	color: #009900;
}
 /*
     some additional styles here...
     like Button.myCustomStyle2
*/
[/code]

That´s what we will use for our example here.

In this case the MXML compiler creates a bunch of classes and inserts the css definitions right into a method called from the constructor in the generated Applications class.

[code lang="actionscript3"]
public function StyleExample() {
	mx_internal::_StyleExample_StylesInit();
}
mx_internal function _StyleExample_StylesInit():void {
	var style:CSSStyleDeclaration;
	// Button
	style = StyleManager.getStyleDeclaration("Button");
	if (!style) {
		style = new CSSStyleDeclaration();
		StyleManager.setStyleDeclaration("Button", style, false);
	}
	if (style.factory == null) {
		style.factory = function():void {
			this.color = 0x990000;
		};
	}
	// myCustomStyle1
	style = StyleManager.getStyleDeclaration(".myCustomStyle1");
	if (!style)
	{
		style = new CSSStyleDeclaration();
		StyleManager.setStyleDeclaration(".myCustomStyle1",
                                           style, false);
	}
	if (style.factory == null)
	{
		style.factory = function():void
		{
			this.color = 0x009900;
		};
	}
	// similar code follows here....
}
[/code]

I only pasted the relevant code, so this is not complete but should give the basic idea.
So what happens here?

A CSSStyleDeclaration is created for the given stylename or class if it´s not already existing in the StyleManager (at a modular application some other application could have already created this style). Then the definitions from the css file (here the color property) are added to the factory function.
It is interesting that the class selectors and type selectors are treated the same way regarding storing the style object in the StyleManager. It is simply the selector name which is used as the key in the _selectors Object (hashtable). Only the “.” in the selector name is the visible difference.

[code lang="actionscript3"]
    public function setStyleDeclaration(
                                selector:String,
                                styleDeclaration:CSSStyleDeclaration,
                                update:Boolean):void
    {
        styleDeclaration.selectorRefCount++;

        _selectors[selector] = styleDeclaration;

        // Flush cache and start over.
        typeSelectorCache = {};

        if (update)
            styleDeclarationsChanged();
    }
[/code]

The CSSStyleDeclaration has also a defaultFactory which is used if there is no factory defined (no css file). Additionally there is an overrides Object which is used to store styles set at runtime vial setStyle(), but this will not be further considered yet.
This leads us to the next part:
How should a default style be applied to a custom component?

How Flex is handling this? The MXML compiler generates a bunch of style classes:
For instance a style class for the Button:

[code lang="actionscript3"]
public class _ButtonStyle
{
    public static function init(fbs:IFlexModuleFactory):void
    {
        var style:CSSStyleDeclaration =
                         StyleManager.getStyleDeclaration("Button");
        if (!style)
        {
            style = new CSSStyleDeclaration();
            StyleManager.setStyleDeclaration("Button", style, false);
        }
        if (style.defaultFactory == null)
        {
            style.defaultFactory = function():void
            {
                this.fontWeight = "bold";
                this.paddingTop = 2;
                this.cornerRadius = 4;
                this.textAlign = "center";
                this.verticalGap = 2;
                this.horizontalGap = 2;
                this.skin = mx.skins.halo.ButtonSkin;
                this.paddingLeft = 10;
                this.paddingBottom = 2;
                this.paddingRight = 10;
            };
        }
    }
}
[/code]

This class is added to the mixins array at the generated _StyleExample_mx_managers_SystemManager class.
The mixins are a strategy to get injected some code at application startup before anything else is instantiated. It calls the static init method, so it is executed before the actual classes constructor is called.
A similar strategy with a static initializer is used in some classes in the datavisualisation package to pack some default styles into a component, so that there is no dependency that there are some styles assigned to the component from outside.
That´s seems to be a pretty nice way to setup the default style a component needs. If the user of the component wants to set some different styles, it can be applied easily via css, styles set in mxml or at runtime via setStyle (if possible, this should be done in the preInitialize phase to avoid performance penalties).

One thing which should be considered when using styles (specially in a large application with modules) is the fact that the style declarations are stored with the selector name as key in an Object (hashtable). So if 2 developers are using the same selector name they end up with a conflict that the later instantiated module will use the already created style and not the one which was compiled to the module.
The same applies if you use the same stylename inside one application, the first one used wins.
To get rid of this problem a kind of namespace strategy can be used ([modulename] + “stylename”). Unfortunately the Flex framework does not give any support for this problem.
Tell me if you know a more elegant solution!

LangDetecten>de YahooC
MXML

Comments Off on A question of style

Category: Flex,SDK

No Comments

No comments yet.

Sorry, the comment form is closed at this time.