Provides MultiBinding functionality for Windows 8.1, Windows Phone 8.1, Windows 10 UWP projects.
WinRT MultiBinding supports most of WPF MultiBinding's features.
This portable library is available as NuGet package for Windows 8.1, Windows Phone 8.1, Windows 10 UWP projects:
https://www.nuget.org/packages/WinRTMultibinding.Universal/1.0.0
Use Package Manager to install package or type the following into the Package Manager Console:
Install-Package WinRTMultibinding.Universal
This library is also available as obsolete NuGet packages for Windows and Windows Phone projects:
https://www.nuget.org/packages/WinRT-Multibinding-Windows/
https://www.nuget.org/packages/WinRT-Multibinding-WindowsPhone/
Use Package Manager to install packages or type the following into the Package Manager Console:
Install-Package WinRT-Multibinding-Windows
Install-Package WinRT-Multibinding-WindowsPhone
NOTE: As these packages are obsolete, they are no longer supported. So, you should better install portable library version described above for use in your Windows 8.1/Windows Phone 8.1 projects.
This library provides you MultiBindingHelper.MultiBindings attached property which you should initialize with a MultiBindingCollection instance which you should populate with MultiBinding items. Instance of MultiBinding class hosts Binding items.
<Pagexmlns:m="using:WinRTMultibinding.Foundation.Data">
<Page.Resources>
<MyMultiValueConverterx:Key="MyMultiValueConverter" />
<MyConverterx:Key="MyConverter" />
<SomeDataProviderx:Key="SomeDataProvider" />
</Page.Resources>
<Buttonx:Name="MyButton"Content="Button content" />
<TextBlock>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"Converter="{StaticResource MyMultiValueConverter}">
<m:BindingPath="First"Converter="{StaticResource MyConverter}" />
<m:BindingPath="Second"Source="{StaticResource SomeDataProvider}" />
<m:BindingPath="Foreground"RelativeSource="{RelativeSource Self}" />
<m:BindingPath="Content"ElementName="MyButton" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBlock>
</Page>- Various Sources (ElementName, Source, RelativeSource, DataContext)
- StringFormat
- Converter
- TargetNullValue
- FallbackValue
- Different Modes (OneTime, OneWay, TwoWay)
- UpdateSourceTrigger
- Attached properties binding
RelativeSource.TemplatedParent and RelativeSouce.None are not yet supported.
UpdateSourceTrigger - There are only PropertyChanged and Explicit modes. PropertyChanged is set by default. To update source explicitly use GetMultiBindingExpression() extension method. It returns MultiBindingExpression object which has UpdateSource() method.
As you can see, you do not set directly MultinBinding instance to target property, but use attached property instead. So, there are no restrictions to set built-in single Binding to the same property, but this binding will be ignored. For example if you do this:
<TextBlockText="{Binding Name}">
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"Converter="{StaticResourc MyMultiValueConverter}">
<m:BindingPath="First" />
<m:BindingPath="Second" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBlock>In this case Text="{Binding Name}" binding will be ignored.
As you've seen above WinRTMultiBinding supports different binding sources. But: RelativeSource supports only Self mode.
<TextBlock>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"Converter="{StaticResource MyMultiValueConverter}">
<m:BindingPath="First"Converter="{StaticResource MyConverter}" />
<m:BindingPath="Second"Source="{StaticResource SomeDataProvider}" />
<m:BindingPath="Foreground"RelativeSource="{RelativeSource Self}" />
<m:BindingPath="Content"ElementName="MyButton" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBlock><TextBlock>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"StringFormat="{}{0} - {1}">
<m:BindingPath="First" />
<m:BindingPath="Second" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBlock>Custom converter must implement IMultiValueConverter interface.
publicinterfaceIMultiValueConverter{objectConvert(object[]values,TypetargetType,objectparameter,stringlanguage);object[]ConvertBack(objectvalue,Type[]targetTypes,objectparameter,stringlanguage);}<TextBlock>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"Converter="{StaticResource MyMultiValueConverter}">
<m:BindingPath="First" />
<m:BindingPath="Second" />
<m:BindingPath="Third" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBlock>MultiBinding's BindingMode(OneWay by default) used as the default value for all the bindings in the collection unless an individual binding overrides this property. For example, if the Mode property on the MultiBinding object is set to TwoWay, then all the bindings in the collection are considered TwoWay unless you set a different Mode value on one of the bindings explicitly. Child bindings can only limit parent Mode, but not vice versa(it's okay to have MultiBinding's Mode set to TwoWay and one of the Bindings Mode set to OneWay, but NOT MultiBinding's Mode set to OneWay and one of the Bindings Mode set to TwoWay).
<TextBox>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"Mode="TwoWay"Converter="{StaticResource MyMultiValueConverter}">
<m:BindingPath="First" />
<m:BindingPath="Second"Mode="OneWay" />
<m:BindingPath="Third" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBox>If you specify TargetNullValue it's returned when your Converter returns null.
<TextBlock>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"TargetNullValue="Null value"Converter="{StaticResource MyMultiValueConverter}">
<m:BindingPath="First" />
<m:BindingPath="Second" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBlock>If you specify FallbackValue it's returned when:
- you specified both StringFormat and Converter to target string property
- your Converter returned null, but TargetNullValue is not specified
- your Converter returned DependencyProperty.UnsetValue
If you did not specify FallbackValue it contains target property type's default value. So, it's always initialized.
<TextBlock>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"FallbackValue="Fallback value"Converter="{StaticResource MyMultiValueConverter}">
<m:BindingPath="First" />
<m:BindingPath="Second" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBlock>If you chose Default or PropertyChanged, your source value is updated every time target property changes. If you chose Explicit:
<TextBoxx:Name="MyTextBox">
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"Mode="TwoWay"Converter="{StaticResource MyMultiValueConverter}"UpdateSourceTrigger="Explicit">
<m:BindingPath="First" />
<m:BindingPath="Second" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBox>Do this:
varmultiBindingExpression=MyTextBox.GetMultiBindingExpression(TextBox.TextProperty);multiBindingExpression.UpdateSource();To bind to attached property you should specify AttachedPropertyOwnerTypeProvider, and set TargetProperty to target property name without attached property owner type name. So, binding to Grid.Row attached property will look as folowing:
<Pagexmlns:m="using:WinRTMultibinding.Foundation.Data">
<Page.Resources>
<MyMultiValueConverterx:Key="MyMultiValueConverter" />
<GridTypeProviderx:Key="GridTypeProvider" />
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBoxText="Binding to Grid.Row attached property">
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingAttachedPropertyOwnerTypeProvider="{StaticResource GridTypeProvider}"TargetProperty="Row"Converter="{StaticResource MyMultivalueConverter}"Mode="OneWay">
<m:BindingPath="First" />
<m:BindingPath="Second" />
<m:BindingPath="Third" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBox>
</Grid>
</Page>In the previous example GridTypeProvider is a class derived from TypeProvider<T> abstract class. This class has method GetType which returns the type of its generic type parameter (explicit implementation of ITypeProvider interface). The only purpose of this class, as you can see, is to provide attached property owner type. You only need to inherit from TypeProvider<T> with an attached property owner type specified as a generic type parameter, so that later you can use it in XAML. GridTypeProvider might look like this:
publicclassGridTypeProvider:TypeProvider<Grid>{}Then you should instantiate it in XAML and pass as a value for AttachedPropertyOwnerTypeProvider property, as shown in the example above.
This awkward and clumsy interface based on type providers might not seem clear at a first glance. But it has the greatest advantage: it's type and assembly independent. This means it allows you to bind not only to built-in controls (such as Grid, Scrollviewer) which have attached properties, but also to custom attached properties hosted by custom types.
For example, if we would like to bind to MyAttachedProperty property which is hosted by MyAttachedPropertyOwner class, we should simply derive from TypeProvider<T> in the following manner:
publicclassMyAttachedPropertyOwnerTypeProvider:TypeProvider<MyAttachedPropertyOwner>{}and set it as AttachedPropertyOwnerTypeProvider for MultiBinding item:
<Pagexmlns:m="using:WinRTMultibinding.Foundation.Data">
<Page.Resources>
<MyMultiValueConverterx:Key="MyMultiValueConverter" />
<MyAttachedPropertyOwnerTypeProviderx:Key="MyAttachedPropertyOwnerTypeProvider" />
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBoxText="Binding to Grid.Row attached property">
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingAttachedPropertyOwnerTypeProvider="{StaticResource MyAttachedPropertyOwnerTypeProvider}"TargetProperty="MyAttachedProperty"Converter="{StaticResource MyMultivalueConverter}"Mode="OneWay">
<m:BindingPath="First" />
<m:BindingPath="Second" />
<m:BindingPath="Third" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBox>
</Grid>
</Page>So this weird interface is payment for allowing you binding to any attached property you want.
Pros:
- possibility to bind to any attached property hosted by any type
Cons:
- cumbersome, clumsy interface (which is fixable by good code styles, naming conventions etc.)
Simply add several MultiBinding items to MultiBindingCollection.
<TextBox>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBindingTargetProperty="Text"Converter="{StaticResource MyMultiValueConverter}">
<m:BindingPath="First" />
<m:BindingPath="Second" />
</m:MultiBinding>
<m:MultiBindingTargetProperty="FontSize"Converter="{StaticResource MyAnotherMultiValueConverter}">
<m:BindingPath="Third" />
<m:BindingPath="Fourth" />
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBox>If you do have a contribution for the package feel free to put up a Pull Request or open Issue.