1

My question is very similar to ones like MVVM in WPF - How to alert ViewModel of changes in Model... or should I? and ViewModel subscribing to Model's PropertyChanged event for a specific property. However, there's a difference.

Background Info

My model is Employee, and the property I'm interested in is DisplayLtdOccupationId.

But in my ViewModel, I don't just have an instance of Employee. Rather I have a collection of Employee objects.

 ObservableCollection<Employee> EmployeeCensus

In my View, I have a DataGrid that's bound to EmployeeCensus. So I have Employee rows in the grid.

Question

What I would like is to be able to respond to PropertyChanged of the particular Employee row where the value of DisplayLtdOccupationId was changed. I want to do this with a handler method in my ViewModel.

In my handler, I would expect that the Employee object that just changed would be in the 'sender' variable. Then I would take its DisplayLtdOccupationId, do a lookup from a collection that I already have in memory in the ViewModel, and set the lookup value in a different property of that same Employee object.

More Details

Both the ViewModel and Employee Model implement INotifyPropertyChanged (via : Microsoft.Practices.Prism.ViewModel.NotificationObject).

The DataGrid displays the DisplayLtdOccupationId property as an inline dropdown list where the user can choose a different value.

Why not just do this in the Employee Model, in the Setter of DisplayLtdOccupationId? Because I don't have access to the lookup collection there.

I don't want to use a trigger in the view to initiate the handler. This was causing an issue, and is why I want to explore a solution using ViewModel and Model only.

There's more I could add, but I'm trying to keep the question brief and to the point. If more information is required, please advise.

Community
  • 1
  • 1
Sandra
  • 608
  • 2
  • 11
  • 23

1 Answers1

1

Something like this. I imagine you were hoping for something a little more clever and less verbose, but this is what you get. Employee_PropertyChanged handles changes to properties of Employee. You could also give your Employee class a specific event that's raised on changes to its DisplayLtdOccupationId property. Then your parent viewmodel would handle that event instead of PropertyChanged. Either way works.

public class ViewModel : ViewModelBase
{
    public ViewModel()
    {
        EmployeeCensus = new ObservableCollection<Employee>();
    }

    #region EmployeeCensus Property
    private ObservableCollection<Employee> _employeeCensus = null;
    public ObservableCollection<Employee> EmployeeCensus
    {
        get { return _employeeCensus; }
        //  Protect this so we don't have to handle the case of somebody giving us
        //  a whole new collection of new Employees. 
        protected set
        {
            if (value != _employeeCensus)
            {
                if (_employeeCensus != null)
                {
                    _employeeCensus.CollectionChanged -= _employeeCensus_CollectionChanged;
                }

                _employeeCensus = value;
                OnPropertyChanged(nameof(EmployeeCensus));

                if (_employeeCensus != null)
                {
                    _employeeCensus.CollectionChanged += _employeeCensus_CollectionChanged;
                }
            }
        }
    }

    private void _employeeCensus_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.OldItems != null)
        {
            foreach (var item in e.OldItems.Cast<Employee>())
            {
                item.PropertyChanged -= Employee_PropertyChanged;
            }
        }
        if (e.NewItems != null)
        {
            foreach (var item in e.NewItems.Cast<Employee>())
            {
                item.PropertyChanged += Employee_PropertyChanged;
            }
        }
    }

    private void Employee_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(Employee.DisplayLtdOccupationId):
                //  Do stuff
                break;
        }
    }
    #endregion EmployeeCensus Property
}
  • Wow, that was really quick. Let me try it out! I wasn't sure if I'd have to add the handler to every instance, and I see that I do! Thanks for the code for safety and removing the handler too. – Sandra Nov 02 '16 at 18:33
  • It's much simpler if you just make the OC property read only. –  Nov 02 '16 at 18:41
  • 'You could also give your Employee class a specific event that's raised on changes to its DisplayLtdOccupationId property' I've gotten into the habit of having these events for darn near every property I create in order to avoid handling PropertyChanged - for better or for worse. – plast1k Nov 02 '16 at 19:17