2010-10-18 14 views
9

Ho il seguente componente del pannello chiamato AdvancedPanel con controlBarContent:Come ereditare gli stati con mxml?

<!-- AdvancedPanel.mxml --> 
<s:Panel> 
    <s:states> 
    <s:State name="normal" /> 
    <s:State name="edit" /> 
    </s:states> 
    <s:controlBarContent> 
    <s:Button 
     includeIn="edit" 
     label="Show in edit" 
     /> 
    <s:Button 
     label="Go to edit" 
     click="{currentState='edit'}" 
     /> 
    </s:controlBarContent> 
</s:Panel> 

ho creato un secondo pannello, chiamato CustomAdvancedPanel sulla base del AdvancedPanel dato che non voglio ridichiarare il controlBarContent

<!-- CustomAdvancedPanel.mxml --> 
<local:AdvancedPanel> 
    <s:Button includeIn="edit" label="Extra edit button" /> 
</local:AdvancedPanel> 

Questo non funziona, perché lo stato 'modifica' in CustomAdvancedPanel non è dichiarato in base al compilatore. Devo ridichiarare lo stato di modifica in CustomAdvancedPanel.mxml come segue:

<!-- CustomAdvancedPanel.mxml with edit state redeclared --> 
    <local:AdvancedPanel> 
     <local:states> 
     <s:State name="normal" /> 
     <s:State name="edit" /> 
     </local:states> 
     <s:Button includeIn="edit" label="Extra edit button" /> 
    </local:AdvancedPanel> 

Uso del CustomAdvancedPanel all'interno di un componente dell'applicazione mostra un pannello vuoto con il pulsante "Vai per modificare". Ma quando faccio clic, il pulsante "Extra edit" diventa visibile, ma il pulsante "Mostra in modifica" all'interno della barra di controllo non lo fa.

Quando CustomAdvancedPanel è vuoto, senza stati dichiarati e "Pulsante di modifica extra", il pannello funziona correttamente.

Penso che sia perché l'oggetto Stato dichiarato in AdvancedPanel non è lo stesso di CustomAdvancedPanel, quindi lo stato è diverso, anche se hanno lo stesso nome. Però. Non posso usare gli stati di AdvancedPanel all'interno di CustomAdvancedPanel senza (re) dichiararli in mxml.

Esiste un modo per ottenere questo tipo di riutilizzo da parte dello stato? O c'è un modo migliore per ottenere lo stesso risultato?

+0

+1 per domande ben formulate, con campioni. – JeffryHouser

risposta

2

Ti suggerisco di utilizzare l'architettura skinning di Spark per ottenere i tuoi obiettivi. Poiché gli stati skin sono ereditati nel componente host, è possibile inserire tutta la logica in modalità OOP. Ma le pelli saranno ancora contengono codice duplicato :(In ogni caso è meglio che il codice duplicato di tutti i componenti

Così il nostro AdvancedPanel sarà simile alla seguente:.

package 
{ 
    import flash.events.MouseEvent; 

    import spark.components.supportClasses.ButtonBase; 
    import spark.components.supportClasses.SkinnableComponent; 

    [SkinState("edit")] 
    [SkinState("normal")] 
    public class AdvancedPanel extends SkinnableComponent 
    { 
     [SkinPart(required="true")] 
     public var goToEditButton:ButtonBase; 
     [SkinPart(required="true")] 
     public var showInEditButton:ButtonBase; 

     private var editMode:Boolean; 

     override protected function getCurrentSkinState():String 
     { 
      return editMode ? "edit" : "normal"; 
     } 

     override protected function partAdded(partName:String, instance:Object):void 
     { 
      super.partAdded(partName, instance); 
      if (instance == goToEditButton) 
       goToEditButton.addEventListener(MouseEvent.CLICK, onGoToEditButtonClick); 
     } 

     override protected function partRemoved(partName:String, instance:Object):void 
     { 
      super.partRemoved(partName, instance); 
      if (instance == goToEditButton) 
       goToEditButton.removeEventListener(MouseEvent.CLICK, onGoToEditButtonClick); 
     } 

     private function onGoToEditButtonClick(event:MouseEvent):void 
     { 
      editMode = true; 
      invalidateSkinState(); 
     } 
    } 
} 

E per CustomAdvancedPanel:

package 
{ 
    import spark.components.supportClasses.ButtonBase; 

    public class CustomAdvancedPanel extends AdvancedPanel 
    { 
     [SkinPart(required="true")] 
     public var extraEditButton:ButtonBase; 
    } 
} 

Ovviamente puoi ereditare dalla classe Panel ma ho reso il codice di esempio più semplice.

E le bucce:

<?xml version="1.0" encoding="utf-8"?> 
<!-- AdvancedPanelSkin.mxml --> 
<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/mx"> 
    <fx:Metadata> 
     [HostComponent("AdvancedPanel")] 
    </fx:Metadata> 
    <s:states> 
     <s:State name="normal" /> 
     <s:State name="edit" /> 
    </s:states> 
    <s:Panel left="0" right="0" top="0" bottom="0"> 
     <s:controlBarContent> 
      <s:Button id="showInEditButton" label="Show in edit" includeIn="edit" /> 
      <s:Button id="goToEditButton" label="Go to edit" /> 
     </s:controlBarContent> 
    </s:Panel> 
</s:Skin> 

E:

<?xml version="1.0" encoding="utf-8"?> 
<!-- CustomAdvancedPanelSkin.mxml --> 
<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/mx"> 
    <fx:Metadata>[HostComponent("CustomAdvancedPanel")]</fx:Metadata> 
    <s:states> 
     <s:State name="normal" /> 
     <s:State name="edit" /> 
    </s:states> 
    <s:Panel left="0" right="0" top="0" bottom="0"> 
     <s:Button includeIn="edit" label="Extra edit button" id="extraEditButton" /> 
     <s:controlBarContent> 
      <s:Button id="showInEditButton" label="Show in edit" includeIn="edit" /> 
      <s:Button id="goToEditButton" label="Go to edit" /> 
     </s:controlBarContent> 
    </s:Panel> 
</s:Skin> 
1

AFAIK lo stato del componente non passa ai componenti ereditati. Pensaci - se fosse il caso (se potessi ereditare degli stati) allora renderebbe la vita davvero complicata ogni volta che vorresti estendere un componente; dovresti essere a conoscenza di tutti gli stati ereditati e non calpestare le dita dei piedi.

+1

Aggiunta di in CustomAdvancedPanel senza ridichiarare gli stati restituisce i tre stati di AdvancedPanel (normale, modifica, disabilitato) Tuttavia. Non puoi usarli in mxml, perché il compilatore dice che non ci sono, ma sono come suggerisce il datagrid. – Treur

0

Penso che sia una limitazione della programmazione OO, ma non sono sicuro di cosa esattamente. Non sono un esperto di Flex ma ci ho pensato da un punto di vista orientato agli oggetti ed ecco cosa penso:

Innanzitutto considera che quando si crea un oggetto, Flex (o qualsiasi linguaggio OO) crea automaticamente un copia di quell'oggetto E una copia privata del suo oggetto genitore, che a sua volta crea una copia privata del suo oggetto genitore e così via sull'intero albero degli oggetti. Potrebbe sembrare strano ma come esempio, quando scrivi super() in un costruttore, stai chiamando il costruttore della classe genitore.

Flex ha ciò che definisce "proprietà". Questo è l'equivalente di ciò che in Java sarebbe un campo membro privato (variabile) con un metodo getter e setter pubblico. Quando si dichiara

<local:states>xyz</local:states> 

si sta effettivamente dicendo

states = xyz 

che a sua volta è l'equivalente di dire

setStates(xyz) 

La parte importante, e questa è una regola generale su proprietà, è che setStates è un metodo pubblico, chiunque può chiamarlo. Tuttavia lo stesso array degli stati è privato. Se non lo si dichiara, CustomAdvancedPanel non ha proprietà states. Né ha un metodo setStates o getStates. Tuttavia, dato che setStates/getStates sono pubblici, li eredita da AdvancedPanel e funziona come se avesse questi metodi.Quando si chiama uno di questi metodi (ottieni o imposta la matrice degli stati), in realtà chiama il metodo dove esiste, che si trova nell'oggetto padre, AdvancedPanel. Quando AdvancedPanel esegue il metodo il valore di l'array di stati in AdvancedPanel stesso viene letto o impostato. Questo è il motivo per cui quando non si ridichiano stati in CustomAdvancedPanel tutto funziona perfettamente - si pensa di impostare e ottenere l'array di stati in CustomAdvancedPanel ma in realtà dietro le quinte si sta operando sull'array states nell'oggetto padre AdvancedPanel, che è perfettamente bene e bene.

Ora si ridefinisce l'array di stati in CustomAdvancedPanel - cosa sta succedendo? Ricorda che dichiarare una proprietà in Flex è come dichiarare una variabile privata a livello di classe e getter e setter pubblici. Quindi stai dando a CustomAdvancedPanel un array privato chiamato stati e getter e setter pubblici per ottenere/impostare quella matrice. Questi getter e setter annulleranno quelli di AdvancedPanel. Così ora la tua applicazione interagirà con CustomAdvancedPanel allo stesso modo ma dietro le quinte non stai più operando sui metodi/variabili di AdvancedPanel ma piuttosto su quelli che hai dichiarato in CustomAdvancedPanel stesso. Questo spiega perché quando si modifica lo stato di CustomAdvancedPanel, la parte ereditata da AdvancedPanel non reagisce, poiché la sua visualizzazione è collegata alla matrice degli stati in AdvancedPanel, che esiste ancora indipendentemente.

Quindi, perché l'inclusione non è consentita nell'esempio di base in cui non si dichiarano nuovamente gli stati? Non lo so. O è un bug, o forse più probabile, c'è un linguaggio legittimo/motivo OO perché non potrebbe mai funzionare.

È possibile che la mia spiegazione non sia completamente accurata. Questo è quanto ho capito le cose. Io stesso non so perché ciò accadrebbe davvero considerando che il Button in questione fa parte della superclasse. Un paio di test interessanti potrebbero essere:

  1. spostare il gestore di clic in un metodo pubblico effettivo anziché in linea.
  2. aggiungi super.currentState = 'modifica' al gestore dei clic.

Se volete saperne di più su tutta questa roba eredità, scrivere alcune semplici classi in ActionScript o Flex con una classe che eredita l'altro, ed eseguire varie funzioni chiamate per vedere cosa succede.

0

"O c'è un modo migliore per ottenere lo stesso risultato?"

Dato che è stato chiesto e poiché non si è chiarito il caso del componente aggiuntivo CustomAdvancedPanel, il "Pulsante di modifica extra" nel componente AdvancedPanel è la soluzione più semplice.

<!-- AdvancedPanel.mxml --> 
<s:Panel> 
    <s:states> 
    <s:State name="normal"/> 
    <s:State name="edit"/> 
    </s:states> 
    <s:Button includeIn="edit" label="Extra edit button"/> 
    <s:controlBarContent> 
    <s:Button 
     includeIn="edit" 
     label="Show in edit"/> 
    <s:Button 
     label="Go to edit" 
     click="{currentState='edit'}"/> 
    </s:controlBarContent> 
</s:Panel> 
0

Naturalmente il modo politicamente corretto è quello di utilizzare le pelli. Tuttavia, per coloro che desiderano davvero ereditare la forza bruta dello stato per le classi MXML, ecco un lavoro che ho trovato.

Perché questo metodo funzioni, la classe MXML di estensione dovrebbe dichiarare esattamente gli stessi stati della classe MXML di base, né più né meno, tutti con nomi identici.

Poi nella classe estende inserire il seguente metodo:

 override public function set states(value:Array):void 
     { 
      if(super.states == null || super.states.length == 0) 
      { 
       super.states = value; 

       for each (var state:State in value) 
       { 
        state.name = "_"+state.name; 
       } 
      } 
      else 
      { 
       for each (var state:State in value) 
       { 
        state.basedOn = "_"+state.name; 
        super.states.push(state); 
       } 
      } 
     } 

Ciò funziona perché come viene realizzato il componente variabile stati è impostata due volte, una volta dalla classe base, e una volta dalla classe estendendo. Questa soluzione alternativa li combina insieme.

Problemi correlati