I have a problem while trying to bind 2 or more Comboboxes SelectedValue to a property, that is null. Only 1 of the comboboxes bound to this property will show the real value.
Below is my Xaml where i use DataTemplate to select a Combobox for presentation of the viewModel.
Xaml:
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:PropertyValueViewModel}">
<ComboBox SelectedValue="{Binding Value}" ItemsSource="{Binding SelectableValues}" DisplayMemberPath="Description" SelectedValuePath="Value"/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Label Content="These uses template:"></Label>
<ContentPresenter Content="{Binding ValueSelector}"></ContentPresenter>
<ContentPresenter Content="{Binding ValueSelector}"></ContentPresenter>
<ContentPresenter Content="{Binding ValueSelector}"></ContentPresenter>
</StackPanel>
And the code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ValueSelector = new PropertyValueViewModel()
{
SelectableValues = new List<SelectableValue>()
{
new SelectableValue("NULL", null),
new SelectableValue("1", 1)
},
Value = null
};
DataContext = this;
}
public static readonly DependencyProperty ValueSelectorProperty = DependencyProperty.Register(
"ValueSelector", typeof(PropertyValueViewModel), typeof(MainWindow), new PropertyMetadata(default(PropertyValueViewModel)));
public PropertyValueViewModel ValueSelector
{
get { return (PropertyValueViewModel)GetValue(ValueSelectorProperty); }
set { SetValue(ValueSelectorProperty, value); }
}
}
/// <summary>
/// My viewModel
/// </summary>
public class PropertyValueViewModel
{
public object Value { get; set; }
public object SelectableValues { get; set; }
}
/// <summary>
/// The items in the combobox
/// </summary>
public class SelectableValue
{
public SelectableValue(string header, object value)
{
Value = value;
Description = header;
}
public object Value { get; set; }
public string Description { get; set; }
}
Now i am wondering why only 1 of them can show the NULL value at startup? I can change the value in any of them, and they will all sync with the value in the property - if i select 1 and then back to NULL, they will all show NULL. It seems like its only the initial value is not shown correctly.
If i avoid using DataTemplate the binding works too. Does anyone know why the DAtaTemplate behaves this way?
Interesting problem.
Fundamentally, this appears to be caused by your choice to use
null
as one of the selectable values.null
, of course, has special meaning, for C#, .NET, and WPF. The problem also involves the order in which the initialization of theComboBox
element is done. TheSelectedValuePath
property is initialized after theSelectedValue
property.This means that as your program is starting up and the
ComboBox
elements are created, whennull
is assigned to theSelectedValue
property through its binding, theComboBox
does not yet have enough information to handle that value as a legitimate item selection. Instead, it interprets it as no selection at all.Why does the last
ComboBox
still get initialized the way you want? I'm not really sure…I didn't investigate very far regarding that. I could speculate, but the odds of my guessing correctly seem low so I won't bother. Since it's the anomaly and not necessarily in keeping with expected behavior (based on above, even if the behavior is the desired behavior) I'll chalk it up to one of WPF's many "quirks". :)I found several work-arounds for the issue:
null
as a selectable value. If every selectable value is non-null, then the non-null value used to initialize each element'sSelectedValue
property is retained and when theSelectedValuePath
is initialized, the current selection for theComboBox
is set correctly.SelectedValuePath
. Instead, just bind toSelectedItem
and initialize theValue
property with the desiredSelectableValue
class instance (e.g. the first one in the list).ComboBox
'sLoaded
event, refresh the target of the binding.The first two are significant departures from your current design. Personally, if at all possible I would go with one or the other. It seems to me that there's a clear danger in using
null
as a selectable value in aComboBox
, and this may not be the only oddity you run into. In the long run, maintenance of this part of the code may cost a lot more if you continue to usenull
.That said, the third option does work, and if you're lucky, the only real hazard in using
null
is on initialization. My proposed work-around for that option would look something like this:XAML:
C#:
This forces WPF to update the target (i.e. the
SelectedValue
property of the control). Since at this point, theSelectedValuePath
has been set, assigningnull
to the property this time correctly updates the selected item for theComboBox
.By the way, I would strongly recommend that you disambiguate the names of the
Value
properties in your models. Having two differentValue
properties used for bindings in a single XAML element is very confusing. I would use, for example,SelectedValue
andItemValue
, for thePropertyValueViewModel
class and theSelectableValue
class, respectively.