I'm pretty new to Flex development in Spark and wanted to clarify the best way to build components.
I had previously tried to use a binding expression to set the selected index of a ViewStack:
public class MyComponentView extends SkinnableComponent
{
public var selectedIndex:int = 0;
}
<s:Skin ...>
<mx:ViewStack selectedIndex="{hostComponent.selectedIndex}">
....
</mx:ViewStack>
</s:Skin ...>
However, this binding expression doesn't appear to work, although it does show the correct number if I move that binding expression to a s:Label.
In order to get this to work I changed the code thus:
public class MyComponentView extends SkinnableComponent
{
[SkinPart(required = "true")]
public var myStack:ViewStack;
private var _selectedIndex:int = 0;
private var _indexChanged:Boolean;
public function set selectedHistoryIndex(value:int):void
{
_selectedIndex = value;
_indexChanged = true;
invalidateProperties();
}
override protected function partAdded(partName:String, instance:Object):void
{
super.partAdded(partName, instance);
switch (instance)
{
case myStack:
_indexChanged = true;
invalidateProperties();
break;
}
}
override protected function commitProperties():void
{
super.commitProperties();
if (_indexChanged && myStack)
{
_indexChanged = false;
myStack.selectedIndex = _selectedIndex;
}
}
}
<s:Skin ...>
<mx:ViewStack id="myStack">
....
</mx:ViewStack>
</s:Skin ...>
Is this the way I'm meant to do it?
As for me, your second way is more preferable. I'd rather change some code to make it better:
public function set selectedHistoryIndex(value:int):void
{
if (_selectedIndex == value)
return;
_selectedIndex = value;
_indexChanged = true;
invalidateProperties();
}
Yes, you can bind to component's properties from skin but this way View (skins in Spark architecture is for View in MVC and host component is for M and C) has knowledge about M which isn't good. The first implementation requires this knowledge from skin.
The second implementation makes View true View (managed by M). And it is good.
Related
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...
when i use the following tree renderer class the the informtions in the tree gets chopped. Is there any solution to fix this bug. please help me.
The PLTree class is as follows:
import flash.events.Event;
import mx.events.ScrollEvent;
import mx.controls.Tree;
import mx.core.ScrollPolicy;
import mx.core.mx_internal;
import mx.events.TreeEvent;
public class PLTree extends Tree
{
private var _lastWidth:Number = 0;
private var _lastHeight:Number = 0;
public function PLTree() {
super();
horizontalScrollPolicy = ScrollPolicy.AUTO;
}
override public function get maxHorizontalScrollPosition():Number
{
return mx_internal::_maxHorizontalScrollPosition;
}
override public function set maxHorizontalScrollPosition(value:Number):void
{
mx_internal::_maxHorizontalScrollPosition = value;
dispatchEvent(new Event("maxHorizontalScrollPositionChanged"));
scrollAreaChanged = true;
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
var diffWidth:Number = measureWidthOfItems(0,0) - (unscaledWidth - viewMetrics.left - viewMetrics.right);
if (diffWidth <= 0) {
maxHorizontalScrollPosition = 0;
horizontalScrollPolicy = ScrollPolicy.OFF;
} else {
maxHorizontalScrollPosition = diffWidth;
horizontalScrollPolicy = ScrollPolicy.ON;
}
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
override protected function scrollHandler(event:Event):void
{
if (mx_internal::isOpening)
return;
// TextField.scroll bubbles so you might see it here
if (event is ScrollEvent){
super.scrollHandler(event);
invalidateDisplayList();
}
}
}
i am attaching the image file of how it looks when executed.
When surfing using google i found a suggesion to fix this bug is it the right way ?
(
Issue: Text getting chopped of at end.
Fix: change
maxHorizontalScrollPosition = diffWidth;
to
maxHorizontalScrollPosition = diffWidth + 10;
or what ever correction factor you need.
)
Kindly help me .Thanks a lot in advance.
Looking at your picture, I'd suspect the issue has nothing to do with the specific tree, and is only slightly related to the renderer. Instead, I think that when the container holding the Tree is created, it doesn't have a size, and when the Tree sizes its renderers, it gives them the wrong size. Since List-based controls don't set the actual width on renderers, choosing to set explicitWidth instead, the renderers aren't triggered into changing their size.
Check out http://www.developria.com/2009/12/handling-delayed-instantiation-1.html for more explicit details and fixes.
similar to the scroll handler in the above mentioned program. Use a mouse wheel scroll handler to handle that event as follows:
override protected function mouseWheelHandler(eventMouse:MouseEvent):void
{ if (mx_internal::isOpening)
return;
if (eventMouse is MouseEvent){
super.mouseWheelHandler(eventMouse);
invalidateDisplayList();
}
}
I am trying to override a Button class, i have a few properties which i wish directly initialise with the mxml description of the component, like :
<sl:TMyButton id="btnX" x="168" y="223" width="290" label="Button" myproperty1="10" myproperty2="101" myproperty3="4"/>
which function is triggered ( in order to override it ) when all properties with mxml description is fully initialised with their values ?
Flex components have 4 methods in protected namespace which should be overridden to solve different tasks:
createChildren() — calls one time to create and add subcomponents.
measure() called in process of lay outing to calculate components size.
updateDisplayList() has real component unscaled width and height as parameter. It is obvious this method is convenient for positioning of children.
commitProperties() is the method I suggest you to override in order to apply properties values which don't need component size to apply.
So in your case it can be updateDisplayList() or commitProperties(). I recommend you the following code snippets:
private var myproperty1Dirty:Boolean;
private var _myproperty1:String;
public function set myproperty1(value:String):void
{
if (_myproperty1 == value)
return;
_myproperty1 = value;
myproperty1Dirty = true;
// Postponed cumulative call of updateDisplayList() to place elements
invalidateDisplayList();
}
private var myproperty2Dirty:Boolean;
private var _myproperty2:String;
public function set myproperty2(value:String):void
{
if (_myproperty2 == value)
return;
_myproperty2 = value;
myproperty2Dirty = true;
// Postponed cumulative call of commitProperties() to apply property value
invalidatePropertues();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (myproperty1Dirty)
{
// Perform children placing which depends on myproperty1 changes
myproperty1Dirty = false;
}
}
override protected function commitProperties():void
{
super.commitProperties();
if (myproperty2Dirty)
{
// Apply changes of myproperty2
myproperty2Dirty = false;
}
}
Hope this helps!
Let's say you have a large number (N) of spark buttons in your app. Let's also say that your buttons all have very similar skins (size, various effects, etc) - the only difference being the specific png that they use as their BitmapImage.
Do you end up with N skin files, all differing by 1 line? Or is there a smarter way to do this while not adding a lot of code when you create the buttons in MXML (in fact, ideally, none).
Creating a custom Button with a icon SkinPart typed as a BitmapImage will allow you to use the same Skin for all buttons :
<YourCustomButton icon="#Embed('yourIconFile.png') />
CustomButton.as
public class CustomButton extends Button
{
[SkinPart(required="false")]
public var iconContainer:BitmapImage;
private var _icon:Object;
public function CustomButton()
{
super();
}
override protected function partAdded(partName:String, instance:Object):void
{
super.partAdded(partName, instance);
if (instance == iconContainer && _icon)
iconContainer.source = _icon;
}
public function get icon():Object
{
return _icon;
}
public function set icon(value:Object):void
{
if (iconContainer)
iconContainer.source = value;
_icon = value;
}
}
I want to capture a change in a property of an item as follows
myItem.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE,listener);
protected function listener(event:PropertyChangeEvent):void {
...
}
the problem i'm having is that when I assign multiple values to the "myItem" object the listened gets kicked off multiple times. for instance
If I do:
myItem.x = new_x;
myItem.y = new_y;
....
the listener kicks off everytime a change happens (after calling first line, then after calling second line..etc). How to prevent that to save processing/memory and avoid inconsistency.
You can listen for Event.COMPLETE (or create a custom event) and manually dispatch that when you've finished changing all your properties. For example:
myItem.addEventListener(Event.COMPLETE, listener);
protected function listener(e:Event):void
{
...
}
then
myItem.x = newX;
myItem.y = newY;
myItem.dispatchEvent(new Event(Event.COMPLETE));
You can override the setter of your component and dispatch a custom event
You can use mx.utils.ObjectProxy class and by overriding its protected "setupPropertyList" method you can specify by youself what properties will trigger PropertyChangeEvent.PROPERTY_CHANGE event
You could do that the "hackish" way. Example follows...
BindingObject.as:
package bindings
{
import mx.core.EventPriority;
import mx.events.PropertyChangeEvent;
import mx.events.PropertyChangeEventKind;
[Bindable]
public class BindingObject
{
private var inEditMode : Boolean = false;
public function BindingObject()
{
this.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChange, false, EventPriority.BINDING - 1);
}
private function onPropertyChange(event : PropertyChangeEvent) : void
{
if (inEditMode)
event.stopImmediatePropagation();
}
public function beginEdit() : void
{
inEditMode = true;
}
public function endEdit() : void
{
inEditMode = false;
this.dispatchEvent(new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE, false, false, PropertyChangeEventKind.UPDATE));
}
public var myX : int = 0;
public var myY : int = 0;
}
}
testapplication.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="onCreationComplete();">
<mx:Script>
<![CDATA[
import mx.events.PropertyChangeEvent;
import bindings.BindingObject;
[Bindable]
private var something : BindingObject = new BindingObject();
private function onCreationComplete() : void
{
something.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, listener);
something.beginEdit();
trace("Begun edit");
trace("changing myX");
something.myX = 3;
trace("changed myX");
trace("changing myY");
something.myY = 6;
trace("changed myY");
trace("Ending edit");
something.endEdit();
trace("Ended");
}
private function listener(event : PropertyChangeEvent) : void
{
trace("in my listener");
}
private function myX(val : int) : String
{
trace("in myX");
return val.toString();
}
private function myY(val : int) : String
{
trace("in myY");
return val.toString();
}
]]>
</mx:Script>
<mx:Label text="{myX(something.myX)}" />
<mx:Label text="{myY(something.myY)}" />
</mx:Application>
So, as you can see, I've added an event listener in the class to be bound, that will be triggered right after the generated binding listeners (note the priority is BINDING - 1) and in there I stop the propagation of the event. Which means whatever listeners still need to be executed, won't. The endEdit method will dispatch the PropertyChange event that will trigger your listener.
Now, assuming that you do something costly in your listener this should solve your problem.