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)
Related
The following code is working:
item renderer:
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(editMode)
dispatchEvent(new Event("editLayoutChange"));
}
Datagrid (event handler function for "editLayoutChange" - "viewport" is the AdvancedDataGrid):
public function editLayoutChange(event : Event) : void {
var viewportTopLeft : Point = viewport.localToGlobal(new Point(0,0));
var viewportBottom : Number = viewportTopLeft.y + viewport.height;
var rendererTopLeft : Point = Container(event.target).localToGlobal(new Point(0,0));
var rendererBottom : Number = rendererTopLeft.y + Container(event.target).getLayoutBoundsHeight();
var offset : Number = rendererBottom - viewportBottom;
if(offset > 0) {
// Typical item height is 21px
var itemHeight : Number = 21;
matrix.verticalScrollPosition += Math.ceil(offset / itemHeight);
}
}
But I'm not sure that overriding updateDisplayList is the cleanest implementation as it's firing quite a bit. I tried dispatching the "editLayoutChange" event in response to the "resize" event on the item renderer, but I'm seeing really erratic behavior. Is there a better choice for dispatching this event than updateDisplayList?
Edit - I'm listening on updateDisplayList because the renderer will change size (grow) when it enters edit mode, and can expand dynamically during editing.
The usual way to do this kind of thing is to dispatch from commitProperties -
In your renderer:
private var _editModeEntered:Boolean = false;
private var _editMode:Boolean;
public function set editMode(value:Boolean):void {
_editMode = true;
_editModeEntered = true;
invalidateProperties();
}
// probably want a getter for editMode too
override protected function commitProperties():void {
super.commitProperties();
if (_enteredEditMode) {
dispatchEvent(new Event("editLayoutChange"));
_enteredEditMode = false;
}
}
This ensures the event is dispatched only when you have changed editMode explictly.
You can use Advanced Data Grid public API scrollToIndex(index) to scroll to the row
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.
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!
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"
/>
in FlashBuilder 4 beta 2, I've subclassed mx.containers.Panel, adding a public method to hide the titleBar:
public function hideTitleBar(): void {
if (null != this.titleBar){
this.titleBar.visible=false;
}
}
I step through the code and see that the method is being invoked and that titleBar exists, and then step through the UIComponent classes and that all looks ok too: the component is initialized and $visible is being set to false. Yet the gray bar across the top of the panel remains. I want to eliminate that bar and would be grateful for some tips on how to do that.
What I ended up doing is to set the style headerHeight to 0
this.setStyle("headerHeight", 0);
The updateDisplayList method of the Panel sets titleBar.visible to true. Subclass the Panel class, override that method, and set it to false inside that. Don't forget to call super.updateDisplayList
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
titleBar.visible = true;
}