Is Flex 4 Panel's controlBarContent dynamic? - apache-flex

I have a custom TitleWindow component (written in ActionScript) that extends spark.componenets.TitleWindow
I want to define some controls to be in the control bar but I don't have all controls at the creation stage. Also, this custom component is the base for other components which need other controls in the control bar.
Is there a way to add controls to the controlBarContent in ActionScript at run time?
Maybe something like the following? (this obviously didn't work)
controlBarContent = [];
.
.
.
controlBarContent.push(new Button());

In general, look out for
addElement()
or
addChild()
methods.
addElement() adds other UI components as sub-elements of other components.
This might be of interest. Finnaly, Adobe provides good help here: 'Working with components'.
UPDATE-1
Sorry, my fault. This works for me:
<s:Panel creationComplete="init();" id='p' controlBarVisible="true" >
<s:controlBarContent>
<!-- will show controls -->
<s:Button label="dd">
</s:Button>
</s:controlBarContent>
<fx:Script>
<![CDATA[
import mx.controls.Button;
import spark.components.Button;
private function init(): void {
var s:spark.components.Label = new spark.components.Label();
s.text = 'My Label';
s.width = 200;
var a:Array = new Array();
a.push( s );
p.controlBarVisible = false;
p.controlBarContent = a;
p.controlBarVisible = true;
p.invalidateDisplayList();
}
]]>
</fx:Script>
</s:Panel>

Related

How do you create a top margin when printing a PrintAdvancedDataGrid

My datagrid prints starting at the very top of the page. I can't figure out how to move the datagrid down. I don't see anything in FlexPrintJob or PrintAdvancedDataGrid that will do this. Do I have to create a blank object to add to the top of my FlexPrintJob object?
Any help or a link that will help.
Thanks,
John
After trying a lot of examples form adobe and others that did not work at all for me, I figured out that I had to put the datagrid in a vBox where I could put a blank header above the datagrid. As all of the examples showed, this was best done with a vBox component.
Below is a working example of what I came up with, which is a bit different than the examples I found. In the vBox component you can do a lot of things to enhance the print job. In my case I added a title and date.
Hope this helps someone out there,
John
I am tusing a generic function to print any AdvancedDataGrid I might have in my application...
public function printAdvancedDataGridContents(advDG:AdvancedDataGrid, xmlListCollection:XMLListCollection, headerText:String):void
{
const printJob:FlexPrintJob = new FlexPrintJob();
if ( printJob.start() ) {
//create an instance of the FormPrintView_ADG component containing the datagrid
var thePrintView:FormPrintView_ADG = new FormPrintView_ADG();
//add the component to my application
FlexGlobals.topLevelApplication.addChild(thePrintView);
//load the datagrid
thePrintView.printDataGrid.dataProvider = xmlListCollection.copy();
//format the datagrid
thePrintView.printDataGrid.width = printJob.pageWidth-45; //set a left margin for the dg in a right justiified vBox
thePrintView.printDataGrid.height = printJob.pageHeight-73; //page adjusted for header title and date
thePrintView.printDataGrid.setStyle("fontSize", 8);
thePrintView.printDataGrid.columns = advDG.columns;
thePrintView.printDataGrid.setStyle("fontFamily", 'Times');
thePrintView.printDataGrid.setStyle("color", 000000);
//set the header text
thePrintView.headerText.height = 45;
thePrintView.headerText.width = printJob.pageWidth-20;
thePrintView.headerText.text = "\r"+headerText;
//add the first page to the print job
printJob.addObject(thePrintView, FlexPrintJobScaleType.NONE);
while (thePrintView.printDataGrid.validNextPage) {
// Move the next page of data to the top of the PrintDataGrid and add it to the printjob
thePrintView.printDataGrid.nextPage();
printJob.addObject(thePrintView, FlexPrintJobScaleType.NONE);
}
//print it and remove the component from my application
printJob.send();
FlexGlobals.topLevelApplication.removeChild(thePrintView);
}
}
Here is the vBox component I am using...
<?xml version="1.0"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
horizontalAlign="right"
creationComplete="init();">
<mx:Script>
<![CDATA[
[Bindable] private var date:String;
private function init():void{
date = new Date().toString();
date = df.format(date);
}
]]>
</mx:Script>
<mx:DateFormatter id="df" formatString="EEEE, MMMM D, YYYY"/>
<!-- this header can contain a title or be left blank to create a top margin -->
<mx:TextArea id="headerText" borderThickness="0" color="#000000" fontWeight="bold"
textAlign="center" textDecoration="none"/>
<!-- date label. set visible to false in the calling function if not needed -->
<mx:Label color="#000000" fontSize="8" fontStyle="normal" fontWeight="normal" text="{date}"/>
<!-- the data grid -->
<mx:PrintAdvancedDataGrid id="printDataGrid"/>
</mx:VBox>

google maps in flex withiout mxml component

How do I add google maps as Class into main.mxml
withiout <maps:Map key="" sensor="" />
like in Flash
UPDATE:
and if i have class
package{
import com.google.maps.Map;
public function myMap extends MovieClip {
var map:Map = new Map();
map.setSize(new Point(300, 300));
this.addChild(map);
}
}
if i use this in mxml
myMap:myMap = new myMap();
addChild(myMap);
return error
addChild() is not available in this class. Instead, use addElement() or modify the skin, if you have one.
if i use this
var container:UIComponent = new UIComponent();
container.width = 300;
container.height = 300;
addChild(container);
myMap:myMap = new myMap();
container.addChild(myMap);
Nothing is not added
thanx
MXML is declarative markup that is translated into actual instances at compile time.
For example:
<s:Label text="Something" />
is the same as running
var label:Label = new Label();
label.text = "Something";
this.addElement(label);
So in your case, just assign a function to execute at some point in the component lifecycle and keep the reference to the map at the class level. (I'm using some VGroup component for example)
<s:VGroup creationComplete="onCrtComplete()" ...>
<fx:Script>
<![CDATA[
private var map:Map;
private function onCrtComplete():void
{
maps = new Map();
//now you can do something with the map.
}
]]>
</fx:Script>
</s:VGroup>
Alternatively, you can add an id attribute to the MXML and then reference it programmatically using that id as the property name:
<maps:Map id="map" key="" sensor="" />
In the case of "addChild()", use "addElement()" instead - it's part of the changes between Flex 3 to 4.

ItemRender data change

I have a List with an ItemRenderer. When I click a button, then a property of the ArrayCollection I bound to the list is changed.
When I click the button, then it does change the property, but the list doesn't change.
How do I solve this.
Here's my code
<fx:Script>
<![CDATA[
[Bindable]
public var controllers:ControllerCollection = new ControllerCollection();
private function hideTheFirstItem(evt:MouseEvent):void
{
(controllers[0] as Controller).meetsRequirements = false;
//these methods don't work unfortunatly
controllers.itemUpdated(controllers[0]);
controllers.refresh();
}
]]>
</fx:Script>
<mx:List id="listControllers" dataProvider="{controllers}">
<mx:itemRenderer>
<fx:Component>
<solutionItems:displaySolutionItem visible="{data.meetsRequirements}" />
</fx:Component>
</mx:itemRenderer>
</mx:List>
<mx:Button label="test" click="hideTheFirstItem(event)" />
(ControllerCollection extends ArrayCollection)
Thanks!
Vincent
Two ways:
collection.refresh()
collection.itemUpdated()
Of course, ControllerCollection is not a standard Flex Collection class; so I am just assuming that it implements the ICollectionView interface.
Update:
I do notice that your code is set to modify the first element of the ArrayCollection
private function hideTheFirstItem(evt:MouseEvent):void
{
(controllers[0] as Controller).meetsRequirements = false;
//these methods don't work unfortunatly
controllers.itemUpdated(controllers[0]);
controllers.refresh();
}
I wanted to be sure to specify that the first element of the collection may not be the first element currently visible in the view. I wonder if that is causing you issues.
Without seeing your item renderer, I need to make some assumptions.
First, I will assume that your item renderer is using data binding to the meetsRequirements property. If that is the case, then the meetsRequirements property needs to notify when that property changes. If you add the [Bindable] tag to that property or the Controller class, then the meetsRequirements property will notify the itemRenderer to update that field based on your data binding.
If my assumptions are wrong, we need to see the code to give you any further thoughts.
First, don't try to create new collections if you don't need to.
I believe your problem lies with this statement: (controllers[0] as Controller).meetsRequirements = false; which should fail on compile because a collection item cannot be retrieved using the square bracket annotation. You need to use getItemAt(index:int) function.
Furthermore, you wouldn't want to set visible to false to an item renderer if you want to 'remove' it because then you'd have an empty spot. What you want to do is filter it out:
<s:Application creationComplete="onCreationComplete()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable] public var data:ArrayCollection = new ArrayCollection();
private function onCreationComplete():void
{
// TODO: need to add data here
// Adding filter function
data.filterFunction = filterItems;
}
private function filterItems(item:Object):Boolean
{
return item.meetsRequirements;
}
private function hideFirstItem():void
{
if(data.length > 0)
{
Controller(data.getItemAt(0)).meetsRequirements = false;
}
data.refresh();
}
]]>
</fx:Script>
<mx:List id="listControllers" dataProvider="{data}" />
<mx:Button label="test" click="hideFirstItem()" />
</s:Application>
This should do it. Untested though.
Try this:
<fx:Script>
<![CDATA[
[Bindable(Event="refreshMyList")]
public var controllers:ControllerCollection = new ControllerCollection();
private function hideTheFirstItem(evt:MouseEvent):void
{
(controllers[0] as Controller).meetsRequirements = false;
dispatchEvent(new Event("refreshMyList"));
}
]]>
</fx:Script>

Adding a custom UI component as a panel titleIcon (should be easy, I'm kind of a newbie)

The concept of this seems easy, but I'm having trouble getting it right and can't find anything to help me on this.
I have a panel I need to perform a drag and drop operation on, but I only want to perform that if the user mouses down on a particular area of the panel. I can add an Icon to the panel by doing this:
[Embed("/img/icon.png")]
[Bindable]
public var dragIcon:Class;
newPanel.titleIcon = dragIcon;
But what I really want to add is a box, which I can then add my listeners to for the drag and mouse down like I do on some canvases created in actionscript like so
var tempBox:Box = new Box;
tempBox.x=0;
tempBox.y=0;
tempBox.width = 20;
tempBox.height = 44;
tempBox.setStyle("horizontalAlign","center");
tempBox.setStyle("verticalAlign","middle");
tempBox.addEventListener(MouseEvent.ROLL_OVER,over);
tempBox.addEventListener(MouseEvent.ROLL_OUT,out);
tempBox.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownAnswer);
var tempImg:Image = new Image();
tempImg.source = grabbableItem;
tempBox.addChild(tempImg);
myCanvas.addChild(tempBox);
So what do I need to do to use that tempBox and turn it into a class to be used as my panels titleIcon?
Edit 12/29/09:
So I came up with something where I'm extending the panel class (shown below) but all this is really doing is covering up the icon with something I can access publicly. I'm sure there's a better way out there right?
package custClass
{
import mx.containers.Box;
import mx.containers.Panel;
import mx.controls.Image;
public class DragPanel extends Panel
{
[Bindable] public var iconBox:Box = new Box();
[Embed("../img/doc_page.png")] [Bindable] public var grabbableItem:Class;
public function DragPanel()
{
super();
}
override protected function createChildren():void{
super.createChildren();
iconBox.x = 10
iconBox.y = 4
iconBox.width = 20;
iconBox.height = 20;
iconBox.setStyle("horizontalAlign","center");
iconBox.setStyle("verticalAlign","middle");
iconBox.setStyle("borderStyle","solid");
iconBox.setStyle("backgroundColor",0x000000);
var tempImg:Image = new Image();
tempImg.source = grabbableItem;
iconBox.addChild(tempImg);
this.rawChildren.addChild(iconBox);
}
}
}
EDIT 1/7/10 (or 16 according to my windows mobile phones text messages):
Using Chaims help from below here is my new answer.
Create a box mxml component like Chaim says but also add the following script block to it.
<mx:Script>
<![CDATA[
import mx.core.Application;
[Embed("/img/doc_page.png")]
[Bindable]
public var grabbableItem:Class;
public function init():void{
this.addEventListener(MouseEvent.MOUSE_DOWN,Application.application.mouseDownSection);
this.addEventListener(MouseEvent.ROLL_OVER,Application.application.over);
this.addEventListener(MouseEvent.ROLL_OUT,Application.application.out);
}
]]>
</mx:Script>
This adds in all the event listeners I want on the Box that will be used as my icon. Now just add the box as an Icon and it's good to go.
panel.titleIcon = DraggableBox;
I guess since it's a separate mxml component it is now a class, though I don't think I understand why.
The Panel expecting titleIcon property value to be a IFactory and create an instance by himself.
Make your box a component (lets name it DraggableBox.mxml):
<?xml version="1.0" encoding="utf-8"?>
<mx:Box xmlns:mx="http://www.adobe.com/2006/mxml"
x="0" y="0" width="20" height="44"
horizontalAlign="center" verticalAlign="middle">
<mx:Image source="{grabbableItem}"/>
</mx:Box>
And assign it to titleIcon:
<mx:Panel titleIcon="{DraggableBox}" >
...
</mx:Panel>
If you want do it in ActionScript use ClassFactory:
panel.titleIcon = new ClassFactory(DraggableBox);

Using a composite MXML component from ActionScript

I'm trying componentize one of the pieces of UI in an AIR application that I'm developing in Flex. In this example, I want to display file information on a single line (which has an icon, some text/link and the size).
My code looks like this (component is called FileDisplay):
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
public function set iconType(source:String):void {
this.ficon.source = source;
}
public function set fileName(name:String):void {
this.fname.htmlText = name;
}
public function set fileSize(size:String):void {
this.fsize.text = size;
}
]]>
</mx:Script>
<mx:Image id="ficon" />
<mx:Label id="fname" left="20" right="30" text="Filename" />
<mx:Label id="fsize" right="0" text="0 K" />
</mx:Canvas>
When I'm using this component in my main application, the actionscript looks like:
for each (var file:XML in result.files) {
var fd:FileDisplay = new FileDisplay();
fd.fileName = ''+file.name+'';
fd.iconType = getFileTypeIcon(file.name);
fd.fileSize = getFileSizeString(file.size);
this.file_list.addChild(fd);
}
However, when I do this, I get an error: Error #1009: Cannot access a property or method of a null object reference. This is because the child components of the FileDisplay are null (or at least they show up that way in the debugger).
Does anyone know if there's a way around this? Am I supposed to be waiting for events indicating the child components were created? Is there a more common pattern that solves this problem?
For now I can manually do everything in ActionScript in my main app (create a Canvas and add children to it) but I would appreciate any insight on how to separate the code more cleanly.
Bindable to the rescue:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]
public var iconType:String;
[Bindable]
public var fileName:String = "Filename";
[Bindable]
public var fileSize:String = "0 K";
]]>
</mx:Script>
<mx:Image id="ficon" source="{iconType}"/>
<mx:Label id="fname" left="20" right="30" text="{fileName}" />
<mx:Label id="fsize" right="0" text="{fileSize}" />
</mx:Canvas>
the values will be automatically updated when the components are created.
The subcomponents haven't been loaded yet.
Read this: http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_2.html#203434.
Then, when like me, you don't understand it (and it's not reliable), listen for the FlexEvent.CREATION_COMPLETE within FileDisplay, and apply your child component properties there.
Or better yet, create the three children programmatically in the "createChildren" function, and apply the settings there.
Both of these methods assume that you're setting filename, icontype, and filesize as local members before applying them to the children components, which you should be doing regardless.
What is the parent component that holds the FileDisplay component? If you're sure that the error is coming from the fact that the child components of FileDisplay aren't being instantiated then you might want to look at the creationPolicy attribute and make sure it's set to ContainerCreationPolicy.ALL on that parent component.
=Ryan
In addition to setting the CreationPolicy to all, you need to add the DisplayObject to the stage via addChild. The children of FileDisplay are not created until you add it is added to the stage. So do:
for each (var file:XML in result.files) {
var fd:FileDisplay = new FileDisplay();
this.file_list.addChild(fd);
fd.fileName = ''+file.name+'';
fd.iconType = getFileTypeIcon(file.name);
fd.fileSize = getFileSizeString(file.size);
}

Resources