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:
Update: See my Drawer Component in Flex 4 post for a custom collapsible drawer component written from scratch in Flex 4.
sandeep
3.26.2009
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
justin
3.27.2009
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
Excellent guide. Helped a ton. Thanks!
Umesh
5.19.2010
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.
justin
5.19.2010
@Umesh: If you want to change the style at runtime, the easiest thing to do is to use
getStyle()
andsetStyle()
.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.And then define two CSS styles for
headOpen
andheadClose
with different background colors.Good luck.
Abhishek
3.28.2011
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?justin
3.28.2011
@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
Thanks! That works for me!
Opher
4.6.2011
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?
justin
4.6.2011
@Opher: You could try to listen for the open/close events directly on the shade like this:
Then you can do whatever you need to do in the event handlers.
xiahui
6.28.2011
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
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?justin
6.28.2011
@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 theopenHandler()
andisOpen = false
in thecloseHandler()
.xiahui
6.29.2011
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
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
justin
4.20.2012
@DJNRLN: sure why not? I say a WindowShade within a WindowShade is possible.