I'm using the MVVMLight Toolkit in my WPF
project. All my ViewModel
s derive from the toolkit's ViewModelBase
class, which implements the INotifyPropertyChanged
for you and does all the notify work.
My current setup is extremely simple. I have a Person
class with a single Name
property.
public class Person
{
public string Name { get; set; }
}
My window has a TextBlock
and a Button
, and to the TextBlock
I bind the Name
property of the Person
class object that I have. DataContext
is set using a ViewModelLocator
class.
<Window x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.galasoft.ch/ignore"
mc:Ignorable="d ignore"
Height="300" Width="300"
Title="MVVM Light Application"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Contact.Name}"/>
<Button Grid.Row="1" Content="Click" Command="{Binding ClickCommand}"/>
</Grid>
</Window>
In my ViewModel
, I set the Name
to Tom
in the constructor, and change it when the button is clicked. I expect Tom
to show up in the TextBlock
when window is loaded (which it does), and to be changed to Jane
when the button is clicked (which it doesn't).
public class MainViewModel : ViewModelBase
{
private Person _contact = new Person();
public Person Contact
{
get { return _contact; }
set { Set(ref _contact, value); }
}
public RelayCommand ClickCommand { get; private set; }
public MainViewModel(IDataService dataService)
{
Contact = new Person() { Name = "Tom" };
ClickCommand = new RelayCommand(Click);
}
public void Click()
{
Contact.Name = "Jane";
}
}
What am I missing?
Setting Contact.Name
does not trigger the INotifyPropertyChanged.NotifyChanged
event as the Contact setter is not executed. You could fix this using by one of the following techniques:
Implement INotifyPropertyChanged also in your model class
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
if (PropertyChanged != null)
PropertyChanged(this, nameof(Name));
}
}
public event PropertyChangedHandler PropertyChanged;
}
Or wrap the PersonClass in a PersonViewModel
public class PersonViewModel : ViewModelBase
{
private readonly Person _person;
public PersonViewModel(Person person)
{
_person = person;
}
public string Name
{
get => _person.Name;
set
{
var name = _person.Name;
if (Set(ref name, value))
_person.Name = name;
}
}
}
and in MainViewModel:
private PersonViewModel _contactViewModel
public PersonViewModel Contact
{
get { return _contactViewModel ?? (_contactViewModel = new PersonViewModel(_contact)); }
}
Or create a separate ContactName property in the MainViewModel
... and using ContactName
instead of Contact.Name
in the binding and the Click event handler.
public string ContactName
{
get { return _contact.Name; }
set
{
var name = _contact.Name;
if (Set(ref name, value))
_contact.Name = name;
}
}