Setting a skinState of a button in AS3 - actionscript-3

I want to set (manually) the skinState (for example 'disabled') of a button (that I skinned) in ActionScript.
For example:
I have a button skin with hostComponent: components.backend.btnMenuBakComp
The button skin has the default button states (up, over, down, ...), but I want to set one of this skinStates in ActionScript.
For example:
subMenu.btnDashboard.currentState = "disabled";
This doesn't work because the state "disabled" is not known in the component (it is only known in the skinState of btnDashboard).
How can I fix this?
Is there another solution then load a new skinClass?
Thanks

Quick and dirty
You can access the skin of any component and just set its state directly:
subMenu.btnDashboard.skin.currentState = "disabled";
That is however not a very clean way to do it. You are telling a Skin class directly what to do and completely bypassing the host component. Hence the host component has no idea of the changes that were made to its skin.
The proper way
A cleaner way to approach this is to expose a property on the host component and then tell the skin to adjust itself to possible changes by overriding the getCurrentSkinState() method.
You could for instance create a property 'enabled' and then tell the skin to update its state by calling invalidateSkinState() whenever 'enabled' is being set.
public function set enabled(value:Boolean):void {
_enabled = value;
invalidateSkinState();
}
Calling invalidateSkinState() will make the skin call getCurrentSkinState() in the next render cycle. This method will then look something like this:
override protected function getCurrentSkinState():String {
return _enabled ? "normal" : "disabled";
}
Do note that since you are skinning a Button (or a subclass of it) all that I've written here is already baked into that component. So the answer to your question might be as simple as : "just set the 'enabled' property to true.
subMenu.btnDashboard.enabled = true;

Related

MVVMCross - display view inside view

I cannot seem to find any simple examples of this.
I have a WPF UI that I wish to display a view as a child control within another view. The MvxWpfView inherits from UserControl so it should be possible, however I cannot seem to work out how to do the binding.
I get a BindingExpression path error, as it cannot find ChildView property in my ParentViewModel.
So how do I bind a view to control content?
Firstly it's possible that you just need to add the BViewModel you want displayed on AView as a property on ViewModelA
E.g.
public class AViewModel: MvxViewModel
{
public BViewModel ChildViewModel
{
get;set;//With appropriate property changed notifiers etc.
}
}
Then inside AView you just add a BView, and you can set the datacontext of BView as follows:
<UserControl DataContext="{Binding ChildViewModel}"/>
However, if you want something more flexible (and you want the presentation handled differently for different platforms) then you will need to use a Custom Presenter
Inside your setup.cs you override CreateViewPresenter:
protected override IMvxWpfViewPresenter CreateViewPresenter(Frame rootFrame)
{
return new CustomPresenter(contentControl);
}
Now create the class CustomPresenter you need to inherit from an existing presenter. You can choose between the one it's probably using already SimpleWpfPresenter or you might want to go back a bit more to basics and use the abstract implementation
The job of the presenter is to take the viewmodel you have asked it to present, and display it "somehow". Normally that mean identify a matching view, and bind the two together.
In your case what you want to do is take an existing view, and bind a part of it to the second view mode.
This shows how I have done this in WinRT - but the idea is very similar!
public override void Show(MvxViewModelRequest request)
{
if (request.ViewModelType == typeof (AddRoomViewModel))
{
var loader = Mvx.Resolve<IMvxViewModelLoader>();
var vm = loader.LoadViewModel(request, new MvxBundle());
if (_rootFrame.SourcePageType == typeof (HomeView))
{
HomeView view = _rootFrame.Content as HomeView;
view.ShowAddRoom(vm);
}
}
else
{
base.Show(request);
}
}
So what I'm doing is I'm saying if you want me to present ViewModel AddRoom, and I have a reference to the HomeView then I'm going to just pass the ViewModel straight to the view.
Inside HomeView I simply set the data context, and do any view logic I may need to do (such as making something visible now)
internal void ShowAddRoom(Cirrious.MvvmCross.ViewModels.IMvxViewModel vm)
{
AddRoomView.DataContext = vm;
}
Hopefully that makes sense! It's well worth putting a breakpoint in the show method of the presenters so you get a feel how they work - they are really simple when you get your head around them, and very powerful.

Defining a "pass-through" style in Actionscript

I have a custom component called ButtonPanel written in Actionscript. Basically it's just a panel that displays a mx:ButtonBar in the upper right of the mx:Panel title bar and responds to the clicks of the buttons in the bar.
A ButtonBar has three styles available for the buttons: buttonStyleName, firstButtonStyleName, and lastButtonStyleName. I want to write these styles for the ButtonPanel so that if it is declared as such:
<comp:ButtonPanel buttonStyleName="myButtonStyle" ... />
then the ButtonPanel will pass the style through and set the corresponding style of the ButtonBar.
I really have no clue where to start on this because I've never messed with defining styles. Can someone help?
What you refer to as "pass-through" styles are actually called inheriting styles. The solution to your question is in fact quite simple.
You use the style metadata on your custom component to declare that ButtonPanel has a stylename called 'buttonStyleName':
[Style(name="buttonStyleName", inherit="yes")]
public class ButtonPanel extends Panel {
....
}
Note the 'inherit' flag which is set to true: this will make sure that any component inside your custom Panel that has the same style will inherit the value that you've given to that style at the Panel level.
Setting this metadata will make sure that FlashBuilder will suggest buttonStyleName as a style and not as a property (as would happen with Sam's solution).
Edit: already defined styles
I didn't realize at first that you were referring to the mx ButtonBar (as it's not explicitly mentioned). The reason this is not working for you is that mx:ButtonBar already has these styles defined as not inheriting. Look at the source code:
[Style(name="firstButtonStyleName", type="String", inherit="no")]
[Style(name="buttonStyleName", type="String", inherit="no")]
[Style(name="lastButtonStyleName", type="String", inherit="no")]
Because of this the compiler will complain when you try to override that definition in your custom Panel, because it simply wouldn't know which of the contradictory instructions to pick. So we'll have to do a little more work if you want to stick with mx:ButtonBar.
First define the styles on ButtonPanel exactly as they are defined in mx:ButtonBar so they have the same signature (you can just copy/paste the three lines above). This will shut up the compiler but the styles won't be inherited anymore, right?
So we'll have to pass them on manually: in your custom Panel skin, override the updateDisplayList() method and - assuming that the ButtonBar's id is 'buttonBar' - add the following:
private const buttonStyles:Array = [
"firstButtonStyleName",
"buttonStyleName",
"lastButtonStyleName"
];
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
if (buttonBar)
for each (var buttonStyle:String in buttonStyles)
buttonBar.setStyle(buttonStyle, getStyle(buttonStyle));
//some other code
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
This will take the styles from the host Panel and pass them on to the ButtonBar.
In order to pass these styles through, you only have to store them as string variables, and then pass them to the internal ButtonBar.
<mx:ButtonBar ... buttonStyleName="{buttonStyleName}" ... />
[Bindable]public var buttonStyleName:String;
If these two lines of code don't explain it fully to you, let me know and I can flesh out my example.

Bindable property change event in Flex

Can anyone please help me solve this mystery:
I've got a component called Box.as that has following two properties, and have their getters & setters defined:
private var _busy:Boolean;
private var _errorMessage:String;
In MXML that uses this component I define it like this:
<components:Box skinClass="skins.components.BoxSkin"
busy="{presenter.boxBusy}"
errorMessage="{presenter.boxErrorMessage}"/>
Where presenter variable is defined here in MXML and a Presenter class has boxBusy and boxErrorMessage variables defined as bindable property change events:
[Bindable(event="propertyChange")]
function get boxBusy():Boolean;
function set boxBusy(value:Boolean):void;
[Bindable(event="propertyChange")]
function get boxErrorMessage():String;
function set boxErrorMessage(value:String):void;
PROBLEM is that whenever I change boxErrorMessage for the presenter, I see the affect in MXML but nothing happens at all when I change boxBusy. Is there something extra I need to do with boolean variable?
Thanks a lot in advance.
You should omit the (event="propertyChange") specification from your [Bindable] metadata tags on both boxBusy and boxErrorMessage. Also, make sure your get/set methods are declared public.
So, the property, boxBusy, would look something like this:
[Bindable]
public function get boxBusy():Boolean { return _busy; }
public function set boxBusy(value:Boolean):void { _busy = value; }
When you qualify [Bindable] with (event="..."), you're telling Flex, "I will dispatch the named event whenever the binding should be updated".
If you omit the event specification, then flex assumes that the event is named propertyChange. But that's not all it does. It also automatically "wraps" your setter with generated code that transparently dispatches a 'propertyChange' event any time the setter is used to modify the value. This is described in more detail here, at adobe livedocs.
So... by explicitly specifying (event="propertyChange"), you disable flex's default behavior. Even though you're using the default event name, flex will not generate the wrapper code -- instead, it will expect you to dispatch the event from your code, at the appropriate time.
I imagine that your boxErrorMessage property appears to be working, because some other [Bindable] property of your class is changing in the same pass -- thus dispatching propertyChange, and causing your boxErrorMessage binding to update as a side-effect.
It is completely possible that if you are setting busyBox to true the first time the setter is getting called but it will not get called again if you again try to set to true. The code that is by the flex compiler when you use the [Bindable] tag will adds a check to see if you are setting the new value to what the getter will currently will return. If that is the cause it isn't called.
If you were to oscillate between true and false it would get called every time because the new value differs from the current value. But setting it to true-true-true-true-false would only result in it getting called the first time to set to your and the last time to set to false.

how do I trigger an Item renderer create children

I have a TileList with a custom item renderer.
I need to change the children of the ItemRenderer when the dataprovider changes for the TileList.
Currently,
override protected function createChildren():void{
Works fine with the inital data, but when the data changes to a different structure I need to recreate the children somehow.
I image there has to be a way to listen to the TileList for a data change from inside the item renderer, but how? Or is that even the best route to go?
maybe this will do the job :
override public function set data( value : Object ) : void
{
super.data = value;
// things to happend when data is changed ...
}
aswell maybe updateDisplayList() shall need to be overrided aswell to match the changes with the visual components ( if there is ).

as3 Access to undefined property?

Can someone help me to find out why I'm getting the error message "Access to undefined property: removeChild(goBack)" on the following snipped?
BTW, this is for flash CS4
function nameOfFunction() {
var goBack:backButton_mc = new backButton_mc();
goBack.x = 10;
goBack.y = 700;
goBack.back_text.text = myXML.*[buildingName].NAME;
goBack.name = "backBtn";
goBack.buttonMode = true;
addChild(goBack);
goBack.addEventListener(MouseEvent.CLICK, anotherFunction);
}
function anotherFunction(e:MouseEvent):void {
removeChild(goBack);
}
You are wrong with the scope. (surprise :-D)
The variable goBack is just defined inside of "nameOfFunction", when you try to access this from a another function like "anotherFunction" it will not exists anymore (even if it is on the display list)
There are different possibilities to solve this problem:
function anotherFunction(e:MouseEvent):void {
removeChild(e.currentTarget);
}
Or the best way would be: promote goBack as a class member of the class holding both functions. (Or if you don't use classes make goBack "global".)
Hippo is correct, but I feel it is important to explain a little more.
You created a local variable, i.e. var someVariable:DataType; within a function. This means that that variable will only be available to objects in the scope (inside) of the function (local to), and it will only last for the lifetime of the function. Soon as that function has ran the code is gone until ran again. It looks like you are probable programming directly inside the flash IDE on the time-line, which is fine, but, if you were using a document class, you could merely declare you variable in the Class scope just above the constructor function, and then set the value in the same function that your using now. This way, the reference to the variable doesn't exist within the function, it is merely set from within. This will allow that variable to be accessed from anywhere in the same class even if set to private.
This may help:
//Frame 1, Actions layer
//Slap goBack right onto the root / stage
var goBack:MovieClip;
/*
I noticed you had this data-typed differently,
i prefer to type to an interface, not an implementation.
Since your class is a movieclip in the library it extends
MovieClip and therefor IS A MovieClip, but ok either way.
*/
function nameOfFunction():void
{
goBack = new backButton_mc();
goBack.x = 10;
goBack.y = 700;
goBack.back_text.text = myXML.*[buildingName].NAME;
goBack.name = "backBtn";
goBack.buttonMode = true;
addChild(goBack);
goBack.addEventListener(MouseEvent.CLICK, anotherFunction);
}
function anotherFunction(e:MouseEvent):void
{
removeChild(goBack);
}
Scope is very important and after a while very easy to tackle. Stick with it, experiment, read up on conventions and standards that can help your development and get to loving the DocumentClass becuase even though it may be daunting to some at first, once you learn it and get used to it, it so hard to go back to programming in the flash IDE on the timeline, where I believe only display objects and audio have any place being.