ericpap
ericpap

Reputation: 2937

How can I detect changes in properties inside my model in viewmodel?

I'm bulding my first production App using WPF MVVM, Caliburn Micro and a WCF service.

I have come to a point where I need my ViewModel to track changes in individual properties inside my model. I give you an example. My VM have properties like this:

public OrdenesTransporteWCFModel OrdenTransporte { get; set; }
public List<EnumeradorWCFModel> TiposCarga { get; set; }
public List<EnumeradorWCFModel> TiposCamion { get; set; }
public List<EnumeradorWCFModel> MediosContacto { get; set; }

OrdenesTransporteWCFModel is a model that comes from a WCF service, and my view could look something like:

<ComboBox ItemsSource="{Binding Path=TiposCarga}" SelectedValuePath="ID" DisplayMemberPath="Descripcion" SelectedValue="{Binding Path=OrdenTransporte.ID_TipoCarga}"></ComboBox>

<ComboBox ItemsSource="{Binding Path=TiposCamion}" SelectedValuePath="ID" DisplayMemberPath="Descripcion" SelectedValue="{Binding Path=OrdenTransporte.ID_TipoMovil}"></ComboBox>

<ComboBox ItemsSource="{Binding Path=MediosContacto}" SelectedValuePath="ID" DisplayMemberPath="Descripcion" SelectedValue="{Binding Path=OrdenTransporte.ID_MedioContacto,ValidatesOnDataErrors=True}"></ComboBox>

<TextBox IsEnabled="False" Text="{Binding Path=OrdenTransporte.Numero}"></TextBox>

<DatePicker SelectedDate="{Binding Path=OrdenTransporte.FechaConfeccion}"></DatePicker>

As you can see I'm binding my controls to individual properties inside my model (OrdenTransporte).

Now what I need is my VM to track changes in this properties: for instance I have a bool in my VM property HasChange that I need to be activated if any field has changed. Also I have a method TipoCamionChange that I need to fire if the ID_TipoCamion Property change.

Is there any way I can achive that? Thanks!

EDIT

As Martin suggest, I implement INotifyPropertyChange on my model and try to subscribe my model to the propertychange event on my model like this:

OrdenTransporte = _svc.OrdenesTransporte_GetById(IDOrden);            
OrdenTransporte.PropertyChanged += Model_PropertyChanged;

private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "ID_Cliente")
        {
            CargarDirecciones();
        }
    }

The problem is that when I subscribe my vm to model PropertyChange, the data in the model has already changes, so PropertyChanged is never called. If I do:

OrdenTransporte.PropertyChanged += Model_PropertyChanged;
OrdenTransporte = _svc.OrdenesTransporte_GetById(IDOrden);            

event is not fired anyway because the entire object is replaced with the one returned by WCF service, incluiding INotifyPropertyChange event. Any Ideas?

Upvotes: 4

Views: 1743

Answers (3)

Martin
Martin

Reputation: 5623

Do the model classes implement INotifyPropertyChanged?

Then you could set up event handlers in your view model to the changes in the model classes:

    modelObject.PropertyChanged += ViewModelOnPropertyChanged;

    private void ViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
    {
        // react to object change here
    }

You could link the events for several model objects all to the same event handler and then set your HasChange view model property for example.

If your model objects do not implement INotifyPropertyChanged I do not know a solution.

Upvotes: 2

Eddie M.
Eddie M.

Reputation: 11

You should be using ObservableCollection<T> instead of List<T> and INotifyPropertyChanged.

public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

private ObservableCollection<EnumeradorWCFModel> _tiposCarga;
public ObservableCollection<EnumeradorWCFModel> TiposCarga
{
    get { return _tiposCarga; }
    set
    {
        if (_tiposCarga != value)
        {
            _tiposCarga = value;
            OnPropertyChanged(nameof(TiposCarga));
        }
    }
}

Upvotes: 1

Mark Feldman
Mark Feldman

Reputation: 16148

You can add IPNC support to your model properties automatically using Dynamic Castle (which encapsulates the instances in a proxy) or Fody (which modifies the IL in a post step at compile time).

UPDATE: If your models are coming from a web service reference then they'll already have IPCN support, you can see this by going to their automatically generated source code in your project. You can also confirm it in code:

using (var client = new ServiceReference1.Service1Client())
{
    var data = client.GetData();

    data.PropertyChanged += (s, e) =>
    {
        Debug.Assert(false, "PropertyChanged handler invoked.");
    };

    data.SomeMember = false; // <-- will cause the assert to trigger
}

Upvotes: 1

Related Questions