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");
Related
What is the best to handle reference type data, for example json data, bind to a component. For example,
<my-component [data]="jsonData"></my-component>
where in .ts jsonData= {'key1':'value1','key2','value2'}
Now if on button click I change my jsonData as
{'key1':'valueX','key2','value2'}
Angular won't be able to detect this change, hence doesn't run ngOnChanges(),because angular only checks reference of the data-binding changed or not as a result view won't be refreshed. How should I deal with it?
I can put logic to refresh my view in ngDoCheck() but it is called a lot of times. If json is large, then processing it each time ngDoCheck runs can be an expensive operation.
One possible solution I could think of is to make a service for that component, and on button click, pass the new json to that service, service will publish that event with changed data to my component and component, listening for the events of service, will refresh the view with that new data.
Is there any easier way to handle JSON/Array changes bind to component?
You can use a setter for #Input property in your component.
Intercept input property changes with a setter
Use an input property setter to intercept and act upon a value from the parent.
#Input() set data(jsondata: any) {
this._data = jsondata;
}
Refer to the documentation here for more details.
Update after comments:
Yes you can implement ngOnChanges as well for this,
as the documentation states:
You may prefer this approach to the property setter when watching multiple, interacting input properties.
See the below example from the documentation,
export class MyComponent implements OnChanges {
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
for (let propName in changes) {
let changedProp = changes[propName];
let to = JSON.stringify(changedProp.currentValue);
if (changedProp.isFirstChange()) {
console.log(`Initial value of ${propName} set to ${to}`);
} else {
let from = JSON.stringify(changedProp.previousValue);
console.log(`${propName} changed from ${from} to ${to}`);
}
}
}
}
You can get hold of the old and new values of your members using previousValue and the currentValue properties respectively.
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.
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...
Using a value converter in WPF, you can return something like DependecyProperty.UnsetValue or Binding.DoNothing as special values to say leave the binding alone. Is there a similar mechanism in MVVMCross?
To be more specific about what I'm trying to do, is I have a view model property that is a three-state enum that I need to bind to 3 binary controls. So I thought I could bind each of the controls to a MyEnum -> bool converter that will have a conversion parameter set to the value of the converter and in the Convert method it will return true if the MyEnum state is equal to the parameter and false otherwise. So far so good. But I want this binding to be two-way, so I need to convert back. My convert back works something like this:
protected override MyEnum ConvertBack(bool value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter is MyEnum)
{
if (value)
{
return (MyEnum)parameter; // this is fine
}
else
{
return ???
}
}
return base.ConvertBack(value, targetType, parameter, culture);
}
Basically, what I want to be able to do is say, if the state of my control is true update the bound property on my view model to be the same as the parameter, if not, leave the view model property alone.
Maybe this is the problem with using the strongly typed value converters?
If anybody else encounters a similar problem, in addition to Stuart's suggestions, this is what I actually ended up doing.
In the View Model
private MyEnum _selectedValue = MyEnum.SomeValue;
public MyEnum ? SelectedValue
{
get
{
return _selectedTab;
}
set
{
if (value.HasValue)
{
_selectedTab = value.Value;
RaisePropertyChanged(() => SelectedTab);
}
}
}
Then in my converter (now a MvxValueConverter<MyEnum?, bool>):
protected override MyEnum? ConvertBack(bool value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter is MyEnum) // this is just a sanity check
{
if (value)
{
return (MyEnum)parameter;
}
else
{
return null
}
}
return base.ConvertBack(value, targetType, parameter, culture);
}
It's not a general solution, but it fits my immediate need.
Update: UnsetValue is now available in mvvmcross - so this answer is no longer required.
Binding.DoNothing, DependencyProperty.UnsetValue or equivalent are currently not implemented in the MvvmCross binding system, and I don't believe there are any feature or pull requests currently open on this.
I'm afraid I don't really understand your example - the 'conversion parameter set to the value of the converter' and the if (parameter is MyEnum) snippet rather threw me - as far as I know, the parameter in windows xaml binding is a constant so I don't quite understand this example. Sorry if I've misunderstood.
Regardless, some possible approaches that might be used to assist with getting this functionality up and running in your mvx app are:
You could throw an exception from the ValueConverter:
For the Convert direction, the binding FallbackValue is used if a ValueConverter fails (throws an exception)
For the ConvertBack direction, the binding does nothing if an exception is thrown (although this would unfortunately generate some trace "error" output each time)
You could implement a custom binding for your control - something which only fires change updates when the change is one you are interested in (some custom bindings are introduced in N=28 in http://mvvmcross.wordpress.com which creates bindings like BinaryEditFooTargetBinding.cs - it would be straight-forward to customise/filter the TargetOnMyCountChanged behaviour in that class)
You could implement a custom binary control - which only fires change updates when the change is one you are interested in (for various platforms, some custom controls are introduced in N=18 to N=20 in http://mvvmcross.wordpress.com)
You could implement ViewModel binary properties which mirror your View and map to your three state enum
You could use a ValueCombiner within the Tibet binding system - it should be relatively straight-forward to work out a combiner based on top of something like MvxSingleValueCombiner.cs
Alternatively/additionally, please do also consider raising a Feature and/or Pull Request for this 'do nothing' type of functionality to be included in the TryGetValue/SetValue methods in https://github.com/slodge/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Binding/Bindings/SourceSteps/MvxSourceStep.cs#L66 - but obviously the timeline on that might be longer.
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.