el-nino
el-nino

Reputation: 399

RaisePropertyChanged for Property of an Object

i have a model user:

public class User
{
    public string Name { get; set; }
    public int Level { get; set; }
}

in the view:

<TextBox Text="{Binding NewUser.Name}"/>
<TextBox Text="{Binding NewUser.Level}"/>

and the property in the VM:

public User NewUser
    {
        get { return _newUser; }
        set
        {
            if (_newUser == value)
                return;
            _newUser = value;
            RaisePropertyChanged("NewUser");
        }
    }

this code does update the property: NewUser = new User() { Name = "test", Level = 1 };

this code does not: NewUser.Name = "test";

what am i doing wrong? i'm using mvvm light.

Upvotes: 3

Views: 2734

Answers (2)

Marc
Marc

Reputation: 13184

When setting NewUser.Name, the RaisePropertyChanged on the ViewModel is not called and therefore no PropertyChangedEvent is fired.

In general you should have a good reason to expose model classes directly in your ViewModel, as you do here (Expose a User model as a public property in your ViewModel). This basically violates the separation of concerns between Models and ViewModels, for which MVVM is designed. Though it seems academic, my experience is that it is really worth it to stay clean here, as in most real-world cases the ViewModels tend to become more complex over time and contain functionality that you don't want to have in your model (like INPC implementations, btw).

Although it involves a bit more coding, you should implement a nested ViewModel here. Here's a bit of code to get you started:

public class ParentViewModel : NotifyingObject
{
    private UserViewModel _user;

    // This is the property to bind to
    public UserViewModel User
    {
        get { return _user; }
        private set
        {
            _user = value;
            RaisePropertyChanged(() => User);
        }
    }

    public ParentViewModel()
    {
        // Wrap the new instance in a ViewModel
        var newUser = new User {Name = "Test"};
        User = new UserViewModel(newUser);
    }
}

This is the extra ViewModel in which the User model class is wrapped:

public class UserViewModel : NotifyingObject
{
    /// <summary>
    /// The model is private here and not exposed to the view
    /// </summary>
    private readonly User _model;

    public string Name
    {
        get { return _model.Name; }
        set
        {
            _model.Name = value;
            RaisePropertyChanged(() => Name);
        }
    }       

    public UserViewModel(User model)
    {
        _model = model;
    }
}

This is your model class. There is no need to implement INotifyPropertyChanged.

public class User
{
    public string Name { get; set; }
}

Upvotes: 2

nvoigt
nvoigt

Reputation: 77294

You did not implement INotifyPropertyChanged for your User class. So changing the property NewUser by assignment will trigger the UI, setting the property Name by assignment will not.

If you follow your pattern, this:

public string Name { get; set; }

should in the end look like this:

public string Name
{
    get { return _name; }
    set
    {
        if (_name == value)
            return;
        _name = value;
        RaisePropertyChanged("Name");
    }
}

Upvotes: 1

Related Questions