I got a Datagrid in my Flex application.
I need to make appear a context menu when the header row is right-clicked.
The latter context menu must not appear when the rest of the datagrid items (the ones containing data) are clicked.
Edit: the application runs in AIR environment, so i got no flash-player troubles
In flex, and more generally in flash, there is no way to catch the the right click event.
I'm not sure about the right mouse click, cos flex apps run in flash player, and right click brings up its menu.
The best bet would be to use headerRelease event on your DatagRid. In your event handler you can then create your menu (maybe in a popup or some hovering panel?) and then do what you need to do there.
Read more about it here
edit:
Maybe you could use a contextMenu class, and attach it to your dataGrid.contextMenu?
Below code may help you: -
I have created sample in which i have added only one ITEM. You can convert it and change logic as per requirement. My idea is to provide one of the base logic. You may gey better solution but this can work for you.
<?xml version="1.0" encoding="utf-8"?>
<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="955" minHeight="600"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<mx:DataGrid id="myDG" width="350">
<mx:dataProvider>
<mx:ArrayCollection>
<mx:source>
<fx:Object Artist="" Price="11.99"
Album="Slanted and Enchanted" />
<fx:Object Artist=""
Album="Brighten the Corners" Price="11.99" />
</mx:source>
</mx:ArrayCollection>
</mx:dataProvider>
<mx:columns>
<mx:DataGridColumn dataField="Artist" headerRenderer="StackLabelRenderer"/>
<mx:DataGridColumn dataField="Album" headerRenderer="StackLabelRenderer"/>
<mx:DataGridColumn id="price" dataField="Price" headerRenderer="StackLabelRenderer"/>
</mx:columns>
</mx:DataGrid>
</s:Application>
StackLabelRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Label xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
click="dispatchClickEvent()">
<fx:Script>
<![CDATA[
import mx.controls.DataGrid;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.controls.dataGridClasses.DataGridListData;
import mx.core.mx_internal;
private function dispatchClickEvent():void
{
trace("Item Clicked")
}
import mx.controls.Alert;
[Bindable]
private var cm:ContextMenu;
override protected function createChildren():void
{
cm = new ContextMenu();
cm.hideBuiltInItems();
cm.addEventListener(ContextMenuEvent.MENU_SELECT, contextMenu_menuSelect);
this.contextMenu = cm;
}
private function contextMenu_menuSelect(evt:ContextMenuEvent):void {
//condition to check length of column data length
var allNull:Boolean=true;
var columnName:String = DataGridColumn(data).headerText;
for each(var o:Object in DataGrid(owner).dataProvider) {
if(o[columnName] != "") {
allNull=false;
break;
}
}
if(!allNull)
{
var cmi:ContextMenuItem = new ContextMenuItem("First Element...", true);
cmi.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, contextMenuItem_menuItemSelect);
cm.customItems = [cmi];
}
}
private function contextMenuItem_menuItemSelect(evt:ContextMenuEvent):void {
}
]]>
</fx:Script>
</mx:Label>
Related
I have been developing an Adobe Flex (v3.5 Flex SDK) based application and I have a question on How we can access (call) a method written in MXML file (embeded in script tag) from the ItemRenderer file.
The MXML component has a datagrid and for one of the columns, the itemrenderer is my own custom item renderer.
In my custom item renderer, for some event I need to call a method which exist in its parent MXML component. How do we get access to its parent MXML instance ?
I have explored for this in google and found that we can access to 'data' object which refers to the dataProvider of the datagrid. But I wanted access to the instance of MXML component (so that I can call a method in it) which has the datagrid.
The AdvancedDataGridColumn in AdvancedDataGrid is like this
<mx:AdvancedDataGridColumn dataField="total" headerText="Total" width="120" itemRenderer="renderers.MyItemRenderer"/>
Here MyItemRenderer is a separate action script file.
Appreciate the response.
Thanks
Raagu
As Raghavendra Nilekani suggested This works:
TestGrid.mxml
<?xml version="1.0" encoding="utf-8"?>
<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="955" minHeight="600">
<fx:Script>
<![CDATA[
[Bindable]
public var data:Array = [
{name:"name",value:"valeu1",timestamp:"423423"},
{name:"name1",value:"valeu2",timestamp:"423423"},
{name:"name2",value:"valeu3",timestamp:"423423"},
{name:"name3",value:"valeu5",timestamp:"423423"}
]
public function calculateValue():Number{
return Math.random();
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<mx:VBox height="100%" width="100%">
<mx:AdvancedDataGrid dataProvider="{data}">
<mx:columns>
<mx:AdvancedDataGridColumn itemRenderer="ItemRenderer">
</mx:AdvancedDataGridColumn>
</mx:columns>
</mx:AdvancedDataGrid>
</mx:VBox>
</s:Application>
e ItemRendere.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:MXAdvancedDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true"
addedToStage="mxadvanceddatagriditemrenderer1_addedToStageHandler(event)"
>
<fx:Script>
<![CDATA[
[Bindable]
var value:Number;
import mx.containers.VBox;
import mx.controls.AdvancedDataGrid;
protected function mxadvanceddatagriditemrenderer1_addedToStageHandler(event:Event):void
{
var grid:AdvancedDataGrid = ((AdvancedDataGrid)(this.owner));
var box:VBox = ((VBox)(grid.owner))
var comp:TestGrid = (TestGrid)(box.owner);
value = comp.calculateValue();
}
]]>
</fx:Script>
<s:Label id="lblData" top="0" left="0" right="0" bottom="0" text="{value}" />
</s:MXAdvancedDataGridItemRenderer>
Anyway I agree with zenbeni that this lead to a not reausable item renderer.
i'm using flex for an application but i'm having some trouble to recover data from a datagrid that has a checkbox in one of the columns, the problem is that i can't use the function selectedIndex or selectedItem, because they are used to save the checked items.
Is there a way to recovery the datagrid informations using the column and row indices? Something like:
MyDataGrid[row][column] or MyDataProvider[row][column] or any combination of functions to allow me recovery an information using only the row and column.
Thanks
If you just want to be able to get a cell value of the data grid using row and col indices, you can extend the DataGrid component and add one simple function.
Here is a working example.
It looks something like this:
//CustomDataGrid
<?xml version="1.0" encoding="utf-8"?>
<s:DataGrid 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="400" height="300">
<fx:Script>
<![CDATA[
import spark.components.gridClasses.GridColumn;
public function getElementAt(row:int, col:int):Object
{
if (this.dataProvider.length < row + 1 || this.columns.length < col + 1)
return null;
else
return this.dataProvider.getItemAt(row)[(this.columns.getItemAt(col) as GridColumn).dataField];
}
]]>
</fx:Script>
</s:DataGrid>
Then you call it in your application like this:
//App
<?xml version="1.0" encoding="utf-8"?>
<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="955" minHeight="600"
xmlns:dgrc="com.dgrc.*">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
[Bindable]private var collection:ArrayCollection = new ArrayCollection([
{fld01:1, fld02:"fld02", fld03:"fld13", fld04:"fld14"},
{fld01:2, fld02:"fld02", fld03:"fld23", fld04:"fld24"},
{fld01:3, fld02:"fld02", fld03:"fld33", fld04:"fld34"},
{fld01:4, fld02:"fld02", fld03:"fld43", fld04:"fld44"},
{fld01:5, fld02:"fld02", fld03:"fld53", fld04:"fld54"}
]);
protected function getElement():void
{
var obj:Object = myDG.getElementAt(nsRow.value, nsCol.value);
Alert.show(String(obj));
}
]]>
</fx:Script>
<s:VGroup x="20" y="20">
<s:HGroup>
<s:NumericStepper id="nsRow" minimum="0" value="0"/>
<s:NumericStepper id="nsCol" minimum="0" value="0"/>
<s:Button label="Get It!" click="getElement()"/>
</s:HGroup>
<dgrc:CustomDataGrid id="myDG" width="300" height="160" dataProvider="{collection}">
<dgrc:columns>
<s:ArrayList>
<s:GridColumn dataField="fld01" headerText="Field 1" width="100"/>
<s:GridColumn dataField="fld03" headerText="Field 3" width="100"/>
<s:GridColumn dataField="fld04" headerText="Field 4"/>
</s:ArrayList>
</dgrc:columns>
</dgrc:CustomDataGrid>
</s:VGroup>
</s:Application>
Be aware that the row and col indices in this example refer a cell of the data grid and not a value of the data grid provider.
If you call
getElementAt(0, 1)
you will get "fld13"
If you are using the original DataGrid control, it is better to use the itemToLabel method of DataGridColumn, because item[dataField] will cause an exception when the property value contains dot notation, such as "customer.name":
public function getElementAt(row:int, col:int):Object {
var item:Object = dataProvider.getItemAt(row)
var column:DataGridColumn = dataGrid.columns[column] as DataGridColumn;
var value:String = column.itemToLabel(item);
return value;
}
This same method is used by the framework to provide the text displayed in the grid cell. If your checkbox in the grid is driven by a boolean property, the returned value will be true or false.
I have a regular spark list which is sorted correctly when no item renderer is used. However, when the following item renderer is used, the list shuffles and displays items from previous instances of the list. Any help would be appreciated.
<?xml version="1.0" encoding="utf-8"?>
<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/mx"
autoDrawBackground="true"
creationComplete="itemRendererInit(event)">
<fx:Script>
<![CDATA[
import flash.utils.setInterval;
import flashx.textLayout.conversion.TextConverter;
import mx.events.FlexEvent;
protected function itemRendererInit(event:FlexEvent):void {
setInterval(resizeItemRenderer, 50);
richtext.textFlow= TextConverter.importToFlow(
'<TextFlow xmlns="http://ns.adobe.com/textLayout/2008"><linkNormalFormat color="#ac0000" textDecoration="none"/><linkHoverFormat color="#ac0000" textDecoration="underline"/><linkActiveFormat color="#ac0000" textDecoration="none"/><p><span fontWeight="bold">' +
data[0].toString() +
':</span><br/><br/>' +
data[1].toString() +
'</p></TextFlow>', TextConverter.TEXT_LAYOUT_FORMAT);
}
private function resizeItemRenderer():void {
richtext.width = parentApplication.width-525;
}
]]>
</fx:Script>
<s:RichEditableText id="richtext"
paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10"
verticalAlign="middle" editable="false" textAlign="left"/>
</s:ItemRenderer>
Just over the top of my head, try overriding commitProperties instead of relying on creationComplete event. On creationComplete, just call invalidateProperties and then call your function from commitProperties().
I eventually found it to be a data binding problem. Thanks #M.D.
I want to disable (and look disabled) some rows in a spark datagrid. I found this answer to stop selection which is great
In flex, is there a way to capture and optionally cancel a row selection event in a DataGrid?
But I want in addition to show that the particular row is not selectable. Ideally I want to have some sort of overlay but I am not sure if that is possible. My alternative solution is to change the text color to grey for the unselectable row. Looking at the datagrid renders they all seem to be column based. I looked at skinning potentially (overriding the alternating color property) but this just sets the background property and not the text color. Is this possible?
Thanks
The most basic solution is using the custom renderer and selection preventing logic you've mentioned:
DataGridRowDisabling.mxml
<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" xmlns:local="*">
<fx:Script>
<![CDATA[
import spark.events.GridSelectionEvent;
private function dataGrid_selectionChangingHandler(event:GridSelectionEvent):void
{
var index:int = event.selectionChange.rowIndex;
var product:Product = dataGrid.dataProvider.getItemAt(index) as Product;
if (product && !product.enabled)
event.preventDefault();
}
]]>
</fx:Script>
<s:DataGrid id="dataGrid" itemRenderer="GridItemRenderer2" selectionChanging="dataGrid_selectionChangingHandler(event)">
<s:dataProvider>
<s:ArrayCollection>
<local:Product name="iPod" price="199.99"/>
<local:Product name="iPad 3" price="499.99"/>
<local:Product name="iPad 4" price="599.99" enabled="false"/>
<local:Product name="iPad 5" price="699.99" enabled="false"/>
</s:ArrayCollection>
</s:dataProvider>
</s:DataGrid>
</s:Application>
GridItemRenderer2.mxml
<s:GridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" clipAndEnableScrolling="true">
<fx:Script>
<![CDATA[
override public function prepare(hasBeenRecycled:Boolean):void
{
if (data is Product)
enabled = Product(data).enabled;
lblData.text = data[column.dataField];
}
]]>
</fx:Script>
<s:Label id="lblData" top="9" left="7"/>
</s:GridItemRenderer>
Product.as
package
{
public class Product
{
public var name:String;
public var price:Number;
public var enabled:Boolean = true;
}
}
I have created a Viewstack and using a Tile component and repeating LinkButtons I was able to make a multi column navigation with the viewstack as the dataprovider. My question is can this be done better? My code is below and I am wondering if I took the long way around this approach.
<?xml version="1.0" encoding="utf-8"?>
<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">
<s:layout>
<s:BasicLayout />
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private var _listItem:Object;
private var n:int=0;
public function get listItem():Object
{
return this._listItem;
}
public function set listItem(listItem:Object):void
{
try{n++;
this.changeSelection(this._listItem);
}catch(e:Error){}
if(n==1 || n > this.viewStack.length){
this._listItem = listItem;
this.changeSelection(listItem);
}
}
private function setSelection(obj:Object):void{
this.viewStack.selectedIndex = this.viewStack.getChildIndex(this.viewStack.getChildByName(obj.target.getRepeaterItem().name));
this.listItem = obj.target;
}
private function checkSelection(obj:Object):void{
if(obj.target.getRepeaterItem() == this.viewStack.selectedChild){
if(this.listItem != obj.target){
this.listItem = obj.target;
}
}
}
private function changeSelection(obj:Object):void{
if(obj.getRepeaterItem() == this.viewStack.selectedChild){
obj.setStyle("color","#000000");
}else{
obj.setStyle("color","#999999");
}
}
]]>
</fx:Script>
<mx:Tile id="tiles" horizontalGap="20" verticalGap="0" y="210" direction="vertical">
<mx:Repeater id="masterList" dataProvider="{viewStack}">
<mx:LinkButton
id="btn"
label="{masterList.currentItem.label}"
click="this.setSelection(event)"
color="#999999"
creationComplete="checkSelection(event);" />
</mx:Repeater>
</mx:Tile>
<mx:ViewStack id="viewStack" height="200" width="300" backgroundColor="#000000" >
<mx:VBox id="vb1" backgroundColor="#FF0000" label="Screen One"/>
<mx:VBox id="vb2" backgroundColor="#00FF00" label="Screen Two"/>
<mx:VBox id="vb3" backgroundColor="#0000FF" label="Screen Three"/>
<mx:VBox id="vb4" backgroundColor="#00FFFF" label="Screen Four"/>
</mx:ViewStack>
</s:Application>
Looks to me like you've got navigation links that expose different and that those links change color based on which one is selected. Assuming that's the case, it sounds an awful lot like a tab-based navigation model. My approach would be to use the spark TabBar and skin the tabs to look like links. That way you can get rid of most of your code and let the tab skin handle changing the colors based on their current state. Also, you wouldn't need any of the code you have for changing the view stack since the TabBar would handle that for you. Hope that helps.