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 the ComboBox
element is done. The SelectedValuePath
property is initialized after the SelectedValue
property.
This means that as your program is starting up and the ComboBox
elements are created, when null
is assigned to the SelectedValue
property through its binding, the ComboBox
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's SelectedValue
property is retained and when the SelectedValuePath
is initialized, the current selection for the ComboBox
is set correctly.SelectedValuePath
. Instead, just bind to SelectedItem
and initialize the Value
property with the desired SelectableValue
class instance (e.g. the first one in the list).ComboBox
's Loaded
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 a ComboBox
, 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 use null
.
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:
<DataTemplate DataType="{x:Type local:PropertyValueViewModel}">
<ComboBox SelectedValue="{Binding Value}"
ItemsSource="{Binding SelectableValues}"
DisplayMemberPath="Description"
SelectedValuePath="Value"
Loaded="comboBox_Loaded"/>
</DataTemplate>
C#:
private void comboBox_Loaded(object sender, RoutedEventArgs e)
{
ComboBox comboBox = (ComboBox)e.OriginalSource;
BindingOperations.GetBindingExpression(comboBox, ComboBox.SelectedValueProperty)
.UpdateTarget();
}
This forces WPF to update the target (i.e. the SelectedValue
property of the control). Since at this point, the SelectedValuePath
has been set, assigning null
to the property this time correctly updates the selected item for the ComboBox
.
By the way, I would strongly recommend that you disambiguate the names of the Value
properties in your models. Having two different Value
properties used for bindings in a single XAML element is very confusing. I would use, for example, SelectedValue
and ItemValue
, for the PropertyValueViewModel
class and the SelectableValue
class, respectively.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With