2008-11-06 3 views
8

J'utilise un rendu d'élément personnalisé dans une zone de liste déroulante pour afficher un dessin personnalisé au lieu de l'étiquette de texte par défaut.Rendu d'élément personnalisé Flex pour l'élément affiché dans la liste déroulante

Cela fonctionne très bien pour la liste déroulante, mais l'élément affiché (lorsque la liste est fermée) est toujours la représentation textuelle de mon objet.

Existe-t-il un moyen d'afficher l'élément affiché de la même manière que dans la liste déroulante?

Répondre

9

Par défaut, vous ne pouvez pas faire cela. Cependant, si vous étendez ComboBox, vous pouvez facilement ajouter cette fonctionnalité. Voici un exemple rapide, il s'agit d'une version approximative et il est probablement nécessaire de tester/peaufiner, mais cela montre comment vous pouvez accomplir cela.

package 
{ 
    import mx.controls.ComboBox; 
    import mx.core.UIComponent; 

    public class ComboBox2 extends ComboBox 
    { 
     public function ComboBox2() 
     { 
      super(); 
     } 

     protected var textInputReplacement:UIComponent; 

     override protected function createChildren():void { 
      super.createChildren(); 

      if (!textInputReplacement) { 
       if (itemRenderer != null) { 
        //remove the default textInput 
        removeChild(textInput); 

        //create a new itemRenderer to use in place of the text input 
        textInputReplacement = itemRenderer.newInstance(); 
        addChild(textInputReplacement); 
       } 
      } 
     } 

     override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { 
      super.updateDisplayList(unscaledWidth, unscaledHeight); 

      if (textInputReplacement) { 
       textInputReplacement.width = unscaledWidth; 
       textInputReplacement.height = unscaledHeight; 
      } 
     } 
    } 
} 
5

J'ai essayé la solution ci-dessus, mais j'ai constaté que l'élément sélectionné ne s'affichait pas lorsque la liste déroulante était fermée. Une ligne de code supplémentaire a été nécessaire pour lier la propriété des données itemRenderer au selectedItem:

  if (!textInputReplacement) { 
        if (itemRenderer != null) { 
          //remove the default textInput 
          removeChild(textInput); 

          //create a new itemRenderer to use in place of the text input 
          textInputReplacement = itemRenderer.newInstance(); 

          // ADD THIS BINDING: 
          // Bind the data of the textInputReplacement to the selected item 
          BindingUtils.bindProperty(textInputReplacement, "data", this, "selectedItem", true); 

          addChild(textInputReplacement); 
        } 
      } 
0

Merci maclema et Maurits de Boer. J'ai ajouté quelques plus de choses à cette classe pour l'adapter à mes besoins:

  • Je l'emportaient sur itemRenderer mis en sorte que cela fonctionnera si vous définissez la itemRenderer par AS au lieu de MXML. J'ai déplacé le code de remplacement d'entrée de texte à sa propre fonction pour éviter la duplication.

  • J'ai ajouté des setters pour 'increaseW' et 'increaseH' pour redimensionner la combobox si nécessaire parce que mon renderer était trop grand pour la combobox au début. J'ai soustrait 25 de la largeur de textInputReplacement afin qu'il ne chevauche jamais le bouton de liste déroulante ... peut-être préférable d'utiliser quelque chose de plus proportionnel pour accueillir différents skins et autres.

code:

package 
{ 
import mx.binding.utils.BindingUtils; 
import mx.controls.ComboBox; 
import mx.core.IFactory; 
import mx.core.UIComponent; 

    public class ComboBox2 extends ComboBox 
    { 
     public function ComboBox2() 
     { 
       super(); 
     } 

     protected var textInputReplacement:UIComponent; 
     private var _increaseW:Number = 0; 
     private var _increaseH:Number = 0; 

    public function set increaseW(val:Number):void 
    { 
    _increaseW = val; 
    } 

    public function set increaseH(val:Number):void 
    { 
    _increaseH = val; 
    } 

    override public function set itemRenderer(value:IFactory):void 
    { 
    super.itemRenderer = value; 
    replaceTextInput(); 
    } 

     override protected function createChildren():void 
     { 
       super.createChildren(); 
    replaceTextInput(); 

     } 

     override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { 

      unscaledWidth += _increaseW; 
      unscaledHeight += _increaseH; 

       super.updateDisplayList(unscaledWidth, unscaledHeight); 

       if (textInputReplacement) { 
         textInputReplacement.width = unscaledWidth - 25; 
         textInputReplacement.height = unscaledHeight; 
       } 
     } 

     protected function replaceTextInput():void 
     { 
     if (!textInputReplacement) { 
         if (this.itemRenderer != null) { 
           //remove the default textInput 
           removeChild(textInput); 

           //create a new itemRenderer to use in place of the text input 
           textInputReplacement = this.itemRenderer.newInstance(); 
           addChild(textInputReplacement); 

           // ADD THIS BINDING: 
          // Bind the data of the textInputReplacement to the selected item 
          BindingUtils.bindProperty(textInputReplacement, "data", this, "selectedItem", true); 

          addChild(textInputReplacement); 

         } 
       } 
     } 
    } 
} 
+0

pouvez-vous me aider: // supprimer la valeur par défaut textInput \t \t \t \t \t removeChild (textInput); Coercition implicite d'une valeur de type mx.core: ITextInput à un type non apparenté flash.display: DisplayObject. –

-1

Cela peut être beaucoup plus simplement atteint que si vous êtes à la recherche d'une sorte de texte mis en forme personnalisée avec des attributs CSS. Remplacez la fonction "get selectedLabel(): String" pour renvoyer toute chaîne à afficher dans la zone de texte. Si vous avez besoin d'une sorte de CSS, définissez le style sur votre textInput (this.textInput) dans la méthode selectedLabel().

0

Je cherchais un moyen de le faire en utilisant le Spark ComboBox.

Ce fil de discussion m'a été très utile mais jusqu'à présent il n'y a eu que des réponses sur la façon de le faire en utilisant un mx: ComboBox. Je pensais que je devais ajouter ma réponse sur la façon de le faire en utilisant une étincelle ComboBox.

  1. Créer une nouvelle peau du ComboBox
  2. Cacher et désactiver le textInput
  3. Insérez votre propre composant

C'est ce que la peau ressemblerait à ceci:

<s:SparkSkin> 

    <... Lots of other stuff/> 

    <s:BorderContainer height="25"> 
     <WHATEVER YOU NEED HERE!/> 
    </s:BorderContainer> 

    <!-- Disable the textInput and hide it --> 
    <s:TextInput id="textInput" 
     left="0" right="18" top="0" bottom="0" 
     skinClass="spark.skins.spark.ComboBoxTextInputSkin" 

     visible="false" enabled="false"/> 


</s:SparkSkin> 

Avec le Spark ComboBox, ce processus est très simple et ne nécessite pas d'extension de ComboBox.

3

J'ai étendu le code de Dane un peu plus loin. Dans certains cas, cliquer n'a pas ouvert la boîte de dépôt avec mon moteur de rendu et j'ai remarqué que les apparences Flex ComboBox normales ne se sont pas déclenchées. Ainsi, dans replaceTextInput(), j'ai ajouté des écouteurs d'événement supplémentaires et enregistré une référence au bouton ComboBox utilisé pour afficher les enveloppes. Maintenant, il se comporte comme le ComboBox normal.

Voici le code:

 
    package 
    { 
    import flash.events.Event; 
    import flash.events.KeyboardEvent; 
    import flash.events.MouseEvent; 

    import mx.binding.utils.BindingUtils; 
    import mx.controls.Button; 
    import mx.controls.ComboBox; 
    import mx.core.IFactory; 
    import mx.core.UIComponent; 
    import mx.events.DropdownEvent; 

    /** 
    * Extension of the standard ComboBox that will use the assigned 'itemRenderer' 
    * for both the list items and the selected item. 
    * 
    * Based on code from: 
    * http://stackoverflow.com/questions/269773/flex-custom-item-renderer-for-the-displayed-item-in-the-combobox 
    */ 
    public class ComboBoxFullRenderer extends ComboBox 
    { 
    protected var textInputReplacement:UIComponent; 
    private var _increaseW:Number = 0; 
    private var _increaseH:Number = 0; 


    /** 
    * Keeps track of the current open/close state of the drop down list. 
    */ 
    protected var _isOpen:Boolean = false; 

    /** 
    * Stores a reference to the 'Button' which overlays the ComboBox. Allows 
    * us to pass events to it so skins are properly triggered. 
    */ 
    protected var _buttonRef:Button = null; 


    /** 
    * Constructor. 
    */ 
    public function ComboBoxFullRenderer() { 
     super(); 
    } 


    /** 
    * Sets a value to increase the width of our ComboBox to adjust sizing. 
    * 
    * @param val Number of pixels to increase the width of the ComboBox. 
    */ 
    public function set increaseW(val:Number):void { 
     _increaseW = val; 
    } 

    /** 
    * Sets a value to increase the height of our ComboBox to adjust sizing. 
    * 
    * @param val Number of pixels to increase the height of the ComboBox. 
    */ 
    public function set increaseH(val:Number):void { 
     _increaseH = val; 
    } 


    /** 
    * Override the 'itemRenderer' setter so we can also replace the selected 
    * item renderer. 
    * 
    * @param value The renderer to be used to display the drop down list items 
    * and the selected item. 
    */ 
    override public function set itemRenderer(value:IFactory):void { 
     super.itemRenderer = value; 
     replaceTextInput(); 
    } 


    /** 
    * Override base 'createChildren()' routine to call our 'replaceTextInput()' 
    * method to replace the standard selected item renderer. 
    * 
    * @see #replaceTextInput(); 
    */ 
    override protected function createChildren():void { 
     super.createChildren(); 
     replaceTextInput(); 
    } 


    /** 
    * Routine to replace the ComboBox 'textInput' child with our own child 
    * that will render the selected data element. Will create an instance of 
    * the 'itemRenderer' set for this ComboBox. 
    */ 
    protected function replaceTextInput():void { 
     if (!textInputReplacement) { 
      if (this.itemRenderer != null && textInput != null) { 
       //remove the default textInput 
       removeChild(textInput); 

       //create a new itemRenderer instance to use in place of the text input 
       textInputReplacement = this.itemRenderer.newInstance(); 
       // Listen for clicks so we can open/close the drop down when 
       // renderer components are clicked. 
       textInputReplacement.addEventListener(MouseEvent.CLICK, _onClick); 
       // Listen to the mouse events on our renderer so we can feed them to 
       // the ComboBox overlay button. This will make sure the button skins 
       // are activated. See ComboBox::commitProperties() code. 
       textInputReplacement.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseEvent); 
       textInputReplacement.addEventListener(MouseEvent.MOUSE_UP, _onMouseEvent); 
       textInputReplacement.addEventListener(MouseEvent.ROLL_OVER, _onMouseEvent); 
       textInputReplacement.addEventListener(MouseEvent.ROLL_OUT, _onMouseEvent); 
       textInputReplacement.addEventListener(KeyboardEvent.KEY_DOWN, _onMouseEvent); 

       // Bind the data of the textInputReplacement to the selected item 
       BindingUtils.bindProperty(textInputReplacement, "data", this, "selectedItem", true); 

       // Add our renderer as a child. 
       addChild(textInputReplacement); 

       // Listen for open close so we can maintain state. The 
       // 'isShowingDropdown' property is mx_internal so we don't 
       // have access to it. 
       this.addEventListener(DropdownEvent.OPEN, _onOpen); 
       this.addEventListener(DropdownEvent.CLOSE, _onClose); 

       // Save a reference to the mx_internal button for the combo box. 
       // We will need this so we can call its dispatchEvent() method. 
       for (var i:int = 0; i < this.numChildren; i++) { 
        var temp:Object = this.getChildAt(i); 
        if (temp is Button) { 
         _buttonRef = temp as Button; 
         break; 
        } 
       } 
      } 
     } 
    } 


    /** 
    * Detect open events on the drop down list to keep track of the current 
    * drop down state so we can react properly to a click on our selected 
    * item renderer. 
    * 
    * @param event The DropdownEvent.OPEN event for the combo box. 
    */ 
    protected function _onOpen(event:DropdownEvent) : void { 
     _isOpen = true; 
    } 


    /** 
    * Detect close events on the drop down list to keep track of the current 
    * drop down state so we can react properly to a click on our selected 
    * item renderer. 
    * 
    * @param event The DropdownEvent.CLOSE event for the combo box. 
    */ 
    protected function _onClose(event:DropdownEvent) : void { 
     _isOpen = false; 
    } 


    /** 
    * When we detect a click on our renderer open or close the drop down list 
    * based on whether the drop down is currently open/closed. 
    * 
    * @param event The CLICK event from our selected item renderer. 
    */ 
    protected function _onClick(event:MouseEvent) : void { 
     if (_isOpen) { 
      this.close(event); 
     } else { 
      this.open(); 
     } 
    } 


    /** 
    * React to certain mouse/keyboard events on our selected item renderer and 
    * pass the events to the ComboBox 'button' so that the skins are properly 
    * applied. 
    * 
    * @param event A mouse or keyboard event to send to the ComboBox button. 
    * 
    */ 
    protected function _onMouseEvent(event:Event) : void { 
     if (_buttonRef != null) { 
      _buttonRef.dispatchEvent(event); 
     } 
    } 
    } // end class 
    } // end package 
0

J'ai trouvé un moyen plus facile de changer le moteur de rendu de l'élément sélectionné. Celui-ci ne fonctionne que si votre élément hérite de la classe TextInput, dans Flex 4.0 ou supérieur.

Dans Flex v4.5, en ComboBase.createChildren à la ligne 1177, vous trouverez que la classe définissable pour le textInput peut être transmis en utilisant le style clé textInputClass:

// Mechanism to use MXFTETextInput. 
var textInputClass:Class = getStyle("textInputClass");    
if (!textInputClass || FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0) 
{ 
    textInput = new TextInput(); 
} 
else 
{ 
    textInput = new textInputClass(); 
} 

changer juste la valeur de cette clé le constructeur de votre combo et maintenant vous avez votre propre moteur de rendu pour le selectedItem.

public function ComboAvailableProfessor() 
{ 
    super(); 

    itemRenderer = new ClassFactory(ProfessorAvailableListItemRenderer); 
    setStyle('textInputClass', ProfessorAvailableSelectedListItemRenderer); 
} 

Enfin, vous devez lier la propriété data à la propriété selectedItem dans votre combo afin d'obtenir des données affichées.

override protected function createChildren():void 
{ 
    super.createChildren(); 

    BindingUtils.bindProperty(textInput, 'data', this, 'selectedItem', true); 
} 
Questions connexes