Search code examples
c#wpfdata-binding

How to notify a parent class and reflect a change in a data bound observable collection


I am having some difficulty reflecting changes in a data bound observable collection.

I have a listbox that is populated with values from the observation collection. When I add or remove items, this is reflected in the bound control. However, when I change the value to be displayed this is not reflected.

For example, Add a week object with a title of 'Week 1' (Works fine) - Now, change the title to 'Hobbies' (Not reflected in control bound to the observable list)

Below is my course management namespace with the parent class courses and the child class weeks

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Windows;

namespace Management.Courses
{
    [DataContract(Name = "Course")]
    class cCourse
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            MessageBox.Show(propertyName);

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public cCourse()
        {
            Weeks = new ObservableCollection<cWeek>();
        }

        #region "Core Properties"
        [DataMember(Name = "Title")]
        public string Title { get; set; }

        [DataMember(Name = "Weeks")]
        private ObservableCollection<cWeek> _Weeks;
        public ObservableCollection<cWeek> Weeks
        {
            get
            {
                return _Weeks;
            }
            set
            {
                if (value != this._Weeks)
                {
                    _Weeks = value;
                    NotifyPropertyChanged();
                }

            }
        }
    }

    [DataContract(Name = "Week")]
    class cWeek
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region "Core Properties"
        [DataMember(Name = "Title")]
        private string _Title { get; set; }
        public string Title
        {
            get
            {
                return _Title;
            }
            set
            {
                if (value != this.Title)
                {
                    _Title = value;
                    NotifyPropertyChanged();
                }

            }
        }
        #endregion
    }
}

And in my window, I have as follows:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Core;

namespace Management.Courses
{
    public partial class MgnCourses_Add : Window
    {
        private cCourse oCourse;

        public MgnCourses_Add()
        {
            InitializeComponent();

            oCourse = new cCourse();

            //Set Data Context
            DataContext = oCourse;


            cWeek oWeek = new cWeek();

            //Get/Set week title
            oWeek.Title = "Week " + e.NewValue;

           //Update week listing
           oCourse.Weeks.Add(oWeek);
        }

private void ctrlTopics_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            oCourse.Weeks[ctrlTopics.SelectedIndex].Title = "New String";
        }
    }
}

The control binding for the listbox is:

<ListBox x:Name="ctrlTopics" Margin="100,131,439,49" ItemsSource="{Binding Weeks}" DisplayMemberPath="Title" MouseDoubleClick="ctrlTopics_MouseDoubleClick">

I can see through debugging that the observable list values are changing correctly - It's just not reflected in the listbox.

Any help / pointer would be greatly appreciated.


Solution

  • @M.B has already provided the best solution for your situation. However, I would like to make things a little more clear and add a walk around to the question. The reason that your UI does not reflects the change you made to data is that your code only notify UI about the entire object change

    Week = other_week;
    

    but not the object 's properties change

    Week.Title = new_week_title;
    

    Now, the walk around I mentioned before: just notify UI about the property change explicitly:

    Week.Title = new_week_title;
    PropertyChange(this, nameof(Week));
    

    You can also assign new value to current object to invoke the notify event:

    Week = new Week(new_week_title);
    

    However, this action will affect the observable collection since you have assign an object that does not belongs to the existing collection and it can introduce a lot of problems. With that being said, the best solution you should take is @M.B 's solution.