Introduction
I had a situation where I had to display messages from several sources, and came up with this concept where I used converter and binding to each of the sources. This significantly simplified the design of the underlying view models, reducing coupling.
Description
The converter required to enable this functionality is derived from IMultiValueConverter
. The convert
method in this class basically just returns the first non-empty value from the array of values:
public sealed class FirstNotNullConverter : MarkupExtension, IMultiValueConverter
{
public FirstNotNullConverter() { }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.FirstOrDefault(value => value != DependencyProperty.UnsetValue && value != null
&& value.ToString() != string.Empty);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
There is one change made to the above code from the original version: added the "value != DependencyProperty.UnsetValue
" which allows the converter to work when there is no property for the Binding to bind to.
The XAML to use this convert would be as follows:
<TextBlock FontWeight="Black">
<TextBlock.Text>
<MultiBinding Converter="{local:FirstNotNullConverter}">
<Binding Path="Message" />
<Binding Path="Child.Message" />
<Binding Path="Child.Child.Message" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
As can be seen in this example, in XAML, Binding can be done to child properties of the DataContext
.
Compared to Priority Binding
The difference between this an a PriorityBinding
is that PriorityBinding will accept any binding, null or not, whereas this will look for a non-empty value.
The XAML for the PriorityBinding
in this sample is actually the following:
<TextBlock FontWeight="Black">
<TextBlock.Text>
<PriorityBinding >
<Binding Path="DummnyDoesNotExist"/>
<Binding Path="Message" />
<Binding Path="Child.Message" />
<Binding Path="Child.Child.Message" />
</PriorityBinding>
</TextBlock.Text>
</TextBlock>
Here it is obvious that the converter is providing the right result, which is the "Message Level 2" text is being displayed since the "Message Level 1" is empty
(null
). However the PriorityBinding
is not showing the right result, and that is because the Binding
with "Message Level 1" is succeeding and the result is null
.
The actual XAML for the converter Binding is:
<TextBlock FontWeight="Black">
<TextBlock.Text>
<MultiBinding Converter="{local:FirstNotNullConverter}">
<Binding Path="Message" />
<Binding Path="DummnyDoesNotExist"/>
<Binding Path="Child.Message" />
<Binding Path="Child.Child.Message" />
</MultiBinding>
In the middle of all the Binding
Path
s above there is the DummnyDoesNotExist
. The above sample works even though they DummnyDoesNotExist
will not be resolved. This is because the IValueConverter
checks if a value is DependencyProperty.UnsetValue
, and treats it like the value is resolved, and is null
or string.Empty
.
History
- 04/27/2016: Initial version
- 05/02/2016: Added
PriorityBinding
comparison and images