WPF automatically updates from INotifyPropertyChanged? - c#

Does WPF framework automatically get the updates from INotifyPropertyChanged derived types to the Bindings in the UI?
Or do I have to do it manually ?

If you implement INotifyPropertyChanged correctly, it's automatically.

Yes it is Semi manually,
string _tText;
public string Text
{
get { return _tText; }
set
{
_tText = value;
OnPropertyChanged("Text");
}
}
Here the property changed show call once the property is set with some value. OnPropertyChanged("Text");

semi-Manually :-)
When you implement "INotifyPropertyChanged" you will implement those methods (which are usually implemented the same)
#region INotifyPropertyChanged
protected void RaisePropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) { PropertyChanged(this, e); }
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
After that, when one of your properties has changed, you need to call "RaisePropertyChanged" with the name of the property that changed. usually in the setter of that property

Related

Could there be any possible issues with this ViewModel?

Are there any possible errors with the ViewModel below?
In other words, is there any part of the code that is likely to create any issues?
Please advise.
Thank you.
public class MyViewModel : INotifyPropertyChanged
{
private string _text;
public string Text
{
get { return _text; }
set
{
if (Equals(_text, value))
return;
_text = value;
OnPropertyChanged(nameof(Text));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged!=null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You may have some threading issues if the OnPropertyChanged method is invoked from different threads. You may possibly end up having NullReferenceException thrown if there is any PropertyChanged event unscubscriptions happening on the other thread paralellely. When you intially check PropertyChanged for not null, it may come out as true, but the next line when you try to invoke the handler, it may be null. So the right and recommend way of doing it is shown below. Also You can enhance the method by adding the [CallerMemberNameAttribute] to the propertyName argument to avoid typos while passing the value for propertyName. With the attribute specified to the argument, you don't have to pass the property name for the property being changed. You just need to call the method with no arguments and the attribute will take care of passing the appropriate property name value.
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

MVVM update view model when model changes

I need to update ViewModel, when Model changes and Model, when ViewModel changes. In my case, I have: ProjectViewModel implementing INotifyPropertyChanged and Project(model) implementing INotifyPropertyChanged. When ViewModel changes I simply change Model directly. But when Model changes, then what? I tried to handle Model's property changed event in ViewModel. But then I will have strong reference from long-live Model and that will cause memory leak, or not? If yes so how to do it otherwise?
EDIT:
class ProjectViewModel : INotifyPropertyChanged
{
private Project Project;
public string Name
{
get
{
return Project.Name;
}
set
{
Project.Name = value;
OnPropertyChanged("Name");
}
}
public ProjectViewModel(Project project)
{
this.Project = project;
project.PropertyChanged += OnProjectChanged;
}
private void OnProjectChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
OnPropertyChanged("Name");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} //and then things for UI....
}
class Project : INotifyPropertyChanged
{
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}//and then logic (for saving)
}
EDIT2: Or can I implement something like IDisposable in ViewModel? Or make model(Project) property in ViewModel public and bind to it directly?
If your VM depends in any way on the value of a property in the model then yes, you will need to subscribe to its INPC notifications. But the reality is that usually the VM doesn't need to know about specific model values unless it is aggregating the values (i.e. summing a value from a list of objects).
When you do need to subscribe to the model's INPC then you'll also need to unsubscribe. This is not as troublesome as it might seem because the VM triggers the fetching or refetching of the models so it is clear when the unsubscribing needs to be done. Check the INotifyCollectionChanged interface - this is a good place to start for the subscribing and unsubscribing.
Personally, I would do what you suggested in your second edit, implementing INotifyPropertyChanged on the Model, making Project a public property, and binding to Project.Name directly.

notify viewmodel2 if viewmodel1 property changes

I have a scenario in wpf +mvvm i.e if my particular property changes in viewmodel1 then i wan to notify viewmodel2 having observable collection that property "A" has bee changed
1)I want to fire it for particular property not for all.
i have tried below code but not working .please let me know how cam i do this.
public class Model1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// Create custom event
public event EventHandler NotifyChange;
private string testProperty;
public string TestProperty
{
get
{
return testProperty;
}
set
{
testProperty = value;
// If changing properties, fire your OnPropertyChanged to update UI
OnPropertyChanged("TestProperty");
}
}
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
// Fire your custom event if a property changed
NotifyChange(this, null);
}
}
}
public class Model2 : INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public Model2()
{
// Assuming there is an accessible instance of model1
Model1 m1Instance = new Model1();
// Hook up your NotifyChange event from model1
m1Instance.NotifyChange += Model1Changed;
}
private void Model1Changed(object sender, EventArgs e)
{
// this will be triggered on change in model1
}
private void OnCollectionChanged(object singleObject)
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, singleObject));
}
}
Use PubSub Events
My suggestion would be to look into PubSub events.
My recommendation of doing this, is to use Prism. Here's some more information: http://www.c-sharpcorner.com/UploadFile/5ffb84/prism-event-aggregator-in-wpf-with-mvvm/
You will be sticking to proper MVVM practices in this case.
Here's MSDN's ever-useful guide: https://msdn.microsoft.com/en-us/library/ff649664.aspx
Have a really good read-up on how this works, and how to use/implement it.
Alternatively
This will work, but I would still defer to using PubSub events if possible.
You could try this:
public class Model1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _property;
public string Property
{
get { return _property; }
set
{
_property = value;
OnPropertyChanged("Property");
}
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Model2
{
public Model2()
{
// You might be storing your Model1 as a property in the Model2?
// I don't know, but I've put it in the constructor just for example.
var model1 = new Model1();
model1.PropertyChanged += OnModel1PropertyChanged;
}
private void OnModel1PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Property")
{
// Do stuff here when the right property has changed in model 1
}
}
}
I have only new'd up a Model1 in the constructor of Model2 as an example - you might be assigning it and storing as a field or property elsewhere in the Model2 ViewModel.
This might be particularly useful if you have ViewModels within ViewModels (parent VM > child VMs).
I use parent > child VMs quite regularly, and I don't think it's against MVVM best practices, but I still use the EventAggregator, instead of events.
As a side note, if you are using C#6:
Use nameof(Property) instead of "magic strings" (e.g. "Property"). This makes for much easier refactoring and compiler can tell you about errors - but essentially does the same job. Use this in the OnPropertyChanged() call in your setter
You can also use the nameof keyword when checking the property name, with the same principle as above. Like this: if (e.PropertyName == nameof(Model1.Property)) ...
Use null propagation: PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));. Changes the method body of your OnPropertyChanged to a nice one-liner, whilst still doing the same job
I digress
I would always look at sticking to proper MVVM practices, where possible.
I use Prism's EventAggregator on a daily basis and will swear by it.
Have a good read up on PubSub Events (you can use any type of event aggregator, but I would say to use Prism's, preferably)
Hope this helps! :)
If this is just to notify Model2 I think you dont need all these implementations. You can do something like
public string TestProperty
{
get
{
return testProperty;
}
set
{
testProperty = value;
// If changing properties, fire your OnPropertyChanged to update UI
OnPropertyChanged("TestProperty");
//Here you can call a method of Model2 sating that its changed
Model2 m2Instance = new Model2();
m2Instance.ValueChanged();
}
}
Add the method ValueChanged in you model 2.
That's because you're not registering to PropertyChanged. You're registering your event handler on NotifyChange so that PropertyChanged in Model1 is NULL and so NotifyChange is not fired.
So, you need to implement your OnPropertyChanged as the following:
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
if (NotifyChange != null)
NotifyChange(this, null);
}
Finally, if you want your NotifyChange to be fired for a specific property, then adjust the code above to check for the propName before you fire the event.

how to implement observable int in wpf ViewModel?

In my mvvm ViewModel I have such field
public int Delta { get; private set; }
However when I update it like that:
Delta = newValue;
UI is not refreshed.
I was thinking that databinding will do that for me. For example I can declare collection as ObservableCollection and then databinding will work.
However there are no ObservableInt, how to say View that it need to be refreshed then?
Probably I should raise some event "notify property changed" or something?
You have two choices:
Implement the INotifyPropertyChanged interface on your class.
Inherit from DependencyObject and implement Delta as a DependencyProperty.
The simplest option is #1. You can implement the INotifyPropertyChanged interface on your class quite easily:
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set { _delta = value; PropertyChanged?.Invoke(nameof(Delta)); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can read more about using and implementing dependency properties on MSDN.
While we're at it with improving the answer, some of the other new additions of c# 6.0 and 7.0 help make it ever more compact:
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get => _value;
set { _value = value; NotifyPropertyChanged(nameof(Value)); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
This way, you aren't using any "embedded values" (i.e - the property's name) and are keeping the code refactor-safe.
And there's also no need for redundant code blocks due to c# 6.0 and 7.0's new Expression body features
Using #LBushKin's Answer, i modified it to
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get { return _value; }
set { _value = value; NotifyPropertyChanged("Value"); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(String propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and to set it up:
class MainWindow ...
// a bool with initial value of true
public static Prop<bool> optionBool { get; set; } = new Prop<bool>{ Value = true };
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// connect UI to be able to use the Prop
DataContext = this;
}
and to use it:
<Grid ...
<CheckBox Content="Da Check" ... IsChecked="{Binding optionBool.Value}"/>
There is also a Collection and 2-Properties version here:
Utils.ObservableProperties.cs (this repo contains several related classes)
Just implement INotifyPropertyChanged Interface in your class and use it to raise a PropertyChanged for your Property and then UI will update. If you are using an MVVM project template then there is a good chance you already have a helper method implemented you only need to use it.
MSDN INotifyPropertyChanged
GalaSoft MVVM Light Toolkit
The ObservableCollection raises events automatically but for your own properties you have to raise the events yourself.
A good example is here: http://www.codeproject.com/Tips/228352/Naming-Properties-in-MVVM?display=Print
I'd suggest using mvvm light: http://mvvmlight.codeplex.com, I used it in silverlight and wpf applications. Very easy to use and provides a messageing system between model, view model and view.
Adding on to https://stackoverflow.com/a/8316100/5725669, there is a new and easy way to do this without remembering to keep track of PropertyChanged?.Invoke(nameof(Delta)); in every location
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set {
_delta = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged();
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public YourClass()
{
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
It makes use of CallerMemberName for skipping manual entries for property name. More details on this MSDN Doc

Refactoring PropertyChangedEventHandler

In my UI code I have a lot classes with the same basic skeleton:
derives from INotifyPropertyChanged
contains the following code:
void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
It seems like a perfect chance to factor into a class and to derive from that instead of INotifyPropertyChanged, but sadly C# doesn't support multiple inheritance so it's not really going to work. Any ideas on how to refactor this kind of code?
Can't you just put this code into your superclass's superclass?
Object
Your concrete NotifyPropertyChanged class <-- Insert here
Whatever your viewmodel inherited from (and stopped you using multiple inheritance
Your concrete viewmodel
Another concrete viewmodel
Most MVVM Frameworks provide such a class for you.
Because of the access rules around events, you can't factor this out into an extension method without reflection, unfortunately.
Maybe you can use something like this:
class A1 : INotifyPropertyChanged
{
private string _myProperty;
private static Expression<Func<A1, string>> myProperty = _ => _.MyProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
InvokePropertyChanged(myProperty);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void InvokePropertyChanged<T>(Expression<Func<A1, T>> property)
{
PropertyChangedEventHandler Handler = PropertyChanged;
if (Handler != null)
{
MemberExpression expression = (MemberExpression)property.Body;
Handler(this, new PropertyChangedEventArgs(expression.Member.Name));
}
}
}
This significally reduce future code changes;)
Or you can use Postsharp plug-in that automatically implements INotifyPropertyChanged.
A common practice is to have a base class implementing INotifyPropertyChanged, like this:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then your classes will derive from this one, and you can call OnPropertyChanged when you wish to notify changes on the value of a property :
public class PersonViewModel : ViewModelBase
{
public PersonViewModel(Person person)
{
this.person = person;
}
public string Name
{
get
{
return this.person.Name;
}
set
{
this.person.Name = value;
OnPropertyChanged("Name");
}
}
}
It is 4 lines of code that will never change. Create a snippet!
This is probably a small help but you could use the params keyword so that you could change more than one property at a time.
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertiesChanged(params string[] Properties)
{
if (PropertyChanged != null)
foreach (string property in Properties)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
This decreases the amount of lines you use when notifying property changes in the future. So you use:
NotifyPropertiesChanged("foo", "bar");
Instead of:
NotifyPropertyChanged("foo");
NotifyPropertyChanged("bar");
Otherwise, I agree with Anders' suggestion of moving it up the inheritance tree would probably be best.

Categories