Reputation: 81
I have a class TestModel that has a FirstName and a LastName property plus a "computed property" that has no setter but just returns the FullName in the format "Lastname, Firstname".
public class TestModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get
{
return $"{LastName}, {FirstName}";
}
}
}
I store multiple objects of this class in an ObservableCollection which is the ItemsSource for a datagrid in a WPF app. When I add items through the GUI, they immediately appear in the grid and everthing is fine.
However, when I change for instance the FirstName of one specific object in the grid, FullName does not change automatically (it does change, actually, but the change is not reflected in the GUI). Do I have to implement INotifyPropertyChanged on this class? If so, how would I do this? I read some examples but I must admit that I was unable to transfer them to my case...
Upvotes: 1
Views: 180
Reputation: 670
In order to receive updates by datagrid your class needs to implement INotifyPropertyChanged interface. And each property that is bound to the datagrid should raise the PropertyChanged event. In the case of the calculated property, you can raise the event manually in particular places as it is advised in other answers. But, another approach is to delegate that work to DependenciesTracking lib:
public class TestModel : INotifyPropertyChanged
{
private readonly IDependenciesMap<TestModel> _dependenciesMap = new DependenciesMap<TestModel>()
.AddDependency(i => i.FullName, i => $"{i.LastName}, {i.FirstName}", i => i.FirstName, i => i.LastName);
private string? _firstName;
private string? _lastName;
private string _fullName;
public string? FirstName
{
get => _firstName;
set
{
if (value == _firstName) return;
_firstName = value;
OnPropertyChanged();
}
}
public string? LastName
{
get => _lastName;
set
{
if (value == _lastName) return;
_lastName = value;
OnPropertyChanged();
}
}
public string FullName
{
get => _fullName;
private set
{
if (value == _fullName) return;
_fullName = value;
OnPropertyChanged();
}
}
public TestModel() => _dependenciesMap.StartTracking(this);
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Tests_SO_68712154
{
[Test]
public void Test_SO_68712154()
{
var sut = new TestModel();
var raisedEventsCount = 0;
sut.PropertyChanged += (_, args) =>
{
if (args.PropertyName == nameof(TestModel.FullName))
++raisedEventsCount;
};
Assert.Multiple(() =>
{
Assert.That(sut.FullName, Is.EqualTo(", "));
Assert.That(raisedEventsCount, Is.EqualTo(0));
});
sut.FirstName = "John";
Assert.Multiple(() =>
{
Assert.That(sut.FullName, Is.EqualTo(", John"));
Assert.That(raisedEventsCount, Is.EqualTo(1));
});
sut.LastName = "Smith";
Assert.Multiple(() =>
{
Assert.That(sut.FullName, Is.EqualTo("Smith, John"));
Assert.That(raisedEventsCount, Is.EqualTo(2));
});
sut.FirstName = null;
Assert.Multiple(() =>
{
Assert.That(sut.FullName, Is.EqualTo("Smith, "));
Assert.That(raisedEventsCount, Is.EqualTo(3));
});
}
}
Upvotes: 0
Reputation: 87
Yes, you need to implement INotifyPropertyChanged in your view model.
example:
public class ViewModelBase: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
protected void RaisePropertyChanged(String propertyName)
{
VerifyPropertyName(propertyName);
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
add it to your programm code like this:
public class Testmodel : ViewModelBase
{
private string _FistName;
public string FirstName
{
get
{
return _FistName;
}
set
{
if(_FistName == value)
{
return;
}
_FistName = value;
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
private string _LastName;
public string LastName
{
get
{
return _LastName;
}
set
{
if (_LastName == value)
{
return;
}
_LastName = value;
RaisePropertyChanged("LastName");
RaisePropertyChanged("FullName");
}
}
public string FullName
{
get
{
return $"{LastName}, {FirstName}";
}
}
}
If you now set a value to FirstName or LastName, it should raise a PropertyChanged event that updates the gui. try it out :)
Upvotes: 0
Reputation: 8743
You'll have to impelement INotifyPropertyChanged
and make sure that the event is invoked whenever your FullName
property changes. This is the case when either your FirstName
or your LastName
property changes.
public class TestModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
public string FirstName { get => _firstName;
set
{
_firstName = value;
InvokePropertyChanged();
InvokePropertyChanged(nameof(FullName));
}
private _string _lastName;
public string LastName
{
get => _lastName;
set
{
_lastName= value;
InvokePropertyChanged();
InvokePropertyChanged(nameof(FullName));
}
}
public string FullName
{
get
{
return $"{LastName}, {FirstName}";
}
}
private void InvokePropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Upvotes: 0