Simple and straight forward.
I extended a mx.controls.TextInput to create a custom component with a different behavior.
I'm trying to set the text property on the keyDownHandler(), and for some reason it doesn't work as I expected. The text on the component just kinda ignore the change.
I'm using Flex 3.6.
Down here is a simple example code that explains what's going on:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:customcomponent="com.test.customcomponent.*">
<customcomponent:TextTest x="20" y="20"/>
</mx:Application>
And below de AS class:
package com.test.customcomponent
{
import flash.events.KeyboardEvent;
import mx.controls.TextInput;
public class TextTest extends TextInput
{
public function TextTest()
{
super();
}
override protected function keyDownHandler(event:KeyboardEvent):void{
text = "lol. It doesn't work";
}
}
}
You need to prevent the default key down event.
override protected function keyDownHandler(event:KeyboardEvent):void{
text = "lol. It doesn't work";
event.preventDefault();
event.stopImmediatePropagation();
event.stopPropagation()
}
In order to be able to prevent the default handling, you have to have a high enough priority on your handler (which keyDownHandler() does not have). This means you need to register your own method with priority > 0.
You can try like this:
public function MyTextInput() {
addEventListener(KeyboardEvent.KEY_DOWN, yourHandler,
false, EventPriority.BINDING, true);
...
}
private function yourHandler(event : KeyboardEvent) : void {
// stop further handling
event.preventDefault();
event.stopImmediatePropagation();
event.stopPropagation();
// do your work here
text = ...;
}
Related
I would like to programmatically change a selected item, in a tree or list, to the item currently "marked/focused" under the mouse pointer .
I'm working with an Flex Air standalone application.
I was thinking in the lines of:
myTree.selectedItem = EVENT.TARGET (where EVENT could be a mouseover/rightclick/rollOver event, and TARGET should be the node/item currently under the mouse pointer).
Is there a way of doing this (or in any other way)?
Ahh, and i want to do it without left clicking ;-)
Thank you in advance,
Sebastian
I found this interesting enough so I am asking if this is the easiest way to achieve this. First off, instead of the list, you need to add the rollOver-listener to the ItemRenderer, not to the list itself (as the event.target and event.currentTarget will just show your list).
So lets create a custom ItemRenderer and add a rollOver listener
<xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="true" height="20" rollOver="itemrenderer1_rollOverHandler(event)">
<fx:Script>
<![CDATA[
protected function itemrenderer1_rollOverHandler(event:MouseEvent):void
{
this.dispatchEvent(new CustomEvent(CustomEvent.SELECT_ITEM, data, true));
}
]]>
<s:Label id="label1" text="{data.label}"/>
</s:ItemRenderer>
You need to somehow get the value of the selected item (which is the data on the itemRenderer) so I created a CustomEvent-class just to do so.
package
{
import flash.events.Event;
public class CustomEvent extends Event
{
public var selectedItem:Object;
public static const SELECT_ITEM:String = "selectItem";
public function CustomEvent(type:String, selectedItem:Object, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.selectedItem = selectedItem;
}
}
}
then I added a eventListener to the main class and set the list.selectedItem property accordingly:
//for the main MXML initializer:
this.addEventListener(CustomEvent.SELECT_ITEM, rollOverChangeSelected);
//and the function:
protected function rollOverChangeSelected(ce:CustomEvent):void{
list.selectedItem = ce.selectedItem;
}
Another way: bindable variable
The list:
s:List id="list" allowMultipleSelection="true" selectionColor="red" rollOverColor="red" itemRenderer="customItemRenderer" selectedItem="{_rollOverSelectedItem}">
The variable and set / get methods:
[Bindable] public var _rollOverSelectedItem:Object;
public function get rollOverSelectedItem():Object
{
return _rollOverSelectedItem;
}
public function set rollOverSelectedItem(value:Object):void
{
_rollOverSelectedItem = value;
}
and the ItemRenderer's rollOver-method:
protected function itemrenderer1_rollOverHandler(event:MouseEvent):void
{
this.parentApplication.rollOverSelectedItem = data;
}
What is the best/proper way?
I have a flex application and a papervision BasicView. I would like to add a new flex UIComponent (such as a button) from within the papervision class. I have posted the full example code below. It works but I would like to be able to accomplish my goal without the "(this.parent.parent as Group).addElement(button);" line.
<!--Application 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/mx" minWidth="50" minHeight="50"
creationComplete="Init()" applicationComplete="Start()">
<fx:Script>
<![CDATA[
import mx.core.UIComponent;
import spark.components.Button;
public var start:QuickStart;
public function Init():void
{
start = new QuickStart();
var uicomp:UIComponent = new UIComponent();
addElement( uicomp );
uicomp.addChild( start );
}
public function Start():void
{
start.GoTime();
}
]]>
</fx:Script>
</s:Application>
//QuickStart.as
package{
import org.papervision3d.view.BasicView;
public class QuickStart extends BasicView
{
public function QuickStart()
{
super(500, 500, true, true);
}
public function GoTime():void
{
var button:Button = new Button;
//this is the offending line
(this.parent.parent as Group).addElement(button);
}
}
}
The version I have does work so please excuse any minor typos.
Logically you would dispatch an event inside your BasicView, listen for it in your main application, and create the button from up there. In a prefect OOP world, every class should be a black box sending events :)
I have a prompt string to be displayed in my combobox - this needs to be displayed in italics. When user makes any selection from the list - i need to change the style of the displayed content.
My css file:
.promptStyle
{
fontStyle: italic;
}
ComboBox.withPrompt
{
color: #FF0000;
fontWeight: normal;
textInputStyleName: promptStyle;
}
.regularStyle
{
fontStyle: normal;
}
ComboBox.withoutPrompt
{
color: black;
fontWeight: normal;
textInputStyleName: regularStyle;
}
My MXML file:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
minWidth="955" minHeight="600" initialize="init()">
<mx:Script>
<![CDATA[
[Bindable]
private var content:Array=new Array("Red", "Blue", "Green");
private function init():void {
StyleManager.loadStyleDeclarations("combos/combo_style.swf");
}
private function changeStyle():void {
var index:int = promptBox.selectedIndex;
if(index != -1)
promptBox.setStyle("styleName","withoutPrompt");
}
]]>
</mx:Script>
<mx:ComboBox id="promptBox" prompt="Select a color" dataProvider="{content}"
styleName="withPrompt" change="changeStyle()"/>
</mx:Application>
I am able to see the style change happening because the color changes; but the change specific to textInputStyleName does not get applied. Any help would be appreciated.
You should assign the style to the internal TextInput subcomponent, but for this you have to derive your own PromptingComboBox to access the protected textInput property.
I think the following class does basically what you want and should give you an idea:
public class PromptingComboBox extends ComboBox implements IFactory
{
private var _dropDown: List = null;
public function PromptingComboBox()
{
super.dropdownFactory = this;
}
public function newInstance(): *
{
_dropDown = new List();
_dropDown.addEventListener(ListEvent.CHANGE, onChangeDropDownList);
return _dropDown;
}
override protected function createChildren():void
{
super.createChildren();
this.textInput.setStyle("fontStyle", "italic");
}
private function onChangeDropDownList(event: Event): void
{
this.textInput.setStyle("fontStyle", "");
}
}
Thank you :) I was able to get it to work by sub-classing ComboBox as you suggested. Updating textInputStyleName in my css would have been a much cleaner solution for me since this is a huge existing application and now i have to get into several MXMLs and change the control to use the custom control instead - im guessing this is a bug in flex?
Anyways, thanks for your help!
I need to dynamically bind properties of components created at runtime. In this particular case please assume I need to use bindProperty.
I don't quite understand why the following simplistic test is failing (see code). When I click the button, the label text does not change.
I realize that there are simpler ways to go about this particular example using traditional non-dynamic binding, but I need to understand it in terms of using bindProperty.
Can someone please help me understand what I'm missing?
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:ns1="Tools.*" minWidth="684" minHeight="484" xmlns:ns2="*" creationComplete="Init();">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.binding.utils.*;
public var Available:ArrayCollection=new ArrayCollection();
public function get Value():String {
return (Available.getItemAt(0).toString());
}
public function Init():void {
Available.addItemAt('Before', 0);
BindingUtils.bindProperty(Lab, 'text', this, 'Value');
}
public function Test():void {
Available.setItemAt('After', 0);
}
]]>
</mx:Script>
<mx:Label x="142" y="51" id="Lab"/>
<mx:Button x="142" y="157" label="Button" click="Test();"/>
</mx:WindowedApplication>
Thanks in advance.
As mentioned by Glenn, you need to add [Bindable] tag on Value.
Also, you haven't defined a setter for the property. Data binding is invoked only when the corresponding setter is called. The flow is something like: you call the setter - Flex updates the data by calling the getter.
[Bindable]
public function get value():String {
return (Available.getItemAt(0).toString());
}
public function set value(v:String):void {
Available.setItemAt(v, 0);
}
public function init():void {
Available.addItemAt('Before', 0);
BindingUtils.bindProperty(Lab, 'text', this, 'Value');
}
public function iest():void {
value = "After";
}
Note that I've changed names of functions and properties to lowercase as per the normal convention. InitialCaps are used only for class names.
I never use the BindingUtils, but my first guess is that you're missing the [Bindable] tag on "Value".
Here's the Class:
package fnc {
import mx.containers.Canvas;
public class Deck extends Canvas {
protected var _chipCount:int;
public function Deck(chipCount:int) {
/* Associate some chips with this deck */
_chipCount = chipCount;
}
public function get chipCount():int {
return _chipCount;
}
}
}
Here's the MXML:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:ns1="fnc.*">
<ns1:Deck horizontalCenter="0" verticalCenter="0">
</ns1:Deck>
</mx:Application>
Running this application gets this error:
ArgumentError: Error #1063: Argument count mismatch on fnc::Deck(). Expected 1, got 0.
at mx.core::Container/createComponentFromDescriptor()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\Container.as:3579]
at mx.core::Container/createComponentsFromDescriptors()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\Container.as:3493]
at mx.core::Container/createChildren()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\Container.as:2589]
at mx.core::UIComponent/initialize()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:5370]
at mx.core::Container/initialize()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\Container.as:2526]
at mx.core::Application/initialize()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\Application.as:846]
at Practice/initialize()[C:\Documents and Settings\LocalService\My Documents\Flex Builder 3\Practice\src\Practice.mxml:0]
at mx.managers::SystemManager/http://www.adobe.com/2006/flex/mx/internal::childAdded()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:2009]
at mx.managers::SystemManager/initializeTopLevelWindow()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:3234]
at mx.managers::SystemManager/http://www.adobe.com/2006/flex/mx/internal::docFrameHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:3064]
at mx.managers::SystemManager/docFrameListener()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:2916]
Adding chipCount="0" to the MXML like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:ns1="fnc.*">
<ns1:Deck chipCount="0" horizontalCenter="0" verticalCenter="0">
</ns1:Deck>
</mx:Application>
Gets this compile error:
Severity and Description Path Resource Location Creation Time Id
Property 'chipCount' is read-only. Practice/src Practice.mxml line 3 1242656555328 26
How do I specify the initial chip count?
You're not able to pass parameters to the constructor of an element when you declare it in MXML. You'll need an empty contructor and then have a property called ChipCount. Your code will also have to be updated to handle ChipCount not being set (or set to 0).
My recommendation would be to change Deck to something like this:
public class Deck extends Canvas {
protected var _chipCount:int;
public function Deck() {
_chipCount = 0; // Default ChipCount and wait for it to be set.
}
public function get chipCount():int {
return _chipCount;
}
public function set chipCount(value:int):int {
// Add logic here to validate ChipCount before setting.
_chipCount = value;
}
}
In answer to brd6644 comment :
package
{
import mx.containers.Canvas;
public class Deck extends Canvas
{
protected var _chipCount:int;
private var chipCountChanged:Boolean;
public function Deck()
{
super();
}
public function set chipCount(value:int):void
{
if (chipCount != value)
{
_chipCount = value;
chipCountChanged = true;
invalidateProperties();
//call invalidateSize() if changing chipCount value may change the size of your component
//call invalidateDisplayList() if changing chipCount value need a redraw of your component
}
}
public function get chipCount():int
{
return _chipCount;
}
override protected function commitProperties():void
{
super.commitProperties();
if (chipCountChanged)
{
chipCountChanged = false;
//here update properties that change because of chipCount new value.
}
}
}
}
I believe that if you're extending a UIComponent you cannot pass arguments to the constructor - you'd have to find another way of setting chip count. I'd suggest listening for the initialize event, and set it then:
<?xml version="1.0" encoding="utf-8"?>
<mx:Script>
public function setChipCount():void{
myDeck.chipCount = 0;
}
</mx:Script>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:ns1="fnc.*">
<ns1:Deck id="myDeck" initalize="setChipCount()" horizontalCenter="0" verticalCenter="0">
</ns1:Deck>
</mx:Application>