Saturnboy
 3.4

Styling Flexlib’s WindowShade

, , ,

A while back I started digging into the WindowShade component in Flexlib. I really needed a set of cool collapsable buckets for a project at work, and WindowShade was perfect for the task. Alas, I couldn’t find too much info on styling a WindowShade other than Doug McCune’s awesome example of WindowShade and Degrafa. So, here is how I went about achieving the look and feel I needed with WindowShade.

Plain Vanilla

I started with an unstyled, super vanilla WindowShade wrapping a List. And of course, I get this super-vanilla output:

shade 1 (view source enabled)

The only styling magic was to add padding="0" to the WindowShade to get the child List component to suck up to the bottom of the WindowShade button.

My First Attempt

In the next pass, I ditched the lame WindowShade button, and went with Flexlib’s CanvasButton, which contains a simple Label plus a CheckBox skinned with a plus or minus graphic. The code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        xmlns:flexlibContainer="flexlib.containers.*"
        xmlns:flexlibControl="flexlib.controls.*"
        viewSourceURL="srcview/index.html">
 
    <mx:Script>
    <![CDATA[
        [Bindable]
        private var goodies:Array = [
            { header:'Ice Cream', items:['Vanilla', 'Chocolate', 'Strawberry', 'Cookies & Cream'] },
            { header:'Candy', items:['Twix', 'Snickers', 'Fire Balls', 'Hot Tamales', 'Mike & Ikes', 'Pez'] },
            { header:'Cookies', items:['Chewy Chips Ahoy', 'Mint Milano', 'Oreo', 'Nutter Butter', 'Fig Newtons'] }];
    ]]>
    </mx:Script>
 
    <mx:Style source="style.css" />
 
    <mx:VBox width="140" styleName="container">
        <mx:Repeater id="r" dataProvider="{goodies}">
            <flexlibContainer:WindowShade width="100%" styleName="shade"
                    data="{r.currentItem.header}"
                    opened="{r.currentIndex == 0}">
                <flexlibContainer:headerRenderer>
                    <mx:Component>
                        <flexlibControl:CanvasButton width="100%" height="30" styleName="shadeBtn">
                            <mx:Script>
                            <![CDATA[
                                import flexlib.containers.WindowShade;
                            ]]>
                            </mx:Script>
 
                            <mx:Label id="header" top="3" left="4" text="{WindowShade(parent).data}" styleName="shadeHead"/>
                            <mx:CheckBox id="chk" top="9" right="6" selected="{WindowShade(parent).opened}" styleName="shadeChk"/>
                        </flexlibControl:CanvasButton>
                    </mx:Component>
                </flexlibContainer:headerRenderer>
 
                <mx:List width="100%" dataProvider="{r.currentItem.items}" rowCount="{r.currentItem.items.length}" styleName="shadeList"/>
            </flexlibContainer:WindowShade>
        </mx:Repeater>
    </mx:VBox>
</mx:Application>

After throwing in some colors from a sweet Kuler theme and embedding Helvetica, we get this:

shade 2 (view source enabled)

At this point, I was really happy with look and feel, but there we still a few minor issues with the CanvasButton header that needed to be fixed before declaring victory.

Fixups

First, I needed the rollover event to flow down to the checkbox, so it would correctly show the overSkin on mouse over. Second, I wanted a color change on the Label on rollover to provide better visual feedback to the user. And lastly, I wanted the entire CanvasButton header to be clickable, not just the label text or the checkbox graphic.

Focusing just on the modified CanvasButton code, used in the headerRendered:

<flexlibControl:CanvasButton width="100%" height="30" styleName="shadeBtn"
        rollOver="header.setStyle('color', 0xffffff); chk.dispatchEvent(event);"
        rollOut="header.setStyle('color', 0xcccccc); chk.dispatchEvent(event);">
    <mx:Script>
    <![CDATA[
        import flexlib.containers.WindowShade;
    ]]>
    </mx:Script>
 
    <mx:Label id="header" top="3" left="4" text="{WindowShade(parent).data}" styleName="shadeHead"/>
    <mx:CheckBox id="chk" top="9" right="6" selected="{WindowShade(parent).opened}" styleName="shadeChk"/>
    <mx:Canvas width="100%" height="100%" backgroundColor="#000000" backgroundAlpha="0" />
</flexlibControl:CanvasButton>

The final result, a nice styled WindowShade:

shade 3 (view source enabled)

I used the parent CanvasButton’s rollover event to set the child Label color and forward the event to the child CheckBox. To make the entire button clickable, I used one of my favorite Flex UI hacks: I added a space-filling transparent Canvas.

Comparison Shopping

And finally, a side-by-side comparison of all three WindowShade skins:

Flash is required. Get it here!

Update: See my Drawer Component in Flex 4 post for a custom collapsible drawer component written from scratch in Flex 4.


Comments

sandeep

3.26.2009

1

Hi,

Happened to do the same thing what you and faced the same problem and the only difference is that i couldn’t resolve the issue you faced. I am not able to roll over or roll out a windowshade when clicked on Windowshade apart from the label. I have included a Label with in the Windowshade but it has made windowshade rolldown functionality to fail. Can you please look at the code and let me know where things have failed, I have tried all the possible ways but failed.

Please find my code below…

and my actionscript methods…

public static function onBackDown(event:Event):void {
ModelLocator.getInstance().dispatchEvent(new Event(“LabelClicked”));
}

public static function onBackClick(event:Event):void {
event.stopPropagation();
}

Would appreciate if you could suggest a solution…

Thanks

3.27.2009

2

As I mentioned above, I’m using a CanvasButton in the headerRenderer of the WindowShade. The CanvasButton hold three components: a Label, a CheckBox, and a basic Canvas. The key to making the entire CanvasButton clickable is the extra Canvas, which is space-filling and transparent:

<mx:Canvas width="100%" height="100%"
    backgroundColor="#000000" backgroundAlpha="0" />

Joel

2.22.2010

3

Excellent guide. Helped a ton. Thanks!

Umesh

5.19.2010

4

Hi,
Thanks for a very good example. I wanted to know how can I have a different header colors applied. I want the header to have a one color when window shade is open and another color when it is closed.

Any help will be much appreciated. Using the most recent Flex 4 version.

5.19.2010

5

@Umesh: If you want to change the style at runtime, the easiest thing to do is to use getStyle() and setStyle().

So, if I wanted to switch styles on the header on open & close, I just listen for the correct events and use setStyle() to make the switch.

shade.addEventListener(WindowShadeEvent.OPEN_BEGIN,
    function(e:Event):void {
        shade.setStyle('headerStyleName', 'headOpen');
    });
shade.addEventListener(WindowShadeEvent.CLOSE_END,
    function(e:Event):void {
        shade.setStyle('headerStyleName', 'headClose');
    });

And then define two CSS styles for headOpen and headClose with different background colors.

Good luck.

Abhishek

3.28.2011

6

Great read! I am using the flexlib:WindowShade for a project myself, and I found your article very helpful. I have a question though – is it possible to draw a border around the whole windowshade? Say, to signify the user wants focus on a current windowshade?

3.28.2011

7

@Abhishek: If you want a border outside the windowshade, I’d be tempted to just put the shade into a container with a border. Simple is best.

Abhishek

3.28.2011

8

Thanks! That works for me!

Opher

4.6.2011

9

Hi,

I’ve been trying to use this technique, but it seems like “WindowShade(parent).opened” doesn’t get updated when opening/closing the shade – it just keeps the initial value, so the checkbox doesn’t change its state. . Any idea what I’m doing wrong?

4.6.2011

10

@Opher: You could try to listen for the open/close events directly on the shade like this:

shade.addEventListener(WindowShadeEvent.OPEN_BEGIN, openHandler);
shade.addEventListener(WindowShadeEvent.CLOSE_END, closeHandler);

Then you can do whatever you need to do in the event handlers.

xiahui

6.28.2011

11

i meet the same quesion with Opher in flex4, i also want to use
shade.addEventListener(WindowShadeEvent.OPEN_BEGIN, openHandler);
but how to write openHandler() function?

xiahui

6.28.2011

12

I maybe describe my question.
I only want to change checkbox’s icon when opening/closing the shade, but WindowShade(parent).opened doesn’t get updated – it just keeps the initial value. how can i do?

6.28.2011

13

@xiahui: Something must get updated with the current state of the shade (open or closed). I suggest using the debugger. Wire up the event listeners and set a breakpoint inside openHandler().

Alternately, you can keep track of the state manually, just say isOpen = true in the openHandler() and isOpen = false in the closeHandler().

xiahui

6.29.2011

14

thanks for your answer. I have solved this question, but the solved method isn’t clever.
the reason of checkbox’s icon can’t updated with the current state of the shade (open or closed) is that checkbox locate in headerRenderer. If open/close the WindowShade, headerRenderer is not changed, so the data of headerRenderer is not changed.

The property selected of checkbox isn’t changed because the render isn’t updated. So, I update the headerRenderer.

protected function shade_creationCompleteHandler(event:FlexEvent):void
{
shade.headerRenderer=new ClassFactory(itemh);
//headerRenderer=”itemh”

}

protected function shade_openedChangedHandler(event:WindowShadeEvent):void
{
shade.headerRenderer=new ClassFactory(itemh);
}
At last,I want to ask why header is only in render,but a component member of windowshade
Thanks

DJNRLN

4.20.2012

15

I am starting to work with WindowShades, a project i am working on would like to have WindowShade2 views within an outer WindowShade2 view.

For example Ice Cream would contain a multiple sub-Views that are defined as windowShade2. the sub-views could be for toppings and cone styles.

is it possible to have WindowShade views within a WindowShade?

thanks

4.20.2012

16

@DJNRLN: sure why not? I say a WindowShade within a WindowShade is possible.

© 2017 saturnboy.com