/// <summary>
        /// Returns the current view (activity) as a list of .NET objects.
        /// </summary>
        /// <typeparam name="TViewModel">The type of the ViewModel associated to the activity.</typeparam>
        /// <param name="bindingActivity">The current activity we want to get as a list of XML elements.</param>
        /// <returns>A list of .NET objects which composed the view.</returns>
        private static List <View> GetViewAsObjects <TViewModel>(BindingActivity <TViewModel> bindingActivity) where TViewModel : BindableObject
        {
            // Get the objects on the view
            var rootView = bindingActivity.Window.DecorView.FindViewById(Resource.Id.Content);

            return(GetAllChildrenInView(rootView, true));
        }
        /// <summary>
        /// Returns the current view (activity) as a list of XML element.
        /// </summary>
        /// <typeparam name="TViewModel">The type of the ViewModel associated to the activity.</typeparam>
        /// <param name="bindingActivity">The current activity we want to get as a list of XML elements.</param>
        /// <param name="viewLayoutResourceId">The id corresponding to the layout.</param>
        /// <returns>A list of XML elements which represent the XML layout of the view.</returns>
        private static List <XElement> GetViewAsXmlElements <TViewModel>(BindingActivity <TViewModel> bindingActivity, int viewLayoutResourceId) where TViewModel : BindableObject
        {
            List <XElement> xmlElements;

            using (var viewAsXmlReader = bindingActivity.Resources.GetLayout(viewLayoutResourceId))
            {
                using (var sb = new StringBuilder())
                {
                    while (viewAsXmlReader.Read())
                    {
                        sb.Append(viewAsXmlReader.ReadOuterXml());
                    }

                    var viewAsXDocument = XDocument.Parse(sb.ToString());
                    xmlElements = viewAsXDocument.Descendants().ToList();
                }
            }

            return(xmlElements);
        }
        public static void Initialize <TViewModel>(BindingActivity <TViewModel> bindingActivity) where TViewModel : BindableObject
        {
            List <View>     viewElements = null;
            List <XElement> xmlElements  = null;

            // 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();
                            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 (_preventUpdateForTargetProperty)
                            {
                                return;
                            }

                            if (bindingRelationships.Any(p => p.SourceProperty.Name == args.PropertyName))
                            {
                                foreach (var bindingRelationship in bindingRelationships.Where(p => p.SourceProperty.Name == args.PropertyName))
                                {
                                    _preventUpdateForSourceProperty = true;

                                    // 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);

                                    bindingRelationship.TargetProperty.SetValue(bindingRelationship.Control, sourcePropertyValue);

                                    _preventUpdateForSourceProperty = false;
                                }
                            }
                        };

                        // 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);

                            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, () =>
                                    {
                                        if (!_preventUpdateForSourceProperty)
                                        {
                                            executeMethodInfo.Invoke(command, new object[] { null });
                                        }
                                    });

                                    // 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, () =>
                                        {
                                            if (!_preventUpdateForSourceProperty)
                                            {
                                                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
                                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(CheckBox))
                                    {
                                        ((CheckBox)bindingOperation.Control).CheckedChange += (sender, args) => UpdateSourceProperty(sourceProperty, viewModel, args.IsChecked, valueConverter, valueConverterParameter);
                                    }
                                    if (controlType == typeof(EditText))
                                    {
                                        ((EditText)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
                                }
                            }
                        }
                    }
                }
            }
        }