I am creating a custom TextInput component that will define an "error" state. I have extended the TextInput class to change the state to "error" if the errorString property's length is greater than 0. In the skin class, I have defined an "error" state, and added some logic to detect the size and position of the error icon. However, if I have this code at the same time I use the "includeIn" property in the bitmap image tag, I get a design view error. If I either A) Only include that code with no "includeIn" property set, it works or B) dont include the code to set the icon size and position and only use the "includeIn" property, it works. Any ideas what could be causing the design view problem when I use both the "includeIn" property and the icon size/position code at the same time?
TextInput Class:
package classes {
import spark.components.TextInput;
public class TextInput extends spark.components.TextInput {
[SkinState("error")];
public function TextInput() {
super();
}
override public function set errorString( value:String ):void {
super.errorString = value;
invalidateSkinState();
}
override protected function getCurrentSkinState():String {
if (errorString.length>0) {
return "error";
}
return super.getCurrentSkinState();
}
}
}
TextInput Skin File:
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
//THIS IS THE CODE THAT SEEMS TO BE CAUSING THE PROBLEM
if(getStyle("iconSize") == "large") {
errorIcon.right = -12;
errorIcon.source = new errorIconLg();
} else {
errorIcon.right = -5;
errorIcon.source = new errorIconSm();
}
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
</fx:Script>
<s:states>
<s:State name="normal"/>
<s:State name="disabled"/>
<s:State name="error"/>
</s:states>
//If I remove the problem code above or if I take out the includeIn
//property here, it works
<s:BitmapImage id="errorIcon" verticalCenter="0" includeIn="error" />
</s:SparkSkin>
In Flex 4, a component is only instantiated when the state it's in is activated. Therefore, when the skin first loads, errorIcon is a null reference. Its instantiation is deferred until the error state becomes active. In order to instantiate it immediately, you set the itemCreationPolicy="immediate" property on it.
<s:BitmapImage id="errorIcon"
source="../images/error.png"
itemCreationPolicy="immediate"
/>
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 need to be able to show a toolTip on disabled checkboxes. A solution I've seen here on stackoverflow and other places is to just wrap the checkbox in a Group and give the Group the toollTip. This works, but I'm trying to do this generically.
I want to be able to set a property on a custom Checkbox component and at that point wrap the Chexbox in a Group that has the toolTip set.
My problem is, I can't figure out how to add the Checkbox to a Group component at run time in the Checkbox ActionScript code. I've tried adding a showDisabledToolTip property to the Checkbox Class and when that is set do something like this:
var parent = this.parent;
var gp:Group = new Group();
gp.toolTip = this.toolTip;
gp.addElement(this);
if(parent is Group) {
parent.addElement(gp);
} else {
parent.addChild(gp);
}
My main problem at that point is this.parent is null. Besides that though, I don't even know if this will really work.
Help is appreciated. Thanks!
i came up with the solution to extend the CheckBox class and create a new CheckBoxSkin with 2 new SkinStates inside (disabledWithTooltip and disabledWithTooltipSelected)
The extended Checkbox class adds a new disabledWithTooltip property and overrides the getCurrentSkinState method and the mouseEventHandler from ButtonBase
Custom CheckBox class
package components
{
import flash.events.Event;
import flash.events.MouseEvent;
import mx.events.FlexEvent;
import spark.components.CheckBox;
[SkinState (disabledWithToolTip)]
[SkinState (disabledWithToolTipSelected)]
public class CustomCheckBox extends CheckBox
{
private var _disabledKeepToolTip:Boolean = false;
public function CustomCheckBox()
{
super();
this.addEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete, false, 0, true);
}
protected function onCreationComplete(ev:FlexEvent):void {
//_storedState = this.currentState;
}
protected override function getCurrentSkinState():String {
if(!_disabledKeepToolTip)
return super.getCurrentSkinState();
else {
if(!selected)
return "disabledWithToolTip";
else
return "disabledWithToolTipSelected";
}
}
protected override function mouseEventHandler(event:Event):void {
var skinState:String = getCurrentSkinState();
if(skinState != "disabledWithToolTip" && skinState != "disabledWithToolTipSelected") {
super.mouseEventHandler(event);
}
}
[Bindable]
[Inspectable(category="General", enumeration="true,false", defaultValue="true")]
public function get disabledKeepToolTip():Boolean {
return _disabledKeepToolTip;
}
public function set disabledKeepToolTip(value:Boolean):void {
_disabledKeepToolTip = value;
this.invalidateSkinState();
}
}
}
Create a new Skin based on (spark) CheckBoxSkin and change the hostcomponent in the metadata
[HostComponent("components.CustomCheckBox")]
and add two new skinStates
<s:State name="disabledWithToolTip" stateGroups="disabledStates" />
<s:State name="disabledWithToolTipSelected" stateGroups="disabledStates, selectedStates" />
Usage e.g.
<s:HGroup>
<components:CustomCheckBox id="custom_chk" label="KeepTooltipCheckbox" skinClass="skins.CustomCheckBoxSkin" toolTip="See this tooltip"/>
<s:CheckBox id="enable_chk" label="enable/disable" change="{custom_chk.disabledKeepToolTip = enable_chk.selected}"/>
</s:HGroup>
You have to adapt your own package structure if it's different...
In version 4 there is a Flex itemEditEnd (in Datagrid) event, but does not exist in Flex 4.5, itemEditEnd this event has been replaced by what event?
The MX DataGrid should not have changed; and according to the documentation, the itemEditEnd is still there.
However, Flex 4.5 introduced a DataGrid based on the Spark Architecture. This is a complete new component, and has many differences from the MX DataGrid.
You might look at the gridItemEditorSessionSave event as an alternate.
As per http://opensource.adobe.com/wiki/display/flexsdk/Data+Grid+Editing
I tried to use:
override public function save():void
{
//data.dataField = value;
}
But I got error: "Incopatible override"
Any success at your side?
FIX,change void to Boolean, than in save() you can do pretty much the same stuff as in itemEditEnd in MX DataGrid:
override public function save():Boolean
{
data.dataField = value;
return true; //to save data to dataprovider
}
Example:
<s:GridItemEditor>
<s:TextInput id="valueDisplay" width="100%"/>
<fx:Script>
<![CDATA[
override public function get value():Object
{
return valueDisplay.text;
}
override public function set value(newValue:Object):void
{
valueDisplay.text = newValue.toString();
}
override public function save():Boolean
{
data.dataField = value;
return true;
}
]]>
</fx:Script>
</s:GridItemEditor>
I have the following MXML:
<mx:Script>
var someBoolean:Boolean = determineSomeCondition();
</mx:Script>
....
<foo:MyComponent somePropertyExpectingIDataRenderer="{
someBoolean
? new Component1ThatImplementsIDataRenderer()
: new Component2ThatImplementsIDataRenderer()
}">
</foo:MyComponent>
I have also overridden the createChildren() function:
override protected function createChildren():void {
super.createChildren();
//do something with somePropertyExpectingIDataRenderer
}
My problem is: createChildren() is being called before the squiggly bracket logic is being evaluated, so in createChildren(), somePropertyExpectingIDataRenderer is null.
However if I pass the component via MXML like this:
<foo:MyComponent>
<bar:somePropertyExpectingIDataRenderer>
<baz:Component1ThatImplementsIDataRenderer/>
</bar:somePropertyExpectingIDataRenderer>
</foo:MyComponent>
Then when createChildren() is called, that same property isn't null. Is this supposed to happen and if so, what other workarounds should I consider?
You have to wait until your component goes through the first invalidation phase in order to get access to the default value setted in your MXML. This happens just after createChildren() has been called, when the initialize event of your component has been dispatched.
Here is how I would do it :
public function set myProperty(value:IDataRenderer):void
{
if (_myProperty != value)
{
myPropertyChanged = true;
_myPropert = value;
invalidateDisplayList();
}
}
protected override function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
...
if (myPropertyChanged )
doWhateverYouNeedToDo();
}
(Of course, this example supposes changing your property needs a redrawing)
Can't seem to bind to data from within a custom component. I've tried BindUtilis and {} but can't seem to fathom it out. Here's what I've got:
I have a class DataModel which has been made bindable
Within Mainn.mxml I have two components: DataGrid (used for testing) & CustomComponent (which extends Canvas)
When the data within DataModel.somelist is updated the DataGrid reflects the changes but the CustomComponent doesn't appear to.
I was expecting to see the trace (CustomComponent.dataProvider) fired whenever this._dataModel.itemList is changed. What am I doing wrong?
Main.mxml looks something like this:
<mx:Script>
<![CDATA[
import model.DataModel;
[Bindable]
private var _dataModel:DataModel = DataModel.getInstance();
]]>
</mx:Script>
<mx:VBox width="100%" height="100%">
<views:ItemDisplayList width="100%" height="300" id="idl" >
<views:dataProvider>
{this._dataModel.itemList}
</views:dataProvider>
</views:ItemDisplayList>
<mx:DataGrid id="dg" width="100%" height="300" >
<mx:dataProvider>
{this._dataModel.itemList}
</mx:dataProvider>
</mx:DataGrid>
</mx:VBox>
The CustomComponent has this AS class:
package code{
import model.DataModel;
import mx.containers.Canvas;
public class CustomComponent extends Canvas{
[Bindable]
private var _dataModel:DataModel = DataModel.getInstance();
private var _dataProvider:ArrayCollection ;
public function CustomComponent(){
super();
_dataProvider = new ArrayCollection();
trace("CustomComponent start");
}
public function get dataProvider() : ArrayCollection {
trace("get dataProvider");
return _dataProvider;
}
public function set dataProvider(value: ArrayCollection) : void {
trace("set dataProvider");
this._dataProvider = value;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
...
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number ) : void{
trace("updateDisplayList");
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
}
}
Are you resetting the dataProvider in your custom component, or by updating you mean adding/removing an item from dataModel.itemList?
If you're adding an item to dataModel.itemList, then your custom component wont update but the DataGrid will. This is because in the DataGrid (maybe it's in the core ListBase component in Flex?), when you set dataProvider, it adds an event listener for CollectionEvent.COLLECTION_CHANGE. When it hears that (when something in your ArrayCollection/IList changes, from remove/add/refresh/etc), it runs a few methods to update the DataGrid's itemRenderers. You'll have to code that manually if you create your own dataProvider property.
If you set the dataProvider explicitly on your custom component (custom.dataProvider = myArrayCollection), it will update. But that's not the most efficient thing to do. I recommend extending one of the List classes that has already implemented a dataProvider property, it's a lot to tackle.
Check out the source for the mx ListBase's dataProvider method to see what they did.
Hope that helps,
Lance