private object GetSourceValue(IBindingMemberInfo targetMember, IDataContext context, bool throwOnError) { object value = GetRawValueInternal(targetMember, context, throwOnError); if (value.IsDoNothing()) { return(BindingConstants.DoNothing); } if (_parameters.ConverterDelegate != null && !value.IsUnsetValue()) { IBindingValueConverter converter = _parameters.ConverterDelegate(context); if (converter != null) { CultureInfo culture = _parameters.ConverterCultureDelegate.GetValueOrDefault(context, BindingServiceProvider.BindingCultureInfo()); object parameter = _parameters.ConverterParameterDelegate.GetValueOrDefault(context); value = converter.Convert(value, targetMember.Type, parameter, culture, context); } } if (value.IsUnsetValue()) { value = _parameters.FallbackDelegate.GetValueOrDefault(context) ?? targetMember.Type.GetDefaultValue(); } if (value == null) { return(_parameters.TargetNullValue); } return(value); }
private object GetSourceValue(IBindingMemberInfo targetMember, IDataContext context, bool throwOnError) { var isDebuggable = IsDebuggable; object value = GetRawValueInternal(targetMember, context, throwOnError); if (isDebuggable) { DebugInfo($"Got a source value: '{value}'"); } if (value.IsDoNothing()) { return(BindingConstants.DoNothing); } if (_parameters.ConverterDelegate != null && !value.IsUnsetValue()) { IBindingValueConverter converter = _parameters.ConverterDelegate(context); if (converter != null) { CultureInfo culture = _parameters.ConverterCultureDelegate?.Invoke(context) ?? BindingServiceProvider.BindingCultureInfo(); object parameter = _parameters.ConverterParameterDelegate?.Invoke(context); if (isDebuggable) { DebugInfo($"Applying converter for source value: '{value}', converter: '{converter}', parameter: '{parameter}', culture: {culture}, target type: '{targetMember.Type}'"); value = converter.Convert(value, targetMember.Type, parameter, culture, context); DebugInfo($"Converter '{converter}' returns value: '{value}'"); } else { value = converter.Convert(value, targetMember.Type, parameter, culture, context); } } } if (value.IsUnsetValue()) { value = _parameters.FallbackDelegate?.Invoke(context) ?? targetMember.Type.GetDefaultValue(); } if (value == null) { return(_parameters.TargetNullValue); } return(value); }
public static void Initialize <TViewModel>(BindableScreen <TViewModel> bindingActivity) where TViewModel : BindableObject { List <View> viewElements = null; List <XElement> xmlElements = null; Childrens.Clear(); // Find the value of the ViewLayoutResourceId property var viewLayoutResourceIdProperty = bindingActivity.GetType().GetProperty(ViewLayoutResourceIdPropertyName); var viewLayoutResourceId = (int)viewLayoutResourceIdProperty.GetValue(bindingActivity); if (viewLayoutResourceId > -1) { // Load the XML elements of the view xmlElements = GetViewAsXmlElements(bindingActivity, viewLayoutResourceId); } // If there is at least one 'Binding' attribute set in the XML file, get the view as objects if (xmlElements != null && xmlElements.Any(xe => xe.Attribute(BindingOperationXmlNamespace) != null)) { viewElements = GetViewAsObjects(bindingActivity); } if (xmlElements != null && xmlElements.Any() && viewElements != null && viewElements.Any()) { // Get all the binding operations inside the XML file. var bindingOperations = ExtractBindingOperationsFromLayoutFile(xmlElements, viewElements); if (bindingOperations != null && bindingOperations.Any()) { // Find the value of the DataContext property (which is, in fact, our ViewModel) var viewModel = bindingActivity.DataContext as BindableObject; if (viewModel != null) { // Load all the converters if there is a binding using a converter if (bindingOperations.Any(bo => !string.IsNullOrWhiteSpace(bo.Converter))) { var valueConverters = GetAllValueConverters(); if (valueConverters != null) { ValueConverters.AddRange(valueConverters.Where(valueConverter => !ValueConverters.Contains(valueConverter))); } } var bindingRelationships = new List <BindingRelationship>(); // OneWay bindings: all changes to any properties of the ViewModel will need to update the dedicated properties on controls viewModel.PropertyChanged += (sender, args) => { if (_busyBindings.Contains(args.PropertyName)) { return; } var bindingRelationShips = bindingRelationships.Where(p => p.SourceProperty.Name == args.PropertyName).ToList(); { foreach (var bindingRelationship in bindingRelationShips) { _busyBindings.Add(args.PropertyName); // Get the value of the source (ViewModel) property by using the converter if needed var sourcePropertyValue = bindingRelationship.Converter == null?bindingRelationship.SourceProperty.GetValue(viewModel) : bindingRelationship.Converter.Convert(bindingRelationship.SourceProperty.GetValue(viewModel), bindingRelationship.TargetProperty.PropertyType, bindingRelationship.ConverterParameter, CultureInfo.CurrentCulture); if (bindingRelationship.Control.Tag != null && bindingRelationship.Control.Tag.Equals("Spinner")) { if (bindingActivity.View == null) { return; } var data = new ArrayAdapter(bindingActivity.View.Context, Android.Resource.Layout.SimpleSpinnerDropDownItem, (IList)sourcePropertyValue); if (bindingRelationship.TargetProperty != null) { bindingRelationship.TargetProperty.SetValue(bindingRelationship.Control, data); } if (bindingRelationship.SelectedItem != null) { PropertyInfo itemProperty = typeof(TViewModel).GetProperty(bindingRelationship.SelectedItem); var itemValue = itemProperty.GetValue(bindingActivity.DataContext, null); if (itemValue != null) { ((Spinner)bindingRelationship.Control).SetSelection( ((IList)sourcePropertyValue).IndexOf(itemValue)); } } } else if (bindingRelationship.Control.Tag != null && bindingRelationship.Control.Tag.Equals("ListView")) { var data = new ArrayAdapter(bindingActivity.View.Context, Android.Resource.Layout.SimpleListItem1, (IList)sourcePropertyValue); bindingRelationship.TargetProperty.SetValue(bindingRelationship.Control, data); ((ListView)bindingRelationship.Control).SetSelection(0); } else { bindingRelationship.TargetProperty.SetValue(bindingRelationship.Control, sourcePropertyValue); } _busyBindings.Remove(args.PropertyName); } } }; // For each binding operations, bind from the source (ViewModel) to the target (Control) // and from the target (Control) to the source (ViewModel) in case of a TwoWay binding. foreach (var bindingOperation in bindingOperations) { var sourceProperty = typeof(TViewModel).GetProperty(bindingOperation.Source); PropertyInfo selectedItemProperty = null; if (!string.IsNullOrEmpty(bindingOperation.SelectedItem)) { selectedItemProperty = typeof(TViewModel).GetProperty(bindingOperation.SelectedItem); } var bindingEvent = bindingOperation.Control.GetType().GetEvent(bindingOperation.Target); if (bindingEvent != null) { // The target is an event of the control if (sourceProperty != null) { // We need to ensure that the bound property implements the interface ICommand so we can call the "Execute" method var command = sourceProperty.GetValue(viewModel) as ICommand; if (command == null) { throw new InvalidOperationException(string.Format("The source property {0}, bound to the event {1}, needs to implement the interface ICommand.", bindingOperation.Source, bindingEvent.Name)); } // Add an event handler to the specified event to execute the command when event is raised var executeMethodInfo = typeof(ICommand).GetMethod("Execute", new[] { typeof(object) }); AddHandler(bindingOperation.Control, bindingOperation.Target, () => { try { executeMethodInfo.Invoke(command, new object[] { null }); } catch (Exception ex) { throw ex; } }); // Add an event handler to manage the CanExecuteChanged event of the command (so we can disable/enable the control attached to the command) var currentControl = bindingOperation.Control; var enabledProperty = currentControl.GetType().GetProperty("Enabled"); if (enabledProperty != null) { enabledProperty.SetValue(currentControl, command.CanExecute(null)); AddHandler(command, "CanExecuteChanged", () => enabledProperty.SetValue(currentControl, command.CanExecute(null))); } } else { // If the Source property of the ViewModel is not a 'real' property, check if it's a method var sourceMethod = typeof(TViewModel).GetMethod(bindingOperation.Source, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); if (sourceMethod != null) { if (sourceMethod.GetParameters().Length > 0) { // We only support calls to methods without parameters throw new InvalidOperationException(string.Format("Method {0} should not have any parameters to be called when event {1} is raised.", sourceMethod.Name, bindingEvent.Name)); } // If it's a method, add a event handler to the specified event to execute the method when event is raised AddHandler(bindingOperation.Control, bindingOperation.Target, () => { sourceMethod.Invoke(viewModel, null); }); } else { throw new InvalidOperationException(string.Format("No property or event named {0} found to bint it to the event {1}.", bindingOperation.Source, bindingEvent.Name)); } } } else { if (sourceProperty == null) { throw new InvalidOperationException(string.Format("Source property {0} does not exist on {1}.", bindingOperation.Source, typeof(TViewModel).Name)); } // The target is a property of the control var targetProperty = bindingOperation.Control.GetType().GetProperty(bindingOperation.Target); if (targetProperty == null) { throw new InvalidOperationException(string.Format("Target property {0} of the XML binding operation does not exist on {1}.", bindingOperation.Target, bindingOperation.Control.GetType().Name)); } // If there is a Converter provided, instanciate it and use it to convert the value var valueConverterName = bindingOperation.Converter; IBindingValueConverter valueConverter = null; if (!string.IsNullOrWhiteSpace(valueConverterName)) { var valueConverterType = ValueConverters.FirstOrDefault(t => t.Name == valueConverterName); if (valueConverterType != null) { var valueConverterCtor = valueConverterType.GetConstructor(Type.EmptyTypes); if (valueConverterCtor != null) { valueConverter = valueConverterCtor.Invoke(null) as IBindingValueConverter; } else { throw new InvalidOperationException(string.Format("Value converter {0} need an empty constructor to be instanciated.", valueConverterName)); } } else { throw new InvalidOperationException(string.Format("There is no converter named {0}.", valueConverterName)); } } var valueConverterParameter = bindingOperation.ConverterParameter; // Get the value of the source (ViewModel) property by using the converter if needed var sourcePropertyValue = valueConverter == null?sourceProperty.GetValue(viewModel) : valueConverter.Convert(sourceProperty.GetValue(viewModel), targetProperty.PropertyType, valueConverterParameter, CultureInfo.CurrentCulture); // Set initial binding value if (sourcePropertyValue is int) { targetProperty.SetValue(bindingOperation.Control, sourcePropertyValue.ToString()); } else if (sourcePropertyValue is bool) { targetProperty.SetValue(bindingOperation.Control, sourcePropertyValue); } else if (bindingOperation.Control.Tag != null && (bindingOperation.Control.Tag.Equals("ListView"))) { if (sourcePropertyValue == null) { return; } var data = new ArrayAdapter(bindingActivity.View.Context, Android.Resource.Layout.SimpleListItemChecked, (IList)sourcePropertyValue); targetProperty.SetValue(bindingOperation.Control, data); } else if (bindingOperation.Control.Tag != null && bindingOperation.Control.Tag.Equals("Spinner")) { if (sourcePropertyValue == null) { return; } var data = new ArrayAdapter(bindingActivity.View.Context, Android.Resource.Layout.SimpleSpinnerDropDownItem, (IList)sourcePropertyValue); targetProperty.SetValue(bindingOperation.Control, data); if (bindingOperation.SelectedItem != null) { PropertyInfo itemProperty = typeof(TViewModel).GetProperty(bindingOperation.SelectedItem); var itemValue = itemProperty.GetValue(bindingActivity.DataContext, null); if (itemValue != null) { ((Spinner)bindingOperation.Control).SetSelection( ((IList)sourcePropertyValue).IndexOf(itemValue)); } bindingRelationships.Add(new BindingRelationship { SourceProperty = sourceProperty, SelectedItem = itemProperty.Name, Control = bindingOperation.Control }); } } else { targetProperty.SetValue(bindingOperation.Control, sourcePropertyValue); } // Add a relationship between the source (ViewModel) and the target (Control) so we can update the target property when the source changed (OneWay binding) bindingRelationships.Add(new BindingRelationship { SourceProperty = sourceProperty, TargetProperty = targetProperty, Converter = valueConverter, ConverterParameter = bindingOperation.ConverterParameter, Control = bindingOperation.Control }); if (bindingOperation.Mode == BindingMode.TwoWay) { // TwoWay bindings: Update the ViewModel property when the dedicated event is raised on the bound control var controlType = bindingOperation.Control.GetType(); // Bind controls' events to update the associated ViewModel property #region Bind controls' events to update the associated ViewModel property // TODO: Need to improve this! if (controlType == typeof(CalendarView)) { ((CalendarView)bindingOperation.Control).DateChange += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, new DateTime(args.Year, args.Month, args.DayOfMonth), valueConverter, valueConverterParameter); } else if (controlType == typeof(ListView)) { ((ListView)bindingOperation.Control).ItemClick += (sender, args) => { var result = ((List <string>)sourcePropertyValue).ElementAt(args.Position); UpdateSourceProperty(selectedItemProperty, viewModel, result, valueConverter, valueConverterParameter); }; } else if (controlType == typeof(Spinner)) { ((Spinner)bindingOperation.Control).ItemSelected += (sender, args) => UpdateSourceProperty(selectedItemProperty, viewModel, args.Parent.SelectedItem, valueConverter, valueConverterParameter); } else if (controlType == typeof(CheckBox)) { ((CheckBox)bindingOperation.Control).CheckedChange += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.IsChecked, valueConverter, valueConverterParameter); } else if (controlType == typeof(EditText)) { ((EditText)bindingOperation.Control).TextChanged += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.Text.ToString(), valueConverter, valueConverterParameter); } else if (controlType == typeof(TextView)) { ((TextView)bindingOperation.Control).TextChanged += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.Text.ToString(), valueConverter, valueConverterParameter); } else if (controlType == typeof(RadioButton)) { ((RadioButton)bindingOperation.Control).CheckedChange += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.IsChecked, valueConverter, valueConverterParameter); } else if (controlType == typeof(RatingBar)) { ((RatingBar)bindingOperation.Control).RatingBarChange += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.Rating, valueConverter, valueConverterParameter); } else if (controlType == typeof(SearchView)) { ((SearchView)bindingOperation.Control).QueryTextChange += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.NewText, valueConverter, valueConverterParameter); } else if (controlType == typeof(Switch)) { ((Switch)bindingOperation.Control).CheckedChange += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.IsChecked, valueConverter, valueConverterParameter); } else if (controlType == typeof(TimePicker)) { ((TimePicker)bindingOperation.Control).TimeChanged += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, new TimeSpan(args.HourOfDay, args.Minute, 0), valueConverter, valueConverterParameter); } else if (controlType == typeof(ToggleButton)) { ((ToggleButton)bindingOperation.Control).CheckedChange += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.IsChecked, valueConverter, valueConverterParameter); } else if (controlType == typeof(SeekBar)) { ((SeekBar)bindingOperation.Control).ProgressChanged += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.Progress, valueConverter, valueConverterParameter); } #endregion } } } } } } }