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
$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'sCanExecute
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 currentDataContext
of control on which binding is set. This is the analogue of WPF{RelativeSource Self, Path=DataContext}
.$args
- returns currentEventArgs
parameter, it can be only used ifTargetPath
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 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 usingDefaultBehaviors
property 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 implementsINotifyDataErrorInfo
interface. 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 bothValidatesOnNotifyDataErrors
andValidatesOnExceptions
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 theDelay
property 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
, theTargetDelay
property defines a timespan after which the target is updated.Converter
- this property allows to users set a class which implementsIBindingValueConverter
interface (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 totrue
so it would allow to manipulate in what manner visual control will react onCanExecute
method's result. In other words,Enabled
property 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,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 totrue
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:
- 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.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:
- Register attached property. An convenient way to do this is implement
IModule
interface and register your attached property inLoad
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:
- 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
.
ReturnsIDisposable
where inDispose
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.
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).
One can easily repeat these tests by himself. Please follow this link on GitHub to get binding examples.
Updated almost 8 years ago