Home > Best Practices, Flex, Opinion, Random Notes, tutorial > 5 tips to help designers style your Flex components

5 tips to help designers style your Flex components

March 3rd, 2009

Alright designers, lets get one thing straight. I don’t like you and you don’t like me. You design components that are either a)impossible/painstakingly difficult to build or b) has more transitional effects that even make a mac snob would vomit. Dare I complain? Too bad. The client already signed off on it and they loved it. Guess what though, jokes on you! Following my typical fashion, I have made it as easy to style as you have to build it. TAKE THAT EDGY COOL GUY(or girl)!.

Unfortunately, this vicious circle doesn’t help you and it doesn’t help me. You can call me lazy and I can call you a moron but it leads us to the same spot. With this in mind, I’ve recently decided to extend the olive branch. I have sat down with one of the main designers at my job and asked “What can I do to my components that will make your job easier?”. With a warm smile, he thanked me and gave me some good tips on how I can make not just his life easier, but also make my components just that much more versatile.

Access to modifying sub components properties (position, height, etc) if applicable
Say you have a composite component comprised of two Buttons. These buttons might look differently depending on the state. Of course, this is going to be something that is going to be application specific. The important part is to allow these properties to the specific sub components accessible from the main component. In this case, simple setter functions to these would work out great. Bare in mind that the amount of accessibility to the subcomponents will be determined upon designing them. If there are different states where the buttons might change expand or shrink, maybe they will move? Who knows. That’s up to the application, but they are what you will have to grant access to.

Ability to define styleNames for each sub-component
This one is somewhat similar to the previous topic. One again, lets say we have a composite component comprised of two buttons. We want to assign full styleNames from our css definition to each of the components. How would we accomplish this? We could create setters for each component, but that would be kind of a pain in my opinion. The better way would be to assign Style meta tags for these components.

[Style(name="bStyleName1", type="String",inherit="no")]
[Style(name="bStyleName2", type="String",inherit="no")]

Here we are assigning two style properties within our container object. These are now valid css attributes for our container component. What we are going to do is when we change out the styleName of our container component, we are granting access to the sub components. Here is what our css will look like:

.firstButtonStyles{
  bStyleName1:"buttonRed";
  bStyleName2:"buttonGreen";
}
.secondButtonStyles{
  bStyleName1:"buttonGreen";
  bStyleName2:"buttonRed";
}
.buttonRed{
        color: #ff0000;
        fillAlphas: 1.0, 0.4;
}
.buttonGreen{
        color:#00ff00;
        fillAlphas: .4, 1.0;
}

Since we have now made these properties valid css properties, we can assign them here and our component will recognize them. Here is what our implementation will look like

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
   layout="absolute" xmlns:codeofdoom="com.codeofdoom.*">
   <mx:Script>
     <![CDATA[
       public function buttonOneClick(e:MouseEvent):void{
         if (bc.styleName=="firstButtonStyles")
            bc.styleName = "secondButtonStyles";
         else
            bc.styleName = "firstButtonStyles";
       }
     ]]>
   </mx:Script>
   <mx:Style source="style.css"/>
   <mx:HBox>
     <mx:Button label="Button color toggle" click="buttonOneClick(event)"/>
     <codeofdoom:ButtonContainer styleName="firstButtonStyles" id="bc"/>
   </mx:HBox>
</mx:Application>

So we have our ButtonContainer and a button that will toggle the styleName. As you can see, when we swap them out, we are using the what is named within our css that references the two css attributes that we added earlier. Next we need to worry about the actual container object.

[Style(name="bStyleName1", type="String",inherit="no")]
  [Style(name="bStyleName2", type="String",inherit="no")]
  public class ButtonContainer extends HBox
  {
    private var _button1:Button;
    private var _button2:Button;
    private var _styleNameChanged:Boolean=true;
    public function ButtonContainer()
    {
      super();
      _button1 = new Button();
      _button2 = new Button();
      _button1.label="One";
      _button2.label="Two";
    }
    override protected function createChildren():void{
      super.createChildren();
      addChild(_button1);
      addChild(_button2);
    }
 
    override protected function updateDisplayList(w:Number,h:Number):void{
      super.updateDisplayList(w,h);
      if (_styleNameChanged){
        _styleNameChanged = false;
        _button1.styleName =getStyle("bStyleName1");
        _button2.styleName =getStyle("bStyleName2");
      }
    }
    override public function styleChanged(styleProp:String):void{
      super.styleChanged(styleProp);
      if (styleProp =="styleName")
        _styleNameChanged=true;
      invalidateDisplayList();
    }
  }

We have our two style attributes that we mentioned earlier. The important part however is our “styleChanged” method. Whenever we call setStyle or styleName on our component, it will trigger this function. Here we call our super so it is able to handle it however it needs to, but then we check to see if the property that changed was the styleName. If it was, mark it with the flag. This is going to be used within our updateDisplayList. *After all that, we set invalidateDisplayList() to ensure that updateDisplayList() gets called.

Inside updateDisplayList, the flag is checked. We check this flag because we don’t want to continuously assign this styles. We want to only worry about them if they have changed. We then look up our new style attributes that we have added and set our flag to false.

No hard coded heights of widths!
Defaults are okay, but you must provide a way to change them. I am including this one because it was emphasized to me how often this is overlooked and the component is locked down to a specific size. You can easily get trapped here if you are overriding measure() and giving it a default height/width there. Also be mindful of its sub-components.

Ability to define and control effects for multistate components
Lets say you have a toolbar component that opened and closed separate views as other drawers were attached. Normally we just give it its default behavior/transition and leave it at that. There is no reason for this to be locked down to one specific effect. Giving the designer access to this will just make it more diverse. In order to do this, we are going to use Meta Tags like we did with our styleNames and we will use both the Event meta tag and the Effect meta tag.

[Event(name="darken", type="flash.events.Event")]
[Event(name="brighten", type="flash.events.Event")]
[Effect(name="darkenEffect", event="darken")]
[Effect(name="brightenEffect", event="brighten")]

So we put this at top of our object and it says “Here are two events that it will be listening for. Here are our two effects. They will be called when the associated event is dispatched”. I have used the same ButtonContainer (out of laziness) and added these two functions:

private var _bright:Boolean;
 
public function set bright(value:Boolean):void{
      _bright = value;
      if (_bright)
          dispatchEvent(new Event("brighten"));
      else
          dispatchEvent(new Event("darken"));
}
public function get bright():Boolean{
    return _bright;
}

Basically we added a _bright property to our object. When it is set, we will dispatch the event associated to the effects in our meta tag. Next we add the effects we want to our applications mxml.

   <mx:Fade id="FadeOut" duration="1000" alphaFrom="1.00" alphaTo=".20"/>
   <mx:Fade id="FadeIn" duration="1000" alphaFrom=".20" alphaTo="1.00"/>

And our implementation of this object has changed to this:

<mx:HBox>
    <mx:Button label="Button color toggle" click="buttonOneClick(event)"/>
    <codeofdoom:ButtonContainer  darkenEffect="{FadeOut}" brightenEffect="{FadeIn}" styleName="firstButtonStyles" id="bc"/>
</mx:HBox>

Lastly within our buttonOneClick handler, we just changed it to have this:

public function buttonOneClick(e:MouseEvent):void{
    bc.bright=!bc.bright;
}

This is what calls the bright setter, which then dispatches the events that our effects are listening to. What is great about this lets say our designer doesn’t want our buttons to fade in and our. He wants them to glow instead. NO PROBLEM! Since we were nice, we gave him the ability to just assign those effects. He could just as easily assign this effect to our component and it will work like a charm.

<mx:Glow id="glow" duration="1000" alphaFrom=".20" alphaTo="1.00" color="red"/>

Ability to change skins
This one is pretty broad one. A perfect example would be on a button. You would want to give the designer access to all the “skin parts”. On our button, we would want to be able to set the upSkin, downSkin, etc. These can be assigned within a style sheet. You can even give them access to classes to reference within the styleSheet.

All in all, it boils down to the fact that the most important thing to a designer is to have as much control as possible over the presentation of the component. Not only that, but to have it using only css or very few mxml properties. As a programmer, I rarely care how it looks. I know that is hard to say as a Flex programmer, but making things look pretty is not my strong point. After hearing all this, I thought to myself “this can turn into overkill very quickly”, but with the use of exposing style properties, effects tags, etc, I have shown that is actually not much more work than I had initially though.

To the designers who might be reading this, what hints/thoughts would you like to pass on? Programmers, what lengths do you go to in order to help out your fellow designers? Feel free to share!

*According to the flex docs and within the source of UIComponent, it does state that :

If you handle the style property, your override of the styleChanged() method should call the invalidateDisplayList() method to cause Flex to execute the component’s updateDisplayList() method at the next screen update.

I find this strange because it calls invalidateDisplayList() within styleChanged() of UIComponent, so calling super.styleChanged takes care of this. Better safe than sorry I suppose?

Marcel Best Practices, Flex, Opinion, Random Notes, tutorial , , ,

  1. Kevin
    April 6th, 2009 at 16:39 | #1

    Just a note: Make sure your changed flags (_styleNameChanged, in this example) are set to TRUE by default. For some reason, initial styles (set via CSS or in MXML) that are applied to components don’t trigger the styleChanged method, so unless these properties are true at startup, updateDisplayList won’t know to set them on your subcomponents.

  2. Anonymous
    August 31st, 2009 at 08:06 | #2

    Just like your ButtonContainer, I have a MXML component where I am trying to use Binding to set the Button Style, but it only works once.

    Any idea why it doesn’t work when I change the component style.

  1. No trackbacks yet.