Switch between Flex Tabbed ViewNavigators - apache-flex

I am working on a Flex TabbedViewNavigatorApplication with three tabs (ViewNavigator elements). I would like to switch from one ViewNavigator to another based upon a user action (via ActionScript code).
I know that switching between Views uses pushView and popView, but I'm working with ViewNavigators, and my searching revealed nothing useful.
I'm trying to switch from Tab2 to Tab1 when an event occurs. In this case, Tab2 contains a list, and when the user makes a selection, I want to jump back to Tab1.
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="onAppReady(event)">
<s:ViewNavigator label="Tab1" width="100%" height="100%" firstView="views.TabOneView"/>
<s:ViewNavigator label="Tab2" width="100%" height="100%" firstView="views.TabTwoView"/>
<s:ViewNavigator label="Tab3" width="100%" height="100%" firstView="views.TabThreeView"/>
</s:TabbedViewNavigatorApplication>
Thanks for your help!

I use the following line of ActionScript to switch from one ViewNavigator to another based on a user action:
TabbedViewNavigator(navigator.parentNavigator).selectedIndex = 1;
It worked like a charm and seems simpler than bubbling events.

This class is strangely undocumented. I have not tried this myself, but from searching online, this is what I found which corroborates with what the rest of the network does.
What you need to do is bubble an event to the TabbedViewNavigatorApplication and from there change the selectedIndex property to whichever tab you need to change to. For example:
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="onCreationComplete()">
<fx:Script>
<![CDATA[
private function onCreationComplete():void
{
this.addEventListener('someEvent', someHandler);
}
private function someHandler(e:Event):void
{
this.selectedIndex = 0; // or whatever index you want.
}
]]>
</fx:Script>
<s:ViewNavigator label="Tab1" width="100%" height="100%" firstView="views.TabOneView"/>
<s:ViewNavigator label="Tab2" width="100%" height="100%" firstView="views.TabTwoView"/>
<s:ViewNavigator label="Tab3" width="100%" height="100%" firstView="views.TabThreeView"/>
</s:TabbedViewNavigatorApplication>
You just need to dispatch a bubbling event from within your children. You could event create a custom event that holds data about which tab to switch to.

Related

How to create and manage a nested TabbedViewNavigator?

I'm trying to implement a nested TabbedViewNavigator (inside a View). I did this by following method 3 described in this post:
Flex - how to make a tabbed panel
Code here:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
<s:TabbedViewNavigator width="100%" height="100%">
<s:ViewNavigator label="1st Tab" width="100%" height="100%"
firstView="views.FirstTabView"/>
<s:ViewNavigator label="2nd Tab" width="100%" height="100%"
firstView="views.SecondTabView"/>
<s:ViewNavigator label="3rd Tab" width="100%" height="100%"
firstView="views.ThirdTabView"/>
</s:TabbedViewNavigator>
However if I call "navigator.pushView(someView)" inside one of the child views - let's say FirstTabView - the new view will be pushed into the nested TabbedViewNavigator instead of the parent's view navigator. That is not what I want. I want to completely change the application state and show a new view. How can I achieve that? Should I listen (in the main view) for changes within the nested TabbedViewNavigator and then push the new view from there? Or should I just do this some other way?
I solved it by using a custom event (it's custom because I need to get data from it).
Added a listener and a handler to the TabbedViewNavigator. The event is dispatched from inside the active ViewNavigator view. The event has bubbles=true so that it goes up to the top of the event chain. Then just push the new view in the event handler.
Main View:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
(...)
private function setup(){
tabbed_panel.addEventListener(CustomEvent.BUTTON_PRESSED, eventHandler);
}
private function eventHandler(ev:CustomEvent):void{
navigator.pushView(MyNewView,ev.event_data);
}
(...)
<s:TabbedViewNavigator id="tabbed_panel" width="100%" height="100%">
<s:ViewNavigator label="1st Tab" width="100%" height="100%"
firstView="views.FirstTabView"/>
<s:ViewNavigator label="2nd Tab" width="100%" height="100%"
firstView="views.SecondTabView"/>
</s:TabbedViewNavigator>
(...)
</s:View>
FirstTabView:
<View...>
(...)
private function buttonClickedHandler(event:Event):void{
dispatchEvent(new CustomEvent(event_data,"BUTTON_PRESSED",true));
}
(...)
</View>
I don't know if this is a corrrect approach to the problem, but it works.

Dragging the AIR application window around the screen

I have an AIR application. It should be moved around the screen with the mouse. In order to achieve this I use the event:
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, true,-2);
It should be activated with the lowest priority compared to inserted elements for example those that should be scrolled, clicked, etc.
I tried the solution shown below with the event priority set to -1 because there might happen 2 different events and my moving application event should be the last one to be serviced or shouldn't be serviced at all.
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="200"
height="200"
applicationComplete="init()">
<fx:Script>
<![CDATA[
import mx.core.Window;
import mx.events.ScrollEvent;
private function init():void {
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, true,-2);
}
private function onMouseDown(event:MouseEvent):void {
trace("clicked on stage "+event.currentTarget.toString());
if(event.currentTarget == stage){
trace("catched stage target");
this.nativeWindow.startMove();
event.stopImmediatePropagation();
}
}
function scrolledCanvasHandler(event:ScrollEvent){
trace("clicked on canvas "+event.currentTarget.toString());
event.stopPropagation();
}
]]>
</fx:Script>
<mx:Canvas x="29" y="34" width="80%" height="80%" backgroundColor="#343434" scroll="scrolledCanvasHandler(event)">
<mx:Label x="25" y="77" text="moving window, moving window"
fontSize="18" color="#FFFFFF" fontWeight="bold"/>
</mx:Canvas>
</s:WindowedApplication>
As you will notice the
event.stopPropagation();
doesn't work.
Perhaps my solution isn't the best suited to achieve this. Are there better solutions?
Chris
that's what i did in an app of mine:
<s:HGroup id="appTitleBar"
width="100%" height="35"
styleName="titleBar"
mouseDown="nativeWindow.startMove();"
doubleClickEnabled="true"
doubleClick="nativeWindow.minimize();"
contentBackgroundColor="#313131"/>
click (+drag) on this HGroup will drag the window. duobleclick will minimize it.
edit
don't make your whole app draggable this will only confuse the user.
and btw priority should be positive not negative - but also don't mess with this. not expected behavior for anyone.

spark List with ItemRenderer click function not working

I am having an issue with my ItemRenderer, which I am using for a spark List. My code is the following:
I have this list:
<s:List
id="productSetList"
dataProvider="{ model.productSets }"
change="model.selectSet( productSetList )"
height="100%" width="100%"
borderVisible="false"
itemRenderer="SideBarItemRenderer" top="20" left="15">
</s:List>
and my itemRenderer is:
<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/halo"
width="160" height="175" autoDrawBackground="false" buttonMode="true" useHandCursor="true"
click="click(event)" cacheAsBitmap="true"
>
<fx:Script>
<![CDATA[
import com.png.vm.model.vos.ProductSet;
protected function click(event:MouseEvent):void
{
trace('arthur');
}
]]>
</fx:Script>
<s:BitmapImage source="{ProductSet(data).image}" width="160" height="175"/>
</s:ItemRenderer>
The thing is, if I scroll the list, and click on an item, it does not trace 'arthur' ! Why is this so ? I must trace that all the time that someone clicks in the list!
EDIT:
If I remove change="model.selectSet( productSetList )" in the list, it works!! but I cannot remove that, some suggestions ? How can I switch that to another function?
Trace only works when you are debugging/using the debugging version of Flash Player. Make sure you are using that. If you want a pop-up message use Alert.show("message")
For more information about trace() check out:
http://livedocs.adobe.com/flex/3/html/help.html?content=logging_08.html
And Alert.show(): http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00001965.html
If you are running debug player. Try originating the click event from an embedded <s:Group> this way whatever you add in here beyond the bitmap will still trigger the click event.
Something like:
<s:Group width="100%" height="100%" click="click(event)" >
<s:BitmapImage source="{ProductSet(data).image}" width="160" height="175"/>
</s:Group>
I've definitely had click events work for me inside of ItemRenderers before
My apologies, I have finally solved it. The problem was that inside the function,model.selectSet, I was calling List.change; I was messing the list up! My function was as follows:
public function selectSet(list:List):void {
list.layout.verticalScrollPosition=100;
// another stuffs
}
So, I just removed the line : list.layout.verticalScrollPosition=100; and now it`s working fine.
Thanks for all the help!

Hiding the content pane in a Flex Panel

I am writing an custom component in Flex 3.2 that extends the panel component. After a user performs a certain action I would like to hide the main content area in the Panel component, as well as the Control Bar if one is specified. Any ideas on how to do this? controlBar.visible does not seem to hide the control bar, and I don't know of another easy way of accessing the main content area besides iterating through all the children of the main panel, and I would like to avoid that if possible. Thanks
Couldn't you set one main container, whether a HBox or VBox etc... inside your Panel that would contain all the children, then you could toggle this container visibility depending on the user's action.
As for the ControlBar , you should be able to change its visibility value...
The reason you can't seem to hide the controlbar, is because you are only setting it's visible property - it's still taking up it's space. So, to truly "hide" it, do this:
myControlBar.includeInLayout = false;
Also, to hide all you children, only requires a simple loop:
for each (var oChild:DisplayObject in idPanel.getChildren()) {
oChild.visible = false;
}
So, the entire application would look like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
private function doit(): void {
idControl.visible = false;
idControl.includeInLayout = false;
for each (var oChild:DisplayObject in idPanel.getChildren()) {
oChild.visible = false;
}
}
]]>
</mx:Script>
<mx:Button x="10" y="10" label="Button" click="doit()"/>
<mx:Panel x="83" y="10" width="250" height="200" layout="absolute" id="idPanel">
<mx:CheckBox x="10" y="10" label="Checkbox"/>
<mx:DateField x="10" y="40"/>
<mx:ControlBar id="idControl">
</mx:ControlBar>
</mx:Panel>
</mx:Application>
Hope that helps!

TextArea component is null on applicationComplete event

I have a weird issue (weird because it is specific to one component) with applicationComplete in a fairly simple application. All the UI components are declared in MXML. I can access them all in applicationComplete, but not a spark.components.TextArea component, named taStatus here; it is null in the handler.
MXML looks sort of like this (there are lots of other components, but nothing special)
<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="710" minHeight="640" applicationComplete="onApplicationComplete(event)" width="710" height="640">
<mx:TabNavigator left="15" right="15" top="15" bottom="340" paddingTop="0">
<s:NavigatorContent label="General" width="100%" height="100%">
<s:Label x="93" y="71" text="Label" id="lblTest"/>
</s:NavigatorContent>
<s:NavigatorContent label="Status" width="100%" height="100%">
<s:TextArea id="taStatus" width="100%" height="100%" text="Startup." editable="false"/>
</s:NavigatorContent>
</mx:TabNavigator>
<fx:Script source="main.as" />
</s:Application>
Here is the handler in main.as
protected function onApplicationComplete(event: FlexEvent) : void
{
lblTest.text = 'abc789'; // OK
taStatus.text = 'abc789'; // Fail
}
TypeError: Error #1009: Cannot access a property or method of a null object reference. So taStatus is null... What is so special about this TextArea?
Update 2010-06-12 02:53
Moving the NavigatorContent (tab) above all other tabs suddenly makes the TextAreas get instantiated on time. Very strange, because all the components are definitely being created; I can see them.
It's because the TextArea is in a child of the TabNavigator that is not the first child, so by default it is not instantiated until the user opens that tab.
Your options are to either wait until the user opens that tab to do whatver you need to do to set up the TextArea or change the child creation policy on the TabNavigator to create all its children at startup rather than waiting for them to be clicked.
To do that, you need to set the creationPolicy property on the TabNavigator to "all".

Resources