Creating Collapsible Panel in Flex 4

It is quite common in Flex applications (and other RIAs) to divide screen content into two parts: the navigation/configuration panel on the left and the actual content on the right.
The examples of such apps are FlexStore sample and famous Style Explorer.

I followed this pattern in many projects and several times we come to the point that navigation/configuration panel requires too much space and that there should be a possibility to minimize it. Probably designers of Style Explorer has a similar idea since their navigation control can be minimized. Unfortunately they haven’t created reusable component to do this.

Some time ago I have created the collapsible panel in Flex 3. The idea of this component was to minimize panel by hiding panel content and rotating the header by 90 degree. So that, minimized panel was visible as vertical bar with the title. It was the nightmare to use rotation in Flex 3 since Flex 3 layouts doesn’t care about rotation. Placing component in the right place requires many tricks and extensive usage of trigonometric functions…

When I read that Gumbo (now Flex 4) layouts supports rotation I decided to test it by reimplementing my collapsible panel. Flex 4 definitely passed the test. Spark component architecture is really cool and it is much easier to create reusable/extendible components in Flex 4 than it was before. You can see the result of my work below.

This example is created using 4.0.0.8847, it will not work with SDK shipped with beta 1.
View source is enabled, you can download zipped sources from here.

Following Spark components architecture I have extended Panel class and created new MXML skin.

Component class

Below you can find the list of steps taken to create CollapsiblePanel main component class. At first sight this list may look long and complicated but after understanding Spark architecture it is really simple.

  • Adding “collapsed” skin state
    [SkinState("collapsed")]
    public class CollapsiblePanel extends Panel
    {
    
  • Adding new collapsed field indicating if panel is collapsed (invalidating skin state in setter)
    public function get collapsed():Boolean
    {
    	return _collapsed;
    }
    public function set collapsed(value:Boolean):void
    {
    	_collapsed = value;
    	invalidateSkinState();
    }
    protected var _collapsed:Boolean;
    
  • Overriding getCurrentSkinState() function to support “collapsed” skin state
    override protected function getCurrentSkinState():String
    {
    	return collapsed ? "collapsed" : super.getCurrentSkinState();
    }
    
  • Adding collapseButton optional skin part
    [SkinPart(required="false")]
    public var collapseButton:Button;
    
  • Implementing handler to toggle collapsed property on collapseButton click
    protected function collapseButtonClickHandler(event:MouseEvent):void
    {
    	collapsed = !collapsed;
    }
    
  • Overriding partAdded()/partRemoved() functions to add collapseButton event handler
    override protected function partAdded(partName:String, instance:Object) : void
    {
    	super.partAdded(partName, instance);
    	
    	if (instance == collapseButton)
    	{
    		Button(instance).addEventListener(MouseEvent.CLICK, collapseButtonClickHandler);
    	}
    }
    override protected function partRemoved(partName:String, instance:Object) : void
    {
    	if (instance == collapseButton)
    	{
    		Button(instance).removeEventListener(MouseEvent.CLICK, collapseButtonClickHandler);
    	}
    	super.partRemoved(partName, instance);
    }
    

MXML Skin

The CollapsiblePanelSkin.mxml skin is a copy of default Spark Panel skin with a number of modification. I used enhanced styles syntax to modify skin in “collapsed” state.

  • Adding “collapsed” state
    <s:states>
        <s:State name="normal" />
        <s:State name="collapsed" />
        <s:State name="disabled" />
    </s:states>
    
  • Grouping all title bar layers inside one Group tag
    <s:Group id="titleBarGroup" left="0" top="0" right="0" bottom="0" 
         maxHeight="32" rotation.collapsed="90">
    
  • Adding collapse button
    <s:Button id="collapseButton" width="16" height="16" top="7" right="7"
        label="-" label.collapsed="+" toolTip="Collapse" toolTip.collapsed="Open" />
    
  • Excluding contentGroup from collapsed state
    <s:Group id="contentGroup" left="1" right="1" top="32" bottom="1" minWidth="0" minHeight="0" 
         visible.collapsed="false" excludeFrom="collapsed">
    </s:Group>
    
  • Adding transitions
    <s:transitions>
        <s:Transition fromState="normal" toState="collapsed">
            <s:Sequence>
                <s:Fade target="{contentGroup}" duration="250" />
                <s:Parallel targets="{[titleBarGroup, this]}" >
                    <s:Rotate target="{titleBarGroup}" duration="250" />
                    <s:Resize target="{this}" duration="250" easer="{collapseEaser}" />
                </s:Parallel>
            </s:Sequence>
        </s:Transition>
        <s:Transition fromState="collapsed" toState="normal">
            <s:Sequence>
                <s:Parallel targets="{[titleBarGroup, this]}" >
                    <s:Rotate target="{titleBarGroup}" duration="250" />
                    <s:Resize target="{this}" duration="250" easer="{uncollapseEaser}" />
                </s:Parallel>
                <s:Fade target="{contentGroup}" duration="250" />
            </s:Sequence>
        </s:Transition>
    </s:transitions>
    

Summarizing, creating CollapsiblePanel in Flex 4 was much easier than in Flex 3 and the resulting component is of much higher quality. Although understanding Spark components architecture and MXML 2009 may require some time it is definitely worth doing!

Share on Google+Share on FacebookShare on LinkedInPin on PinterestTweet about this on Twitter

40 thoughts on “Creating Collapsible Panel in Flex 4”

  1. Hi,
    I have corrected paths to the sources. You can download it from here. Unfortunately html source view generated by FB4 is incomplete but zipped sources are OK.
    Iwo

  2. Hi,
    Thanks for sharing collapsible panel. im trying to get a Collapsible Panel in flex 3. can you provide some direction to achieve the same.

    thanks

  3. Hi,
    nice component. But it doesn’t really work when the panel has an explicit width. It get’s minimized but the border, shadow and background keep their width. I tried to put two of this panels inside a DividedBox. Everything works fine until you resize the box (or give the panel an explicit width). Perhaps you can fix this?

  4. Hi Thomas,
    This is rather the demo of Flex 4 potential than the complete component so some other features may not work as well. To fix your problem you can set explicit and percent width to NaN when panel is being collapsed and restore them to previous values when it is un-collapsed. You can do that by adding two “backup variables” and modifying collapsed setter:

    public function set collapsed(value:Boolean):void
    {
        if (value)
        {
            uncollapsedExplicitWidth = explicitWidth;
            uncollapsedPercentWidth = percentWidth;
            explicitWidth = NaN;
            percentWidth = NaN;
        }
        else
        {
            explicitWidth = uncollapsedExplicitWidth;
            percentWidth = uncollapsedPercentWidth;
        }
        _collapsed = value;
        invalidateSkinState();
    }
    protected var _collapsed:Boolean;
    protected var uncollapsedPercentWidth:Number = NaN;
    protected var uncollapsedExplicitWidth:Number = NaN;
    

    If you would like to polish this component I can add it to the reusable-fx google project so that you can share your changes. Give me a note if you are interested.

  5. Hi,

    thank’s for your code – I have thought of something in the same way. I’m just playing around with Flex 4 stateful skins and am evaluating to use this kind of panel for the application I am going to build. If I do further modification I’ll give you a note 🙂

  6. Hi Karthik,

    As you can read above creating collapsible panel in Flex 3 is much harder than in Flex 4 because Flex 3 layouts doesn’t care about rotation. Here are some key concepts regarding my implementation (I can’t share the code since it is copyrighted to my ex-employer):

    – CollapsiblePanel extends Canvas and contains standard Panel.

    – Panel is placed/rotated inside Canvas using absolute layout (some calculations are necessary to determine correct x/y/width/height during animation.

    – addChild, addChildAt etc. functions are overridden to pass child to inner Panel.

    – Animation is done using AnimateProperty Prallel and Sequence.

    Also remember to use embedded font (other ways all texts disappear after rotation).
    All in all, the resulting components code looks quite dirty and is in no way as reusable/extendible as Flex 4 code.

    Good luck creating your component! And don’t forget to show us the result of your work 🙂

  7. @Iwo Banas
    Thanks for the direction. Will work on it and sure will post the code.
    think the embedded font will do the trick. i got stuck , when the title text disappeared.

    -Karthik

  8. Hello,
    I applied your component successfully. However in my project, there are other states for the Application resize: one is “expanded” and one is “closed”. Only in state “expanded” we will touch the CollapsiblePanel (collapsed/normal states).

    In the skin parts, the contentGroup, titleBarGroup and the collapsible button are still remain unchanged when resize.

    Have we got any solution for this?

    Thanks.

  9. Hi,

    Is there a way to make this component generalizable to work with any theme? I realize the button in the panel would need to be designed and skinned, but is there a way to get the panel’s skin to just “inherit” the colors/fonts/etc of the Panel objects as skinned by the current theme?

    I tried removing sections where you explicitly defined colors and fontsizes, as well as a few other things, but instead of inheriting the Panel skin in use, I just got a plain looking Collapsible Panel.

    Any thoughts?

  10. Thank you for this example!

    Could I have a question? I would like to know if is possible to use this scenario also for maximizing the panel. I tried to add another state “maximized” and set all neccessary things. But I have problem with setting the percent width and height to the skin class. The percent settings doesn’t apply on the panel component. For maximizing I need to use 100% width and height. Is possible to do that in skin class?

    Thank You!

  11. Thank you! I found this very useful, but I can’t get it to work in Google Chrome from the source code. However, the example on this page works fine. Anyone else having an issue in Chrome?

  12. hi.
    i’ve trying to use your component but i’m getting the following error:
    css type selectors are not suported in components

    for what i understand of flex, it’s not implementing the css.
    can you give some ideas on how to solve this?

  13. Hi,

    I have tried your collapsible panel very nice.
    But I got the problem when I want to change the direction of collapse panel. Could you please help me with this.

  14. Dear friend,
    Nice component. Thanks a lot. But could you please tell me how can we use this to build a CollapsiblePanel where header remains same, only the contents part height changes to zero on collapse and retain on open.

    Thanks for your reply

  15. The collapse function isn’t working even using last update (Thomas question)
    Is there any updates?

  16. Great work !

    But it seems that the collapse button cannot have a label…

    “label=”-” label.collapsed=”+” ” doesn’t work…

  17. Hi,

    I Have a Flex 4.0 project using your collapsible panel. Now I have Flex 4.5. I tried to import the same project and there was an error of property missing in the collapsible panel.
    Are you aware of that ???
    Could you update your code to fix this problem.

    Thanks in aadvance

  18. Hi,

    May i know how do i change the titlebar color on collapsed?

    Because when it is not in collapsed state, i setting my titlebar to be white? I want it to however change to another color in collapsed state.

    How can i achieve that?

    Thanks!

  19. can anyone please guide me how i can implement this in flex 4.5.1 ? i m getting error in flex 4.5.1 with this code….Please help me….

  20. I m getting this error :
    The required skin state ‘disabledWithControlBar’ is missing.
    -The required skin state ‘normalWithControlBar’ is missing.
    -The required skin state ‘disabledWithControlBar’ is missing.

  21. You need to add this two additional states to the skin. <s:State name="normalWithControlBar" stateGroups="withControls"/> <s:State name="disabledWithControlBar" stateGroups="withControls"/>

    Have a look at original 4.5.1 PanelSkin, you may want to include controlBarGroup in you skin as well.

  22. I cannot solve the error for Simple text in Collapsible Panel Skin. If I change it to text input, I had to remove line break and vertical Align parameters. After I removed it also, I got the error
    “The required skin state ‘disabledWithControlBar’ is missing.
    -The required skin state ‘normalWithControlBar’ is missing.
    -The required skin state ‘disabledWithControlBar’ is missing”

    So I tried your above solution. And now I am getting

    The skin part type ‘spark.components:TextInput’ must be assignable to ‘spark.core:IDisplayText’. CollapsiblePanelSkin.mxml

    Could you please help me?

  23. I am trying using this component with Parsley3.0 but once the component is collapsed and then opened back the parsley events are not working for the coponents inside it. Like a data grid with checkbox as item renderer and header renderer. I don’t know what is the issue. Thanks in advance.

  24. use RichText instead of SimpleText in panel skin. Add those two states which solves missing error.

  25. Hi

    I want to collapse on the click of panel header.
    Can you please suggest some ideas.

    Regards,
    Varun.

  26. Hi, I am using this component in 4. SDK. The only problem I face here , the content are getting disappeared and getting blank. If I set collapsed= true initially, it does not work fine, but if it uncollapsed state, then the contents are not getting disappeared. Plz help

Leave a Reply

Your email address will not be published. Required fields are marked *