For a day or two I've been trying to figure out a weird WPF bug. I have a control with a ListBox that is bound to an ObservableCollection of PolicyId's, which is a class implement both INotifyProperty and IEquatable and has one and only one field, called Id (which is a string). The PolicyId's are pointers to various policicy items which modifies the workflow in other parts of the program.
The ListBox is bound to the collection, and there is an add and a remove button that simply adds and removes Id's to the ListBox. There is also a drop down ComboBox which can be used to change the PolicyId of the selectedItem in the ListBox to another Id (what Id's are avaliable for selection is modified elsewhere). Setting the value of the ListBox.SelectedItem.Id property is done in backend when the ComboBox.SelectionChanged event is triggered, and the values in the ComboBox is changed when the ListBox.SelectionChanged event is triggered, this to ensure that none of the items in the ListBox are ever identical.
Now, this implementation works for the most part. All the policies are listed in the ListBox, and you can add new ones (which adds the first avaliable not previously selected policy at the bottom of the list) and select and remove any policy you like.
As long as you haven't replaced one policy with another using the comboBox, that is. Once you do that the index gets stuck to the one you changed and there isn't a thing you can do to select another policy (setting ListBox.SelectedIndex has no effect). Click around long enough and eventually the following exception is thrown: "System.ArgumentException: An item with the same key has already been added." I suspect the error is caused by WPF trying to add the selected item to the "selectedItems" collection of the listBox even though the item is already there or something like that.
I know the bug has something to do with PolicyId overriding Equals (yes, I've remembered the constraint that if Equals says two objects are equal to eachother then GetHashCode should also return the same value for the two, so GetHashCode is overridden as well) because a similiar setup is used elsewhere in the program without problems. I don't know how that messes things up though, especially since there are never any duplicates in the collection.
This is the ListBox:
<ListBox Name="policyIdList" MinHeight="50" ItemsSource="{Binding Path=DataItem}" IsSynchronizedWithCurrentItem="True" SelectionChanged="policyIdList_SelectionChanged">
<ListBox.Resources>
<Style TargetType="ScrollViewer">
<Setter Property="HorizontalScrollBarVisibility" Value="Hidden" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding Path=Id}" Text="{Binding Converter={StaticResource PolicyIdToName}}">
<TextBlock.ToolTip>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource PolicyByOidListItem}" />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And, for good measures, the comboBox:
<ComboBox Name="policyIdCombo" Grid.Row="1" Grid.Column="0"
ItemTemplate="{StaticResource PolicyByOidListItem}" SelectionChanged="policyIdCombo_SelectionChanged" />
I think the problem is that you are changning the item's hashcode when you change it's ID, and the ListBox does not expect this. See here for more information.
I have just upgraded an application from .NET 3.5 to 4.0 and have encountered this problem. If like me you are unable to use a fixed hashcode then a workaround is:
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