Unable to Bind warning: class is not an IEventDispatcher - apache-flex

I found another similar question, but don't quite follow the explanation there, and not sure if it applies the same to me.
I am getting the error:
warning: unable to bind to property 'Description' on class 'Object' (class is not an IEventDispatcher)
this is only when the data is bound to a List as an ArrayList, though. I had it in a Datagrid before (just as an Array) and it did not cause any issue. I'm not expecting to be able to bind any data back to the class 'object' not even sure which object exactly it's referring to.
My list data-provider is an ArrayList.. populated by a result event from an SQL query contained in another class:
private function loadDayComplete():void
{
var Meals:Array = _day.MealResults;
var MealsListResult:ArrayList = new ArrayList(Meals);
MealPanelDataGrid.dataProvider = Meals;
MealListView.dataProvider = MealsListResult;
{
The day class I have is a data holder to get all the data (from several tables) for a 24 hour span.. I have classes for each individual datatype for editing them.
But I'm not sure do I even need to worry about the warning if I don't plan on editing the values? it completely spams the console though whenever I touch anything, so I really would like to get rid of it.
I tried an object proxy as I saw described elsewhere but nothing changed. And I'm pretty confused why it's suddenly an issue on a list component when it wasn't on a datagrid... The text is in label fields right now anyway, which can't even be edited.

The objects causing this warning are probably items inside Meals array.
Make sure that these items are strongly typed (Data Transfer Objects / Value Objects patterns) and that Description field is marked [Bindable].
Depending on the remoting mechanism you are using you may be able to use something like makeObjectsBindable flag which replaces returned Object items with bindable ObjectProxy instances.
But I'll advise strong typing anyway.

You can get rid of it by making your dataProvider an EventDispatcher: ObjectProxy is an EventDispatcher. It will automatically wrap your data to a specified or infinite depth (default behavior).
metaPanelDataGrid.dataProvider = new ObjectProxy(meals);
It's basically the same thing as with ArrayCollection but deeper.

Related

Simple binding with JavaFX8

I have been exploring JavaFX8 for a few days and am trying to educate myself with the concept of binding. I've created an ObservableList like this
private static ObservableList<XYChart.Series<String, Number>> chartData = FXCollections.<XYChart.Series<String, Number>>observableArrayList();
I then go through a few steps to create an AreaChart and have had success calling the setAll() method below
areachart.getData().setAll(chartData);
It is my understanding though that bindings would allow me to remove this step by associating the data property with the list. If the list changes, the chart data property would automatically be "updated".
I've tried to bind the data property like this
areachart.dataProperty().bind(chartData);
However, its asking for a syntax that I'm not familiar with at all
(ObservableValue<? extends ObservableList<Series<String, Number>>> arg0)
Can someone please help me create the correct syntax to bind the list to the area chart? If I've gotten the concept of binding all mixed up please let me know.
JavaFX's ObservableList is special List which can be observed for its children manipulations by attaching an event handler to it. Please read related javadocs for more info.
In JavaFX, the ancestor of all charts, XYChart has a dataProperty() which is ObjectProperty<ObservableList<XYChart.Series<X,Y>>>. Namely this is a property field that holds an object whose type is ObservableList. And this is the field you need to set its holding observable list directly as
areachart.setData(chartData);
Now the data of areachart and chartData are referencing the same list, any removing/adding to chartData will be "reflected" to areachart's data and vice versa.
The
areachart.getData().setAll(chartData);
is also valid but it will copy list-to-list. I.e. it will internally loop the chartData and copy its children (XYChart.Series in this case) into the data observable list of areachart. Resulting to different lists, so no relation, no "reflection" between them.
The
areachart.dataProperty().bind(chartData);
is wrong. Because it is trying to bind, roughly saying, the property<list> to list.
Even though the dataProperty is ObjectProperty<ObservableList<XYChart.Series<X,Y>>>, binding to it through dataProperty().bind() will expect ObservableValue<? extends ObservableList<Series<String, Number>>>, where ObservableValue is a superclass of ObjectProperty. This is by design, there were discussions on this topic on SO, but cannot remember the actual Q&A entry. Try to search them.
Before going deeper in JavaFX refer to Using JavaFX Properties and Binding and other resources to understand it better. Generally speaking property is a wrapper to its holding object, by providing an observer like pattern to its state changes and also providing flexibility to plug up to other property objects through JavaFX APIs. See for example Bindings and its usage examples on the net.

Multiple "default" properties/methods in a VB6 class?

I am trying to make a replacement VB6 class for the Scripting.Dictionary class from SCRRUN.DLL. Scripting.Dictionary has (among other things) a "Keys" method that returns an array of keys, and a read/write "Item" property that returns the item associated with a key. I am confused about this, because both of them seem to be defaults for the class. That is:
For Each X In MyDict
Is equivalent to:
For Each X In MyDict.Keys
Which to me implies that "Keys" is the default operation for the class, but:
MyDict("MyKey") = "MyValue"
MsgBox MyDict("MyKey")
Is equivalent to:
MyDict.Item("MyKey") = "MyValue"
MsgBox MyDict.Item("MyKey")
Which to me implies that "Item" is the default operation for the class.
I've never before created a VB6 class that had a default operation, so upon realizing this, I thought perhaps I could define multiple default operations as long as they all have different signatures, which they do: Keys is nullary, the Item getter takes a Variant, and the Item setter takes two Variants. But this doesn't seem to be allowed: When I use "Tools/Procedure Attributes" to set the Keys function to be the default, and then I use it to set the Item property to be the default, the IDE complains that a default has already been set.
So I think I'm misunderstanding something fundamental here. What is going on in the Scripting.Dictionary object that makes it able to act as if "Keys" is the default in some contexts, but as if "Item" is the default in others? And whatever it is, can I accomplish the same thing in VB6?
OK, answering my own question: I haven't tried this yet, but I gather that "Item" should be made the default, and that I should add an entirely new function called "NewEnum" that looks something like the following (slightly modified from an example in Francesco Balena's "Programming Microsoft Visual Basic 6.0" book):
Public Function NewEnum() As IUnknown
Set NewEnum = m_Keys.[_NewEnum]
End Function
(where "m_Keys" is a Collection containing the keys), and then use Tools/Procedure Attributes to hide NewEnum and to set its ProcID to -4.
What you are observing is the difference between the default member and a collection enumerator. A COM object (including VB6 classes) can have both.
You can identify the default property of a class by looking in the Object Browser for the tiny blue globe or the words "default member of" in the description (see Contents of the Object Browser). The Object Browser will not identify an enumerator method, but if you look at the class's interface definition using OLE View or TypeLib Browser (free but registration required) it's DispId will be 0xfffffffc or -4.
In your own class, you can mark the default property by setting the Procedure ID to "(default)" in the Procedure Attributes dialog (see Making a Property or Method the Default). You already listed the steps for setting up the collection enumerator in your own answer, but you can find this listed as well in the Programmer's Guide topic Creating Your Own Collection Class: The House of Bricks.
Scripting.Dictionary has a dirty secret:
It does not handle enumeration at all, it returns big ugly Variant arrays and your For Each loops iterate over those.
This is one of the reasons why a Dictionary can actually be far less efficient than a standard VB6 Collection.

Populate comboboxes INSIDE a datagrid with UNIQUE data IN actionscript (flex)

i've searched for several hours, and didn't find an answer for my problem.
i'm trying to place comboboxes in a datagrid filled with DYNAMIC data. (the number and the content of these comboboxes always change. i don't know in advance how many columns there are, where i need to use comboboxes. so every single combobox gets a unique dataprovider, which comes from an external source, WHEN the program runs.)
-i found MANY threads discussing this problem, but solving via crappy mxml files, filling the comboboxes inside the sourcecode by hand. i want to point out, that isn't good for me.
-i found a better solution, in which they used some sort of custom itemrenderer to get the data from the internet. (kind of a country chooser thing) but sadly that wasn't good enough, because the number and name of the countries in the world are static more or less, they don't change. so their renderer class didn't depend on any parameters from the main algorithm.
but in my program i calculate the data in my own actionscript objects, then fill an arraylist with that. so at the beginning i load the desired data from the net, and when i get the response of the urlrequest, AFTER that i start to populate the datagrid/combobox.
i can fill any datagrid or combobox without trouble, but to put that combobox inside a datagrid cell seems to be impossible.
could anyone please help? it drives me crazy. i managed to do this in several languages before, c#, java, even php+html, but in flex it looks way too complicated then it should be.
EDIT:
i'm aware, that this amount of network activity could mean some load on the server. i didn't design the philosophy behind it, i just need to wrote a client which meets the expectations. my program looks something like this:
(i'm willing to rewrite any part of it, just to make those nasty comboboxes work)
=========
main.mxml file
this is the main program, i handle some login related stuff here, and set basic design properties for the datagrids. (for example: maxwidth, maxheight, layout constraints etc.)
nothing interesting, except the command when i instantiate the actionscript class, which i wrote to fill the datagrid.
"..<fx:Script>
<![CDATA[
private var myGrid1:MyGridType;
..
somefunction {
myGrid1 = new MyGridType(theDatagridDefinedBefore, "argumentNeededToFillDataGridsWithUniqueData");
}
]]>
</fx:Script>.."
=========
MyGridType.as file
in the constructor i call a urlrequest with the help of the second argument, then add an eventlistener to it. when the data arrives, the eventlistener fires the filler function: i read the results into an arraycollection, then make it the dataprovider for the the datagrid in the first argument.
so far so good.
here comes the trouble with the comboboxes. for a specific number columns, i instantiate my combobox class. let's call that class "MyComboBoxType".
"..
blablabla = new MyComboBoxType(theDatagridDefinedBefore, param1, param2, param3);"
=========
MyComboBoxType.as file
i do nearly exactly the same, what i did in the MyGridType class. call for help from the net with param1-2-3. when i receive the data, fill an arraycollection. maybe set that arraycollection to be the dataprovider for a combobox. AAAAAAAND now i want that arraycollection or combobox to be on the datagrid "theDatagridDefinedBefore".
I know it's not exactly what you're trying to accomplish, but I had a somewhat similar issue in the past. Take a look at How to get the value of a ComboBox within a DataGrid to see if it helps.
If it were me, I would populate the entire ArrayCollection set before binding them to the datagrid if at all possible.
You should build up your custom [Bindable] data structure - say MyGridData class - for the rows in the grid (if you haven't done it yet);
the dataProvider of your grid should
be an Array / ArrayCollection /..
of MyGridData objects.
this step clearly works already, but
for the integrity: override the
getItemEditor function, or specify
it explicitly using mxml, to return
the combobox when needed.
as for the dataProvider of the
combobox, you should specify the
data.comboArray from inside the
renderer class, where data is the
MyGridData instance used by the row
you are processing. (overriding the
set data(value: Object):void
function, you can pre-process it.)
this way, you are working with the
reference of your original instances,
and by the binding you can detect /
show any changes to them directly.

Writing changes from editable datagrid back to local data store - Adobe Air/Flex

I am using Adobe Air to get data from SalesForce, and present it in a datagrid.
I am using a query to get the data, and then put it into an arraycollection that is bound to the datagrid, this works correctly and the data is displayed.
I have made the datagrid editable, and I am able to change the values in the datagrid, but I cannot find how to save the changes to the local database.
I am using the following code:-
protected function VisitReportGrid_changeHandler(event:ListEvent):void{
app.wrapper.save(VisitReportGridProvider)
}
But this has the following error when I try and compile it:-
1067: Implicit coercion of a value of type mx.collections:ArrayCollection to an unrelated type mx.data:IManaged.
Obviously I am doing this wrong, but I cannot find the correct syntax.
Thanks in advance for your help
Roy
This code is not enough to understand where actually is the problem
Need to know what is VisitReportGridProvider, what is wrapper.save() method.
**after comment:
F3DesktopWrapper.save():
public function save(item:IManaged):void
Saves the specified Managed object to the local database. You must make an explicit call to syncWithServer() to update the data on the salesforce server. However, do not call syncWithServer() too often (batch your save calls) as this may use up your alloted API usage. If the item is in conflict, the conflict will be resolved.
Parameters:
item:IManaged — The managed object to create or update.
you're passing parameter with type ArrayCollection which doesn't implement IManaged interface.
You need to pass the item in the ArrayCollection that was changed to the save function. Like:
acc.fieldCollection.updateObject(new AsyncResponder(function(o:Object, t:Object):void {
app.wrapper.save(o as Account);
}, function(e:Object):void {
}));

React to change on a static property

I'm re-writing an MXML item renderer in pure AS. A problem I can't seem to get past is how to have each item renderer react to a change on a static property on the item renderer class. In the MXML version, I have the following binding set up on the item renderer:
instanceProperty={callInstanceFunction(ItemRenderer.staticProperty)}
What would be the equivalent way of setting this up in AS (using BindingUtils, I assume)?
UPDATE:
So I thought the following wasn't working, but it appears as if Flex is suppressing errors thrown in the instanceFunction, making it appear as if the binding itself is bad.
BindingUtils.bindSetter(instanceFunction, ItemRenderer, "staticProperty");
However, when instanceFunction is called, already initialized variables on the given instance are all null, which was the cause of the errors referenced above. Any ideas why this is?
You have 2 options that I am aware of:
Option 1
You can dig into the code that the flex compiler builds based on your MXML to see how it handles binding to static properties. There is a compiler directive called -keep-generated-actionscript that will cause generated files to stick around. Sleuthing through these can give you an idea what happens. This option will involve instantiating Binding objects and StaticPropertyWatcher objects.
Option 2
There is staticEventDispatcher object that gets added at build time to classes containing static variables see this post http://thecomcor.blogspot.com/2008/07/adobe-flex-undocumented-buildin.html. According to the post, this object only gets added based on the presence of static variables and not getter functions.
Example of Option 2
Say we have a class named MyClassContainingStaticVariable with a static variable named MyStaticVariable and another variable someobject.somearrayproperty that we want to get updated whenever MyStaticVariable changes.
Class(MyClassContainingStaticVariable).staticEventDispatcher.addEventListener(
PropertyChangeEvent.PROPERTY_CHANGE,
function(event:PropertyChangeEvent):void
{
if(event.property == "MyStaticVariable")
{
someobject.somearrayproperty = event.newValue as Array;
}
});
I think you need to respond to the "PropertyChanged" event.
If you're going to do that, use a singleton instead of static. I don't think it will work on a static. (If you have to do it that way at all, there are probably a couple ways you could reapproach this that would be better).
var instance:ItemRenderer = ItemRenderer.getInstance();
BindingUtils.bindProperty(this, "myProperty", instance, "theirProperty");
After fiddling with this for a while, I have concluded that this currently isn't possible in ActionScript, not even with bindSetter. It seems there are some MXML-only features of data bindings judging by the following excerpt from the Adobe docs (though isn't it all compiled to AS code anyways)?
You cannot include functions or array
elements in property chains in a data
binding expression defined by the
bindProperty() or bindSetter() method.
For more information on property
chains, see Working with bindable
property chains.
Source: http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_7.html
You can create a HostProxy class to stand in for the funciton call. Sort of like a HostFunctionProxy class which extends from proxy, and has a getProperty("functionInvokeStringWithParameters") which will invoke the function remotely from the host, and dispatch a "change" event to trigger the binding in typical [Bindable("change")] Proxy class.
You than let the HostProxy class act as the host, and use the property to remotely trigger the function call. Of course, it'd be cooler to have some TypeHelperUtil to allow converting raw string values to serialized type values at runtime for method parameters (splitted by commas usually).
Example:
eg.
var standInHost:Object = new HostFunctionProxy(someModelClassWithMethod, "theMethodToCall(20,11)");
// With BindingUtils.....
// bind host: standInHost
// bind property: "theMethodToCall(20,11)"
Of course, you nee to create such a utlity to help support such functionality beyond the basic Flex prescription. It seems many of such (more advanced) Flex bindings are usually done at compile time, but now you have to create code to do this at runtime in a completely cross-platform Actionscript manner without relying on the Flex framework.

Resources