I Have a base Class that Implements INPC named Bindable this is my Event
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
Everything Works fine but if I have two delegates for my PropertyChangedEventHandler, only once is fired.
public class FirstClass : Bindable
{
public FirstClass()
{
PropertyChanged += Delegate1;
}
Void Delegate1 ....
}
public class SecondClass : Bindable
{
private FirstClass _MyFirstClass
public FirstClass MyFirstClass
{
get { return _MyFirstClass; }
set { SetProperty(ref _MyFirstClass, value); }
}
public SecondClass()
{
MyFirstClass = new FirstClass();
MyFirstClass.PropertyChanged += Delegate2;
}
Void Delegate2 ....
}
In this simple only Delegate1 runs Delegate2 never happens.
How Can I do implment my method properly for trigger any delegate?
EDIT 1: Corrected some code and instantiation of Firsrtclass.
EDIT 2: Full Implementation
public class Bindable : INotifyPropertyChanged //, INotifyPropertyChanging
{
#region BindableBase
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
} // Clase que implementa INotifyPropertyChanged
The problem is that when you create a new instance of FirstClass and set it, the event already fires, before giving you a chance to subscribe to it.
You could move the contents of FirstClass' constructor to a new method and call that method after the events are bound.
Here is your code, fixed:
public class FirstClass : Bindable
{
public void Init()
{
PropertyChanged += Delegate1;
// other stuff
}
Void Delegate1 ....
}
public class SecondClass : Bindable
{
private FirstClass _MyFirstClass
public FirstClass MyFirstClass
{
get { return _MyFirstClass; }
set { SetProperty(ref _MyFirstClass, value); }
}
public SecondClass()
{
MyFirstClass = new FirstClass();
MyFirstClass.PropertyChanged += Delegate2;
MyFirstClass.Init();
}
Void Delegate2 ....
}
Your code is set up wrong for this to run correctly, at all.
If you just instantiate a FirstClass, you would expect just Delegate1 to fire, as no SecondClass object was created.
If you instantiate a SecondClass, the reference to FirstClass is null, so it should throw a NullReferenceException right away on the event registration. You need to either create a FirstClass when you create a SecondClass, or pass SecondClass a FirstClass instance in its constructor that you can then use to register for the event.
Related
I try to binding textblock usercontrol with property of my class, but it only works at initial stage, I have implement IPropertyChnaged in my class.
In my class, _Feedbackpos (field of property) would change in background, I don't know how to solve this problem.
my class
public class TestControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyname)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
private double _Feedbackpos;
public double Feedbackpos
{
get
{
return _Feedbackpos;
}
set
{
_Feedbackpos = value;
NotifyPropertyChanged("Feedbackpos");
}
}
//it's a callback function, it would excute when detect feedback position of controller change
private void ReadFeedbackpos()
{
_Feedbackpos = Controller.Read();
}
}
application windows
TestControl TestDll = new TestControl();
Binding BindingTxtBlk = new Binding(){Source= TestDll, Path = new Property("Feedbackpos")};
FeedbackPosTxtBlk.Setbinding(Textblock.TextProperty,BindingTxtBlk);
Change the function ReadFeedbackpos() to
private void ReadFeedbackpos()
{
Feedbackpos = Controller.Read();
}
Otherwise NotifyPropertyChanged("Feedbackpos"); will never get called.
Here's what I have now:
class MyClass
{
public string status;
private void DoSomething()
{
// do something and make change to this.status;
}
}
class MyClass2
{
public List<MyClass> MyClassLst;
private void DetectChangeInList()
{
// if the status property of an item in this.MyClassLst changed, remove this item from list
}
}
I have a List<MyClass>, and each of the MyClass will do some work and change the property status. I want to detect if any of the MyClass has its status changed and remove this item from MyClassLst.
I read about something on event but not very clearly about how to make it work.
If you need to be notified about changes to individual properties of each MyClass instance, it's not something that can magically happen.
Your MyClass will have to be responsible for firing an event whenever something changes (usually the PropertyChanged event, i.e. the INotifyPropertyChanged interface), and the other class will have to attach a handler to each item in the list to get notified.
C#6 has a couple of syntactic improvements which simplify this a bit, but you still have lots of work to do for each property:
public class Model : INotifyPropertyChanged
{
// this is the event which gets fired
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// you need to raise the event in each property's setter
private _someValue;
public string SomeValue
{
get { return _someValue; }
set { if (value != _someValue) { _someValue = value; OnPropertyChanged(); } }
}
private _anotherVal;
public string AnotherValue
{
get { return _anotherVal; }
set { if (value != _anotherVal) { _anotherVal = value; OnPropertyChanged(); } }
}
}
In your case, it would be:
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// Never use public fields!
// Status should be a property which gets and sets
// the actual private backing field (_status).
private _status;
public string Status
{
get { return _status; }
set { if (value != _status) { _status = value; OnPropertyChanged(); } }
}
}
You will also most likely want to change List<MyClass> into your own implementation of ICollection<T> which will attach and detach these handlers as you add or remove items. It is usually done by deriving from Collection<T> and overriding relevant methods. If you are not comfortable with that, a slightly simpler approach might be to make the list private and expose Add/Remove and similar methods where you will attach/detach to the PropertyChanged event.
I have a simple POCO with a lot of Properties. To simplify things let´s assume the POCO looks like this:
public class Project
{
public int ProjectId {get; set;}
}
Now I want to create an Event that fires when the ProjectId is changed. What I have now is this:
public class Project
{
public int ProjectId {get; set;}
public event EventHandler ProjectChanged;
private void OnProjectChanged(EventArgs args)
{
if (ProjectChanged != null) ProjectChanged (this, args);
}
}
Now I have to extend the Property in order to call the Eventhandler:
public class Project
{
private int mProjectId;
public int ProjectId
{
get { return this.mProjectId;}
set
{
this.mProjectId = value;
this.OnProjectChanged(EventArgs.Empty);
}
}
public event EventHandler ProjectChanged;
private void OnProjectChanged(EventArgs args)
{
if (ProjectChanged != null) ProjectChanged(this, args);
}
}
I wonder if there is an easier way to attach the Eventhandler. Maybe some kind of annotation ? Like
public class Project
{
[OnChange("OnProjectChanged", EventArgs.Empty)]
public int ProjectId {get; set;}
public event EventHandler ProjectChanged;
private void OnProjectChanged(EventArgs args)
{
if (ProjectChanged != null) ProjectChanged (this, args);
}
}
Taking some ideas from the question I posted in the comments, you could implement an abstract base class which implements INotifyPropertyChanged. Each time you declare a property, you call SetField to trigger the PropertyChanged event.
This avoids two things:
1) Explicitly implementing INotifyProperty changed each time using the abstract class
2) Explicitly implementing a method to trigger the event each time, shortening the set code to one line.
abstract class ModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
class Project : ModelBase
{
private string _name;
public string Name
{
get { return _name; }
set { SetField(ref _name, value, "Name"); }
}
}
class TestRunner
{
public TestRunner()
{
Project p = new Project();
p.PropertyChanged += (o, e) =>
{
// Changed
};
p.Name = "Test";
}
}
I'd like to implent the System.ComponentModel.INotifyPropertyChanged interface for a property on a base class, but I'm not quite sure how to hook it up.
Here's the signature for the property I'd like to get notifications for:
public abstract bool HasChanged();
And my code in the base class for handling the change:
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
How do I handle the hookup of the event in the base class without having to call OnPropertyChanged() in each child class?
Thanks,
Sonny
EDIT:
OK... so I think that when the value for HasChanged() changes, I'm supposed to call OnPropertyChanged("HasChanged"), but I'm not sure how to get that into the base class. Any ideas?
Is this what you are after?
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
//make it protected, so it is accessible from Child classes
protected void OnPropertyChanged(String info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Notice the OnPropertyChanged accessible level is protected. And then in your concrete class or child classes, you do:
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");
}
}
}
EDIT: after reading the OP question again, I realize that he does not want to call the OnPropertyChanged in the child class, so I am pretty sure this will work:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool hasChanged = false;
public bool HasChanged
{
get
{
return this.hasChanged;
}
set
{
this.hasChanged = value;
OnPropertyChanged("HasChanged");
}
}
//make it protected, so it is accessible from Child classes
protected void OnPropertyChanged(String info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
and in child class:
public class PersonViewModel : ViewModelBase
{
public PersonViewModel()
{
base.HasChanged = true;
}
}
I just recently discovered an INotifyPropertyChange interface. I managed to implement this interface in my clss and everything works fine. However I was wondering if it is possible to intercept this event in code and fire a function
Let's say that I have a function
DoStuff()
and I wan't to fire this function everytime property1, property2 or property3 changes.
Of course I could put this function in set block in my class but this is not a good idea(I think).
If you mean to internal method that'll handle this event you can do it by registering to the event in the class constructor. For example:
public class AnswerViewModel : IAnswerViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private string content;
public AnswerViewModel()
{
PropertyChanged += (sender, args) => DoStuff();
}
public string Content
{
get { return content; }
set
{
content = value;
PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
public void DoStuff()
{
// this method will be called whenever PropertyChanged event raised
}
}
If the intercepting method belongs to other class:
public class PropertiesInterceptor
{
private readonly AnswerViewModel viewModel;
private readonly List<string> propertiesToIntercept =
new List<string> { "property1", "property2", "property3" };
public PropertiesInterceptor(AnswerViewModel viewModel)
{
this.viewModel = viewModel;
viewModel.PropertyChanged += OnPropertyChanged;
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (propertiesToIntercept.Contains(args.PropertyName))
{
DoStuff();
}
}
private void DoStuff()
{
// Do something with viewModel
}
}
Intercept the PropertyChanged Event:
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
You could fire the method from a RaisePropertyChanged() method:
public int Property1
{
get { return this.property1; }
set
{
if (this.property1 != value)
{
this.property1 = value;
RaisePropertyChanged("Property1");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
DoStuff(); // Call DoStuff here.
}
Stealing Elisha's answer to answer your question in Merlyn's answer
public class AnswerViewModel : IAnswerViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private string property1;
private string property2;
private string propertyX;
public AnswerViewModel()
{
PropertyChanged += (sender, args) =>
{
if(args.PropertyName == "Property1" || args.PropertyName == "Property2")
DoStuff();
}
}
public string Property1
{
get { return content; }
set
{
property1 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Property1"));
}
}
public string Property2
{
get { return content; }
set
{
property2 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Property2"));
}
}
public string PropertyX
{
get { return content; }
set
{
propertyX = value;
PropertyChanged(this, new PropertyChangedEventArgs("PropertyX"));
}
}
public void DoStuff()
{
// this method will be called whenever PropertyChanged event raised from Property1 or Property2
}
}
If the class DoStuff is in is a member you can do
private otherClass
public AnswerViewModel()
{
PropertyChanged += (sender, args) =>
{
if(args.PropertyName == "Property1" || args.PropertyName == "Property2")
otherClass.DoStuff();
}
}
Otherwise you can just have otherClass register a event on its own in your main code.
Did you need it to replace the existing NotifyPropertyChanged event handlers, or just get called when NotifyPropertyChanged is called?
If you mean the second, you can simply register an event handler
edit
You can add an event handler that gets called on NotifyPropertyChanged, checks if the property parameter is equal to Property1, Property2, or Property3, and only then forwards it to the actual function you want to call.