mvvm update calculated fields - wpf

Do you know best practices in wpf+mvvm to update Calculated fields?
What I can do instead OnPropertyChanged(nameof(Summary))?
Also calculated field can be in another viewmodel and this viewmodel should not know about all dependences.
This is my code :
public class Model
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public int Summary => Prop1 + Prop2;
}
public class ViewModel : BaseViewModel
{
public Model Model { get; }
public int Prop1
{
get
{
return Model.Prop1;
}
set
{
Model.Prop1 = value;
OnPropertyChanged();
OnPropertyChanged(nameof(Summary));
}
}
public int Prop2
{
get
{
return Model.Prop2;
}
set
{
Model.Prop2 = value;
OnPropertyChanged();
OnPropertyChanged(nameof(Summary));
}
}
public int Summary => Model.Summary;
}

Calling OnPropertyChanged on the calculated property is perfectly acceptable. If you have a relatively simple model like the one you wrote that'll be enough.
If you have multiple calculated properties on the model, you might consider creating a method to call all of them from a single place, instead of calling each one from every property.
Something like this:
public int Prop1
{
get
{
return _prop1;
}
set
{
_prop1 = value;
OnPropertyChanged();
NotifyCalculatedProperties();
}
}
public int Calc1 { get { /* ... */ } }
public int Calc2 { get { /* ... */ } }
public int Calc3 { get { /* ... */ } }
public void NotifyCalculatedProperties()
{
OnPropertyChanged(nameof(Calc1));
OnPropertyChanged(nameof(Calc2));
OnPropertyChanged(nameof(Calc3));
}
In case the calculated properties exist in a different model, you can register in that Model\VM to the source's PropertyChanged event, and then invoke the change notification there.
Like that:
void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Prop1")
OnPropertyChanged(nameof(Calc1));
OnPropertyChanged(nameof(Calc2));
OnPropertyChanged(nameof(Calc3));
}
Just don't forget to unsubscribe when the Model\VM changes, or you'll have a memory leak on your hands.
Lastly, you can always use the Messenger to pass messages between unrelated VMs, though you should use caution since it's a very powerful tool, and can easily be misused.
I don't know what MVVM framework you're using, but each has it's own implementation. You can find more general details on the Messenger pattern here: https://msdn.microsoft.com/en-us/magazine/jj694937.aspx

Related

PropertyChanged for an extended class

My VS2015 solution consists of two projects: DataModel and DesktopClient.
DataModel has a Customer class - thats an EntityFramework 6 DB entity. Customer has a FirstName property.
In DesktopClient there is an extended class CustomerExt.
In DesktopClient, is it possible to have a notification to CustomerExt.FirstName changes? Defining a partial Customer across two projects won't work - DataModel is compiled first and it won't have partial properties defined in DesktopClient.
public class CustomerExt : Customer, INotifyPropertyChanged
{
public object Clone()
{
return this.MemberwiseClone();
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
this._isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
Unfortunately, if your base class does not implement INotifyPropertyChanged the safest way is to just write a wrapper class and only use that in your software. You can fit this in with your CustExt, or make it separate if you feel you want the extra layer.
This also assumes that while you may not control the Customer class, you control all of the code creating/editing the Customer instances, so that you can use this new class instead, then convert it to the original Customer class only when needed (such as a database transaction).
public class CustomerExt: INotifyPropertyChanged
{
Customer _customer = new Customer();
public object Clone()
{
return this.MemberwiseClone();
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
this._isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
#region WrapperProperties
public bool FirstName
{
get { return _customer.FirstName; }
set
{
_customer.FirstName= value;
NotifyPropertyChanged("FirstName");
}
}
#endregion
public Customer ToCustomer()
{
// returning a copy of the _customer instance here is safer than returning
// the reference, otherwise the properties could be altered directly
}
#region INotifyPropertyChanged
...
}
Some of this gets a little easier if you have an ICustomer interface and that is used during the database calls, then you can skip the formality of retaining a Customer instance.
I remember there being some third party libraries that have tried to automate this process - but I have never tried them and/or didn't trust them to work properly.
Let me see if I understand, you want update the View when your date is updated on the database?
You have to find a way to request this information from your ViewModel.
some kind of RefreshFirstNameAsync
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
this._firstName= value;
NotifyPropertyChanged("FirstName"); // There is better ways to implement that line
}
}
private void RefreshFirstName(){
FirstName = _userRepo.GetFirstNameAsync();
}

Wpf MVVM best practices in designing a viewmodel

I'm using WPF and am currently designing the getters/setters for my viewmodels. My question is should I be applying the changes to the model as soon as the user changes it (via the setter) or should I be copying the changes over to the model only when the Save() method is called? Eg:
Model
public class Customer {
string Name { get; set; }
int Age { get; set; }
}
ViewModel
public class CustomerVM {
//getters and setters are bound to the model.
public string Name {
get
{
return model.Name;
}
set {
model.Name = value;
}
}
public int Age {
get {
return model.Age;
}
set {
model.Age = value;
}
}
public Customer model { get; set; }
public CustomerVM(Customer model) {
SetModel(model);
}
private void SetModel(Customer model) {
this.model = model;
}
public void Save() {
CustomerService.Update(model);
}
}
is this preferred over..
public class CustomerVM {
string name;
public string Name {
get
{
return name;
}
set {
name = value;
}
}
int age;
public int Age {
get {
return age;
}
set {
age = value;
}
}
public Customer model { get; set; }
public CustomerVM(Customer model) {
SetModel(model);
}
private void SetModel(Customer model) {
//values are copied over to VM when upon initialization
this.model = model;
this.name = model.Name;
this.age = model.Age;
}
public void Save() {
//values are copied to the model when user saves
model.Name = name;
model.Age = age;
CustomerService.Update(model);
}
}
From an end-user perspective both will function exactly the same, i.e. the data will be saved when the Save method is invoked. If this is the correct behaviour for your app, you should use the simplest implementation, which is the first one.
I can see the second implementation being useful if you want to add 'undo' functionality, i.e. allow the user to reset the property values to the current model value.
Anyhow, I typically start with a view model that adapts the model values until I need somethng ore complex.

Alternatives to OnPropertyChanged

I'm trying to work out an issue I'm having with implementing MVVM in WPF. My Contact class below is my model that's being populated by Entity Framework.
public class Contact : INotifyPropertyChanged
{
public string _firstName;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public string _lastName;
public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
//INotifyPropertyChanged implementation omitted for brevity
}
Here's my ViewModel:
public class ContactViewModel
{
public Contact MyContact { get; set; }
public string FullName
{
get
{
return MyContact.FirstName + " " + MyContact.LastName;
}
}
}
So I set my View's datasource to an instance of ContactViewModel, and I'm binding two TextBoxes to MyContact.FirstName and MyContact.LastName. I'm binding a TextBlock to FullName. When I change either of my TextBoxes the Full Name TextBlock doesn't update (obviously, I'm not doing an OnPropertyChanged("FullName") anywhere).
The question is, where do I add OnPropertyChanged("FullName")? I don't necessarily want to modify my model because it's being used elsewhere and I don't to tie it to my ViewModel.
Do I need to rethink my architecture?
Do I need to rethink my architecture?
This can be solved with your current architecture. You just need to propagate the call from your Contact object to your viewModel object.
You will need to implement INotifyPropertyChanged in the viewModel to achieve this.
Something like this:
public class ContactViewModel : INotifyPropertyChanged
{
//INotifyPropertyChanged implementation omitted for brevity...
private Contact _myContact;
public Contact MyContact
{
get
{
return _myContact;
}
set
{
_myContact.PropertyChanged -= myHandler;
_myContact = value;
_myContact.PropertyChanged += myHandler;
}
}
public string FullName
{
get
{
return MyContact.FirstName + " " + MyContact.LastName;
}
}
private void myHandler(Object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("FullName");
}
}
I would also recommend taking a look at MVVM Foundation as this includes a class called PropertyObserver which is designed to make wiring up this sort of thing much easier.
If you want to take the more MVVM pure approach suggested by Big Daddy, you would need to do something like this:
public class ContactViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation omitted for brevity...
// You will require some way of setting this, either via a property
// or the viewModel constructor...
private Contact _myContact;
public string FirstName
{
get { return _myContact.FirstName; }
set
{
_myContact.FirstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("FullName");
}
}
public string LastName
{
get { return _myContact.LastName; }
set
{
_myContact.LastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("FullName");
}
}
public string FullName
{
get
{
return MyContact.FirstName + " " + MyContact.LastName;
}
}
}
Do I need to rethink my architecture?
Maybe...
It looks to me like you're binding your view's properties to your view-model (ContactViewModel) and your model (Contact). Your view can see your public model's properties, etc. via your view-model - I don't think this is good. It looks like a violation of the Law of Demeter. I'd rather see you use your view-model as a wrapper/façade to your model. This creates more work for sure, but I think it gives you a better design and more flexibility. Your view-model will need to implement INotifyPropertyChanged for this to work.

Store custom class instance in IsolatedStorage in Silverlight

I need to store different objects in IsolatedStorage and i'm using IsolatedStorageSettings class to do that. Some of that objects are base types so stored and retrieved well. But some of them are custom classes instances and they stored well, but when i try to retrieve them i get instances with the initial values.
How can i store custom classes instances in IsolatedStorage and retrieve them?
Phil Sandler, i guess so. but i don't know what type of serialization use isolated storage, so i don't know how to make my class serializable. Private fields also must be stored.
Here is the code of custom class:
public class ExtentHistory : INotifyPropertyChanged
{
private const int Capacity = 20;
private List<Envelope> _extents;
private int _currentPosition;
public event PropertyChangedEventHandler PropertyChanged;
public int ItemsCount
{
get { return _extents.Count; }
}
public bool CanStepBack
{
get { return _currentPosition > 0; }
}
public bool CanStepForward
{
get { return _currentPosition < _extents.Count - 1; }
}
public Envelope CurrentExtent
{
get { return (_extents.Count > 0) ? _extents[_currentPosition] : null; }
}
public ExtentHistory()
{
_extents = new List<Envelope>();
_currentPosition = -1;
}
public void Add(Envelope extent)
{
if (_extents.Count > Capacity)
{
_extents.RemoveAt(0);
_currentPosition--;
}
_currentPosition++;
while (_extents.Count > _currentPosition)
{
_extents.RemoveAt(_currentPosition);
}
_extents.Add(extent);
}
public void StepBack()
{
if (CanStepBack)
{
_currentPosition--;
NotifyPropertyChanged("CurrentExtent");
}
}
public void StepForward()
{
if (CanStepForward)
{
_currentPosition++;
NotifyPropertyChanged("CurrentExtent");
}
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And here are the functions of storing and retrieving:
private IsolatedStorageSettings _storage;
public void Store(string key, object value)
{
if (!_storage.Contains(key))
{
_storage.Add(key, value);
}
else
{
_storage[key] = value;
}
}
public object Retrieve(string key)
{
return _storage.Contains(key) ? _storage[key] : null;
}
I don't want to serialize manually every object to add, i want to make custom class serializable by default to store it in isolated storage (if it's possible)
My inital guess would be a serialization problem. Do all your properties have public setters? Post the classes you are storing and the code you are using to store them.
I believe IsolatedStorageSettings uses the DataContractSerializer by default. If you want ExtentHistory to be serialized, you should read up on what you need to do to get it to work properly with this serializer:
DataContractSerializer Class
You might create a separate object strictly for the purpose of storing the data in Isolated storage (sort of like a DTO). This will allow you to keep ExtentHistory as-is.

MVVM with aggregated model classes - how to wrap in ViewModels?

I'm currently trying to create a small application using the MVVM pattern. However I don't really know how to correctly wrap up aggregated Model classes in my ViewModel. From what little I know about MVVM, you're not supposed to expose Models in your ViewModel as properties or else you could directly bind to the Model from your View. So it seems I have to wrap the nested Model in another ViewModel, but this imposes some problems while synching Model and ViewModel later on.
So how do you do that efficiently?
I'll give a short example. Let's suppose I have the following model classes:
public class Bar
{
public string Name { get; set; }
}
public class Foo
{
public Bar NestedBar { get; set; }
}
Now I create two ViewModel classes accordingly, wrapping the Models, but run into problems with the FooViewModel:
public class BarViewModel
{
private Bar _bar;
public string Name
{
get { return _bar.Name; }
set { _bar.Name = value; }
}
}
public class FooViewModel
{
private Foo _foo;
public BarViewModel Bar
{
get { return ???; }
set { ??? = value; }
}
}
Now what do I do with the Bar-property of FooViewModel? For "get" to work I need to return a BarViewModel instance. Do I create a new field of that type in FooViewModel and just wrap the _foo.NestedBar object in there? Changes to that field's properties should propagate down to the underlying Bar instance, right?
What if I need to assign another BarViewModel instance to that property, like so:
foo.Bar = new BarViewModel();
Now that won't propagate down to the model, which still holds the old instance of type Bar. I'd need to create a new Bar object based on the new BarViewModel and assing it to _foo, but how do you do that elegantly? It's pretty trivial in this sample, but if Bar is much more complex with lots of properties, that'll be a lot of typing... not to mention it'd be very prone to errors, if you forget to set one of the properties.
#Goblin
There are some flaws with your code: e.g. what if I get a list of Foo objects from database and I want to wrap each of them in an ObservableCollection?
then your Constructor of FooViewModel should accept the Foo model as parameter and not create it inside the Constructor!
Normally you do this to wrap a model into a viewmodel and put it the same time into a bindable Collection:
IEnumerable<Foo> foos = fooRepository.GetFoos();
foos.Select( m => viewmodelCollection.Add(new ViewModel(m,e.g.Service)));
The models properties are not copied to the ViewModel hell no!!! The ViewModel does delegate its properties to the model properties like:
public class FooViewModel
{
private Foo _foo;
public FooViewModel(Foo foo,IService service)
{
_foo = foo;
}
public string FoosName
{
get{return _foo.Name };
set
{
if(_foo.Name == value)
return;
_foo.Name = value;
this.NotifyPropertyChanged("FoosName");
}
}
}
And like Goblin said all UI-Specific interfaces like:
IDataErrorInfo
INotifyPropertyChanged
IEditableObject
etc...
are implemented the by the ViewModel ONLY.
My above answer only makes sense if you are doing DDD - if you are not - you can solve your problem like this - simply 'flattening' the model:
public class FooViewModel
{
private Foo _foo;
public string Name
{
get { return _foo.Name; }
set { _foo.Name = value; }
}
public string BarProperty
{
get { return _foo.Bar.Property; }
set { _foo.Bar.Property = value; }
}
}
Or you could do like I showed in the prior example - just ignore everything about Aggregates... should still work.
Okay - first things first - using the term Aggregate implies you are adhering to DDD? If you are - you are doing an encapsulation no-no :-). One Aggregate should never be allowed to edit another Aggregate. If what you have is that both are really Aggregate they would become associated (which is perfectly 'legal' in a DDD-sense - but then your propety on the FooViewModel wouldn't be of type BarViewModel, but rather type Bar. That way Bar would (as it should) be responsible for updating itself - and we only maintain the link in FooViewModel.
However, if what you are doing is AggregateRoot with a ValueType child - then here is what you could do given a slightly modified domain model:
public class Foo
{
public string SomeProperty { get; set; }
public Bar Bar { get; set; }
public void Save()
{
//Magically saves to persistent storage...
}
}
public class Bar
{
public Bar(string someOtherProperty)
{
SomeOtherProperty = someOtherProperty;
}
public string SomeOtherProperty { get; private set; }
}
And then for the ViewModels:
public class FooViewModel
{
private Foo _foo;
public FooViewModel()
{
Bar = new BarViewModel();
}
public BarViewModel Bar { get; private set; }
public void SetFoo(Foo foo)
{
_foo = foo;
SomeProperty = foo.SomeProperty;
Bar.SetBar(foo.Bar);
}
public string SomeProperty { get; set; }
public void SaveChanges()
{
_foo.SomeProperty = SomeProperty;
_foo.Bar = Bar.CreateUpdatedBar();
_foo.Save();
}
}
public class BarViewModel
{
public string SomeOtherProperty { get; set; }
public void SetBar(Bar bar)
{
SomeOtherProperty = bar.SomeOtherProperty;
}
public Bar CreateUpdatedBar()
{
return new Bar(SomeOtherProperty);
}
}
This way - the FooViewModel is now capable of controlling the BarViewModel (which does nothing but accept a valuetype - and create a new one when asked). This also solves a common UI-problem ('How do we edit an object that has no setters?' - answer: 'We don't - we create a new one'). A lot of fleshing out is missing (INotifyPropertyChanged, dirty-tracking etc., but those are easy if you get through this leap of thinking :-).
I hope this makes a wee bit of sense :-) Otherwise, I'll be happy to elaborate.

Resources