I have a problem that involves a bunch of code, but I've isolated it down. If you want a TL;DR; jump to it further down. If you want a bit of context, here's my situation:
I have created three data converters for my bindings. One of them is a "string prefixer": it prefixes whatever you put in with a fixed string. In the current example, that fixed string is "ms-appx:///cache/". The second one turns a string type into an ImageSource, and the third one chains multiple converters together.
I've then created a Xaml resource which is called LocalCacheFile. Everything works as you would think. Xaml code for this looks like so:
<Image Source="{x:Bind imageSource,Converter={StaticResource LocalCacheFile}}" />
However, I'm having the following problem. If I try to use the FallbackValue to put a placeholder image for when imageSource is empty, I get weird behaviour in x:Bind only.
The following code works as one would expect:
<Image Source="{Binding imageSource,FallbackValue='ms-appx:///Assets/default.png',Converter={StaticResource LocalCacheFile}}" />
But
<Image Source="{x:Bind imageSource,FallbackValue='ms-appx:///Assets/default.png',Converter={StaticResource LocalCacheFile}}" />
does not!
I've isolated it down to just one converter and it is DependencyProperty.UnsetValue that x:Bind seems not to be handling.
TL;DR; Here is the code for my string prefixer, which if I use alone as a test triggers the same faulty behaviour:
public class StringPrefix : IValueConverter
{
public string prefix { get; set; }
public object Convert(object value, Type typeName, object parameter, string language)
{
if (value == DependencyProperty.UnsetValue || value == null || (string)value == "")
return DependencyProperty.UnsetValue ;
return (prefix + value.ToString());
}
public object ConvertBack(object value, Type typeName, object parameter, string language)
{
throw new NotImplementedException();
}
}
The above converter works as you would expect it to (i.e. if the input string is empty, the fallback value is properly used) when using Binding. It raises a type exception when used with x:Bind.
What's up with this?
Edit: details about the exception.
This is the generated code:
private void Update_project_imageSource(global::System.String obj, int phase)
{
if((phase & ((1 << 0) | NOT_PHASED | DATA_CHANGED)) != 0)
{
XamlBindingSetters.Set_Windows_UI_Xaml_Controls_Image_Source(this.obj16, (global::Windows.UI.Xaml.Media.ImageSource)this.LookupConverter("LocalCacheFile").Convert(obj, typeof(global::Windows.UI.Xaml.Media.ImageSource), null, null), null);
}
}
Exception details:
System.InvalidCastException was unhandled by user code
HResult=-2147467262
Message=Unable to cast object of type 'System.__ComObject' to type 'Windows.UI.Xaml.Media.ImageSource'.
Source=Test
StackTrace:
at Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update_project_imageSource(String obj, Int32 phase)
at Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update_project(Project obj, Int32 phase)
at Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update_(ProjectView obj, Int32 phase)
at Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update()
at Test.Pages.ProjectView.<.ctor>b__6_0(FrameworkElement s, DataContextChangedEventArgs e)
InnerException:
(to me, it looks like the generated code just doesn't deal with the default value possibility. Btw, that __ComObject is the DependencyProperty.UnsetValue.
Edit 2: I should add that if I change the Convert function to return null instead of DependencyProperty.UnsetValue, x:Bind functions, but then neither x:Bind nor Binding do their expected job of using the FallbackValue
The
FallbackValueinBindingandx:Bindis different.In
Binding,FallbackValueis the value to use when the binding is unable to return a value.But in
x:Bind,FallbackValuespecifies a value to display when the source or path cannot be resolved. It can't work withDependencyProperty.UnsetValue.As you've already know,
x:Bindgenerates code at compile-time and it's strongly typed. When you useConverterinx:Bind, it will regard theConverter's return value of the same type as the target property and cast it like in your code:If you return
DependencyProperty.UnsetValuein yourConverter, it will throw exception asDependencyProperty.UnsetValuecan't cast toImageSource.For your scenario, you can use
TargetNullValue.For example using following code is XAML.
And in the
Convert, returnnullinstead ofDependencyProperty.UnsetValue.This works when running the app and the
imageSourceis empty. But to gain design time benefit, we still need useFallbackValue. So we can usex:Bindlike following: