Forgive me, I'm new to Flash Builder 4 and Actionscript 3 (actually, to programming as a whole beyond some very simplistic stuff). I have watched / read a bunch of tutorials, and started a project but now seem to have hit a wall. The answer is most likely simple, but seems to be alluding me.
How do I (or What approach should I take) to control visual elements, for instance, BorderContainer's, that I created dynamically?
As is, I have an Application containing a BorderContainer and a DataGrid. At runtime, 3 new BorderContainers (which are dragable, and resizeable) are created based on XML data that contains X & Y co-ordinates, and Height and Width values, and then added to the pre-existing BorderContainer. How would I go about getting the properties of these children BorderContainers to be displayed and remain up-to-date in the DataGrid (such as when they are moved/resized)?
My intentions in the future would be to have a custom component which displays a summary of these items in a separate area (think photoshop "layers" control, but much more simplistic), but wanted to get a better understanding of what's going on first.
Any input, documentation, examples, etc. is all appreciated. Again, I apologize for what may be an incredibly easy solution, or if any of my language is unclear, I'm new to this ^_^;
I would create an ArrayCollection of the BorderContainers with their various properties set (also make sure you call addElement on the parent BorderContainer). Make sure your ArrayCollection is declared as Bindable, then set it as the dataProvider for your DataGrid. Then specify the columns for your DataGrid based on whatever properties you want to display (height, width, etc). Now whenever the properties of the BorderContainers change, the DataGrid will automatically update.
Assuming a pure AS3 project, the best approach is to build a dictionary of your objects.
Let's also assume you've created identifiers for the components, or can easily create them at runtime.
var containers:Dictionary = new Dictionary();
private function _init():void
{
//some loop to create objects
containers[newObject.name] = newObject;
}
Later you can quickly access it by just grabbing the hashed index from the containers dictionary.
Now, assuming a Flex project, we have a few more approaches we can take:
DisplayObjectContainer implements getChildByName()
Group implements getElementAt, and numElements to iterate over, check names, and return value expected.
Personally, I still prefer the dictionary approach...
As for keeping things up to date, you can look into Binding (typically a Flex-only solution) or more appropriately investigate the events dispatched:
Event.RESIZE
Event.MOVE
etc.
In the handlers, just update your UI!
HTH, otherwise post more info and we'll see what we can figure out.
Related
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.
I'm trying to extend ButtonBarButton to include an additional label that shows the number of updates for the respective box in a viewstack (which is the tabbar's dataProvider).
I can get the additional label (named indicatorLabel) to read an initial value from data, but I can't get it to update automatically like the actual "label" attribute. My understanding was that you could bind to the data object, but that doesn't appear to be the case.
The box that is used in the viewstack has an attribute called _indicator
[Bindable]
public var _indicator:String;
Which I know is updating properly because I can see it updating in the box (which also has a label bound to it). It appears to just not update the buttonbarbutton.
My buttonbarbutton class has the following (where init() is called in creationComplete
[SkinPart]
public var indicatorLabel:spark.components.Label;
private function init():void
{
indicatorLabel = data._indicator;
addEventListener("dataChange", onDataChangeHandler);
}
private function onDataChangeHandler(e:Event):void
{
trace(e.target.label + ' ' + e.target._indicator);
}
I'm guessing my assumptions for either databinding or the data obj that gets passed to the button are incorrect. Any help is appreciated.
I have answered a question here just a few days ago that is closely related to this one. It's a bit more abstract, but it should answer yours too. Have a look at Flex : Communicate between the skin and the data model?
As for your additional question about performance improvement: you just have to keep in mind that whenever you use binding, the compiler will generate some event listeners to listen for changes in the data. Of course that's not an issue with a one-off component or even with a bunch of instances, but it may become one if you have a List with thousands of components that all use binding. First of all the garbage collector won't clean these components up so easily when they're no longer needed and secondly - depending on your implementation - thousands of events may be firing at once.
bottom line: you could consider the commitProperties() approach performance tuning, hence you shouldn't consider it until you actually run into a performance issue.
on the other hand from an architectural point of view: that approach allows for a cleaner separation of the component and its skin. With the hostComponent / binding approach you could say the skin knows too much: it has to know about its host component and its properties. While the other one allows you to have a completely 'dumb' skin. So again I tend to use binding with one-off components and commitProperties() with highly reusable ones.
In the end it's all a trade-off (because the 'clean' way is more complex and more work) and it's up to you to make a weighed decision for each particular situation.
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.
I want to verify that a generated class (single entity or collection) from an O/RM tool is data binding compatible.
I read that supported data binding types in WCF are: one time, one way, two way, one way from source in WCF. But how about "old school" .NET 1.1 data binding ?
It looks kind of difficult to check in code what kind of data binding support there is. You also have difference in runtime and design time data binding support. When reading some webpages I read different kind of implementations: implement IList, IComponent, INotifyPropertyChanged, IBindingList.... pffffff I don't know exactly where to look for...
You can databind to virtually any class. Let's imagine you create a very simple class, with a few properties, say for instance, Person with a Name and Age. I am talking about a plain simple class with absolutely nothing fancy about it.
If you create an instance of Person, you can do several things with it, and I will assume you are working with Windows Forms, but this mostly applies to other frameworks:
- You can bind its properties to properties of controls.
- You can bind it to datagrids, lists, etc. In the former case you can set mappings of which properties bind to which columns. In the latter, which property is displayed in the list, which property is the value selected by the user.
- Even better, you can bind it to a bindingSource.
Binding a single instance to a grid or a list isn't that useful, so what usually is done is that you create a list of instances and bind those to the grid. Even more correct is to bind the list to a bindingsource and the grid to the bindingsource also.
You can see a good article here about how to do all this.
Now, about all the interfaces you mention, they all only add more value to the databinding experience. Let's talk about a few of them.
INotifyPropertyChanged. Person is not less "databindable" than any other object if it does not implement this interface. What instances of Person are not able to do, however, is notify the controls their properties are bound to that the latter have changed. Try this: Bind the Name property of a Person instance to the Text property of a TextBox. Create a button that when clicked changes the value of that instance's Name. You will see the TextBox does not update after clicking the button. If, on the other hand, you implement INotifyPropertyChanged and have the setter of the Name property raise the PropertyChangedEvent that is defined by the interface, after repeating the experience, you will see that the textbox is updated automatically.
IEnumerable. If instead of a single Person, you want to databind not to a set of people, you can create a list of people and databind to that list. Let's take for instance, List lst = new List(); How do the databinding controls like datagrid, bindingSource, etc., know you want to bind to a set of Person(s) and not to the properties of lst itself? It is because List implements IEnumerable. So, whenever you bind these controls to an instance of anything that implements IEnumerable, the controls know that you intend to bind not to the properties of the list, but to the instances the list refers to. How do they know the type of objects the list contains? To be more generic and support any type of IEnumerable implementation, they just check the type of the first element in the list and assume all others are equal.
IBindingList: Even if Person implements IPropertyChanged, if you group instances of Person into a List bind that list to a control and, by code, change the value of a property of one of the instances, you will see nothing happen in the screen. This happens because Person is notifying, not the binding source, but the list. But the list wasn't made for databinding, so it is not listening, nor propagating the event to the control. Lists that implement IBindingList, like BindingList, offer a better databinding support precisely by listening to the PropertyChangedEvent events of their contents and propagating them up to the databound control.
I am affraid I have given you no way of determining if an object is databoundable, because virtually all them are, but I hope I've given you a way of determining different levels of databinding support (INotifyPropertyChanged and IBindingList). I am assuming you know how to check for these via reflection.
Any instance of a class with properties is data bindable. (in fact instances of any class with fields or properties at all are data bindable)
Using reflection in .NET makes it very easy to discover/use data in an object. (at a small performance cost)
In order to answer this question you'd need to provide the specific usage scenarios you'll be encountering.
Rui gives some good explanation of different common data binding patterns, but each of them is for solving specific problems.
The right answer is always dependent on the context.
:)
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.