Skip navigation
Currently Being Moderated

Extending Skins - Lifecycle question

May 26, 2009 11:29 AM

Ok, so I think I am working off an unintended path for skins.  But here it is.  I am building an AIR app using the Flex 4 sdk.  I though I would be cute and start with a panel as the base for my UI. I just needed to add a couple buttons to the title bar of the panel ( titlewindow like).  So I thought hey this will be great I will just extend PanelSkin and add my buttons.  This of course didnt work.  I can extend the skin and as long as I don't add any visual controls everything is fine.  As soon as I add a group or rect, then I override the default content of the parent and the PanelSkin goes away.

 

So I set up my buttons in the declarations block and am trying to find out when the right place in the skin's lifecycle is to add them in.  Normally I'd say this is in createChildren, but we are not supposed to use that in Flex 4.  I tried updateDisplay but I can't seem to get a reference to the actual entity I want to add the buttons to.  Not to mention that I can't use addChild and if I use addElement then the app wont launch.

 

So the real question is how do I make this work. I am sure no one intended this workflow, but I want it.  I know I could copy the code from PanelSkin to start my own, but thats not right.  What if you guys fix a bug in PanelSkin or something, I just want to position a couple controls in the corner of your panel.

 

That being said, am I perhaps going at this the wrong way?  Should I be using composition instead of inheritence?

 

I'd appreciate any help.

 

Thanks,

=Sim

 
Replies
  • Currently Being Moderated
    Jun 2, 2009 1:13 PM   in reply to Simeon Bateman

    Simeon - Excellent, excellent question.


    Subclassing skins (or any MXML component for that matter) is not a supported or recommended practice in Flex. Instead, there are two options available for your panel skin workflow: copy/paste, or compositing. With copy/paste, you would copy the packaged PanelSkin.mxml file into a new skin file and augment it as you see fit (add in your buttons, etc). With compositing, basically you put a PanelSkin inside your custom skin and proxy out the parts.


    Check out the attached examples. PanelSkinTest.mxml instantiates a Spark Panel where a custom skin is used for that Panel. The custom skin, MyPanelSkin.mxml, composites in a default Spark PanelSkin and proxies out the expected parts of a Panel.


    Hope this makes sense and is helpful.


    Cheers,

    Deepa Subramaniam

    Flex Framework Engineer

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 2, 2009 1:18 PM   in reply to deepa subramaniam (adobe)

    Gr, attaching files doesn't seem to be working too well for me. Instead, here is the code:

     

    PanelSkinTest.mxml

     

    <?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/halo" minWidth="1024" minHeight="768">
        <s:layout>
            <s:VerticalLayout />
        </s:layout>
       
        <s:Panel title="Default Skin" width="200" height="200">
            <s:layout>
                <s:VerticalLayout />
            </s:layout>
            <s:Button label="Button"/>
            <s:CheckBox label="CheckBox" />
        </s:Panel>
       
        <s:Panel title="Custom Skin" width="200" height="200" skinClass="MyPanelSkin">
            <s:layout>
                <s:VerticalLayout />
            </s:layout>
            <s:Button label="Button"/>
            <s:CheckBox label="CheckBox" />
        </s:Panel>
    </s:Application>

     

    MyPanelSkin.mxml

     

    <?xml version="1.0" encoding="utf-8"?>
    <s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
            xmlns:s="library://ns.adobe.com/flex/spark"
            xmlns:mx="library://ns.adobe.com/flex/halo"
            alpha.disabled="0.5">
           
        <fx:Script>
            <![CDATA[
                import spark.primitives.SimpleText;
                import spark.components.Group;
               
                public function get titleField():SimpleText
                {
                    return pSkin.titleField;
                }

               
                public function get contentGroup():Group
                {
                    return pSkin.contentGroup;
                }

            ]]>
        </fx:Script>
       
        <s:states>
            <mx:State name="normal" />
            <mx:State name="disabled" />
        </s:states>
       
        <skins:PanelSkin xmlns:skins="spark.skins.default.*" id="pSkin" left="0" right="0" top="0" bottom="0" />
           
        <s:Button top="7" right="7" width="18" height="18" />
    </s:Skin>

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 8, 2009 6:42 AM   in reply to Simeon Bateman

    Simeon,

     

    You're mixing and matching what deepa said was possible.  You cannot extend Skin classes in MXML, as she said, yet you're extending the model portion of the component in TitleWindow.as.

     

    I believe what you want to be doing in your example - to create a TitleWindow class - (and deepa, please correct me if I'm leading him in the wrong direction) is have TitleWindow extend SkinnableContainer, and proxy the contentGroup via the Panel you add in the skin.

     

    I'll try to play around w/ this as an example to see if I'm right and reply further when I have the chance.  Otherwise, hopefully deepa will have the chance soon to shed some guidance.

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 8, 2009 9:36 AM   in reply to Simeon Bateman

    You're misinterpreting what I said - I don't think my post made it sound like I think you cannot extend components to add behavior - but I also didn't fully understand what you were trying to do.  Initially I was thinking that your mixing of inheritance w/ composition in the skin wouldn't & shouldn't work, but now I see your point.

     

    My guess at how the framework currently works (although I haven't gotten the chance to dig into it deeply enough yet to fully understand this) is that for now you'd have to copy in the Panel skin code to your custom skin, and augment it manually to add the close button.  I do agree this is a shortcoming, but I also think it'd be weird for public getters defined in a Skin class that compose extended sub-parts should be passed into partAdded.  Although, maybe not.

     

    I'm interested in seeing what the framework team has to say about this.

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 8, 2009 10:54 AM   in reply to gjastrab

    Although, I just finally tried running your example, and it worked fine for me.  The partAdded successfully fired for the titleBar.  I put in a trace and saw it when debugging.  Attached is a screenshot, the code I ran, and the SWF as well.  I built this code using revision 7584 of the SDK trunk.

    Attachments:
     
    |
    Mark as:
  • Currently Being Moderated
    Jun 19, 2009 10:28 PM   in reply to gjastrab

    This seems to be a problem with the build that ships with the flash builder beta (7219 i believe). If you checkout the latest from trunk on SVN and use that instead this issue is resolved.

     
    |
    Mark as:
  • Currently Being Moderated
    Jul 11, 2009 10:12 AM   in reply to Simeon Bateman

    I've run into the same issue.  Here's a simple workaround...

     

    In the extended component, paste the following:

     

    protected override function findSkinParts() : void

    {

    repairSkinParts();

    super.findSkinParts();

    }

     

     

    protected function repairSkinParts():void

    {

    var desc:XML = describeType(this);

     

    for each (var variable:XML in desc.elements("variable"))

    {

    var varName:String = variable.@name;

    for each (var metadata:XML in variable.elements("metadata"))

    {

    var metaName:String = metadata.@name;

    if (metaName == "SkinPart")

    {

    for each (var arg:XML in metadata.elements("arg"))

    {

    var required:Boolean = false;

    if (arg.@key == "required")

    if (arg.@value == "true")

    required = true;

    }

    skinParts[varName] = required;

    }

    }

    }

    }

     

     

     

    ...basically repairs the protected skinParts object so that it includes skin parts in the current component AND its ancestors.

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 27, 2009 6:02 PM   in reply to Simeon Bateman

    I'm trying out Simeon's demo of this here, and am having issues with styling. Most obviously, the backgroundColor style is not being set for the PanelSkin within his TitleWindowSkin. There is no backgroundColor property on the PanelSkin's _nonInhertingStyles object, and so the background of the TitleWindow ends up being black. While other properties do have values set, attempting to change them via CSS has no effect. Styling for Panels is fine; the problem only occurs for TitleWindows.

     

    I have seen this problem building against two versions of the SDK: the one packaged with Flash Builder Beta 2, and 4.0.0.10485_mpl. Is this a known framework bug?

     
    |
    Mark as:
  • Currently Being Moderated
    Jul 28, 2011 7:06 AM   in reply to deepa subramaniam (adobe)

    Hi Deepa,

     

    Thanks for the explanation. You have unknowingly solved my issue for creating custom Component based on Panel.

     

    Arunkumar

     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (0)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points