Introduction into MugenMvvm Bindings

At the foundation of MVVM pattern laid the idea of data bindings. Data bindings provides the way to connect any View with appropriate ViewModel. The idea of data bindings should be well known to every Mvvm developer.

MugenMvvm has it's own implementation data binding idea. This implementation provides the way to write consistent code for every platform.

MugenMvvm data bindings possess the next abilities:

  • Extensibility - bindings are based on syntax tree and could be easily extended without any text manipulations. The approach used for binding implementation is very similar to C# expression trees.
  • C# syntax support - support of all base operators of C# is implemented. It means that all C# operators (??, ?:, +, -, *, /, %, ==, !=, <, >, <=, >=, &&, ||, |, &, !, ~) are allowed to use inside a binding expression. Priority of operations are taken into account in compliance with C# language specification. Also supported: lambda expressions, generic type inference, method calls, extension method calls, LINQ. You could see examples in Binding Examples section.
  • Events Bindings - support of binding on control events is implemented. The access to EventArgs parameter can be got by usage of $args keyword.
  • Validation Support - validation is provided by INotifyDataErrorInfo interface. It allows to show error messages on every platform you needed. With help of $GetErrors() method you can get all errors on the view or only errors for particular properties. It could be useful whenever exists a necessity to show an error to end-user.
  • Extended Command Bindings - an end-user can manipulate in what manner visual control will react on CanExecute method's result. In other words, "Enabled" property of any control can be controlled by bound command's CanExecute method.
  • Relative Bindings - bindings can be setted in relative manner i.e. data context of control can be setted on any other control in visual tree. It is the analogue of RelativeSource property on XAML platforms (WPF, WinRT, etc).
  • Attached Bindings - MugenMvvm bindings support attached properties, methods and events. It allows end-users to extend any visual control on any platform.
  • Dynamic Resource Bindings - any object from resources are available through binding mechanism. For instance, cross-platform localization of application can be easily implemented.
  • Fluent syntax - as long as MugenMvvm support text-format bindings which are included in View layout files, C# based syntax for binding creation are also provided. Please see examples of fluent syntax in appropriate section.
  • Support of cross-platform development - MugenMvvm bindings are fully cross-platform, so any platform has equal set of binding's capabilities.
  • Performance - MugenMvvm bindings are incredibly fast in comparison with standard platform's bindings. Nevertheless, they have many more capabilities than standard ones. To get more information about binding bindings please read a text in MugenMvvm slack channel: Some words about MugenMvvm Bindings.

MugenMvvm Bindings Syntax

Base syntax of MugenMvvm bindings in xaml or xml markup is:

Binding TargetPath SourcePath, [any of avail. props (Mode, Default, etc)]

Here TargetPath is the path for binding from the control, SourcePath is a path for binding from the source and the comma followed by any of available binding properties (see below).
Presented below is a list of keywords available in MugenMvvm bindings:

  • $this - returns reference on bound control.
  • $self - returns current control on which binding is set. This is analogue of WPF {RelativeSource Self}.
  • $root - returns current root element of control on which binding is set.
  • $context - returns current DataContext of control on which binding is set. This is the analogue of WPF {RelativeSource Self, Path=DataContext}.
  • $args - returns current EventArgs parameter, it can be only used if TargetPath points on an event.
  • $Element(ElementName), $El(ElementName) - provides an ability to search an element by its ElementName.
  • $Relative(Type), $Relative(Type, 2), $Rel(Type), $Rel(Type, 2) - provides an ability to search an element by its Type among parent controls. The level of parent element (the second parameter) will be taken into account (if parameter is set).

Presented below are available binding properties:

  • Mode - gets or sets a value that indicates the direction of the data flow in the binding. Available alias is M. There are four binding modes:
    • Default - uses the default Mode value of the binding target. The default value for each dependency property is set to OneWay. However, these values can be overriten by using DefaultBehaviors property on IBindingProvider.
    • OneTime - updates the binding target when the application starts or when the data context changes. This type of binding is appropriate if you are using data where either a snapshot of the current state is appropriate to use or the data is truly static.
    • OneWay - updates the binding target property when the binding source changes. This type of binding is appropriate if the control being bound is implicitly read-only. For instance, you may bind to a source such as a stock ticker. Or perhaps your target property has no control interface provided for making changes, such as a data-bound background color of a table. If there is no need to monitor the changes of the target property, using the OneWay binding mode avoids the overhead of the TwoWay binding mode.
    • OneWayToSource - updates the source property when the target property changes.
    • TwoWay - causes changes to either the source property or the target property to automatically update the other. This type of binding is appropriate for editable forms or other fully-interactive UI scenarios.
  • ValidatesOnNotifyDataErrors - when this property is set to true, the binding checks for and reports errors that are raised by a data source that implements INotifyDataErrorInfo interface. Available alias is ValidatesOnErrors.
  • ValidatesOnExceptions - when this property is set to true, it checks for exceptions that are thrown during the update of the source property. When the user enters an invalid value, a the error message will be reported.
  • Validate - this property allows to activate/deactivate both ValidatesOnNotifyDataErrors and ValidatesOnExceptions properties simultaneously.
  • DefaultValueOnException - one can use this property to set default value which will be set whenever an validation exception will be thrown.
  • SourceDelay - one can use the Delay property to specify an amount of time to pass after the property changes on the target before the source updates. Available alias is Delay.
  • TargetDelay - in opposite to the SourceDelay, the TargetDelay property defines a timespan after which the target is updated.
  • Converter - this property allows to users set a class which implements IBindingValueConverter interface (or inherits generic ValueConverterBase<TFrom, TTo> class) in order to provide a way to apply custom logic to a binding.
  • ConverterParameter - one can use this property to bind the parameter to pass to the Converter.
  • ConverterCulture - one can use this property to bind the culture in which to evaluate the converter.
  • Fallback - one can set the value to use when the binding is unable to return an expected value.
  • TargetNullValue - one can set the value that is used in the target when the value of the source is null.
  • CommandParameter - represents a user defined data value that can be passed to the command when it is executed.
  • Behavior - one can set the value to add some behavior to the binding.
  • ToggleEnabledState - one can set it to true so it would allow to manipulate in what manner visual control will react on CanExecute method's result. In other words, Enabled property of any control can be controlled by bound command's CanExecute method. Available alias is ToggleEnabled.
  • DebugTag - one can set this property to get detailed information about binding. If string is set then it will be used as tag for binding debug info. Also, DebugTag can be set on delegate in order to hook binding debug info.
  • DisableEqualityChecking - property that allows to control the property check for equality for both target and source sides simultaneously.
  • TargetDisableEqualityChecking - property that allows to control the property check for equality target side.
  • SourceDisableEqualityChecking - property that allows to control the property check for equality source side.
  • HasStablePath - improves the performance for binding where the type of the path is not changed depending on the data context. Please see examples on Some Binding Performance Tips section.
  • Observable - this value specifies whether the binding should observe the binding path. Please see examples on Some Binding Performance Tips section.
  • Optional - one can set it to true and then binding will not throw an exception if the path is not available.

How MugenMvvm Binding Engine Tracks Property Changes

MugenMvvm binding engine tracks property changes by the next algorithm:

  1. Binding engine is trying to search an event with name "PropertyName" + "Changed" = "PropertyNameChanged". If it is found then binding engine invoke it.
  2. Binding engine is trying to search an event with name "PropertyName" + "Change" = "PropertyNameChange". If it is found then binding engine invoke it.
  3. Binding engine will invoke INotifyPropertyChanged.OnPropertyChanged implementation.

In order to better understand the MugenMvvm bindings please refer to MugenMvvm Binding Samples.

Approaches to Binding Creation

There are exists several approaches to create a binding in MugenMvvm. Some platforms support all of them, the others support only particular approaches.

Presented below is the list with available approaches on different platforms:

Xamarin.Android

  • Fluent
  • View markup

Xamarin.iOS

  • Fluent

Xamarin.Forms

  • Fluent
  • View markup
  • Attached Properties
  • DataBindingExtensions

UWP

  • Fluent
  • View markup
  • Attached Properties

View markup

Presented below is a list of platform on which view markup approach to create bindings is available:

  • Xamarin.Android
  • Xamarin.Forms
  • UWP

Xamarin.Android

On Xamarin.Android you have to import into view the next namespace:

xmlns:pkg="http://schemas.android.com/apk/res-auto"

After that you can use Bind attribute to write bindings:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:pkg="http://schemas.android.com/apk/res-auto"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">
  <TextView android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            pkg:Bind="Text ResourceUsageInfo" />  
</LinearLayout>

Xamarin.Forms

On Xamarin.Forms you have to import into view the next namespace:

xmlns:mugen="clr-namespace:MugenMvvmToolkit.Xamarin.Forms.MarkupExtensions;
assembly=MugenMvvmToolkit.Xamarin.Forms"

After that you can use mugen:DataBinding to write bindings:

<?xml version="1.0" encoding="utf-8"?>

<ContentPage 
	 xmlns="http://xamarin.com/schemas/2014/forms"
	 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
	 xmlns:mugen="clr-namespace:MugenMvvmToolkit.Xamarin.Forms.MarkupExtensions;
	  assembly=MugenMvvmToolkit.Xamarin.Forms"
     x:Class="Binding.Views.MainView">
<Grid>
 <Grid.RowDefinitions>
   <RowDefinition Height="Auto" />
   <RowDefinition Height="*" />
 </Grid.RowDefinitions>
 <Label Grid.Row="0" Text="{mugen:DataBinding ResourceUsageInfo}" />
 <ListView ItemsSource="{mugen:DataBinding Items}" Grid.Row="1">
   <ListView.ItemTemplate>
    <DataTemplate>
	   <ViewCell>
		  <ViewCell.View>
		    <Button Command="{mugen:DataBinding
                         '$Rel(ListView).DataContext.ShowCommand', Mode=OneTime}" 
				    Text="{mugen:DataBinding Item1, Mode=OneTime}" 
				    CommandParameter="{mugen:DataBinding Item2, 
                              Mode=OneTime}" />
      </ViewCell.View>
	  </ViewCell>
	 </DataTemplate>
   </ListView.ItemTemplate>
 </ListView>
</Grid>
</ContentPage>

UWP

On UWP you have to import into view the next namespace:

xmlns:markupExtensions="using:MugenMvvmToolkit.WinRT.MarkupExtensions"

After that you can use markupExtensions:View.Bind to write bindings:

<Page
    x:Class="Binding.UniversalApp.Views.BindingModeView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:markupExtensions="using:MugenMvvmToolkit.WinRT.MarkupExtensions">
    <Grid>
        <ScrollViewer>
            <StackPanel>
                <TextBox Header="Header1" 
                         markupExtensions:View.Bind="Text Text, Mode=OneTime" />
                <TextBox Header="Header2" 
                         markupExtensions:View.Bind="Text Text, Mode=OneWay" />
                <TextBox Header="Header3"
                         markupExtensions:View.Bind="Text Text, Mode=OneWay,
                                                     TargetDelay=1000" />
                <TextBox Header="Header4" 
                         markupExtensions:View.Bind="Text Text, 
                                                     Mode=OneWayToSource" />
            </StackPanel>
        </ScrollViewer>
    </Grid>
</Page>

Fluent Syntax

Presented below is a list of platform on which fluent approach to create bindings is available:

  • Xamarin.Android
  • Xamarin.iOS
  • Xamarin.Forms
  • UWP

Let's look at the example. Typical binding on Label's Text property can be set through extension method Bind as follows:

label.Bind(nameof(label.Text)).To(nameof(MyText)).Build();

Also bindings can be set through BindingSet, this approach is useful whenever you have to set bind many properties on one viewmodel:

using (var set = new BindingSet<ForumThreadDetailViewModel>())
{
  set.Bind(label1, nameof(label1.Text)).To(nameof(MyText1));
  set.Bind(label2, nameof(label2.Text)).To(nameof(MyText2));
  set.Bind(label3, nameof(label3.Text)).To(nameof(MyText3));
}

Binding from text representation of valid binding is also supported through the BindFromExpression extension method:

this.BindFromExpression("HasContent User != null && ErrorMessage == null")

To method has second parameter ctx which allows to work with such binding features as $element, $context etc. Let's look at example:

using (var set = new BindingSet<MainViewModel>())
{                
  set.Bind(TestButton)To(() => 
                    (m, ctx) => 
                    ctx.Relative<UITableView>().DataContext<MainViewModel>()
                    .ActionManager.TestCommand);
}

In this example, we are working with ctx variable. This variable allow us to find relative UITableView element and bind TestButton to TestCommand command in bound ViewModel.

Attached Properties

Presented below is a list of platform on which attached property approach to create bindings is available:

  • Xamarin.Forms
  • UWP

The platforms presented above support an convenient way to create bindings through attached properties.
The algorithm to use this approach contains next steps:

  1. Register attached property. An convenient way to do this is implement IModule interface and register your attached property in Load method:
public class Module : IModule
{  
  #region Implementation of IModule
	
  public int Priority { get; } = 1;
  
  public void Unload(IModuleContext context)
  {
  }
  
  public bool Load(IModuleContext context)
  {
    BindingServiceProvider.MemberProvider
      .Register(AttachedBindingMember.CreateMember<Label, string>("FormattedText",
                                                                                                     GetFormattedTextValue, SetFormattedTextValue, ObserveFormattedTextValue));
    return true;
  }

  /// <summary>
  ///     Used to observe member.
  /// </summary>
  private static IDisposable ObserveFormattedTextValue(IBindingMemberInfo bindingMemberInfo, Label label, IEventListener arg3)
  {
    return null;
  }

  /// <summary>
  ///     Called every time when value updated.
  /// </summary>
  private static void SetFormattedTextValue(IBindingMemberInfo bindingMemberInfo, Label textBlock, string value)
  {
    textBlock.Text = "Formatted " + value;
  }

  /// <summary>
  ///     Called every time when value changed.
  /// </summary>
  private static string GetFormattedTextValue(IBindingMemberInfo bindingMemberInfo, Label textBlock)
  {
    return textBlock.Text;
  }

  #endregion
}

And after that it can be used as follows:

<Page
    x:Class="Binding.UniversalApp.Views.AttachedMemberView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:markupExtensions="using:MugenMvvmToolkit.WinRT.MarkupExtensions"
    mc:Ignorable="d">

    <Grid>
        <StackPanel>
            <TextBlock Text="Attached custom property" />
            <TextBlock markupExtensions:View.Bind="FormattedText Text" />
        </StackPanel>
    </Grid>
</Page>

DataBindingExtensions

Presented below is a list of platform on which DataBindingExtensions approach to create bindings is available:

  • Xamarin.Forms

C# Language Binding Support

The most powerful feature which supported by MugenMvvm bindings is C# language support. Below will be presented some examples in order to demonstrate it.

C# Operators

Support of all base operators of C# is implemented. It means that all C# operators (??, ?:, +, -, *, /, %, ==, !=, <, >, <=, >=, &&, ||, |, &, !, ~) are allowed to use inside a binding expression.

Presented below is Android sample. Binding on Text property is created: pkg:Bind="Text 2+2*3". So, whenever application will be started, TextView will shows the value 8.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:pkg="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"        
        pkg:Bind="Text 2+2*3" />
</LinearLayout>

As you can see priority of operations are taken into account. Priority of all C# operators are taken into account in compliance with C# language specification.

Let's look at another example:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:pkg="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"        
        pkg:Bind="Text IsValid ? 'Is valid!' : 'Is not valid!'" />
</LinearLayout>

In this example, whenever IsValid is set to true the Text property will be set to 'Is valid!'.

C# language support is implemented for bindings on all platforms that supported by MugenMvvm. Any valid C# expression can be easily used inside a binding. You can test other operators by yourself.

Instance Methods

Binding engine supports calls of instance methods. Let's look at example.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:pkg="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        pkg:Bind="Text $context.GetMessage()" />
</LinearLayout>

In this example, we invoked method GetMessage from bound ViewModel through $context keyword.

Static Methods

MugenMvvm bindings support static method invocation. So, any static method can be easily called from binding.
In order to use static methods, please follow the next steps:

  1. Create static class with methods you needed.
  2. Register this class on BindingServiceProvider.ResourceResolver.
  3. Now you can use methods inside bindings.

Let's use extension methods. Create a static class and name it StaticMethods'.

public static class StaticMethods
{
  public static string WrapIntoDoubleQuotes(string text)
  {
    return "\"" + text + "\"";
  }
}

Then register it on BindingServiceProvider.ResourceResolver:

BindingServiceProvider
.ResourceResolver
.AddType(nameof(StaticMethods), typeof(StaticMethods));

And now you can use it inside bindings:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:pkg="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"        
        pkg:Bind="Text $StaticMethods.WrapIntoDoubleQuotes(TextToWrap)" />
</LinearLayout>

So, whenever application will be started, TextView shows the value of TextToWrap wrapped into double quotes.

Note: there are exists AddMethod method inside BindingServiceProvider.ResourceResolver class. So, instead of register a whole class, you can register only one method.

Extension Methods

Also, binding engine supports calling of extension methods through $this keyword. Let's create extension method for TextView control that returns its TextSize value:

public static class ExtensionMethods
{
  public static string GetTextSize(this TextView textView)
  {
    return textView.TextSize.ToString();
  }
}

Now, register this class:

BindingServiceProvider
.ResourceResolver
.AddType(nameof(ExtensionMethods), typeof(ExtensionMethods));

After that, we can use it inside a binding:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:pkg="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        pkg:Bind="Text $this.GetTextSize()" />
</LinearLayout>

So, whenever application will be started, TextView shows the value of its TextSize property.

Lambda Expressions

MugenMvvm bindings support lambda expressions. So, any valid lambda expression method can be easily called from binding.

Let's look at example.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:pkg="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"        
        pkg:Bind="Text ShopItems.Count(x => !x.IsReserved)" />
</LinearLayout>

In this example, we want to show a number of items not reserved at the moment. So, Linq Count method can be applied here. This expression will be evaluated by binding engine and valid result will be shown.

Note: Binding engine supports generic type inferring. So, instead of ShopItems.Count you can use simplified expression ShopItems.Count(x => !x.IsReserved)".

Some Binding Tips and Tricks

Usage of Observable Property

You can disable path observation by setting Observable binding property to false to improve performance. It most used for ListControl. Look at next examples to understand how it works:
Imagine that you use this class for your items:

public class MyModel : NotifyPropertyChangedBase
{
  private string _mutableProperty;

  public string ImmutableProperty { get; }

  public string MutableProperty
  {
    get { return _mutableProperty; }
    set
    {
      if (value == _mutableProperty) return;
      _mutableProperty = value;
      OnPropertyChanged();
    }
  }
}

You are completely sure that ImmutableProperty property will not be changed but binding doesn't know about this. And binding will subscribe to PropertyChanged event because MyModel implements INotifyPropertyChanged interface. In order to avoid this we have to give a clue to binding do not subscribe on PropertyChanged event. So we have to set set Observable to false:

⁠⁠⁠⁠<TextView app:Bind="Text ImmutableProperty, Observable=false"/>⁠⁠⁠

By default all properties has Observable property set on true but you can change default value by using the BindingServiceProvider.ObservablePathDefault property.

Usage of HasStablePath Property

You can improve binding performance by notice binding about "stable path". In order to do that you have to set HasStablePath to true. To understand meaning of "stable path" let's look at the next example. Suppose you have the next class:

public class Alpha
{
  public string P1 { get; set; }
}

public class Bravo
{
  public string P1 { get; set; }
}

public class MyModel : NotifyPropertyChangedBase
{
  public MyModel()
  {
    // you can set only Alpha object to this property, 
    // it means that binding will get information about property type once 
    // and then reuse it on every property change
    StablePath = new Alpha();
    // you can set any object to this property, 
    // it means that binding should update information 
    // about property type on every property change
    UnstablePath = new Alpha();
    UnstablePath = new Bravo();
  }

  public Alpha StablePath { get; set; }

  public object UnstablePath { get; set; }
}

So you can set HasStablePath to true because you definitely know that StablePath is stable:

<TextView app:Bind="Text StablePath, HasStablePath=true"/>⁠⁠

By default all properties has HasStablePath is set to false but you can change default value by setting BindingServiceProvider.HasStablePathDefault property. We recommend to set default value to true because 90% of all binding path are stable.

Usage of OneTime Property

If you are using Relative or Element binding to a command with ListControl(RecyclerView, UITableView, etc) then you can use OneTime binding mode to improve performance. Binding will set command once and not invalidate it on every data context change during scroll:

⁠⁠⁠Click $Rel(RecyclerView).DataContext.ShowDataCommand, 
CommandParameter=$context, Mode=OneTime

Usage of Fake Properties

You can call an method rather than bind to property. In order to do this you can use fake properties.
First of all, create method that you going to invoke:

public static class MyBindExtensions
{
    public static void SetUnreadCount(this TextView textView, 
                                      bool isUnread, int count)
    {      
      // some useful code here
    }
}

After that you must to register it by using ResourceResolver:

⁠⁠⁠⁠BindingServiceProvider.ResourceResolver.AddType(typeof(MyBindExtensions));⁠⁠⁠

Finally, you can use this method as a trigger whenever bound property is changed:

<!-- Property should start with Fake prefix, you can customize this using
 BindingServiceProvider.FakeMemberPrefixes property --> 
<TextView 
  app:Bind="FakeAnyPropertyName $this.SetUnreadCount(IsUnread, UnreadCount)"/>

AttachedBindingMember Overview

The AttachedBindingMember allows to create attached members in MugenMvvm. There are exists two types of attached members:

  • Properties
  • Events

On each platform there are exists "AttachedMembers" class that contains a collection of predefined attached members that have to be useful on current platform (Android, iOS).

Properties

Property can be added in two ways. First way is use CreateAutoProperty method. There are several overloads of this method, let's take a look on the simplest one:

public static INotifiableAttachedBindingMemberInfo<TTarget, TType> CreateAutoProperty<TTarget, TType>(BindingMemberDescriptor<TTarget, TType> path, TType defaultValue,
		Action<TTarget, AttachedMemberChangedEventArgs<TType>> memberChangedHandler = null, Action<TTarget, MemberAttachedEventArgs> memberAttachedHandler = null)
		where TTarget : class

The arguments are:

  • TTarget - type on the control in which binded property have to be added.
  • TType - type on the property which will be added into control.
  • path - the name of target binded property.
  • defaultValue - the default value of property.
  • memberChangedHandler - handler is invoked when property is changed.
  • memberAttachedHandler - handler is invoked when property is attached for the first time.
AttachedBindingMember.CreateAutoProperty<ActionBar, bool>
  ("DisplayShowHomeEnabled", (actionBar, args) => 
   actionBar.SetDisplayShowHomeEnabled(args.NewValue));

ActionBar in Android doesn't have the DisplayShowHomeEnabled property and we added it using CreateAutoProperty method.

The property can be also added through CreateMember method:

public static IAttachedBindingMemberInfo<TTarget, TType> CreateMember<TTarget, TType>(BindingMemberDescriptor<TTarget, TType> path,
		[CanBeNull]Func<IBindingMemberInfo, TTarget, TType> getValue,
		[CanBeNull]Action<IBindingMemberInfo, TTarget, TType> setValue,
		[CanBeNull]Func<IBindingMemberInfo, TTarget, IEventListener, IDisposable> observeMemberDelegate,
		Action<TTarget, MemberAttachedEventArgs> memberAttachedHandler = null, MemberInfo member = null)
		where TTarget : class
  • TTarget - type on the control in which binded property have to be added.
  • TType - type on the property which will be added into control.
  • path - the name of target binded property.
  • getValue - handler is invoked to get the property.
  • setValue - handler is invoked to set the property.
  • observeMemberDelegate - delegate that is invoked when property is attached. If it specified, you have to track property changes and notify IEventListener.
    Returns IDisposable where in Dispose method the code to unsubscribe is placed.
  • memberAttachedHandler - handler is invoked when property is attached for the first time.

The CreateMember method is used when you want to contol the logic of set, get and observe methods and you are responsible where you will store the state of this member.
Presented below is simple example:

public class AttachedPropertySample
{
    private string _autoProperty;

    // this is equivalent to AttachedBindingMember
	  // .CreateAutoProperty<AttachedPropertySample, string>
	  // ("AutoProperty", OnAutoPropetyChanged);
    // In auto member you cannot control logic of set and get 
    // method you can also listen when this property is changed
    public string AutoProperty
    {
        get { return _autoProperty; }
        set
        {
            _autoProperty = value;
            OnAutoPropetyChanged();
        }
    }

    // this is equivalent to ​AttachedBindingMember
	  // .CreateMember<AttachedPropertySample, string>("CustomMember", 
	  // (info, sample) => "My custom value", 
	  // (info, sample, arg3) => CustomMemberSetter(arg3))
    // In custom member you can control logic of set, get and observe methods
    public string CustomMember
    {
        get { return "My custom value"; }
        set { CustomMemberSetter(value); }
    }

    private void CustomMemberSetter(string value)
    {
    }
​
    private void OnAutoPropetyChanged()
    {
    }
}
AttachedBindingMember.CreateMember<IMenuItem, bool>(nameof(IMenuItem.IsVisible), (info, item) => item.IsVisible, (info, item, value) => item.SetVisible(value));

IMenuItem already has the IsVisible getter and the SetVisible method and we combined them using CreateMember.

Events

Attached event can be added by using CreateEvent method:

public static IAttachedBindingMemberInfo<TTarget, object> CreateEvent<TTarget>(BindingMemberDescriptor<TTarget, IEventListener> path,
		Func<IBindingMemberInfo, TTarget, IEventListener, IDisposable> setValue, Action<TTarget, MemberAttachedEventArgs> memberAttachedHandler = null)
		where TTarget : class
  • TTarget - type on the control in which binded property have to be added.
  • path - instance of BindingMemberDescriptor which contains the name of event and listener.
  • setValue - handler is invoked to set the event.
  • memberAttachedHandler - handler is invoked when event is attached for the first time.

Let's take a look on the example:

MemberProvider.Register(AttachedBindingMember                              .CreateEvent(AttachedMembers.UITableView.SelectedItemChangedEvent));
public abstract class UITableView : UIView //UIScrollView
{
    // some fields            

    public static readonly BindingMemberDescriptor
    <UIKit.UITableView, IEventListener> SelectedItemChangedEvent;

    // some fields            

    static UITableView()
    {
        // some initialization code
        SelectedItemChangedEvent = new BindingMemberDescriptor
        <UIKit.UITableView, IEventListener>("SelectedItemChanged");
    }
}

In this example we added SelectedItemChangedEvent event through CreateEvent method.

MugenMvvm Binding Performance Overview

MugenMvvm binding engine is extremely fast.

Test environment configuration:

  • Core i5-2400 3.10 GHz
  • 16 Gb DDR3 1333 MHz
  • Windows 10 64 bit Anniversary Update

Let's look at the next example.

522

WPF 1000000 binding update iterations. You can see that MugenMvvm is more than three times faster than native WPF binding engine.

In this example we made 1 000 000 assignments to binded value.
'Native binding' is built-in WPF binding engine. It has time 3.49 s. 'Mugen binding' it is two-way binding based on MugenMvvm binding engine. It has time 0.98 s. 'Mugen binding ((Property ?? $string.Empty).Length + Property) is one-way expression binding based on MugenMvvm binding engine. It has time 0.91 s. (it is slightly faster than previous one because it is one-way binding). The last one 'No binding' test set value without any bindings i.e. directly.
As one can see, MugenMvvm binding is more than three times faster than native WPF bindings.

Presented below are examples for Xamarin.Forms (Xamarin.Forms Android).

670

Xamarin.Forms Android 1000000 binding update iterations

One can easily repeat these tests by himself. Please follow this link on GitHub to get binding examples.