user1807768
user1807768

Reputation: 695

How does WPF handle binding to the property of a null object?

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

Answers (2)

Mike Nakis
Mike Nakis

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:

  • In the case of <DataGrid ItemsSource>, the DataGrid appears empty.
  • In the case of a <Button Command>, the button is clickable, but when you click it nothing happens.
  • In the case of <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

Anatoliy Nikolaev
Anatoliy Nikolaev

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

Related Questions