Bindings
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
$argskeyword. - Validation Support - validation is provided by
INotifyDataErrorInfointerface. 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
CanExecutemethod's result. In other words, "Enabled" property of any control can be controlled by bound command'sCanExecutemethod. - 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
RelativeSourceproperty 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 currentDataContextof control on which binding is set. This is the analogue of WPF{RelativeSource Self, Path=DataContext}.$args- returns currentEventArgsparameter, it can be only used ifTargetPathpoints 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 isM. There are four binding modes:Default- uses the default Mode value of the binding target. The default value for each dependency property is set toOneWay. However, these values can be overriten by usingDefaultBehaviorsproperty onIBindingProvider.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 totrue, the binding checks for and reports errors that are raised by a data source that implementsINotifyDataErrorInfointerface. Available alias isValidatesOnErrors.ValidatesOnExceptions- when this property is set totrue, 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 bothValidatesOnNotifyDataErrorsandValidatesOnExceptionsproperties 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 theDelayproperty to specify an amount of time to pass after the property changes on the target before the source updates. Available alias isDelay.TargetDelay- in opposite to theSourceDelay, theTargetDelayproperty defines a timespan after which the target is updated.Converter- this property allows to users set a class which implementsIBindingValueConverterinterface (or inherits genericValueConverterBase<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 theConverter.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 totrueso it would allow to manipulate in what manner visual control will react onCanExecutemethod's result. In other words,Enabledproperty of any control can be controlled by bound command's CanExecute method. Available alias isToggleEnabled.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,DebugTagcan 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 totrueand 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:
- Binding engine is trying to search an event with name "PropertyName" + "Changed" = "PropertyNameChanged". If it is found then binding engine invoke it.
- Binding engine is trying to search an event with name "PropertyName" + "Change" = "PropertyNameChange". If it is found then binding engine invoke it.
- Binding engine will invoke
INotifyPropertyChanged.OnPropertyChangedimplementation.
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:
- Register attached property. An convenient way to do this is implement
IModuleinterface and register your attached property inLoadmethod:
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:
- Create static class with methods you needed.
- Register this class on
BindingServiceProvider.ResourceResolver. - 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.
ReturnsIDisposablewhere inDisposemethod 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
BindingMemberDescriptorwhich 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.

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).

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.
Updated over 8 years ago
