C# – WPF ComboBox Binding not updating properly

bindingccomboboxdata-bindingwpf

enter image description here

I have an updating issue with Binding in my ComboBox. I have created a simplified example illustrate this problem. I will paste all the related code below.

So in the image above, I have a TextBlock (in black) that shows the 'SelectedPerson.FullName' property for the SelectedPerson. The FirstName and LastName properties of the SelectedPerson are shown in the 2 TextBoxes below the FullName. The ComboBox DisplayMemberPath is bound to 'FullName' and the combobox contains a list of Person objects.

The 'FullName' property should be the same in the TextBlock, ComboBox, and ComboBox dropdown list.

However, it is ONLY updating correctly in the TextBlock. The comboBox Dropdown just updates the first time it is opened and doesn't update after that. So if I edit the FirstName in the textbox and click the dropdown and then edit it some more and click the dropdown again, we end up with 3 different 'FullName' values being displayed for the SelectedPerson.

I am using INotifyPropertyChanged in my code behind to notify when "SelectedPerson" is changed. This appears to work fine updating the TextBlock, but for some reason, ComboBox isn't updating with the new "FullName".

I have attempted to send a "FullName" notify when I send 'SelectedPerson' notify, but it doesn't make it work either.

Can anybody tell me why the ComboBox isn't updating when I change the FirstName text?

Thanks.

MainWindow.xaml:

<Window x:Class="ComboBoxBinding.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"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <StackPanel Margin="50" Width="150">
        <TextBlock Margin="0,0,0,10" Height="30" Padding="8" Background="Black" Foreground="White" Text="{Binding SelectedPerson.FullName}"/>
        <TextBox Text="{Binding SelectedPerson.FirstName, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox Text="{Binding SelectedPerson.LastName, UpdateSourceTrigger=PropertyChanged}"/>
        <ComboBox Margin="0,16,0,0"
                  ItemsSource="{Binding People}"
                  DisplayMemberPath="FullName"
                  SelectedItem="{Binding SelectedPerson, UpdateSourceTrigger=PropertyChanged}">
        </ComboBox>
    </StackPanel>
</Window>

MainWindow.xaml.cs:

using System.Collections.Generic;
using System.ComponentModel;

namespace ComboBoxBinding
{
    public partial class MainWindow : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private List<Person> _people = new List<Person>();

        public List<Person> People 
        {
            get { return _people; }
        }

        private Person _selectedPerson;

        public Person SelectedPerson
        {
            get { return _selectedPerson; }
            set 
            {
                _selectedPerson = value;
                OnPropertyChanged("SelectedPerson");
            }
        }

        public MainWindow()
        {
            GenerateLaneConfigurations();
            InitializeComponent();
        }

        private void GenerateLaneConfigurations()
        {
            People.Add(new Person("Elmer", "Fudd"));
            People.Add(new Person("Bugs", "Bunny"));
            People.Add(new Person("Donald", "Duck"));

            foreach (Person person in _people)
            {
                person.Changed += person_Changed;
            }

            SelectedPerson = People[0];
        }

        void person_Changed()
        {
            OnPropertyChanged("SelectedPerson");
        }
    }
}

Person.cs:

namespace ComboBoxBinding
{
    public class Person
    {
        private string _firstName;
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; OnChanged(); }
        }

        private string _lastName;
        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; OnChanged(); }
        }

        public string FullName
        {
            get { return string.Format("{0} {1}", FirstName, LastName);}
        }

        public Person(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
        }

        public delegate void ChangedEventHandler();
        public event ChangedEventHandler Changed;

        protected void OnChanged()
        {
            if (Changed != null)
            {
                Changed();
            }
        }
    }
}

Best Answer

You need to implement the notification system in your Person object as well. Saying that the SelectedPerson changed isn't good enough. Just implement INotifyPropertyChanged on Person like you already do on your MainWindow and raise the PropertyChanged event instead of your custom Changed event and it should work.

Related Topic