Resource Page DescriptionWPF seems to have a problem databinding to nullable value types, this page shows a couple of workarounds.
The problem
When you try to apply TwoWay DataBinding between a WPF control dependency property (e.g. the SelectedValue property of a ComboBox) and a property of your data item
which is a nullable type, it doesn't always work as expected.
Specifically, if the
initial value of the data property is
null then WPF appears to 'lose' the binding - e.g.
<ComboBox Name="cboVehicleType"
SelectedValue="{Binding Path=VehicleTypeId, Mode=TwoWay}"
SelectedValuePath="Tag">
<ComboBoxItem Tag="1">Car</ComboBoxItem>
<ComboBoxItem Tag="2">Motorbike</ComboBoxItem>
<ComboBoxItem Tag="3">Lorry</ComboBoxItem>
</ComboBox>
If the initial value of VehicleTypeId (a nullable int) is set to 1 then the initial selected item is 'Car' and when you select another item then the VehicleTypeId changes appropriately (e.g. if you select 'Motorbike' then VehicleTypeId will get set to 2 via databinding) - as expected.
If the initial value of VehicleTypeId is set to null then the initial SelectedIndex is -1 (i.e. no item selected) - as expected.
However, when you select another item then the VehicleTypeId does
not change - it always stays as
null.
The workaround
Use a ValueConverter to convert the null value into an intermediate value which WPF likes (which you can later convert back from in TwoWay databinding mode).
e.g. the following example converts a null int to -1 (and -1 back to null):
using System;
using System.Globalization;
using System.Windows.Data;
namespace WpfNullableSamples
{
[ValueConversion(typeof(int?), typeof(int))]
public class NullableIntToNegativeOneConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return -1;
}
else
{
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
int i;
if (value == null)
{
return null;
}
else
{
if (int.TryParse(value.ToString(), out i))
{
if (i == -1)
{
return null;
}
else
{
return value;
}
}
else
{
return value;
}
}
}
}
}
The above converter can be used like so:
<Window x:Class="WpfNullableSamples.NullableWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfNullableSamples"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="Nullable Window" SizeToContent="WidthAndHeight">
<Window.Resources>
<local:NullableIntToNegativeOneConverter x:Key="nullableIntToNegativeOneConverter"/>
</Window.Resources>
<StackPanel>
<ComboBox Name="cboVehicleType"
SelectedValue="{Binding Path=VehicleTypeId, Mode=TwoWay, Converter={StaticResource nullableIntToNegativeOneConverter}}"
SelectedValuePath="Tag">
<ComboBoxItem Tag="1">Car</ComboBoxItem>
<ComboBoxItem Tag="2">Motorbike</ComboBoxItem>
<ComboBoxItem Tag="3">Lorry</ComboBoxItem>
</ComboBox>
</StackPanel>
</Window>
The following example converts a null boolean to '[[NullString]]' (and back from '[[NullString]]' to null):
using System;
using System.Globalization;
using System.Windows.Data;
namespace WpfNullableSamples
{
[ValueConversion(typeof(bool?), typeof(string))]
public class NullableBooleanConverter : IValueConverter
{
private const string NullString = "[[NullString]]";
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool parsedValue;
if (value == null)
{
return NullString;
}
else
{
if (bool.TryParse(value.ToString(), out parsedValue))
{
return parsedValue ? bool.TrueString : bool.FalseString;
}
else
{
return NullString;
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool parsedValue;
if (value == null)
{
return null;
}
else
{
if (bool.TryParse(value.ToString(), out parsedValue))
{
return parsedValue;
}
else
{
return null;
}
}
}
}
}
An example of using this nullable boolean converter with a RadioButtonList can be found
here