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.
<Page xmlns:m="using:WinRTMultibinding.Foundation.Data">
<Page.Resources>
<MyMultiValueConverter x:Key="MyMultiValueConverter" />
<MyConverter x:Key="MyConverter" />
<SomeDataProvider x:Key="SomeDataProvider" />
</Page.Resources>
<Button x:Name="MyButton" Content="Button content" />
<TextBlock>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBinding TargetProperty="Text" Converter="{StaticResource MyMultiValueConverter}">
<m:Binding Path="First" Converter="{StaticResource MyConverter}" />
<m:Binding Path="Second" Source="{StaticResource SomeDataProvider}" />
<m:Binding Path="Foreground" RelativeSource="{RelativeSource Self}" />
<m:Binding Path="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:
<TextBlock Text="{Binding Name}">
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBinding TargetProperty="Text" Converter="{StaticResourc MyMultiValueConverter}">
<m:Binding Path="First" />
<m:Binding Path="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:MultiBinding TargetProperty="Text" Converter="{StaticResource MyMultiValueConverter}">
<m:Binding Path="First" Converter="{StaticResource MyConverter}" />
<m:Binding Path="Second" Source="{StaticResource SomeDataProvider}" />
<m:Binding Path="Foreground" RelativeSource="{RelativeSource Self}" />
<m:Binding Path="Content" ElementName="MyButton" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBlock>
<TextBlock>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBinding TargetProperty="Text" StringFormat="{}{0} - {1}">
<m:Binding Path="First" />
<m:Binding Path="Second" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBlock>
Custom converter must implement IMultiValueConverter interface.
public interface IMultiValueConverter
{
object Convert(object[] values, Type targetType, object parameter, string language);
object[] ConvertBack(object value, Type[] targetTypes, object parameter, string language);
}
<TextBlock>
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBinding TargetProperty="Text" Converter="{StaticResource MyMultiValueConverter}">
<m:Binding Path="First" />
<m:Binding Path="Second" />
<m:Binding Path="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:MultiBinding TargetProperty="Text" Mode="TwoWay" Converter="{StaticResource MyMultiValueConverter}">
<m:Binding Path="First" />
<m:Binding Path="Second" Mode="OneWay" />
<m:Binding Path="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:MultiBinding TargetProperty="Text" TargetNullValue="Null value" Converter="{StaticResource MyMultiValueConverter}">
<m:Binding Path="First" />
<m:Binding Path="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:MultiBinding TargetProperty="Text" FallbackValue="Fallback value" Converter="{StaticResource MyMultiValueConverter}">
<m:Binding Path="First" />
<m:Binding Path="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:
<TextBox x:Name="MyTextBox">
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBinding TargetProperty="Text" Mode="TwoWay" Converter="{StaticResource MyMultiValueConverter}"
UpdateSourceTrigger="Explicit">
<m:Binding Path="First" />
<m:Binding Path="Second" />
</m:MultiBinding>
</m:MultiBindingCollection>
</m:MultiBindingHelper.MultiBindings>
</TextBox>
Do this:
var multiBindingExpression = 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:
<Page xmlns:m="using:WinRTMultibinding.Foundation.Data">
<Page.Resources>
<MyMultiValueConverter x:Key="MyMultiValueConverter" />
<GridTypeProvider x:Key="GridTypeProvider" />
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Text="Binding to Grid.Row attached property">
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBinding AttachedPropertyOwnerTypeProvider="{StaticResource GridTypeProvider}" TargetProperty="Row"
Converter="{StaticResource MyMultivalueConverter}" Mode="OneWay">
<m:Binding Path="First" />
<m:Binding Path="Second" />
<m:Binding Path="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:
public class GridTypeProvider : 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:
public class MyAttachedPropertyOwnerTypeProvider : TypeProvider<MyAttachedPropertyOwner>
{
}
and set it as AttachedPropertyOwnerTypeProvider
for MultiBinding
item:
<Page xmlns:m="using:WinRTMultibinding.Foundation.Data">
<Page.Resources>
<MyMultiValueConverter x:Key="MyMultiValueConverter" />
<MyAttachedPropertyOwnerTypeProvider x:Key="MyAttachedPropertyOwnerTypeProvider" />
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Text="Binding to Grid.Row attached property">
<m:MultiBindingHelper.MultiBindings>
<m:MultiBindingCollection>
<m:MultiBinding AttachedPropertyOwnerTypeProvider="{StaticResource MyAttachedPropertyOwnerTypeProvider}"
TargetProperty="MyAttachedProperty" Converter="{StaticResource MyMultivalueConverter}" Mode="OneWay">
<m:Binding Path="First" />
<m:Binding Path="Second" />
<m:Binding Path="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:MultiBinding TargetProperty="Text" Converter="{StaticResource MyMultiValueConverter}">
<m:Binding Path="First" />
<m:Binding Path="Second" />
</m:MultiBinding>
<m:MultiBinding TargetProperty="FontSize" Converter="{StaticResource MyAnotherMultiValueConverter}">
<m:Binding Path="Third" />
<m:Binding Path="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.