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);
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        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
                                }
                            }
                        }
                    }
                }
            }
        }