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));
}
Related
There is currently little documentation surrounding the limitations of events with the new C#8 default interface implementations (traits). I am particularly confused with the spec proposal. Not only is the example given invalid C# (the "override" event is missing an identifier), but implementing any of these in C#8 (VS2019, .NET Core 3.0) returns a host of compiler exceptions. In addition, the release notes for C#8 don't make any mention of events for interface traits. As I continued to try and track down an answer, I also couldn't gather anything useful from the open issues list.
So the questions are: is this feature implemented and usable? If so, what is the proper syntax?
Default interface members are used for traits, not just versioning, and an INPC trait would make sense.
Unfortunately, it's not possible to use DIMs to raise events right now, and implementing this seems to be a pain - it would require overhauling the events mechanism and break a ton of code, especially library code. We can use DIMs to add or remove handlers, but that's not so useful.
It would be nice to have something like :
interface InpcTrait : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private T Set(T value,String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
return value;
}
}
class Customer
{
private string _name;
public string Name
{
get=>_name;
set=>_name=Set(value,"Name");
}
}
Unfortunately, this isn't possible. That's because the event keyword in a class generates a backing field that holds the event handler and add/remove accessors. When we raise the event, we call that event handler.
Interfaces can't have state, which means we can't access that event to raise it.
When we specify an event in an interface, we create a virtual event and the compiler only allows adding/removing event handlers to it. Raising the interface still requires access to the backing field.
This Sharplab.io example shows that :
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Generates
[CompilerGenerated]
private PropertyChangedEventHandler m_PropertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
[CompilerGenerated]
add
{
//some code
}
[CompilerGenerated]
remove
{
//some code
}
}
private void NotifyPropertyChanged(string propertyName = "")
{
if (this.m_PropertyChanged != null)
{
this.m_PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
What we can do, is add or remove event handlers, but we can't even check whether the event already has other handlers. We risk adding the same event handler multiple times.
This is valid :
interface INPCtrait:System.ComponentModel.INotifyPropertyChanged
{
private void AddSomeDefaultHandler()
{
PropertyChanged+=Something;
}
private void RemoveDefaultHandler()
{
PropertyChanged-=Something;
}
public void Something(Object sender,System.ComponentModel.PropertyChangedEventArgs args)
{
}
}
But we have no way of knowing whether we need to add that default handler or not.
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.
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
I am currently working on a project where we would like to use the Fody PropertyChanged IL weaver. It appears to work fantastically, but it is doing strange things to our FxCop analysis built into visual studio.
We have CA1062 and CA2214 enabled: The former validates arguments of public methods and the latter checks for calls to overridable methods in constructors.
The second one, we have figured out is due to the fact that we are also using Caliburn.Micro which supplies a method called NotifyOfPropertyChanged which is virtual. This is a simple fix of changing the method to not be virtual.
The first issue is much harder and seemingly random. We have been hard core about validating our arguments from the get go and did not have any FxCop analysis errors before Fody, but when I added it through NuGet FxCop was seeing changes in places that shouldn't have any weaved code for property changed. I even looked at a diff of the de-compiled source before and after Fody, and there are FxCop conflicts in places where methods weren't changed at all.
So my question is whether I can somehow postpone Fody weaving till after the analysis is finished, or if there is some other way to get Fody and FxCop to interact nicely?
Thanks in advance
Example:
public class Base : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Descendent : Base
{
public Descendent()
{
DetermineDefaultMyPropertyValue();
}
public string MyProperty { get; set; }
private void DetermineDefaultMyPropertyValue()
{
MyProperty = "Default value";
}
protected override void OnPropertyChanged(string propertyName)
{
// some property changed logic
}
}
It could be solved by changing the auto implemented property to use a backing field.
( Only the ones that are set while constructing the object. )
public class Descendent : Base
{
public Descendent()
{
DetermineDefaultMyPropertyValue();
}
private string _myProperty;
public string MyProperty {
get { return _myProperty; }
set { _myProperty = value; }
}
private void DetermineDefaultMyPropertyValue()
{
_myProperty = "Default value";
}
protected override void OnPropertyChanged(string propertyName)
{
// some property changed logic
}
}
This is just something I was thinking as I was learning on Attributes and I was using the INotifyPropertyChanged too much, is just and Idea and I would like to hear some opinios about it.( I know this would require some work on the compiler and not on the cosumer side)
Since INotifyPropertyChanged is used with the same Pattern most of the time .. just like calling the method that fire ups the event with the name of the property ,could it be designed as and Attribute and using Auto-Properties? So that the compiler knows it need to add the call to the PropertyChanged event?
So if we have the class....
public class DemoCustomer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private string companyNameValue = String.Empty;
...
}
Instead of declaring the property
public string CompanyName
{
get
{
return this.companyNameValue;
}
set
{
if (value != this.companyNameValue)
{
this.companyNameValue = value;
NotifyPropertyChanged("CompanyName");
}
}
}
we could do something like this if we can indicate to the compiler by this attribute that it needs to generate a call to the PropertyChanged with the name of the property if the new value is different from the previous
[NotifyPropertyChanged]
public string CompanyName
{
get;set;
}
We could still keep coding in the old way for some custom behaviours when no using the Attribute..
In case anyone happens across this thread and is using C# 5 (VS 2012+, .NET 4.5+). This case be done "more easily" now with CallerMemberNameAttribute. This attribute is applied to a string parameter and causes the compiler to pass in the name of the calling method/property when the default value us used (i.e. when and argument is not passed). Making implementing INotifyPropertyChanged less tiresome. For example:
public class MyClass
{
private string myProperty;
public string MyProperty
{
get { return myProperty; }
set
{
myProperty = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
So, you just need OnPropertyChanged() in each setter to send out the event and don't have to deal with a hard-coded string for the property name.
This style of thinking is called Aspect Oriented Programming (or AOP). You can achieve the end result by adding a post build action using Mono's Cecil to go through properties with that attribute and modify the behavior of the property and spit out the newly compiled assembly with the appropriate behavior. You can look at Jason Bock's Dimecast on "Leveraging Cecil to inject code into your Assemblies"