2010-10-18 21 views
9

tengo el siguiente componente del panel denominado AdvancedPanel con controlBarContent:¿Cómo heredar estados 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> 

he creado un segundo panel, llamado CustomAdvancedPanel basado en la AdvancedPanel ya que no quiero redeclare la controlBarContent

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

Este no funciona, porque el estado 'editar' en CustomAdvancedPanel no se declara de acuerdo con el compilador. Tengo que redeclare el estado de edición en CustomAdvancedPanel.mxml de la siguiente manera:

<!-- 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> 

Utilizando el CustomAdvancedPanel dentro de un componente de la aplicación muestra un panel de vacío con el botón "Ir a editar". Pero cuando hago clic en él, el "botón de edición adicional" se vuelve visible, pero el botón "Mostrar en edición" dentro de la barra de control no.

Cuando CustomAdvancedPanel está vacío, sin estados redeclarados y "Botón de edición adicional", el panel funciona bien.

Creo que es porque el objeto de estado declarado en AdvancedPanel no es lo mismo que CustomAdvancedPanel, por lo que el estado es diferente, incluso si tienen el mismo nombre. Sin embargo. No puedo usar los estados de AdvancedPanel dentro de CustomAdvancedPanel sin (re) declararlos en mxml.

¿Hay alguna manera de lograr este tipo de reutilización de estado? ¿O hay una mejor manera de obtener el mismo resultado?

+0

+1 para una pregunta bien redactada, con ejemplos. – JeffryHouser

Respuesta

2

Le sugiero que utilice la arquitectura de la máscara de Spark para obtener sus objetivos. Debido a que los estados de la piel se heredan en el componente de host, puede colocar toda la lógica en modo OOP. Pero pieles seguirá conteniendo código duplicado :(De todos modos es mejor que el código duplicado de todos los componentes

Así que nuestra AdvancedPanel se verá como la siguiente:.

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(); 
     } 
    } 
} 

Y para CustomAdvancedPanel:

package 
{ 
    import spark.components.supportClasses.ButtonBase; 

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

Por supuesto, puede heredar de la clase Panel pero hice el código de muestra más simple.

Y la piel:

<?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> 

Y:

<?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 el estado del componente no se transfiere a los componentes heredados. Piénselo: si ese fuera el caso (si pudiera heredar estados), la vida sería realmente complicada siempre que quiera extender un componente; tendrías que estar al tanto de todos los estados heredados y no ponerte de puntillas.

+1

Al agregar en CustomAdvancedPanel sin redeclar los estados, se devuelven los tres estados de AdvancedPanel (normal, editar, deshabilitar) Sin embargo. No puede usarlos en mxml, porque el compilador dice que no están ahí, pero son como sugiere la cuadrícula de datos. – Treur

0

Creo que es una limitación de la programación OO, pero no estoy seguro de qué es exactamente. No soy un experto en Flex pero lo pensé desde un punto de vista de programación orientada a objetos y esto es lo que creo que sucede:

Primero considere que cuando crea un objeto, Flex (o cualquier lenguaje OO) crea automáticamente un copia de ese objeto Y una copia privada de su objeto principal, que a su vez crea una copia privada de su objeto principal y así sucesivamente en todo el árbol de objetos. Puede sonar raro, pero como ejemplo de esto, cuando escribes super() en un constructor estás llamando al constructor de la clase padre.

Flex tiene lo que llama "propiedades". Esto es el equivalente de lo que en Java sería un campo de miembro privado (variable) con un método getter y setter público. Cuando se declara

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

que está diciendo efectivamente

states = xyz 

que a su vez es el AS equivalente a decir

setStates(xyz) 

La parte importante, y esto es una regla general acerca de las propiedades, es que setStates es un método público, cualquiera puede llamarlo. Sin embargo, la matriz de estados en sí es privada. Si no declara uno, CustomAdvancedPanel no tiene propiedad de estados. Tampoco tiene un método setStates o getStates. Sin embargo, como setStates/getStates son públicos, los hereda de AdvancedPanel para que funcionen como si tuvieran estos métodos.Cuando llama a uno de estos métodos (obtiene o establece la matriz de estados), en realidad llama al método donde existe, que está en su objeto principal, AdvancedPanel. Cuando AdvancedPanel ejecuta el método, se lee o establece el valor de la matriz de estados en AdvancedPanel en sí. Esta es la razón por la que cuando no redeclara ningún estado en CustomAdvancedPanel, todo funciona perfectamente: cree que está configurando y obteniendo la matriz de estados en CustomAdvancedPanel pero, de hecho, detrás de las escenas está operando en la matriz states en el objeto principal AdvancedPanel, que es perfectamente bien y bien.

Ahora redefine la matriz de estados en CustomAdvancedPanel - ¿qué está pasando? Recuerde que declarar una propiedad en Flex es como declarar una variable privada de nivel de clase y getters y setters públicos. Por lo tanto, le está dando a CustomAdvancedPanel una matriz privada llamada estados y getters y setters públicos para obtener/establecer esa matriz. Estos getters y setters anularán los de AdvancedPanel. Así que ahora su aplicación interactuará con CustomAdvancedPanel de la misma manera, pero detrás de las escenas ya no está operando en los métodos/variables de AdvancedPanel, sino más bien en los que ha declarado en CustomAdvancedPanel. Esto explica por qué cuando cambia el estado de CustomAdvancedPanel, la parte que se hereda de AdvancedPanel no reacciona, ya que su visualización está vinculada a la matriz de estados en AdvancedPanel, que todavía existe de forma independiente.

Entonces, ¿por qué no está permitido el includeIn en el ejemplo básico en el que no redeclara los estados? No lo sé. O es un error, o tal vez más probable, hay un idioma legítimo/OO por lo que nunca podría funcionar.

Es posible que mi explicación no sea totalmente precisa. Eso es lo que entiendo las cosas. Yo mismo no sé por qué eso sucedería realmente teniendo en cuenta que el botón en cuestión es parte de la superclase. Un par de pruebas interesantes serían:

  1. mueve el manejador de clics a un método público real en lugar de en línea.
  2. add super.currentState = 'edit' al manejador de clics.

Si desea obtener más información acerca de todo este material de herencia, escriba algunas clases simples en ActionScript o Flex con una clase heredando otra, y ejecute varias llamadas a funciones para ver qué sucede.

0

"¿O hay una forma mejor de obtener el mismo resultado?"

Como ha preguntado, y como no dejó en claro la necesidad del componente adicional CustomAdvancedPanel, poner el "botón de edición adicional" en el componente AdvancedPanel es la solución más simple.

<!-- 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

Por supuesto, la forma políticamente correcta es utilizar pieles. Sin embargo, para aquellos que realmente solo quieren la herencia del estado de fuerza bruta para las clases de MXML aquí hay un trabajo que he encontrado.

Para que este método funcione, la clase MXML extendida debe declarar exactamente los mismos estados de la clase base MXML, ni más ni menos, todos con nombres idénticos.

Luego, en la clase que se extiende inserte el siguiente método:

 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); 
       } 
      } 
     } 

Esto funciona porque como se crea el componente de la variable de estados se establece dos veces, una vez por la clase base, y una vez por la clase que se extiende. Esta solución simplemente los combina.