I’m having an issue with a ComboBox which is bound to an ObservableCollection and I was wondering if anyone can point to what I am missing.
I have a ComboBox which is bound to a simple ObservableCollection<string>. Also I bind the SelectedIndex in a OneWay binding to some property.
In my application I get to a point where I want to clear the collection and repopulate it with different data and setting the SelectedIndex to a new value. for some reason the SelectedIndex binding does not work.
I’m attaching a little repro of the problem:
public partial class Window1 : Window, INotifyPropertyChanged
{
private int j;
public event PropertyChangedEventHandler PropertyChanged;
public Window1()
{
InitializeComponent();
DataContext = this;
Tables = new ObservableCollection<string>();
}
public ObservableCollection<string> Tables { get; set; }
private int _TheIndex;
public int TheIndex
{
get { return _TheIndex; }
set
{
_TheIndex = value;
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("TheIndex"));
}
}
}
private void aaaa(object sender, RoutedEventArgs e)
{
j = (j + 1)%10;
Tables.Clear();
for(int i = 0; i < 10 ; i++)
{
Tables.Add(i.ToString());
}
TheIndex = j;
}
}
the xaml is :
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<StackPanel>
<ComboBox x:Name="TablesCombobox"
ItemsSource="{Binding Tables}"
SelectedIndex="{Binding TheIndex, Mode=OneWay}"/>
<Button Content="asdasd" Click="aaaa"/>
</StackPanel>
</Grid>
</Window>
The problem is entirely caused by the
Tables.Clear()line in youraaaa()method. SinceTablesis an observable collection, wiping out all of the contents of the collection causes WPF to update the display with a new, empty list. Then it tries to select the currently active item usingSelectedIndex, which does not exist (because the list is now empty). As a result, the binding engine is left with a value that cannot be applied, and decides to deactivate and detach the binding logic:By the time it gets to the ‘TheIndex = j;’ line, the binding is no longer active and does not see the change to TheIndex, which means that desired index is no longer selected.
There are a couple of solutions to solve this:
TwoWaybinding. This works because now the ComboBox participates in binding; you clearTables, the binding tries to set but can’t find the index so the ComboBox resets to the special ‘no index’ position of -1, which then writes back toTheIndex(the two-way part), which is a valid value so the binding logic doesn’t detach.Select no index (-1) before clearing the collection. If no index (-1) is selected when
Tablesis cleared, then ComboBox doesn’t try to applySelectedItem, which means it doesn’t ‘see’ the collection emptied and re-filled, and therefore, doesn’t detach.For performance, architectural, and clarity reasons, I’d highly recommend option 1, though I realize that your actual scenario may be more complex and require something along the lines of 3.
Sidenote:
Locating the reason behind binding issues like this is reasonably easy when using binding traces like the one posted above. Turn them on for a single binding by declaring the System.Diagnostics namespace and adding
PresentationTraceSources.TraceLevel=Highto the binding that is causing trouble:More ways of debugging WPF bindings are here.