I have successfully received data from sqlite. I see that the data appear in trace function, however I am having a problem with displaying these data correctly. I think it has something to do with binding objects. I must be missing something somewhere, so please correct where I am wrong. Also, this question is actually a continuation of Adobe Air: drag & drop grouped components
Anyway, here is my ItemRenderer:
<?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">
<fx:Script>
[Bindable]
public var tree:TreeClass_Full;
[Bindable]
public var dataText:String = "empty";
public function setTest():void{
resultTextArea.text = "AAA";
}
</fx:Script>
<s:HGroup x="5" y="5" width="350" height="150" horizontalAlign="center" verticalAlign="middle">
<mx:Image id = "resultImage" width="100" height="100"/>
<s:TextArea id="resultTextArea" height="141" editable="false"
text = "{tree.Common_Name}"/>
</s:HGroup>
</s:ItemRenderer>
Here is the function which will be invoked after sqlite query is successful:
[Bindable]
public var dragDropLeft:ArrayCollection = new ArrayCollection();
[Bindable]
public var dragDropRight:ArrayCollection = new ArrayCollection();
protected function ResultFill():void
{
// TODO Auto-generated method stub
if (arr != null){
for (var i:int = 0; i < arr.length; ++i){
var tc:TreeClass_Full = arr[i];
var rr:resultRenderer = new resultRenderer();
rr.tree = tc;
trace("--Common Name:" + tc.Common_Name);
rr.dataText = tc.Common_Name;
rr.resultImage.source = tc.Picture1;
rr.resultTextArea.text = tc.Common_Name;
trace("----Common Name:" + rr.resultTextArea.text);
dragDropLeft.addItem(rr);
}
}
}
After the sqlite query is successful, the above function will be invoked. This is where I put in the information of my Object (my ItemClass from sqlite) into my ItemRenderer.
My ItemRenderer has two components: Image and TextArea. sqlite contains the respective image, which will be used for Image. For TextArea, all the displayable text from the database will be put there - meaning strings from multiple fields of the database will all be put together there. (but for now, I just want only one field in there, which is Common_Name)
After that, I put these ItemRenderers into my list (dragDropLeft, in this case). However, the data does not appear on the program. Yet, trace function outputs the text correctly.
I've tried a few things and I left some of the codes intact, and all of them yield the same result: the text in the TextArea does not change at all. What have I forget here?
UPDATE:
This code here covers the place that I used my Bindable ArrayCollection:
<s:HGroup width="100%" height="85%" verticalAlign="middle">
<s:List dataProvider="{dragDropLeft}" width = "45%" height = "95%"
dragEnabled="true" dragMoveEnabled="true" dropEnabled="true"
itemRenderer="resultRenderer"/>
<s:List dataProvider="{dragDropRight}" width = "45%" height = "95%"
dragEnabled="true" dragMoveEnabled="true" dropEnabled="true"
itemRenderer="resultRenderer"/>
</s:HGroup>
Basically, I tried to manipulate my ItemRenderer on my own by giving it values from sqlite, but from one of the commentors, it seems this is not correct. So how do we do this? How can we send in the data to the ItemRenderer objects that will be added in the list?
As I said in the comments: List is not a container. You cannot just add children to it. You should add data objects to its dataProvider. Basically the whole ResultFill method should be reduced to this:
dragDropLeft.dataProvider = new ArrayCollection(arr);
The ItemRenderers that represent this data will be created internally by the List component. Inside your custom ItemRenderer you can now access this data through the data property, so it would become something like this:
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
autoDrawBackground="true">
<s:HGroup x="5" y="5" width="350" height="150"
horizontalAlign="center" verticalAlign="middle">
<s:Image source="{data.Picture1}" width="100" height="100"/>
<s:TextArea text="{data.Common_Name}" height="141" editable="false" />
</s:HGroup>
</s:ItemRenderer>
Note that I am assuming that the properties of TreeClass_Full are bindable for this example.
Related
While converting a mx:AdvanceDataGrid to a s:DataGrid, my variable row heights caused the height of my DataGrid to be calculated incorrectly. I am attempting to show all rows without using a scrollbar.
This image shows the incorrect height (the last row is cut off):
The issue occurs when a row increases in size, to accommodate word wrap for example, causing the row to not be the same size as the typicalItem. When my project was utilizing the mx:AdvanceDataGrid we worked around this issue by using the measureHeightOfItems() method outlined here https://stackoverflow.com/a/1889005 using this code:
private function calculateTableHeight():void
{
var tableHeightPixelHack:Number = 30;
var numRows:Number = dataGrid.dataProvider != null ? dataGrid.dataProvider.length : 0;
tableHeight = dataGrid.measureHeightOfItems(-1, numRows) + tableHeightPixelHack;
}
My question is, how can I achieve the same result in a s:DataGrid as I did using the measureHeightOfItems method on the mx:AdvanceDataGrid?
s:DataGrid Component
<s:DataGrid id="dataGrid"
width="100%"
columns="{hostComponent.columns}"
dataProvider="{hostComponent.data}"
selectionMode="singleRow"
sortableColumns="false"
styleName="goalsDataGrid"
variableRowHeight="true"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
doubleClickEnabled="true"
/>
Custom Item Renderer (assigned through code)
<?xml version="1.0" encoding="utf-8"?>
<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"
focusEnabled="true">
<s:Rect top="0" bottom="0" right="0" left="0" z="-1">
<s:fill>
<s:SolidColor color="{data.rowColor}"/>
</s:fill>
</s:Rect>
<s:Label id="lblData"
styleName="tableRow"
text="{label}"
width="100%"
height="100%"
maxDisplayedLines="-1"
lineBreak="toFit" />
</s:GridItemRenderer>
Set verticalScrollPolicy="auto" in your DataGrid
For Spark DataGrid you can calculate the height of the DataGrid as below:
private function calculateTableHeight():void
{
var totalHeight:Number = 0;
for (var row:int = 0; row < dataGrid.dataProvider.length; row++) {
var renderer:IGridItemRenderer = dataGrid.grid.getItemRendererAt(row,1);
if (renderer){
totalHeight += renderer.height;
}
}
dataGrid.height = totalHeight + dataGrid.columnHeaderGroup.height + 2;
}
Here is an example app:
<?xml version="1.0"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
updateComplete="sizeDGExactly()"
width="100%"
height="100%">
<fx:Script><![CDATA[
import mx.collections.ArrayCollection;
import spark.components.gridClasses.IGridItemRenderer;
[Bindable]
private var notes:ArrayCollection = new ArrayCollection([
{who: 'Bikram', note_txt: 'ActionScript 3 has empowered Flash developers with faster code execution and a ton of API enhancements.Last line.'},
{who: 'Anup', note_txt: 'Unfortunately, it has also led to the need for a much higher level of developer responsibility than ever before. ' +
'In order to prepare and educate developers on how to deal with some of this new responsibility, \n\nI am writing a series of articles on resource ' +
'management in AS3, Flex 2, and Flash 9. \n\nThe first of these articles discussed the mechanics of the Garbage Collector in Flash Player 9. ' +
'This article will focus on the implications some of the new features of AS3 have on resource management, and the potential headaches they could ' +
'cause you even in simple projects. \n\nThe next article in the series will introduce some of the new tools we have at our disposal to deal with these issues.Last line.'},
{who: 'Mark', note_txt: 'The biggest change in AS3 that affects resource management is the new display list model. In Flash Player 8 and below, ' +
'when a display object was removed from the screen (with removeMovie or unloadMovie), it and all of its descendants were immediately removed from ' +
'memory, and halted all code execution.Last line.'},
{who: 'George', note_txt: 'It is very important to note that not only will the display object continue to use memory, it will also continue to ' +
'execute any “idle” code, such as Timers, enterFrames, and listeners outside its scope. A couple of examples may help illustrate this issue:'+
'You have a game sprite that subscribes to its own enterFrame event. Every frame it moves and carries out some calculations to determine ' +
'it’s proximity to other game elements. In AS3, even after you remove it from the display list and null all references to it, it will continue ' +
'to run that code every frame until it is removed by garbage collection. You must remember to explicitly remove the enterFrame listener when the sprite ' +
'is removed.\n\n'+
'Consider a MovieClip that follows the mouse by subscribing to the stage’s mouseMove event (which is the only way to achieve this effect in ' +
'the new event model). Unless you remember to remove the listener, the clip will continue to execute code every time the mouse is moved, even after ' +
'the clip is “deleted”. By default, the clip will execute forever, as a reference to it exists from the stage for event dispatch (we will look at ' +
'how to avoid this in the next article).\n\n'+
'Now imagine the implications of instantiating and removing a bunch of sprites before the GC does a sweep, or if you failed to remove all references. ' +
'You could inadvertently max out the CPU fairly easily, slowing your application or game to a crawl, or even stalling the users’ computers entirely. ' +
'There is NO WAY to force the Flash Player to kill a display object and stop it executing. You must do this manually when it is removed from the display. ' +
'I will examine strategies to manage this task in a future article.\n\n'+
'Here\’s a simple example (Flash Player 9 required). Click the “create” button to create a new Sprite instance. The sprite instance will start ' +
'outputting a counter. Click remove and note how the output continues, despite the fact that all references to the sprite have been nulled. You can create ' +
'multiple instances to see how this issue compounds over the life of an application. Source code is available at the end of this article. Last line.'}
]);
private function sizeDGExactly():void
{
if (notesDataGrid)
{
var totalHeight:Number = 0;
for (var row:int = 0; row < notesDataGrid.dataProvider.length; row++) {
var renderer:IGridItemRenderer = notesDataGrid.grid.getItemRendererAt(row,1);
if (renderer){
totalHeight += renderer.height;
}
}
notesDataGrid.height = totalHeight + notesDataGrid.columnHeaderGroup.height + 2;
}
}
public function onDeleteButtonClick(item:Object):void
{
notes.removeItemAt(notes.getItemIndex(item));
sizeDGExactly();
}
]]></fx:Script>
<s:Scroller width="100%" height="100%"
left="10" right="10" top="10" bottom="10"
horizontalScrollPolicy="off" verticalScrollPolicy="auto">
<s:VGroup width="100%" height="100%">
<s:Group width="100%">
<s:Panel title="Empty Top Panel" width="50%" horizontalCenter="0">
<s:Label text="This is just an additional component in the layout." width="100%"/>
</s:Panel>
</s:Group >
<s:HGroup width="100%">
</s:HGroup>
<s:HGroup width="100%">
<s:DataGrid id="notesDataGrid"
verticalCenter="0"
width="100%"
dataProvider="{notes}"
sortableColumns="false"
variableRowHeight="true">
<s:columns>
<s:ArrayList>
<s:GridColumn width="200"
headerText="Who Noted"
dataField="who"/>
<s:GridColumn dataField="note_txt"
headerText="Note">
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer>
<s:Label width="100%" height="100%" text="{data.note_txt}"
paddingTop="10"
paddingBottom="10"
paddingLeft="10"
paddingRight="10"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
<s:GridColumn width="70">
<s:itemRenderer>
<fx:Component>
<s:GridItemRenderer>
<s:Button width="60" height="20" label="Delete"
horizontalCenter="0" verticalCenter="0"
click="outerDocument.onDeleteButtonClick(data)"/>
</s:GridItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
</s:HGroup>
<s:Group width="100%">
<s:Panel title="Empty Bottom Panel" width="50%" horizontalCenter="0">
<s:Label text="This is just an additional component in the layout." width="100%"/>
</s:Panel>
</s:Group>
</s:VGroup>
</s:Scroller>
</s:Application>
I am currently getting an error at line 148 during runtime:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at PlantDB/ResultFill()[C:\Users\usern\Adobe Flash Builder 4.6\PlantDB\src\SQLiteCode.as:148]
The code at line 148 is:
leftList.dataProvider = new ArrayCollection(arr);
Before coming to this line 148, the program has checked that Array arr is not null with if (arr != null), so I am 100% sure that arr is not null.
The content of arr is from sqlite:
arr = sqls.getResult().data;
There are several objects in the database, and I see that I can get the content from the database just fine. In fact, before line 148, I've checked the content inside arr by:
var tree:TreeClass_Full = arr[0];
In the debugger, I checked the tree object above, and I see that all variables from the sqlite database are transferred to the tree object correctly. In fact, I've checked arr[1], arr[2], etc. as well, and I see that the objects got correct data.
In my opinion, sqlite and all that do not seem to be a problem, but when I add this Array arr to var tree:TreeClass_Full = arr[0];, the error pops out.
I don't get it. Cannot access a property or method of a null object reference? What are the hidden process in dataProvider that causes this error?
My leftList is here:
<s:HGroup width="100%" height="85%" verticalAlign="middle">
<s:List id = "leftList" width = "45%" height = "95%"
dragEnabled="true" dragMoveEnabled="true" dropEnabled="true"
itemRenderer="resultRenderer"/>
<s:List id = "rightList" width = "45%" height = "95%"
dragEnabled="true" dragMoveEnabled="true" dropEnabled="true"
itemRenderer="resultRenderer"/>
</s:HGroup>
and my itemRenderer (resultRenderer) is:
<?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">
<s:HGroup x="5" y="5" width="350" height="150" horizontalAlign="center" verticalAlign="middle">
<mx:Image id = "resultImage" width="100" height="100"/>
<s:TextArea id="resultTextArea" height="141" editable="false"
text = "{data.Common_Name}"/>
</s:HGroup>
</s:ItemRenderer>
If anybody knows what causes this error during dataProvider, please help.
As noted above:
It is leftList that doesn't exist. The ResultFill method is probably called before leftList is created. leftList is indeed null. Because my HGroup is in a different viewstack (although the same .mxml file), it does not exist until I choose to view the respective viewstack.
The datagrid gets its data from a back end database which has records like
RecordID Division Department Date_Report_Submitted
1. Finance Accounting 11/1/2010
2. Engineering Design 4/2/2011
3. Engineering Implementation 4/2/2011
4. Support Chat_Support 2/4/2010
Clicking on the headers in the Datagrid column(Department) results in a sort based on recordID like
Division Department Date_Report_Submitted
Finance Accounting 11/1/2010
Engineering Design 4/2/2011
Engineering Implementation 4/2/2011
Support Chat_Support 2/4/2010
whereas I want it to be sorted alphabetically for the Datagrid column(Department) like
Division Department Date_Report_Submitted
Finance Accounting 11/1/2010
Support Chat_Support 2/4/2010
Engineering Design 4/2/2011
Engineering Implementation 4/2/2011
since Accounting should come before Chat_Support as per lexicographical order.
Looked at http://blog.flexexamples.com/2008/04/09/creating-a-custom-sort-on-a-datagrid-control-in-flex/#more-590 and have something like
<mx:DataGrid id="myRecords" dataProvider="{myRecords_dp}" width="810" height="274"
itemClick="getRecordData(event)">
<mx:columns>
<mx:DataGridColumn id="firstCol" width="180" fontFamily="Arial" fontSize="12"
wordWrap="true" />
<mx:Button label="Click to Sort" click="mysort()" />
</mx:columns>
</mx:DataGrid>
and
private function mysort():void
{
var sortField:SortField = new SortField();
sortField.compareFunction = mycompare;
sortField.descending = false;
var sort:Sort = new Sort();
sort.fields = [sortField];
myRecords.sort = sort;
myRecords.refresh();
}
private function mycompare(lhs:Object, rhs:Object):int
{
var valueA:String = lhs.text();
var valueB:String = rhs.text();
return ObjectUtil.stringCompare(valueA, valueB);
}
I get errors like
1061: Call to a possibly undefined method refresh through a reference with static type mx.controls:DataGrid.
for myRecords.refresh();
and
Access of possibly undefined property sort through a reference with static type mx.controls:DataGrid.
for myRecords.sort
Any suggestions would be appreciated.
The code in your question is a bit of a mess. For example, you cannot use a button as a column inside a DataGrid. That gives a compiler error.
Nevertheless, I put together a sample, based off your sample data and the code you shared, in order to show you that DataGrid sorting does not work the way you claim it does.
Play with it here.
<?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" viewSourceURL="srcview/index.html">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var myRecords_dp : ArrayCollection = new ArrayCollection([
{recordID:1, division:'Finance', department:'Accounting', Date_Report_Submitted:new Date(2010,11,1)},
{recordID:2, division:'Engineering', department:'Design', Date_Report_Submitted:new Date(2010,4,2)},
{recordID:3, division:'Engineering', department:'Implementation', Date_Report_Submitted:new Date(2011,4,2)},
{recordID:4, division:'Support', department:'Chat_Support ', Date_Report_Submitted:new Date(2010,2,4)},
])
public function mysort():void{
}
public function getRecordData(event:Event):void{
}
]]>
</fx:Script>
<mx:DataGrid id="myRecords" dataProvider="{myRecords_dp}" width="810" height="274"
itemClick="getRecordData(event)">
<mx:columns>
<mx:DataGridColumn id="firstCol" width="180" fontFamily="Arial" fontSize="12"
wordWrap="true" dataField="division" />
<mx:DataGridColumn id="secondCol" width="180" fontFamily="Arial" fontSize="12"
wordWrap="true" dataField="department" />
<mx:DataGridColumn id="thirdCol" width="180" fontFamily="Arial" fontSize="12"
wordWrap="true" dataField="Date_Report_Submitted" />
<!-- <mx:Button label="Click to Sort" click="mysort()" />-->
</mx:columns>
</mx:DataGrid>
</s:Application>
To get more help with your issue; you're going to have to share some real code to demonstrate some issue; not a mish-mash of non-compileable stuff you found on the Internet.
For the issues:
1061: Call to a possibly undefined method refresh through a reference
with static type mx.controls:DataGrid. for myRecords.refresh(); and
Access of possibly undefined property sort through a reference with
static type mx.controls:DataGrid. for myRecords.sort
Try casting the dataProvider to an ArrayCollection or XMLListCollection (Whichever you're using):
ArrayCollection(myRecords.dataProvider).sort = sort;
ArrayCollection(myRecords.dataProvider).refresh();
I have one MXML File as
<objecthandles:ObjectHandles xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" backgroundAlpha="1" xmlns:mx="library://ns.adobe.com/flex/mx"
allowRotate="true" minHeight="25" minWidth="60" height="100" width="200">
<s:BorderContainer id="borderCon" width="100%" height="100%" minHeight="25" minWidth="60"
backgroundAlpha="0" borderVisible="false" borderAlpha="0">
<s:HGroup id="hgText" width="100%" height="100%" gap="0" >
<mx:TextArea id="txtAdd" width="100%" height="100%" color="black"
minHeight="25" minWidth="60" horizontalScrollPolicy="off" verticalScrollPolicy="off" focusOut="txtAddKeyUpHandler(event)"
keyUp="txtAddMirrorKeyUpHandler(event)"
creationComplete="onTextCreationComplete()" />
</s:HGroup>
</s:BorderContainer>
</objecthandles:ObjectHandles>
When ever i create the object of the Fileas
var txtElement:TextElement = new TextElement();
txtElement.txtAdd.text = "Hello";
Then it showing the null object reference that
txtElement.txtAdd seems to be null
Need Perfect Solution?
In the Flex component lifecycle subcomponents will not be created until the parent component is added to the displaylist. Only when the component is added to the displaylist and fully built, will you be able to access its subcomponents. When the component is completely ready for usage, it will dispatch a FlexEvent.CREATION_COMPLETE event.
So do something like this:
var txtElement:TextElement = new TextElement();
txtElement.addEventListener(FlexEvent.CREATION_COMPLETE, initTxtElement);
addElement(txtElement);
private function initTxtElement(event:FlexEvent):void {
txtElement.txtAdd.text = "Hello";
}
Or better yet, since it's a custom component: expose the 'text' property as a property of 'TextElement' and handle the deferred setting of the property internally, so that you can write: txtElement.text = "hello".
I'm new to Flex and am using TileList bound to an ArrayCollection. The array collection is empty at load time, and then updates with the results from am HTTPService call. The problem is that the item renderers aren't being rendered as expected, I'm guessing because there was no data when they were first rendered at load time. Here's simplified example:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" >
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var myList1:ArrayCollection = new ArrayCollection();
[Bindable]
public var myList2:ArrayCollection = new ArrayCollection([{item:"foo"}, {item:"bar"}]);
public function updateMyList():void
{
myList1.source = [{item:"foo"}, {item:"bar"}];
}
]]>
</mx:Script>
<mx:Button id="myButton" label="Update My List"
click="updateMyList();"/>
<mx:TileList dataProvider="{myList1}"
direction="vertical"
width="800" >
<mx:itemRenderer>
<mx:Component >
<mx:Canvas backgroundColor="yellow" >
<mx:Label text="{data.item}" width="800" />
</mx:Canvas>
</mx:Component>
</mx:itemRenderer>
</mx:TileList>
<!-- This one renders as expected -->
<mx:TileList dataProvider="{myList2}"
direction="vertical"
width="800" >
<mx:itemRenderer>
<mx:Component >
<mx:Canvas backgroundColor="yellow" >
<mx:Label text="{data.item}" width="800" />
</mx:Canvas>
</mx:Component>
</mx:itemRenderer>
</mx:TileList>
</mx:Application>
You will notice that the second TileList whose bindings has data at load time renders as expected (800px wide), bit the first TileList is rendered is not the correct width and has scrollbars around it.
Could anyone explain why this is happening or even provide some work arounds to avoid this?
Regards,
Chris
It's likely that this section is causing the problems:
public function updateMyList():void
{
myList1.source = [{item:"foo"}, {item:"bar"}];
}
From here:
source of data in the ArrayCollection.
The ArrayCollection object does not
represent any changes that you make
directly to the source array. Always
use the ICollectionView or IList
methods to modify the collection.
This property can be used as the
source for data binding. When this
property is modified, it dispatches
the listChanged event.
So I'd probably change the line to:
myList1= new ArrayCollection([{item:"foo"}, {item:"bar"}]);
http://livedocs.adobe.com/flex/3/langref/mx/controls/TileList.html
Check the API.
Set the columnWidth and rowHeight properties like this,
<mx:TileList dataProvider="{myList1}"
direction="vertical"
width="800" columnWidth="800" rowHeight="25">
There is probably a more "proper" way to do it, but that should get you started.