11 Replies Latest reply on Jul 28, 2011 7:06 AM by Arun - RIA-2MmnpH

    Extending Skins - Lifecycle question

    Simeon Bateman Level 2

      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

        • 1. Re: Extending Skins - Lifecycle question
          deepa subramaniam (adobe) Level 2

          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

          • 2. Re: Extending Skins - Lifecycle question
            deepa subramaniam (adobe) Level 2

            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>

            • 3. Re: Extending Skins - Lifecycle question
              Simeon Bateman Level 2

              Hey Deepa,

               

              Thanks for your response.  This doe work and is very close to what I had in my own testing.

               

              The problem comes when you decide to make a component out of the extended Panel.  You can extend Panel and define your own skinParts for the button, but the partAdd function never fires for the skinParts declared in the parent. 

               

              Panel defines these 2 skin Parts in particular

              [SkinPart(required="true")]

              public var titleField:SimpleText;

               

              [SkinPart(required="true")]

              public var contentGroup:Group;

               

              And I have used the class below to extend Panel and work on my skin.

               

              TitleWindow.as

              package view.components

              {

              import flash.events.Event;

              import flash.events.MouseEvent;

               

              import spark.components.Button;

              import spark.components.Panel;

              import spark.primitives.SimpleText;

               

              [Event(name="close")]

               

              public class TitleWindow extends Panel

              {

               

              [SkinPart(required="true")]

              public var closeButton:Button;

               

              public function TitleWindow()

              {

              super();

              }

               

              override protected function partAdded(partName:String, instance:Object) : void

                  {

                      super.partAdded(partName, instance);

                     

                      if ( partName == "closeButton" ) {

                      Button(instance).addEventListener(MouseEvent.CLICK, onCloseClick);

                      }

                      if ( partName == "titleField" ) {

                      SimpleText(titleField).text = title;

                      }

                    

                  }

                  protected function onCloseClick(event:MouseEvent) : void

                  {

                  dispatchEvent(new Event("close") );

                  }

                 

                  override public function set title(value:String) : void

                  {

                  super.title = value;

                  if ( titleField ) {

                  titleField.text = value;

                  }

                  }

                 

              }

              }

               

              TitleWindowSkin.as

              <?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" xmlns:default="spark.skins.default.*">

              <fx:Script>

              <![CDATA[

              import spark.primitives.SimpleText;

              import spark.components.Group;

               

              public function get contentGroup():Group {

              return p.contentGroup;

              }

               

              public function get titleField():SimpleText {

              return p.titleField;

              }

              ]]>

              </fx:Script>

              <s:states>

              <mx:State name="normal" />

              </s:states>

              <default:PanelSkin id="p" top="0" bottom="0" left="0" right="0" />

              <s:Button id="closeButton" skinClass="view.skins.CloseButtonSkin" right="10" top="{p.titleField.height/3}" />

               

              </s:Skin>

              In the code above the partAdd if block for titleField never gets called. 

               

              Any clue on how I can get the skinAdd method to execute for skinParts declared in the parent object?

               

              Thanks, it was great seeing you again at FlashCamp.

               

              =Sim

              • 4. Re: Extending Skins - Lifecycle question
                gjastrab

                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.

                • 5. Re: Extending Skins - Lifecycle question
                  Simeon Bateman Level 2

                  Hi gjastrab

                   

                  Thanks for your comments.  I think you are wrong though, and it goes back to something Deepa and I talked about at FlashCamp.

                   

                  I understand that it is not the intended behaviour to extend and existing skin through inheritience. I don't have a problem with that which is why I created a new skin and included the panelskin inside thereby creating a new skin and using the old skin through composition.

                   

                  However to think that I could not extend panel to add behaviors just seems wrong.  I understand that the lifecycle of a skin as that it should not be extended. But even if I created my skin completely from scratch I would still be extending Panel to get the built in behavours.  I have done this with buttons and other classes with no problem.

                   

                  The real issue is about wether or not the partAdd method should be called for skinParts that are defined in the parent.  And I think this may be a bug. In our discussion last week, Deepa mentioned that the bug may already be fixed so I am just looking for confirmation on that.

                   

                  Thanks again for your comments though.

                   

                  simeon

                  • 6. Re: Extending Skins - Lifecycle question
                    gjastrab Level 1

                    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.

                    • 7. Re: Extending Skins - Lifecycle question
                      gjastrab Level 1

                      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.

                      • 8. Re: Extending Skins - Lifecycle question
                        Zgavin

                        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.

                        • 9. Re: Extending Skins - Lifecycle question
                          ykessler

                          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.

                          • 10. Re: Extending Skins - Lifecycle question
                            Mr. McBob

                            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?

                            • 11. Re: Extending Skins - Lifecycle question
                              Arun - RIA-2MmnpH

                              Hi Deepa,

                               

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

                               

                              Arunkumar