How does FallbackValue work with a MultiBinding? - wpf

I ask because it doesn't seem to work.
Assume we're binding to the following object:
public class HurrDurr
{
public string Hurr {get{return null;}}
public string Durr {get{return null;}}
}
Well, it would appear that if we used a MultiBinding against this the fallback value would be shown, right?
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} to the {1}"
FallbackValue="Not set! It works as expected!)">
<Binding Path="Hurr"/>
<Binding Path="Durr"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
However the result is, in fact, " to the ".
Even forcing the bindings to return DependencyProperty.UnsetValue doesn't work:
<TextBlock xmnlns:base="clr-namespace:System.Windows;assembly=WindowsBase">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} to the {1}"
FallbackValue="Not set! It works as expected!)">
<Binding Path="Hurr"
FallbackValue="{x:Static base:DependencyProperty.UnsetValue}" />
<Binding Path="Durr"
FallbackValue="{x:Static base:DependencyProperty.UnsetValue}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Tried the same with TargetNullValue, which was also a bust all the way around.
So it appears that MultiBinding will never ever use FallbackValue. Is this true, or am I missing something?
A little more messing around and I found that a converter can return the UnsetValue I need:
class MultiValueFailConverter : IMultiValueConverter
{
public object Convert(
object[] values,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
if (values == null ||
values.Length != 2 ||
values.Any(x=>x == null))
return System.Windows.DependencyProperty.UnsetValue;
return values;
}
public object[] ConvertBack(
object value,
Type[] targetTypes,
object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("Too complex hurt brain.");
}
}
However, this seems like a dirty filthy hack. I'd think a scenario like this would be accounted for in the framework. I can't find anything in Reflector, however.

This is a bit of an old question, but it could use some explanation.
From the FallbackValue documentation:
A binding returns a value successfully if:
The path to the binding source resolves successfully.
The value converter, if any, is able to convert the resulting value.
The resulting value is valid for the binding target (target) property.
If 1 and 2 return DependencyProperty.UnsetValue, the target property
is set to the value of the FallbackValue, if one is available. If
there is no FallbackValue, the default value of the target property is
used.
In the example provided, the binding successfully resolves to the Hurr and Durr properties. Null is valid value for a string which means the binding is valid.
In other words, the FallbackValue is used when the binding is unable to return a value and in the example provided, the binding does provide a valid value.
Take for example each of the following snippets that are based off the original example:
Example 1
The Hurr and Durr properties are bound correctly; null is a valid value and the FallbackValue will never be seen.
<TextBlock>
<TextBlock.Text>
<MultiBinding FallbackValue="Binding is valid. I will never be seen." StringFormat="{}{0} to the {1}">
<Binding Path="Hurr" />
<Binding Path="Durr" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Example 2
The Hurr and Durr properties are not bound correctly; the FallbackValue will be seen.
<TextBlock>
<TextBlock.Text>
<MultiBinding FallbackValue="Binding paths are invalid. Look at me." StringFormat="{}{0} to the {1}">
<Binding Path="xHurr" />
<Binding Path="xDurr" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Example 3
If one binding path is invalid, then the FallbackValue will be seen.
<TextBlock>
<TextBlock.Text>
<MultiBinding FallbackValue="One binding path is invalid. Look at me." StringFormat="{}{0} to the {1}">
<Binding Path="xHurr" />
<Binding Path="Durr" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Example 4
As with previous examples, the binding is correct, so the FallbackValue will not be used. Further, the FallbackValue for each of the child Binding properties of the MultiBinding parent should refer to a FallbackValue to be used for the target property of the MultiBinding, not for the child Bindings.
<TextBlock xmlns:base="clr-namespace:System.Windows;assembly=WindowsBase">
<TextBlock.Text>
<MultiBinding FallbackValue="Binding is valid. I will never be seen." StringFormat="{}{0} to the {1}">
<Binding FallbackValue="{x:Static base:DependencyProperty.UnsetValue}" Path="Hurr" />
<Binding FallbackValue="{x:Static base:DependencyProperty.UnsetValue}" Path="Durr" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Example 5
The binding is still valid even though a path is not provided in Binding properties since the binding will use whatever object it is bound to.
<TextBlock xmlns:base="clr-namespace:System.Windows;assembly=WindowsBase">
<TextBlock.Text>
<MultiBinding FallbackValue="Binding is still valid. I will never be seen." StringFormat="{}{0} to the {1}">
<Binding FallbackValue="{x:Static base:DependencyProperty.UnsetValue}" />
<Binding FallbackValue="{x:Static base:DependencyProperty.UnsetValue}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Example 6
Finally, if a converter is added to any of the Binding properties to force an UnsetValue, then the MultiBinding FallbackValue will be seen:
Converter
internal class ForceUnsetValueConverter : IValueConverter
{
#region Implementation of IValueConverter
public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
return DependencyProperty.UnsetValue;
}
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
{
throw new NotImplementedException();
}
#endregion
}
XAML
<TextBlock>
<TextBlock.Text>
<MultiBinding FallbackValue="Binding is valid, but look at me. I'm an UnsetValue." StringFormat="{}{0} to the {1}">
<Binding Converter="{StaticResource ForceUnset}" Path="Hurr" />
<Binding Path="Durr" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>

Related

WPF) Why does the exact same binding work in one place but not another?

So i am at a complete loss why the exact same binding works for one element but not another (on the same control, code for binding is copy and pasted).
I have made a MultiValueConverter that takes in 4 values. values[0] determines which one of the values[1-3] should be returned. (Ternary logic)
This converter works great. I use this to choose which color and image a control should have based on an enum. But, when using the same converter for tooltip to choose between string, then i get a binding error.
The weird thing is that is that when i use the same converter inside a template for choosing which string for the ToolTip, then it works! The exact same code copy and pasted.
When i bind with the ToolTip (not in a template) the value[0] is "{DependencyProperty.UnsetValue}", instead of the enum that i have binded to.
Code inside a UserControl)
<v:ColoredImage Width="20" Height="20" HorizontalAlignment="Right">
<v:ColoredImage.Color> //THIS WORKS
<MultiBinding Converter="{StaticResource TernaryConverter}">
<Binding Path="ParamStatus" ElementName="pn"/> <-- SAME BINDING
<Binding Source="{StaticResource GreenLight}"/>
<Binding Source="{StaticResource YellowLight}"/>
<Binding Source="{StaticResource RedLight}"/>
</MultiBinding>
</v:ColoredImage.Color>
<v:ColoredImage.Image> // THIS WORKS
<MultiBinding Converter="{StaticResource TernaryConverter}">
<Binding Path="ParamStatus" ElementName="pn"/> <-- SAME BINDING
<Binding Source="{StaticResource OkIcon}"/>
<Binding Source="{StaticResource WarningIcon}"/>
<Binding Source="{StaticResource ErrorIcon}"/>
</MultiBinding>
</v:ColoredImage.Image>
<v:ColoredImage.ToolTip>
<ToolTip> //THIS PART DOES NOT WORK
<MultiBinding Converter="{StaticResource TernaryConverter}">
<Binding Path="ParamStatus" ElementName="pn"/> <-- SAME BINDING
<Binding Source="OK"/>
<Binding Source="Not Synced"/>
<Binding Source="ERROR"/>
</MultiBinding>
</ToolTip>
</v:ColoredImage.ToolTip>
</v:ColoredImage>
Code Inside a Style and ControlTemplate (this code work, even though it is the same)
<v:ColoredImage Height="24" Width="24" Margin="65,65,0,0" VerticalAlignment="Center">
<v:ColoredImage.Color>
<MultiBinding Converter="{StaticResource TernaryConverter}">
<Binding Path="Status" RelativeSource="{RelativeSource TemplatedParent}"/>
<Binding Source="{StaticResource GreenLight}"/>
<Binding Source="{StaticResource YellowLight}"/>
<Binding Source="{StaticResource RedLight}"/>
</MultiBinding>
</v:ColoredImage.Color>
<v:ColoredImage.Image>
<MultiBinding Converter="{StaticResource TernaryConverter}">
<Binding Path="Status" RelativeSource="{RelativeSource TemplatedParent}"/>
<Binding Source="{StaticResource OkIcon}"/>
<Binding Source="{StaticResource UnidentifiedIcon}"/>
<Binding Source="{StaticResource ErrorIcon}"/>
</MultiBinding>
</v:ColoredImage.Image>
<v:ColoredImage.ToolTip>
<ToolTip>
<MultiBinding Converter="{StaticResource TernaryConverter}">
<Binding Path="Status" RelativeSource="{RelativeSource TemplatedParent}"/>
<Binding Source="OK"/>
<Binding Source="Unidentified"/>
<Binding Source="ERROR"/>
</MultiBinding>
</ToolTip>
</v:ColoredImage.ToolTip>
</v:ColoredImage>
I could fix this by doing a style/template for my first UserControl. But i feel like i shouldnt have too, and either way i wanna know why the EXACT same code works in one place but not another. I'm completely dumbfounded.
Code for the Converter, this is not where problem occurs, but i figured someone is going to ask me to post it anyway:
public class TernaryConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
int index = (int)values[0];
if (index < 0 || index > 2)
{
return values[1];
}
return values[index+1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new InvalidOperationException("EnumToImageConverter can only be used OneWay.");
}
}
The reason why ElementName="pn" doesn't work in a ToolTip is that a ToolTip resides in its own element tree and there is no element named "pn" in the namescope of this tree.

How to set a DynamicResource as PriorityBinding?

I have a multilanguage application which contains two dictionary en.xaml and it.xaml, suppose that I have the following situation:
I need to display a default text in a TextBlock such as: Contact saved 0 in memory using a DynamicResource key that contains the text above.
Unfortunately xaml doesn't allow to use DynamicResource in a StringFormat or as FallbackValue so I used this code:
<TextBlock Tag="{DynamicResource defaultContactStr}">
<TextBlock.Text>
<PriorityBinding>
<Binding Path="ContactSaved" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</PriorityBinding>
</TextBlock.Text>
this will display only 0 which is the default value of the proeprty ContactSaved, but I need to display: Contact saved 0 in memory, or if the value change: Contact saved 5 in memory etc...
how can I manage this situation?
Using a StringFormat works only if the format (the translation in your case) is known at compile-time:
<TextBlock>
<TextBlock.Text>
<Binding Path="ContactSaved" StringFormat="{}Contact saved {0} in memory" />
</TextBlock.Text>
</TextBlock>
Othwerwise you may handle this using a converter and a MultiBinding:
public class CustomConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string resource = values[0] as string;
string contactSaved = values[1].ToString();
return string.Format(resource, contactSaved);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
<TextBlock Tag="{DynamicResource defaultContactStr}">
<TextBlock.Text>
<MultiBinding>
<MultiBinding.Converter>
<local:CustomConverter />
</MultiBinding.Converter>
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="ContactSaved" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
en.xaml:
<system:String x:Key="defaultContactStr">Contact saved {0} in memory</system:String>
Remember that XAML is nothing but a markup language.

Binding StringFormat

I have a collection of textblocks that I'm going to be showing and I'm needing the text of each textblock to be displayed differently. I'm currently saving the format string in the tag property and I'm needing to display the text in this format. How do I bind the StringFormat section?
Something like the section below:
<TextBlock Tag="{Binding MyFormatString}" Text="{Binding MyProperty, StringFormat='{}{0:MyTag}'}" />
Since BindingBase.StringFormat is not a dependency property, I do not think that you can bind it. If the formatting string varies, I'm afraid you will have to resort to something like this
<TextBlock Text="{Binding MyFormattedProperty}" />
and do the formatting in your view model. Alternatively, you could use a MultiBinding and a converter (example code untested):
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myStringFormatter}">
<Binding Path="MyProperty" />
<Binding Path="MyFormatString" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
public class StringFormatter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return string.Format((string)values[1], values[0]);
}
...
}
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0},{1}">
<Binding Path="MyProperty" />
<Binding Path="MyFormatString" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
The String Formatting is a display setting and therefore should live close to the UI layer, you can either declare it in the Xaml or have formatted string properties on a ViewModel and perform the formatting in the Get of those properties and bind the TextBlock to it the ViewModel properties. It would source its data from the original datasource.

Converters on child bindings in a MultiBinding

Suppose I have this MultiBinding:
<MultiBinding Converter="{StaticResource FooBarConverter}>
<Binding Path="Foo" Converter="{StaticResource FooConverter}" />
<Binding Path="Bar" Converter="{StaticResource BarConverter}" />
</MultiBinding>
This doesn't seem to work: the values array passed to FooBarConverter contains DependencyProperty.UnsetValue for each value (two, in this case). Removing the converters on the child bindings (FooConverter and BarConverter) gives me the actual values. By the way: those converters are properly invoked, it just looks like their result is discarded.
Is this intended behavior? I want to bind 2 properties by I need to convert at least one of them before throwing them into the MultiValueConverter...
The developers in Kishore's shared link came to the conclusion that to make such a MultiBinding, the child Bindings must return the same type of result as the parent MultiBinding. So, in my case, if I wanted the parent MultiBinding to return a value of type Visibility, the child Bindings must also return Visibility values. Not doing so will pass UnsetValues to your converter method, likely giving you undesireable results.
Here is a snippet of code that works for me. Note that Converters "VisibleIfTrue" and "EnumToVisibility" both return type Visibility values:
<Grid.Visibility>
<MultiBinding Converter="{StaticResource MultiVisibilityConverter}">
<Binding Path="JobHasData" Converter="{StaticResource VisibleIfTrue}" />
<Binding Path="CurrentMode" Converter="{StaticResource EnumToVisibility}" ConverterParameter="{x:Static Mode.Setup}" />
</MultiBinding>
</Grid.Visibility>
It is annoying that you can't pass it different value types to process and give you the result you want. (I initially tried to pass bools to the converter.)
Hope this helps anyone who waited the seven years for the answer. ;)
you have mention converter in the Multibinding tag like this
<TextBlock Grid.Row="3" Grid.Column="1" Padding="5">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource sumConverter}">
<Binding Path="FirstNum" />
<Binding Path="SecondNum" />
<Binding Path="ThirdNum" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
If WPF was prior to 4.0 then it is a known and fixed bug that have workaround.
Here located a sample implementation of the workaround for poor man that are forced to work with elder versions.
To say shortly, old wpf versions are trying to convert values from multibinding child bindings that have converters directly into type of target dependency property. Workaround is to create hidden label, move multibinding or its child binding converter to label.content as it expects object and then bind desired property to it.
public class DataClass
{
public string FirstName { get; set; }
public string Surname { get; set; }
}
public class NameMultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return String.Format("{0} {1}", values[0], values[1]);
}
}
The XAML looks basically like this
<Window xmlns:local="clr-namespace:BlogIMultiValueConverter">
<Window.Resources>
<local:NameMultiValueConverter x:Key="NameMultiValueConverter" />
</Window.Resources>
<Grid>
<TextBox Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding Path=Surname, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MultiValueConverter}">
<Binding Path="FirstName" />
<Binding Path="Surname" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>

WPF - Converter hides dependency on a DependencyProperty

I have a TextBlock (acutally a whole bunch of TextBlocks) where I set the Text to "" if a DependencyProperty in my ViewModel is set to Visiblity.Hidden. I do this via a converter as follows:
<TextBlock Margin="0,0,5,0">
<TextBlock.Text>
<Binding Converter="{StaticResource GetVisibilityOfColumnTitles}"
Path="Name" />
</TextBlock.Text>
</TextBlock>
The converter looks like this:
public object Convert(object value, Type targetType,
object parameter,System.Globalization.CultureInfo culture)
{
if (MainMediator.Instance.VisibilityOfWorkItemColumnTitles
== Visibility.Visible)
return value;
else
return "";
}
I admit that this is a bit convoluted way to do this, but I have my reasons (DataContext complications and spacing for the TextBlock)
The problem I have is that when VisibilityOfWorkItemColumnTitles is changed, even though it is a dependency property, TextBlock.Text does not realize there is a dependency there (because it's used in the converter).
Is there a way in the code behind (preferably in the converter) to say, this TextBlock wants to update this binding when VisibilityOfWorkItemColumnTitles changes?
Since your converter depends on both the Text property from the TextBox and the VisibilityOfWorkItemColumnTitles property on your MainMediator class, you'll probably need to use a MultiBinding and include both properties back in the XAML.
<TextBlock Margin="0,0,5,0">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource GetVisibilityOfColumnTitles}">
<Binding Path="Name" />
<Binding Path="VisibilityOfWorkItemColumnTitles" Source="{x:Static my:MainMediator.Instance}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
(I've used "my" as the XML namespace for your MainMediator class in that code sample.)
Then change your converter to an IMultiValueConverter, and reference values[0] for the text and values1 for the "visibility" property. Now the binding will know if either property changes, and call off to the converter appropriately.

Resources