Bidirectional binding for flex ComboBox? - actionscript-3

I have a collection that I want to bind as data input for a ComboBox:
private static var LOGOS:ArrayCollection = new ArrayCollection([
{index:0, label=logo1},
{index:1, label=logo2}
]);
<s:ComboBox selectedItem="#{model.labelIndex}" labelField="#label" dataProvider="{LOGOS}" />
Now, when selecting an item, the binding should send the associated index property of the objext to the model and update labelIndex.
Of course it does not work as above, because labelIndex is of different datatype than the ArrayCollection.
[Bindable]
private var model:MyModel;
[Bindable]
class MyModel {
public var:Number labelIndex;
}
Question: how can I map the array element to the model and vice versa?

What you are looking for will require some scripting, binding isn't smart enough to figure out how to handle this on its own.
You can use the BindingUtils class to define the bindings, and use the chain argument of the bindProperty method to modify how values are being looked up.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/binding/utils/BindingUtils.html
For the combo.selectedItem to model.labelIndex binding, you can specify the chain as an array, where the elements define the path where to look for the value:
BindingUtils.bindProperty(model, 'labelIndex', combo, ['selectedItem', 'index']);
This will bind to the selectedItem property, and pass the value of the items index property.
The other way around is a little more tricky and will require using a getter function which grabs the object from the datasource, based on the labelIndex value:
BindingUtils.bindProperty(combo, 'selectedItem', model, {
name: 'labelIndex',
getter: function(host:MyModel):Object
{
return LOGOS.source.filter(function(item:Object, index:int, array:Array):Boolean
{
return item.index === host.labelIndex;
})[0];
}
});
This will bind to the labelIndex property, and the getter function will be invoked when the property changes. The function will filter the datasource based on the models changed labelIndex property value, and return the source object with the matching index property value, which will finally be set for the combobox selectedItem property.
Your combobox definition will of course need an id in order to be targetable via script
<s:ComboBox id="combo" dataProvider="{LOGOS}" />
Note that there's no need forthe the # in the labelField property, this is only used with XML datasources where you need to target an attribute. However, actually you don't need to specify this at all, since label is the default value of the labelField property.

Related

Caliburn.Micro cannot bind to LongListSelector

Windows phone 8 with Caliburn.Micro 2.0.1 cannot bind to LongListSelector. For Caliburn tries to bind items to Visibility property.
Here is XAML
<phone:LongListSelector
x:Name="Items"
ItemTemplate="{StaticResource MyItemTemplate}">
</phone:LongListSelector>
and view model property is pretty basic:
IObservableCollection<Item> _Items;
public IObservableCollection<Item> Items
{
get { return _Items; }
set
{
_Items = value;
NotifyOfPropertyChange(() => Items);
}
}
public class Item : PropertyChangedBase
{
string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; NotifyOfPropertyChange(() => Name); }
}
}
Here is the debug output
System.Windows.Data Error: 'MS.Internal.Data.DynamicValueConverter' converter failed to convert value 'Caliburn.Micro.BindableCollection`1[Checklists.ViewModels.ItemsPageViewModel+Item]' (type 'Caliburn.Micro.BindableCollection`1[CLS.ViewModels.ItemsPageViewModel+Item]'); BindingExpression: Path='Items' DataItem='CLS.ViewModels.ItemsPageViewModel' (HashCode=38524289); target element is 'Microsoft.Phone.Controls.LongListSelector' (Name='Items'); target property is 'Visibility' (type 'System.Windows.Visibility').. System.InvalidOperationException: Can't convert type Caliburn.Micro.BindableCollection`1[CLS.ViewModels.ItemsPageViewModel+Item] to type System.Windows.Visibility.
at MS.Internal.Data.DefaultValueConverter.Create(Type sourceType, Type targetType, Boolean targetToSource)
at MS.Internal.Data.DynamicValueConverter.EnsureConverter(Type sourceType, Type targetType)
at MS.Internal.Data.DynamicValueConverter.Convert(Object value, Type targetType, Object parameter, CultureInfo culture)
at System.Windows.Data.BindingExpression.ConvertToTarget(Object value).
The convention you're trying to make use of here is the one Caliburn.Micro has set up for ItemsControl, unfortunately it doesn't appear that LongListSelector inherits from ItemsControl. It's odd that it doesn't so you'd assume everything would just work.
A very basic convention for LongListSelector could be added with the following
ConventionManager.AddElementConvention<LongListSelector>(LongListSelector.ItemsSourceProperty, "DataContext", "Loaded");
which would be called in your Bootstrapper. Note that this convention does't set up a default ItemTemplate that the one for ItemsSource does.
Edit:
The first property is used for property binding conventions, if you have a property matching the x:Name then this is the property bound to.
The second is the parameter property, if you refer to the element as a parameter in a message such as cm:Message.Attach="DoStuff(Items)" then what property is used.
The third is event that fires the action if there is one attached.
The one mentioned in the comments
ConventionManager.AddElementConvention<LongListSelector>(LongListSelector.Items‌​SourceProperty, "SelectedItem", "SelectionChanged");
is better in that if you're using any of the other features that require the second two properties. The first convention simply uses boiler plate parameters.
which makes sense its not a bug since LongListSelector isn't in the ConventionManager list of controls for windows Phone. You would have to add it as a custom convention for that control. Otherwise just bind normally...

WP8 bind an object array to checkbox

How to bind an array with a checkbox visibility.
I was initially using a visibilityconverter for binding one object but how do i bind it with more than one objects, as now the visibility depends on more than one parameters.
Any clue as to how to bind a object[] to checkbox in a listbox in wp8
I'd create an additional property and bind my checkbox's visibility to it:
public Visibility checkboxVisibility
{
get
{
return (condition) ? Visibility.Visible : Visibility.Collapsed;
}
}
Don't forget to notify checkbox when checkboxVisibility value might change- when you modify any object included in condition use(after implementing INotifyPropertyChanged interface):
NotifyPropertyChanged("checkboxVisibility");

Creating custom ExpandableListView, how to bind to "GroupTemplate" in axml

I've created a bindable version of ExpandableListView based off of https://github.com/hlogmans/MvvmCross.DeapExtensions/ and put it in my app. I want to add a GroupTemplate that I can bind to in the axml which would be similar to MvxListView's ItemTemplate.
Do I need to subclass MvxAndroidBindingResource? I'm also confused as to how the MvxBindingAttributes fits in.
The easiest route for this might be for you to take a read through how MvxListView and MvxAdapter work.
The MvxBindingAttributes (https://github.com/MvvmCross/MvvmCross/blob/v3.1/nuspec/DroidContent/MvxBindingAttributes.xml) allow MvvmCross to add new xml tags to the axml files.
The MvxAndroidBindingResource class (https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding.Droid/ResourceHelpers/MvxAndroidBindingResource.cs) is the C# code to parse the values for the attribute tags defined in MvxBindingAttributes.
You can see this in action for an MvxListView in https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding.Droid/Views/MvxListView.cs#L33
public MvxListView(Context context, IAttributeSet attrs, IMvxAdapter adapter)
: base(context, attrs)
{
// Note: Any calling derived class passing a null adapter is responsible for setting
// it's own itemTemplateId
if (adapter == null)
return;
var itemTemplateId = MvxAttributeHelpers.ReadListItemTemplateId(context, attrs);
adapter.ItemTemplateId = itemTemplateId;
Adapter = adapter;
}
In particular, the line:
var itemTemplateId = MvxAttributeHelpers.ReadListItemTemplateId(context, attrs);
This uses the id values parsed in MvxAndroidBindingResource to read the axml tag value for local:MvxItemTemplate

Flex custom TreeItemRenderer that changes based on type of object?

I have a Flex tree control that holds a tree full of various types of objects. I'd like to change the label of the item based on this type (and other properties). I'd prefer to do this in a custom TreeItemRenderer rather than via label or labelfunction.
It seems that whatever I do, I cannot typecheck nor cast the objects and thus I get [Object object] in the nodes of my tree. Here is my renderer:
public class MyCustomTreeItemRenderer extends TreeItemRenderer {
// Overriding to set the text for each tree node.
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (super.data) {
trace("Rendering node:");
if (super.data is MyClassA) {
trace(" MyClassA");
super.label.text = MyClassA(super.data).name
}
if (super.data is MyClassB) {
trace(" MyClassB");
super.label.text = MyClassB(super.data).id;
}
}
}
public function NavigateTreeItemRenderer() {
super();
}
}
Examining the trace shows I am rendering the node, but I never end up inside either of the two if statements. When I run in the debugger, I can actually the properties on the "data" that correspond to MyClassA and MyClassB!
According your conditions you could use another way:
override public function set data(value : Object) {
super.data = value;
label.text = ...
}
But much simple is write labelFunction
Turns out I was bitten by a subtle issue whereby the ActionScript3 compiler automatically dropped my classes from my remote services SWC because it did not detect any direct reference to those classes. Because those objects are instantiated by RemoteObject result handlers and placed into an child ArrayCollection, there was no reference to them that the compiler could detect.
When this happens, rather than throwing an exception, Flex's RemoteObject implementation simply (and quietly) hydrates them into a generic Object, thus negating the value of their strongly typed interface.
The workaround was to declare a variable within each derived service class for every type of value object that could ever appear within the their object graphs.

Playing nicely with "for each" in ActionScript?

Lets say I have an ActionScript class: MyClass and that class has data in it. Now, lets say I want to iterate over that data using "for each":
var myData:MyClass = new MyClass();
myData.Populate(fromSource);
for each(var item in myData) {
DoSomethingWith(item);
}
Of course, this does nothing, because MyClass is a custom class, and I haven't done anything special to it yet.
What do I need to do to MyClass to make it play nicely with "for each"? Can I hand it an iterator or an enumerator or something?
I believe you need to extend Proxy class and implement nextValue(index:int). It is used by for each.
Ok, I figured it out.
#alxx helped me get to the answer. Here is a complete answer:
public class MyClass extends Proxy
{
override flash_proxy function nextNameIndex (index:int):int {
// This is the command to move to the next item or start over (index == 0)
// return an incremented index when there is data
// return 0 when you are done.
}
override flash_proxy function nextValue(index:int):* {
// This is the command to get the data
// The index that is passed in is the index returned in nextNameIndex
}
}
You should check out the Adobe livedocs page on for each ... in. They have your answer there.
"[for each ... in] Iterates over the items of a collection and executes statement for each item. Introduced as a part of the E4X language extensions, the for each..in statement can be used not only for XML objects, but also for objects and arrays. The for each..in statement iterates only through the dynamic properties of an object, not the fixed properties. A fixed property is a property that is defined as part of a class definition. To use the for each..in statement with an instance of a user-defined class, you must declare the class with the dynamic attribute."