Reputation: 695
I have a listBox using an itemTemplate that contains the following line:
<Image Source="{Binding MyProperty.PossiblyNullObject.UrlProperty}"/>
Bound to this listBox is a model view collection that loads components of the items in the collection on a separate thread. The 'PossiblyNullObject' may not be set to a value when the xaml code is first rendered by the composition engine.
How does WPF handle this? Does it use a default value(no image source so no image) and continue on? Does it wait? Does it automatically detect when the value is initialized and rerenders with the new source? How does it not throw object null exceptions in the same way it would if I called 'MyProperty.PossiblyNullObject.UrlProperty' programmatically? How can I reproduce this functionality in my own code when I try to call it?
Thanks for any suggestions. I am embarrassingly new to WPF and I'm trying to tackle a problem out of my depth. The image load is a perf problem so I found a solution to load, decode, then freeze the image source on a background thread so it wouldn't lock up the UI. Unfortunately, I ran across this null exception problem when I tried replacing the image source binding with my solution that calls the same property. WPF somehow handles the possible null objects and I'd like to do it the same way to keep things clean.
Upvotes: 22
Views: 19701
Reputation: 61984
Note: The upvoted and accepted answer does not answer the question; it explains how you can get {Binding A}
to work if A
is null
, which is trivial to handle anyway, but it does not explain what happens and how to handle the much more interesting case of {Binding A.B}
when A
is null
, and that is specifically what the question is asking. What follows is the answer to the question as stated.
WPF generally handles the case where A
is null when you use A.B
in a binding; I have not tried specifically with <Image Source>
, but I have tried with <DataGrid ItemsSource>
and with <Button Command>
.
When WPF handles these cases, what I have observed happening is that there is no error or warning in the output window, and the application malfunctions a bit, but it does not crash:
<DataGrid ItemsSource>
, the DataGrid appears empty.<Button Command>
, the button is clickable, but when you click it nothing happens.<Image Source>
I would expect that no image will appear.(Note that all these are cases of silent failure, so whoever decided that WPF should behave this way should be shot by firing squad at the central square with great celebrations and live music and big giveaways.)
The way we generally handle these cases depends on the nature of the element at hand.
For images, if an empty image is acceptable, then you do not need to do anything. If some image must be shown despite the property being null, then the accepted answer probably provides a solution.
For grids, not showing anything when the property is null is probably the desired behavior.
For buttons, the solution is to use an additional binding to the IsEnabled
property of the button.
So, for example:
<Button Command="{Binding A.B}" IsEnabled="{Binding HasA}"/>
Where HasA
is defined in the viewmodel as follows:
bool HasA => A != null;
Upvotes: 1
Reputation: 22702
In BindingBase
have two properties: TargetNullValue
and FallbackValue
.
TargetNullValue
returns your value when the value of the source is null.
FallbackValue
returns your value when the binding is unable to return a value.
Example of using:
<!-- xmlns:sys="clr-namespace:System;assembly=mscorlib" -->
<Window.Resources>
<!-- Test data -->
<local:TestDataForImage x:Key="MyTestData" />
<!-- Image for FallbackValue -->
<sys:String x:Key="ErrorImage">pack://application:,,,/NotFound.png</sys:String>
<!-- Image for NULL value -->
<sys:String x:Key="NullImage">pack://application:,,,/NullImage.png</sys:String>
</Window.Resources>
<Grid DataContext="{StaticResource MyTestData}">
<Image Name="ImageNull"
Width="100"
Height="100"
Source="{Binding Path=NullString, TargetNullValue={StaticResource NullImage}}" />
<Image Name="ImageNotFound"
Width="100"
Height="100"
Source="{Binding Path=NotFoundString, FallbackValue={StaticResource ErrorImage}}" />
</Grid>
See this links, for more information:
BindingBase.TargetNullValue Property
BindingBase.FallbackValue Property
Upvotes: 34