WPF DisplayMemberPath not Updating when SelectedItem is removed
combobox, mvvm, wpf
Solution
A nice binding issue you found there. But as always, it's our fault, not theirs :)
The issue(s) is(are), using `DisplayMemberPath` with `SelectedItem`. The `DisplayMemberPath` doesn't give a f*** about the changed `SelectedItem`.
What you have to do, to resolve this issue, are two things:
First, in the `RemoveSelected` method, set the `Selected` property to null (to force an update on the binding):
public void RemoveSelected()
{
Items.Remove(Selected);
Selected = null;
}
Then, in the XAML-definition, change the bound property:
<ComboBox ItemsSource="{Binding Items}"
SelectedValue="{Binding Selected, Mode=TwoWay}"
DisplayMemberPath="Name"/>
Binding the `SelectedValue` property will correctly update the displayed text in the `ComboBox`.
Problem
I have simplified this problem down as much as I can. Basically I am overriding the "null" value of a combobox. So that if the item selected is deleted, it reverts back to "(null)". Unfortunately the behaviour of this is wrong, I hit delete, the ObservableCollection item is removed, thus the property binding is updated and it returns the "(null)" item as expected. But the combobox appearance shows blank. Yet the value its bound to is correct... this problem can be reproduced with the code below. To reproduce this problem you select an item, and hit remove. Notice at this point the following line is called (when you remove the selected item). So its a good place to breakpoint. ``` if (m_Selected == null) { return Items[0]; //items 0 is ItemNull } ``` Also notice that I have attmpted to fix it by Forcing a property update on the DisplayMemberPath. This did not work. MainWindow.xaml ``` <Window x:Class="WPFCodeDump.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <StackPanel> <ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding Selected, Mode=TwoWay}" DisplayMemberPath="Name"></ComboBox> <Button Click="ButtonBase_OnClick">Remove Selected</Button> </StackPanel> </Window> ``` MainWindowViewModel.cs ``` using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; namespace WPFCodeDump { public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } } //Item class public class Item : ViewModelBase { public Item(string name) { m_Name = name; } public string Name { get { return m_Name; } } private string m_Name; public void ForcePropertyUpdate() { OnPropertyChanged("Name"); } } //Item class public class ItemNull : Item { public ItemNull() : base("(null)") { } } class MainWindowViewModel : ViewModelBase { public MainWindowViewModel() { m_Items.Add(new ItemNull()); for (int i = 0; i < 10; i++) { m_Items.Add(new Item("TestItem" + i)); } Selected = null; } //Remove selected command public void RemoveSelected() { Items.Remove(Selected); } //The item list private ObservableCollection<Item> m_Items = new ObservableCollection<Item>(); public ObservableCollection<Item> Items { get { return m_Items; } } //Selected item private Item m_Selected; public Item Selected { get { if (m_Selected == null) { return Items[0]; //items 0 is ItemNull } return m_Selected; } set { m_Selected = value; OnPropertyChanged(); if(m_Selected!=null) m_Selected.ForcePropertyUpdate(); } } } } ``` MainWindow.xaml.cs ``` using System.Windows; namespace WPFCodeDump { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new MainWindowViewModel(); } private void ButtonBase_OnClick(object sender, RoutedEventArgs e) { ((MainWindowViewModel) DataContext).RemoveSelected(); } } } ``` Result: