I want to put a textinput box on the header of a datagrid in flex, whose values changes dynamically according to the no of data or value present in that perticular column.
Please any one help me, am posting the image acctually what I want.
Thanks in advance.
I'll assume you can use Spark DataGrid. In that case you just need to create a custom headerRenderer. To keep things simple we'll start from the default headerRenderer. In your Flex SDK sources, find the class spark.skins.spark.DefaultGridHeaderRenderer. Copy this file into your project and rename it appropriately.
Inside this class, find the components labelDisplayGroup and sortIndicatorGroup (they're at the bottom). They're inside an HGroup, so we can simply add our counter component in between.
<!-- I removed the original comments for brevity -->
<s:HGroup left="7" right="7" top="5" bottom="5" gap="2" verticalAlign="middle">
<s:Group id="labelDisplayGroup" width="100%" />
<!-- our counter component -->
<s:Label id="numRowsDisplay" />
<s:Group id="sortIndicatorGroup" includeInLayout="false" />
</s:HGroup>
So far for the visual component; now we have to update its text property appropriately. In the script block add the following snippet:
private var dp:IList;
override public function set owner(value:DisplayObjectContainer):void {
if (dp) dp.removeEventListener(CollectionEvent.COLLECTION_CHANGE, updateNumRows);
if (super.owner) super.owner.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChange);
super.owner = value;
dp = value ? DataGrid(value).dataProvider : null;
updateNumRows();
if (dp) dp.addEventListener(CollectionEvent.COLLECTION_CHANGE, updateNumRows);
if (value) value.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChange);
}
private function onPropertyChange(event:PropertyChangeEvent):void {
if (event.property == 'dataProvider') {
dp = event.newValue as IList;
updateNumRows();
}
}
private function updateNumRows(event:CollectionEvent=null):void {
numRowsDisplay.text = (dp ? dp.length : 0) + "";
}
What happens here? the owner property of the renderer refers to the data component that holds this renderer; in this case a DataGrid. So when the owner is assigned to the renderer, we access its dataProvider and use its length to update the counter component.
So what are those listeners for? There are two cases you might want to foresee.
The number of items in the dataprovider changes: that's why we listen on the dataprovider for CollectionChange events.
The entire dataProvider is changed or nullified: for this we listen for PropertyChangeEvents on the DataGrid and update the counter only when the 'dataProvider' property changes
Related
When I start a drag operation I would like to be able to highlight the possible valid Drop objects. For this I need to know when the drag operation starts and which items are being dragged. I am trying to do this using the dragStart, but the event.dragSource is null on this event.
I have this list:
<s:List
width="100%"
height="100%"
id="productsListing"
dragEnabled="true"
dataProvider="{products}"
dragStart="dragStartHandler(event);"
dragComplete="dragCompleteHandler(event);"
itemRenderer="views.productListed" />
And I have the listener as:
public function dragStartHandler(event:DragEvent):void {
var itemsVector:Vector.<Object> = event.dragSource.dataForFormat('itemsByIndex') as Vector.<Object>;
//Verify Items
//Highlight appropriated dropZones
}
Anyone have a good sugestion how to overcome this?
The problem here is that your dragStartHandler is taking higher precedence than the List components internal dragStartHandler - which is where the drag operation is started and the dragSource property created.
Suggestion, manually add your dragStartHandler with a lower precedence than the List components dragStartHandler method - looking at the code this needs to be less than -50.
MXML Code:
<s:List width="100%" height="100%"
id="productsListing"
dragEnabled="true"
dataProvider="{products}"
initialize="productsListing_initializeHandler(event)"
dragComplete="productsListing_dragCompleteHandler(event)"
itemRenderer="views.productListed"
/>
AS Code:
protected function productsListing_initializeHandler(event:FlexEvent):void
{
// Needs to be handled AFTER the List component has handled the event and attached the dragSource data, hence priority is -51
this.productsListing.addEventListener(DragEvent.DRAG_START, productsListing_dragStartHandler, false, -51, true);
}
protected function productsListing_dragStartHandler(event:DragEvent):void
{
// Your code here...
}
I hope you find that useful.
This is the in built solution for drag drop in flex
we can implement the Object Handles for all component
it is easy to drag, drop and resize.
refer this http://code.google.com/p/flex-object-handles/
When I click a row in the datagrid, the "PeopleDetails" state is loaded. On the details state, I have a checkbox. This checkbox was automatically generated when I created the form. This is because the field in the People table is Boolean.
I actually do not want to have a checkbox, but I want the value Yes/No printed next to the label.
So I write some AS code embedded in the MXML code:
<s:Form includeIn="PeopleDetails">
<s:FormItem label="Is Present?">
<fx:Script>
<![CDATA[
if(person!= null ){
if(person.present==true){
Alert.show("Test - Yes");
}
}
else{
Alert.show("No");
}
]]>
</fx:Script>
<s:CheckBox id="personCheckBox2" enabled="false" selected="{person.present}"/>
</s:FormItem>
</s:Form>
Just for testing purposes, I have Alert popups. Eventually, I would change to printing to screen the values Yes/No.
The problem:
-I do not know how to test whether the attribute present in the object person is true or false.
In the above, I get a FB complaint 1120: Access of Undefined property person.
If I remove the AS code, the checkbox works fine. The checkbox uses person.present to know whether it should be checked or not. Why cannot I use person.present to do the if-else test?
I would appreciate any help on this.
If I'm reading you right, you want a Yes/No value rather than a checkbox, this should do it for you:
Change :
<s:CheckBox id="personCheckBox2" enabled="false" selected="{person.present}"/>
To :
<s:Label text="{(person.present)?'Yes':'No'}"/>
So the new form looks like this :
<s:Form includeIn="PeopleDetails">
<s:FormItem label="Is Present?">
<s:Label text="{(person.present)?'Yes':'No'}"/>
</s:FormItem>
</s:Form>
You can't place ActionScript code on a script manner within Script tag. You can only place properties and methods there following OOP way. So put your code inside a method and call this method as a reaction on some event (creationComplete for example).
I hope the above code is part of an ItemRenderer.
Just move the above Script block code to set data() function as below:
override public function set data( value: Object ): void
{
super.data = value;
if(person != null ){
if(person.present==true){
Alert.show("Test - Yes");
}
}
else{
Alert.show("No");
}
}
Also, I don't think Alert will work in Itemrenderer, so replace it with trace("") statement. Hope it helped.
I have a custom component ExpandCollapseMenu that extends SkinnableContainer. This component can have state "normal" or "expanded".
Inside this component I have buttons, with different skin based on ExpandCollapseMenu's state.
This works fine when defining the buttons inside ExpandCollapsMenu's skin class:
<s:Group id="contentGroup" top="20" left="10" right="10" bottom="10">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<component:ExpandCollapseMenuButton label="Foo" skinClass.expanded="component.ExpandCollapseMenuButtonExpandedSkin" skinClass.normal="component.ExpandCollapseMenuButtonSkin" />
</s:Group>
But I don't want to define the buttons inside ExpandCollapsMenu's skin class, I want to define them where I use ExpandCollapseMenu. Like this:
<component:ExpandCollapseMenu skinClass="component.ExpandCollapseMenuSkin">
<component:ExpandCollapseMenuButton label="Foo" />
</component:ExpandCollapseMenu>
At this level, I can't reference skinclass.expanded, but I got it working by using CSS like this:
component|ExpandCollapseMenu:expanded component|ExpandCollapseMenuButton {
skinClass: ClassReference("component.ExpandCollapseMenuButtonExpandedSkin");
}
component|ExpandCollapseMenu:normal component|ExpandCollapseMenuButton {
skinClass: ClassReference("component.ExpandCollapseMenuButtonSkin");
}
Is this a good way to change skin based on parent containers state? Or is there a better way?
I recommend you to solve the problem on a ExpandCollapseMenu container level. When ExpandCollapseMenu's state is changed you should iterate over children and set some flag for each of them.
So I suggest to introduce expanded:Boolean flag for ExpandCollapseMenuButton.
The problem occurs if ExpandCollapseMenu can contain other instance's types. In this case we can solve it using the following two ways:
Check components in process of iterating if they are ExpandCollapseMenuButton instances (using is operator).
Introduce simple Expandable interface like the following:
public interface Expandable
{
function set expanded(value:Boolean);
}
and implement this interface by ExpandCollapseMenuButton. So you can use is operator in iterator body more flexible way without ExpandCollapseMenuButton dependency.
So the last part of puzzle is to implement setter in ExpandCollapseMenuButton class and switch skin:
private var expandedDirty:Boolean;
private var _expanded:Boolean;
public function set expanded(value:Boolean)
{
if (value == _expanded)
return;
_expanded = value;
expandedDirty = true;
invalidateProperties();
}
override protected function commitProperties():void
{
super.commitProperties();
if (expandedDirty)
{
if (_expanded)
setStyle("skinClass", ExpandCollapseMenuButtonExpandedSkin);
else
setStyle("skinClass", ExpandCollapseMenuButtonSkin);
expandedDirty = false;
}
}
As I am somewhat new to Flex I may be missing something fundamental here. I have a Spark List container whose dataProvider is bound to a result set coming back from a RemoteObject call. Pretty standard stuff.
<s:List id="list" dataProvider="{model.stuff}" width="100%" height="100%"
selectedIndex="#{selectedSlider.value}"
itemRenderer="{stuffRenderer}">
</s:List>
The selectedIndex is associated with an HSlider, but that is not the problem. My issue is that I would like to automatically select a certain "preferred" element from the list (only initially...to guide the user).
I tried to do that in a creationComplete event but my data hadn't shown up yet...setting selectedIndex didn't work...it was too early.
What's the right way to do this?
private function findAllUsers_resultHandler(e:ResultEvent):void
{
list.dataProvider = new ArrayCollection(e.result as Array);
if(firstTry)
{
list.selectedIndex = 0;
firstTry = false;
}
}
spark.components.List has spark.components.SkinnableDataContainer in its class hierarchy which dispatches a dataProviderChanged event whenever the dataProvider changes. Unfortunatly there is no [Event] metadata in SkinnableDataContainer that allows using this event in MXML. So, you'll need to create your own custom component that extends List.
package
{
import spark.components.List;
[Event(name="dataProviderChanged", type="flash.events.Event")]
public class MyList extends List
{
public function MyList()
{
super();
}
}
}
By using your custom component you can add an event listener for dataProviderChanged and update your selectedIndex accordingly.
<ns1:MyList id="list" dataProvider="{model.stuff}" width="100%" height="100%"
dataProviderChanged="selectedIndex = selectedSlider.value"
selectedIndex="#{selectedSlider.value}"
itemRenderer="{stuffRenderer}">
</ns1:MyList>
BTW: This works with other List-based components (like DropDownList) too.
I believe it should work if you just set the initial value of the slider to the index you want to be selected at the beginning.
Something like this:
<s:List dataProvider="{yourData}" selectedIndex="{hSlider.value}" /> <s:HSlider id="hSlider" minimum="0" maximum="{yourData.length - 1}" stepSize="1" value="theIndexYouWantAsInitial" liveDragging="true" />
That should work.
HTH
FTQuest
I have a data grid that has a checkbox item renderer in a cloumn to allow row selections:
Main application:
<mx:DataGrid id="dg">
<mx:columns>
<mx:DataGridColumn id="ir" itemRenderer="renderers.RowCheckbox" />
<mx:DataGridColumn dataField="Name" headerText="Name" />
</mx:columns>
</mx:DataGrid>
Item renderer:
<-- RowCheckbox -->
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" horizontalAlign="center">
<mx:CheckBox id="chk"/>
</mx:HBox>
How can I get a handle to the item renderer / checkbox so that I may determine which rows are checked?
Just a word of advice: We had a similar problem in our application and we solved it by adding a "selected" property to the entities in the dataprovider of the datagrid. The selected property of the checkBox was then bound to the selected property of our entity. To know which ones were selected, we just looped over the entities in the dataprovider instead of the item renderers. After a lot of different approaches, this really was the best option.
If I remember correctly, the problem was that the itemrenderers did not remember the selected state correctly and the datagrid was completely messed up when you scrolled up and down. The wrong rows were selected after scrolling.
Another option would be to dispatch an event in the item renderer which bubbles up all the way to the control hosting the datagrid. You could then listen for these events and update your model to reflect the changes.
I ran into similar issues with the DataGrid and multiple item renderers and the reuse of item renderers when scrolling. In order to access DataGrid item renderers I extended the DataGrid. My first thought was to use the indicesToIndex() followed by indexToItemRenderer(). Unfortunately these methods didn't do what I expected so I added the indicesToItemRenderer() method:
package com.whatever.controls
{
import mx.controls.DataGrid;
import mx.controls.listClasses.IListItemRenderer;
public class CustomDataGrid extends DataGrid
{
public function CustomDataGrid()
{
super();
}
public function indicesToItemRenderer(rowIndex:int, colIndex:int):IListItemRenderer
{
var firstItemIndex:int = verticalScrollPosition - offscreenExtraRowsTop;
if (rowIndex < firstItemIndex ||
rowIndex >= firstItemIndex + listItems.length
)
{
return null;
}
return listItems[rowIndex - firstItemIndex][colIndex];
}
}
To resolve the reused item renderers when scrolling issue, refer to this article:
http://www.adobe.com/devnet/flex/articles/itemrenderers_pt1.html
It boils down to overriding the data setter and storing properties in data. For example, I had one column using a CheckBox itemRenderer and another column using ComboBox. For both I listen for the change event and store selected, selectedIndex, etc in data whenever properties change and override the data setter to set those properties:
override public function set data(value:Object):void
{
if (value != null)
{
super.data = value;
if (data.hasOwnProperty('selected') && data.selected)
{
selected = data.selected;
}
else
{
selected = false;
}
}
}
You can use the indexToItemRenderer() method exposed by all subclasses of ListBase.
For example:
private function someFunction(index:int):void
{
var rowCheckbox:RowCheckbox = dg.indexToItemRenderer(index) as RowCheckbox;
trace(rowCheckbox.chk.selected.toString());
}
... where index represents the index of the DataGrid item whose "chk" property you want test.
In the ItemRenderer, try putting Checkbox Component in a VBox..resolves the scrolling issue.