One of the features that’s coming with Flex 4 is “Per-Module Style Management”. There is a lot that can be written about this feature and it probably can’t be covered in a single post. So for starters here is a small introduction.
The feature for Per-Module Style Management was done to allow modules and sub applications to be able to manage their own styles. With this feature StyleManager will no longer be a singleton and as a result that will allow modules and sub applications to have their own instances of StyleManager.
Modules and sub applications will continue to inherit styles from the parent application/module. But only the style properties not defined by the child modules or sub applications will trickle down (i.e. child module/application will be able to override a style property defined by the parent). There will also be merging of styles properties.
To understand this better, lets take a look at a sample. Following is the code for a parent application:
<?xml version="1.0" encoding="utf-8"?> <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" minWidth="1024" minHeight="768" creationComplete="application1_creationCompleteHandler(event)"> <fx:Style> @namespace s "library://ns.adobe.com/flex/spark"; @namespace mx "library://ns.adobe.com/flex/mx"; global { chromeColor: #DDAA66; } s|DropDownList { fontStyle: italic; } s|Button#gumboButton { fontStyle: italic; } s|Panel s|Label { fontStyle: italic; } s|Button.myStyle { fontStyle: italic; } s|HGroup s|RichText { fontStyle: italic; } .classOfStyle { fontStyle: italic; } </fx:Style> <fx:Script> <![CDATA[ import mx.events.FlexEvent; import mx.collections.ArrayList; import mx.events.ModuleEvent; import mx.events.StyleEvent; import mx.controls.Alert; private var arr:Array = [ { label:'Apple', data:10.00}, { label:'Banana', data:15.00 }, { label:'Melon', data:3.50 }, { label:'Kiwi', data:7.65}, { label:'Strawberry',data:12.35 }, { label:'Other', data:00.00} ]; private var listArr :ArrayList = new ArrayList(arr); protected function application1_creationCompleteHandler(event:FlexEvent):void { appList.dataProvider = listArr; } public function load():void { if(mod_loader.url == null) { mod_loader.url = "LoadStylesModule.swf"; } else { mod_loader.loadModule(); } } public function unload():void { mod_loader.unloadModule(); } ]]> </fx:Script> <s:layout> <s:VerticalLayout /> </s:layout> <s:HGroup> <s:VGroup id="groupId"> <s:VGroup > <s:CheckBox id="checkBox" label="Check Box" /> <s:Label text="Label Outside Panel" /> </s:VGroup> <s:HGroup rotation="10"> <s:NumericStepper id="numericStepper" stepSize="1" minimum="1" maximum="10" /> <s:RichText text="This Text is Rich !!" /> </s:HGroup> <s:RichText text="This Text is also Rich !!" /> <s:Panel title="Gumbo Panel" id="appPanel" rotation="-5"> <s:layout> <s:VerticalLayout /> </s:layout> <s:Button id="gumboButton" label="Gumbo Button" /> <s:Button id="gumboButton2" label="Second Gumbo Button" styleName="myStyle" /> <s:Label text="Spark Label" /> <s:Label text="Spark Label class selector" styleName="classOfStyle" /> <s:DropDownList id="appList" /> </s:Panel> </s:VGroup> <mx:ModuleLoader id="mod_loader"/> </s:HGroup> <s:Button label="Load Module" click="load()" /> <s:Button label="Unload Module" click="unload()" /> </s:Application>
If you notice, the above code defines a bunch of styles. It sets the chrome color to DDAA66 in the global selector and it sets the fontStyle to italic using various selectors (class, Id, Type, descendant).
Now let’s take a look at the code of the module (LoadStylesModule.mxml):
<?xml version="1.0" encoding="utf-8"?> <mx:Module xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" creationComplete="module1_creationCompleteHandler(event)"> <fx:Script> <![CDATA[ import mx.collections.ArrayList; import mx.controls.Alert; import mx.events.FlexEvent; import mx.events.StyleEvent; private var arr:Array = [ { label:'Apple', data:10.00}, { label:'Banana', data:15.00 }, { label:'Melon', data:3.50 }, { label:'Kiwi', data:7.65}, { label:'Strawberry',data:12.35 }, { label:'Other', data:00.00} ]; private var listArr :ArrayList = new ArrayList(arr); protected function module1_creationCompleteHandler(event:FlexEvent):void { moduleList.dataProvider = listArr; localStyleManager = StyleManager.getStyleManager(this.moduleFactory); } private var localStyleManager:IStyleManager2 = null; public var eventDispatcher:IEventDispatcher = null; protected function loadStylesButton_clickHandler(event:MouseEvent):void { eventDispatcher = localStyleManager.loadStyleDeclarations("testStyles.swf"); } protected function unloadStylesButton_clickHandler(event:MouseEvent):void { localStyleManager.unloadStyleDeclarations("testStyles.swf"); } ]]> </fx:Script> <s:VGroup> <s:CheckBox id="checkBox" label="Check Box Module" /> <s:Label text="Label Outside Panel" /> <s:HGroup rotation="-10"> <s:NumericStepper id="numericStepper" stepSize="1" minimum="1" maximum="10" /> <s:RichText text="This Text is Rich Module !!" /> </s:HGroup> <s:RichText text="This Text is also Rich !!" /> <s:Panel title="Gumbo Module Panel" id="appPanel" rotation="-5"> <s:layout> <s:VerticalLayout /> </s:layout> <s:Button id="gumboButton" label="Gumbo Module Button" /> <s:Button id="gumboButton2" label="Second Gumbo Module Button" styleName="myStyle" /> <s:Label text="Spark Module Label" /> <s:Label text="Spark Label Module class selector" styleName="classOfStyle" /> <s:ComboBox id="moduleList" height="24" width="147" /> </s:Panel> </s:VGroup> <mx:Button label="Load styles" id="loadStylesButton" click="loadStylesButton_clickHandler(event)" /> <mx:Button label="Unload styles" id="unloadStylesButton" click="unloadStylesButton_clickHandler(event)" /> </mx:Module>
The code for the module above does not define any styles. So what that means is that it will inherit the styles from the parent app. It will have the chrome color of DDAA66 for all components and also the font styles will be italic. So far that’s all normal and boring stuff. Now you probably also noticed that the module loads a styles declaration swf (which is a css compiled to a swf), and when you load the styles swf you will see the magic of per-module style management. The module will override style definition for some properties (originally defined in the main app) and for other properties styles merging will happen.
Here is the code for the css (testStyles.css):
@namespace s "library://ns.adobe.com/flex/spark"; @namespace mx "library://ns.adobe.com/flex/mx"; s|DropDownList { borderColor: #FF0000; fontSize: 16; } s|Button#gumboButton { chromeColor: #AAFFAA; fontSize: 16; } s|Panel s|Label { fontSize: 16; } s|Button.myStyle { chromeColor: #FFFFAA; fontSize: 14; } s|HGroup s|RichText { chromeColor: #BBAAAA; fontSize: 14; } .classOfStyle { color: #0FFFFA; fontSize: 20; }
Here is the running swf:
Click on the “Load Module” button to load the module. Once the module is loaded, notice the chrome color of most components is the same as in the parent app and the font is italicized for most components – this shows that the module inherits styles from the parent app. Now click on the “Load styles” button in the module, once the styles swf is loaded you can notice that the font size changes for most components but the fonts in the module are still italicized – this shows styles merging because we were only overriding the fontSize in the css and not the fontStyle property. Also notice the new chrome color for components in the module, its now different from the global chrome color (DDAA66). So the module is able to override the styles properties it chooses.
So, hopefully you find this short introduction useful. Stay tuned for more on this.. And if you want me to write about something else, please say so with your comments (which btw are welcome
)
[...] Stylish modules and sub applications. [...]
Hi,
i try your code with the last stable sdk, ans seems doesn t work, what sdk do you use for your example ?
Regards
It should with the latest Flex 4 SDK. What error are you getting?
Hi, I’m experiencing some problem with module not inheriting embedded fonts from the parent application. Do you know if the module embeds a new font, will it merge with the embedded fonts from the parent application (if the module font is different from the parent one) or will the module only recognize its own embedded fonts? thanks!
Yes, module should inherit the fonts from the parent app. What SDK build are you using? If you have a simple testcase, you can log a bug at http://bugs.adobe.com/jira
Nice overview.
However, I am running into a challenge in applying styles to a custom component in a static initializer in a custom component since the standard way to apply styles is to get the styleManager off the FlexGlobals.topLevelApplication, but I want the styles off the parentApplication that does not exist in a static initializer on my custom class.
Any ideas how to do this so that I can can initialize styles on my custom component when its styles are defined in the sub-application, not the top level application?
Thanks.
If it’s defined in the parent app it will show up when you use IStyleManager2.getMergedStyleDeclaration() but not via IStyleManager2.getStyleDeclaration()
Then I assume you would call FlexGlobals.topLevelApplication.getMergedStyleDeclaration(“myStyle”)
or
StyleManager.getStyleManager(null).getMergedStyleDeclaration(“myStyle”)
in the static initializer. Is that correct?
That was supposed to be:
FlexGlobals.topLevelApplication.styleManager.getMergedStyleDeclaration(“myStyle”)
When using FlexGlobals.topLevelApplication there is no need to use getMergedStyleDeclaration() because no merging happens at the top-level. You can simple check using getStyleDeclaration()
Forgive my ignorance, but your last comment seems to lead me in a circle. I have a class that is in a sub-application. Its style is defined in the sub-application. The associated style is not defined in the topLevelApplication.
So, how do I exactly access all the merged style declarations in a static function? If the topLevelApplication has no merged styles, you cannot access the “parentApplication” in a static function. So, it appears you cannot access the sub-applications style selectors in a class in a loaded sub-application.
What am I missing? Thanks.
I thought you were trying to find out the styles defined in the parent app that’s why I suggested using getStyleDeclaration() on FlexGlobals.topLevelApplication.styleManager.
BTW, using static function to set default styles is an older way(from flex 3). With Flex 4 and later builds, you should be overriding the “set moduleFactory” in you application to set the default values. That would solve the issue that you are facing..
very nice post!
This has showed me that i have lot’s of reading to do about the new Flex 4 styling.
I didn’t know about the option to use various selectors (class, Id, Type, descendant), only classes.
I have a problem with loading modules from an absolute URL, i keep getting the following error:
VerifyError: Error #1014: Class mx.modules::Module could not be found.
Is there a known issue about that? I could hardly found any posts regarding Flex 4 and modules.
Thanks!
Make sure that the root tag of the swf that you are loading is
thanks,
of course it’s based on , i am using a module after all
for some reason when i load the module with a relative url it loads fine (i get an error after it loads, but at least it loads), when trying to load the swf from the server (got the module swf from the bin-release or bin-debug folder of the application) i get a ModuleEvent.ERROR with an errorText of “SWF is not a loadable module”.
when loading it locally i get this error after the module is loaded (using ModuleLoader and handling the ready event):
this has to do with styling…
Main Thread (Suspended: TypeError: Error #1009: Cannot access a property or method of a null object reference.)
mx.charts.chartClasses::DualStyleObject/http://www.adobe.com/2006/flex/mx/internal::initProtoChain
mx.core::UIComponent/regenerateStyleCache
mx.core::UIComponent/http://www.adobe.com/2006/flex/mx/internal::addingChild
mx.core::UIComponent/addChild
mx.charts.chartClasses::CartesianChart/commitProperties
mx.core::UIComponent/validateProperties
mx.managers::LayoutManager/validateProperties
mx.managers::LayoutManager/doPhasedInstantiation
mx.managers::LayoutManager/doPhasedInstantiationCallback
The module swf may not have been copied correctly on the server. You should compare the checksum of the local swf vs the one on the server.
To resolve the RTE: you probably need to pass in the moduleFactory when loading the module.