I've been using MVVM Light for a while now - it's extremely useful and almost always the first library I add to a new project!
I'm wondering what the implications would be of developing a class that implements INotifyPropertyChanged to encapsulate a bindable property (example below).
public class BindableProperty<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private T mValue;
public T Value
{
get { return mValue; }
set
{
if (!EqualityComparer<T>.Default.Equals(mValue, value))
{
mValue = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
}
}
public BindableProperty(T default_value)
{
mValue = default_value;
}
}
With this class, I have to change my Xaml, but I believe my ViewModel might be more readable (below) - especially when the number of properties grows.
<TextBox Text="{Binding FirstName.Value, UpdateSourceTrigger=PropertyChanged}"/>
public class MainVM
{
public BindableProperty<string> FirstName { get; private set; }
public BindableProperty<string> LastName { get; private set; }
public MainVM()
{
FirstName = new BindableProperty<string>("");
LastName = new BindableProperty<string>("");
}
}
I know MVVM Light is designed to be extremely flexible, lightweight, and provide complete control (which it does very well). I can of course combine implementations and use the BindableProperty class above for some properties and more explicit ViewModelBase code for other properties in more complex situations.
Am I missing something obvious? What are some of the trade offs for this design that I might not be aware of (e.g. implications of changing xaml binding, data validation...)?
There is no extra value in encapsulating a property. This may work for very simple scenarios, but as soon as your ViewModel becomes more complex, you'll end up wiring your encapsulated classes in odd ways.
This approach won't work very well with validation neither it will if you have properties that depend on each other, i.e. FirstName, LastName and FullName where FullName is just a public string FullName { get { return FirstName+" "+LastName; } }.
Same applies for validation (with IDataErrorInfo). With the base class your code looks like
public string FirstName
{
get { return firstName; }
set
{
if(string.IsNullOrEmpty(value))
{
// where errors is a Dictionary<string, string>
errors.Add(nameof(FirstName), "First name can't be empty.");
return;
}
if(value.Length <2)
{
errors.Add(nameof(FirstName), "First name must be at least 2 characters long.");
return
}
Set(ref firstName, value);
errors.Remove(nameof(FirstName));
}
}
This will be a pain to implement in encapsulated properties
Related
If I wanted to create a Attribute (derived from System.Attribute) that hooks into the .NET Build process and translates/converts a standard C# auto property like:
[Notify]
public string Name { get; set; }
to this code, which then is compiled:
private string _nameField;
public string Name
{
get => _nameField;
set
{
if (!EqualityComparer<string>.Default.Equals(_nameField, value))
{
_nameField = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
How would I achive it? What would I have to do?
How can I let the attribute hook into the Build?
As you can see I have no clues at all of the Build process, nor of Roslyn.
But I want to get rid of superfluous MVVM boilerplate code and no longer spent too much time for dull repetitive typing....
Thx, Chris
There's a Fody weaver for this, called PropertyChanged.
Fody is a system for modifying your code at the end of the compile process. The PropertyChanged weaver automatically impements change notification for all properties in all classes that implement INotifyPropertyChanged. It has a number of ways to control the generation using attributes, implementing methods, etc.
The example from their GitHub project page starts like this:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName => $"{GivenNames} {FamilyName}";
}
The weaver interprets the above and generates code like this:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get => givenNames;
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged(InternalEventArgsCache.GivenNames);
OnPropertyChanged(InternalEventArgsCache.FullName);
}
}
}
string familyName;
public string FamilyName
{
get => familyName;
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged(InternalEventArgsCache.FamilyName);
OnPropertyChanged(InternalEventArgsCache.FullName);
}
}
}
public string FullName => $"{GivenNames} {FamilyName}";
protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
PropertyChanged?.Invoke(this, eventArgs);
}
}
internal static class InternalEventArgsCache
{
internal static PropertyChangedEventArgs FamilyName = new PropertyChangedEventArgs("FamilyName");
internal static PropertyChangedEventArgs FullName = new PropertyChangedEventArgs("FullName");
internal static PropertyChangedEventArgs GivenNames = new PropertyChangedEventArgs("GivenNames");
}
Of course you won't have access to that version, since it happens somewhere around the end of the compilation pass and your source is unaffected. Debugging the code can be a little difficult.
To help with that the PropertyChanged weaver looks for pre-implemented methods matching various rules and generates code to call those. If you have an OnPropertyChanged method in the class it'll call that instead of generating a boilerplate version. Or you can add an OnFamilyNameChanged method to the class above and that will be called before the OnPropertyChanged method.
You can use Roslyn source generators for that: Source Generators Cookbook
There's an INotifyPropertyChanged example.
I am developing a ViewModel based on BindableBase.
This VM holds an instance of a domain model.
The VM exposes a property, say Name from which I want to not use local storage (i.e. storage in VM), but rather delegate to the model object's property.
I would like to use SetProperty(), but the storage reference cannot be a property.
Do I have to implement INotifyPropertyChanged my self ?
Is it at all a good idea to delegate to the model this way ?
Would it be possible to extend BindableBase (or team to add) to cover for this ?
So first you must chose how you will architect your VMs and Models. As you mentioned, there are a few options. The easiest and the way I recommend, is to just expose your Model as a property and then bind your View to the model properties:
public class MyViewModel : BindableBase
{
private Person _myPerson;
public Person Person
{
get { return _myPerson; }
set { SetProperty(ref _myPerson, value); }
}
}
If you don't want to do that and would rather wrap each individual model property, you would do it like this.
public class MyViewModel : BindableBase
{
private Person _myPerson;
private string _name;
public string Name
{
get { return _myPerson.Name; }
set { _myPerson.Name = value }
}
}
Keep in mind, your Person model object still has to implement INPC.
EDIT: If you don't have control over your models and need them to implement INPC, you could try to use IL weaving, or create a façade/decorator for your models and wrap them individually.
public class MyPersonFacade : BindableBase
{
private Person _myPerson;
private string _name;
public string Name
{
get { return _myPerson.Name; }
set
{
_myPerson.Name = value;
OnPropertyChanged();
}
}
}
Then use this as your Model in your VM.
Here's a simplified ViewModel:
public class EditViewModel : BaseViewModel
{
private Item _currentItem;
public Item CurrentItem
{
get
{ return _currentItem; }
set
{
if (_currentItem != value)
{
_currentItem = value;
OnPropertyChanged("CurrentItem");
}
}
}
private ObservableCollection<Property> _itemProperties;
public ObservableCollection<Property> ItemProperties
{
get { return _itemProperties; }
set
{
_itemProperties = value;
OnPropertyChanged("ItemProperties");
}
}
public void AddProperty() //this is called from an ICommand
{
Property p = new Property{ ItemId = CurrentItem.ItemId };;
CurrentItem.Properties.Add(p);
ItemProperties.Add(p);
}
}
What I'd like to do is to separate out the business logic here into a separate class. It keeps all the annoying MVVM boilerplate out of the way of the useful stuff, and in theory should lead to organizing the code into a more testable state.
We're starting to do this by creating separate "Logic" classes which inherit from BaseViewModel and then have the actual ViewModels inherit from their logic class. So:
public class EditLogic : BaseViewModel
{ }
public class EditViewModel : EditLogic
{ }
Then the logic goes in the logic class.
For some business logic this separation is simple - nice and clean. However, in the example I've given above I can't see a simple way of pulling that method out without a lot of unnecessary faff. Something like this (untested):
public class EditLogic : BaseViewModel
{
public Property GetNewProperty(Item currentItem)
{
Property p = new Property{ ItemId = currentItem.ItemId };
currentItem.Properties.Add(p);
return p;
}
}
public class EditViewModel : BaseViewModel
{
public void AddProperty() //this is called from an ICommand
{
ItemProperties(GetNewProperty(CurrentItem))
}
}
This seems potentially confusing - since it's relying on CurrentItem implicitly being passed by reference - and unnecessarily convoluted to no great gain.
This is, of course, a very simple example which isn't worth fussing over. But it illustrates the point that in MVVM it's very easy to end up mixing your presentation/binding code with your business logic for the sake of convenience.
I could move some of the properties out from the EditViewModel to the EditLogic but then we're losing the advantages of separating these two out in the first place.
So: is it worth bothering with this at all? If so, how far should we pursue it? And are there any better methods for maintaining separation?
What you are looking for are services.
public interface IPropertyService
{
Property GetNewProperty(Item currentItem);
}
You will of course need an implementation:
public class MyPropertyService : IPropertyService
{
public Property GetNewProperty(Item currentItem)
{
//TODO
}
}
You can then inject this service into the constructor of your view model as a dependency.
public class MyViewModel
{
private IPropertyService _PropertyService;
public MyViewModel(IPropertyService propertyService)
{
_PropertyService = propertyService;
}
public void AddProperty() //this is called from an ICommand
{
Property p = _PropertyService.GetProperty(CurrentItem);
CurrentItem.Properties.Add(p);
ItemProperties.Add(p);
}
}
This will ensure that you don't need to create a myriad of view model base classes for your business logic. Instead, encapsulate your business logic in services and pass them into view models that depend on them.
There seems to be conflicting thoughts on whether INotifyPropertyChanged should be implemented in the Model or not. I think that it should be implemented in the ViewModel, but I can't figure out how it would be accomplished. There are plenty of mentions of this same idea all over stackoverlow.com ( In MVVM model should the model implement INotifyPropertyChanged interface?, In MVVM should the ViewModel or Model implement INotifyPropertyChanged?), but I can't find any example to show how to do it.
Let's say for example I have a model Person:
Public Person {
public int Age { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public void NameChange( string newName );
}
How would I implement the ViewModel so that changes in Age, FirstName, or LastName are all recognized?
Public PersonViewModel : INotifyPropertyChanged {
Person _person;
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName) {
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
//ctor, Properties, etc...
}
EDIT - Clarification:
So without changing the Person model how do I modify the ViewModel to get notified of the updates?
Is that even possible? If not, how are those that subscribe to the "INPC in the model is baaaad" get notified of changes in the model?
ViewModel should definitely implement INotifyPropertyChanged. I don't have a strong opinion on whether it should be implemented in the Model as well. I don't think you need it when the model properties don't change independently from the ViewModel while it is bound to the View.
Anyway, this is how I'd implement INotifyPropertyChanged in the ViewModel when it is not already implemented in the Model:
public class PersonViewModel : INotifyPropertyChanged
{
private Person person;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public PersonViewModel(Person person)
{
this.person = person;
}
public int Age
{
get { return person.Age; }
set
{
if (value != person.Age)
{
person.Age = value;
OnPropertyChanged("Age");
}
}
}
public string FirstName
{
get { return person.FirstName; }
set
{
if (value != person.FirstName)
{
person.FirstName = value;
OnPropertyChanged("FirstName");
}
}
}
public string LastName
{
get { return person.LastName; }
set
{
if (value != person.LastName)
{
person.LastName = value;
OnPropertyChanged("LastName");
}
}
}
}
Seeing how you updated you question, I need to add that without having INotifyPropertyChanged (or a similar custom notification event) implemented in the model, you can't get notified about the changes in the model that happen in it independently from the ViewModel. I guess you should be able to avoid that. Otherwise just implement INotifyPropertyChanged in it. There's nothing wrong with that if you need it.
Interesting question. I've read about MVVM for more than a year now, and I'm still not sure about it.
If your application is representing a state of a process for example, and this state is modified internally without any interaction of the user, then your model needs to be able to notify your viewmodel that it changed.
So if your model implement INotifyPropertyChanged, and your viewmodel only pass the same informations to the view, then... does your viewmodel really need to exist...?
In our company, we consider two main cases:
We structure our software with a quite strict UML analysis before developping (not so agile). When we then want to display our objects on screen, they return us their different views, which are used when needed with Bindings (using ContentControl or so). Most of the views we need for our software display these kinds of object, that implement INotifyPropertyChanged and are therefore also kind of ViewModels.
To build the software main Views (view structure), we create global views and ViewModels for them. That's when we really follow the MVVM practices.
Maybe I missed a point about MVVM, but in my experience, it's not a pattern that you absolutely have to always follow. It's a very good way of thinking to develop WPF applications, but creating ViewModels for each and every view seems to me like a big overhead.
What do all of you think of this way of doing?
Best regards,
Antoine
EDIT 31.03.2012
I have found a very interesting article explaining how to handle your model properties in the viewmodel, without having to implement a proxy property in the viewModel for each one of them.
Also the writer say some words about having INPC implemented in the model, and the viewmodel listening to it.
I think this is the most practical oriented article I've read about MVVM so far.
Check it out :
http://msdn.microsoft.com/en-us/magazine/ff798279.aspx
In my experience, Model objects don't have to (and probably shouldn't) know that they are being constructed in a View. Often, Model objects are entities that should never be allowed to be in an invalid state. ViewModel objects are the things that construct the Model objects.
So, since you never want to create a person who is very old or very young, and every person needs a name, your Person class might look like this:
public class Person {
public int Age { get; private set; }
public string Name { get; private set; }
public Person(int age, string name) {
if (age < 0 || age > 150) throw new ArgumentOutOfRangeException();
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException();
Age = age;
Name = name;
}
}
And your PersonViewModel might look like this::
class PersonViewModel : INotifyPropertyChanged {
private int _Age;
private int _Name;
public int Age {
get { return _Age; }
set {
if (_Age.Equals(value)) return;
_Age = value;
RaisePropertyChanged("Age");
}
}
public string Name {
get { return _Name; }
set {
if (_Name.Equals(value)) return;
_Name = value;
RaisePropertyChanged("Name");
}
}
public Person CreatePerson() {
return new Person(_Age, _Name);
}
}
You can then put whatever values you want in your PersonViewModel without worrying about creating an invalid Person object. You can also implement validation in the PersonViewModel that may be more strict than the validation in the Person class (for example, restricting the age range to adults over 18 years old (see IDataErrorInfo)).
Save for the typos you pretty much have it ;)
All you would need to add is your constructor and property definitions:
public class PersonViewModel : INotifyPropertyChanged
{
Person _person;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
public PersonViewModel(Person person)
{
_person = person;
}
public int Age
{
get
{
return _person.Age;
}
set
{
_person.Age = value;
OnPropertyChanged("Age");
}
}
}
If you have a choice, I would definitely recommend implementing INotifyPropertyChanged in the Model because you won't havae to worry about translating Models to ViewModels and back.
But if you can't, see above :)
If I try "var mainpage new Mainpage()"
I will run the mainpage constructor and then all the fields in the XAML object will return to null. How to I access XAML objects in silverlight that are from a different class but part of the same namespace?
Let me explain by example. If you look at the first answer, here is what I am encountering
public class MyPage
{
MyPage()
{
// the constructor makes all the variables from the xaml null
}
public TextBox MyTextBox
{
get { return SomeTextBox; }
}
}
public class SomeOtherClass
{
private void SomeFunction()
{
var page = new MyPage(); // this makes the text empty
var sometext = page.MyTextBox.Text; // so sometext will be empty
}
}
So whatever the user imputs when the program first runs turns to null when I run SomeFunction.
What I am first going to try is to see if when SomeClass is created, the values are put into that class.
If that fails, I am going to try MVVM. I have seen the http://www.vimeo.com/8915487 video and I got the sample mvvm code
Here is the Model:
namespace SimpleMVVM.Model
{
public class SimpleModel
{
// super easy version
//public string SomeSimpleValue { get; set; }
private string _SomeSimpleValue = string.Empty;
// actually do something version...
public string SomeSimpleValue
{
get
{
return "some value";
}
set
{
_SomeSimpleValue = value;
}
}
}
}
here is the view:
and here is the viewmodel.cs
using Simple;
using SimpleMVVM.Model;
namespace SimpleMVVM.ViewModel
{
public class SimpleViewModel : SimpleViewModelBase
{
private SimpleModel MyModel = new SimpleModel();
public string SomeSimpleValue
{
get { return MyModel.SomeSimpleValue; }
set
{
if (MyModel.SomeSimpleValue != value)
{
MyModel.SomeSimpleValue = value;
RaisePropertyChanged("SomeSimpleValue");
}
}
}
}
}
Using this example, I am wondering if it will just as easy as injecting a ViewModel and then changing the bindings in the Model and the View.
Is MVVM really this easy?
There is one more. It is the viewmodel base class
using System.ComponentModel;
namespace Simple
{
public class SimpleViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string PropertyName)
{
var e = new PropertyChangedEventArgs(PropertyName);
PropertyChangedEventHandler changed = PropertyChanged;
if (changed != null) changed(this, e);
}
}
}
OK, so now the hard part. If I create a new class. How do I get the data from the viewmodel class?
First, let me get this rant out of the way: what you propose is very bad design. It fits the definition of smelly code.
If you insist on doing it this way, the "best" approach to take is to declare some public variables on your page that return the actual UI elements.
<UserControl x:Class="MyNamespace.MyPage" ...>
<Grid>
<TextBox x:Name="SomeTextBox" Width="100" />
</Grid>
</UserControl>
public class MyPage
{
public TextBox MyTextBox
{
get { return SomeTextBox; }
}
}
public class SomeOtherClass
{
private void SomeFunction()
{
var page = new MyPage();
page.MyTextBox.Text = "some text";
}
}
Of course the preferred method would be to use something like the MVVM pattern to implement binding from your window to its viewmodel, then you can just read the property values from the viewmodel, this way you avoid trying to touch any UI elements from a totally different class.
Another way to do it (without going the full MVVM route) is to inject the necessary values into the constructor of the control/page that you are instantiating, and from there you can assign them to the appropriate UI element properties. This is still smelly, but better than directly accessing the UI elements from the outside.