Clarifications:
1.- I don't know if this has an specific name or word to reference it in English or programming slang, so maybe this can be a duplicate post, since I'm can't look about it.
2.- I'm totally newbie with this stuff, I've never used handlers, so that's part of the problem.
I'm trying to understand how the NotifyPropertyChanged mechanism works. Based on: INotifyPropertyChanged, focusing on the example. (I'm looking it in Spanish, above you can change it to the original English one if it doesn't change auto.
Now I'm going to extract the main code that makes me wonder, and try to analyze it. Hope you can show me where (if exist) i'm wrong and what I can't understand. Let's focus on the class that implements the interface.
// This is a simple customer class that
// implements the IPropertyChange interface.
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;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
Well, what do I understand? (or believe it).
From:
public event PropertyChangedEventHandler PropertyChanged;
1.- PropertyChanged is a method. The one which would be executed when ProperyChanged event triggers.
Doubt: But this method is never implemented...
From:
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
2.- NotifyPropertyChanged is a method. Created by us, can have any name we desire. This method will be launched by us when a property gets modified.
Question: Does this method triggers ProperyChanged event?
Doubt: For me, as I can see there, no one launches this event, but the method we've created to be launch when triggers. But since it doesn't triggers, and instead of it we directly launch the method...
Mixture final think: NotifyPropertyChanged throws the event using the Hanlder, in order to be caught by the "superior entity" (the binding source in the example code), which receives the modified property in order to can update it. Then, if I want to know which elements/classes can be aware of this kind of events, what can I do?
I think this last one is the correct one, but since I'm not and expert but my thinking while trying to understand it and writing this question, I'd like you to correct me.
Thanks so much!
UPDATED
Thanks so much to all! Then, Can I suscribe to the event with the method i want? I've tried:
objetos[usados] = new ItemDB();
objetos[usados].PropertyChanged += mensaje();
With:
public async void mensaje(string cadena)
{
var dlg = new ContentDialog(){
Title = "My App",
Content = cadena,
PrimaryButtonText = "Yes",
SecondaryButtonText = "No"
};
var result = await dlg.ShowAsync();
}
But then VS says:
Error 1 Ninguna sobrecarga correspondiente a 'mensaje' coincide con el 'System.ComponentModel.PropertyChangedEventHandler' delegado
Translated:
Error 1 No one overload corresponding to 'mensaje' matches with 'System.ComponentModel.PropertyChangedEventHandler' delegate
Why doesn't it work, since my event is given with an arg that is a string, and mensaje receives as an argument and string?
I recommend you look up Events and Delegates in C# for further reading.
public event PropertyChangedEventHandler PropertyChanged;
PropertyChanged ist an EventHandler, which is a delegate. Other code can register here and get executed when the delegate is called.
So what happens with INotifyPropertyChanged is:
Some Code (propably the binding in Xaml) registers for the PropertyChanged event:
yourobject.PropertyChanged += MethodThatShouldBeCalledWhenThePropertyChanges;
(This is most properly auto generated somewhere, because it happens from xaml, but you could as well to it this way by hand.)
In the NotifyPropertyChanged Method, the event delegate gets executed.
It simply executes all methods that were added to the event.
So to answer your questions:
Yes, the code in NotifyPropertyChanged "triggers" the event. It calls every method that was added to the event.
Any code can register for events.
As of your Update:
I again recommend reading into delegates.
You can think of delegates as Method Interface. It defines that a method has to take specific parameter types to match the delegate.
PropertyChanged is of type PropertyChangedEventHandler which takes an object and a PropertyChangedEventArgs parameter.
So any method like this is suitable:
void MethodName(
Object sender,
PropertyChangedEventArgs e
)
First, you are correct, NotifyPropertyChanged is a user-defined function. It is indented just to avoid doubling of logic as soon as more properties are used. Second, NotifyPropertyChanged will not be executed when the event triggers, but the other way round; as soon as NotifyPropertyChanged is called, the event is triggered. If a suitable control is bound, it will, so to speak, consume the event and probably update itself. The event can be seen as an outlet on which other code can register callbacks. Furthermore, the Attribute CallerMemberName was introduced with .NET 4.5. The same result can be achieved without using it, but for each call of NotifyPropertyChanged the name of the property would have to be given explicitly.
Related
I have been looking in to subscribing to an event using a weak event pattern. With the .NET 4.5 framework, we have a slick looking WeakEventManager class. Weakly subscribing to an event is as simple as
WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);
I'm not a big fan of 'stringly-typed' code however. I have been trying to find a way around using the string name of the event to subscribe to. The only way I have found to obtain the name of the event is using a lambda expression in the class that defines the event. In my scenario, I own the class defining the event so I can change it however I like. I have been trying to find a clean way to subscribe and unsubscribe to my event and here is what I disliked the least.
public event EventHandler<EventArgs> LoggingOn;
public event EventHandler<EventArgs> LoggingOn_Weak
{
add
{
var eventName = this.GetEventName(() => this.LoggingOn);
WeakEventManager<CurrentUser, EventArgs>.AddHandler(this, eventName, value);
}
remove
{
var eventName = this.GetEventName(() => this.LoggingOn);
WeakEventManager<CurrentUser, EventArgs>.RemoveHandler(this, eventName, value);
}
}
// In a base class view model in my scenario
private string GetEventName<T>(System.Linq.Expressions.Expression<Func<T>> expression)
{
return (expression.Body as System.Linq.Expressions.MemberExpression).Member.Name;
}
protected void OnLoggingOn(object sender, EventArgs e)
{
var handler = this.LoggingOn;
if (handler != null)
{
handler(sender, e);
}
}
Using custom event accessors I was able to avoid clunky (in my opinion) methods like LoggingOn_Subscribe(EventHandler) or adding name properties for each event. Unfortunately it is not so intuitive in that people subscribing to the event are doing so in the classic manner but have no idea other than the "_Weak" part of the name that indicates it is being subscribed to weakly.
As for my questions..
1) I have never used weak events or custom event accessors before. The code above appears to work, however, I would just like to make sure there is nothing technically wrong with it. Is there anything I'm doing here to shoot myself in the foot?
2) From a design perspective, is this a terrible idea? Are there any major design concerns I should consider? Is there better alternative? Should i just suck it up and subscribe from my subscriber using a stringly-typed event name?
Thoughts?
With .NET 4.6 you can now use the nameof() expression:
WeakEventManager<IMyGrid, MyEventArgs>.AddHandler(myGrid, nameof(IMyGrid.MouseDown), OnMouseDown);
What you could do is use the built-in System.ComponentModel.EventHandlerList. This class is a container for all of your object's event handler delegates. The primary benefit is that no storage is allocated on your object for each event unless there is actually someone subscribed to an event.
The secondary benefit is that in order to use it, you must provide a key for your event.
class MyObject
{
protected EventHandlerList Events = new EventHandlerList();
public static Event1Key = new object();
public event Event1
{
add { Events.AddHandler(Event1Key, value); }
remove { Events.RemoveHandler(Event1Key, value); }
}
}
Now you could create a variation of WeakEventManager that accepted keys rather than string names. So the consumer could say
WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, EventSource.Event1Key, source_SomeEvent);
Technology: .NET 4, C#, WinForms, Visual Studio 2010
I am in the processing of learning data binding and have been unable to get even a simple example to work as expected. I have a form with a label that I am binding to that shows the current mouse cursor coordinates.
public partial class Form1 : Form, INotifyPropertyChanged
{
[Bindable(true)]
private String cursorPosition;
public String CursorPosition
{
get
{
return cursorPosition;
}
set
{
cursorPosition = value;
NotifyPropertyChanged("CursorPosition");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
CursorPosition = "(" + Convert.ToString(e.X) + " , " + Convert.ToString(e.Y) + ")";
}
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
From the designer, I have set the label's Data Binding to bind the Text property to form1BindingSource - CursorPosition. What am I missing?
Edit: Updated code snippet.
From the designer, I have set the label's Data Binding to bind the Text property to form1BindingSource - CursorPosition. What am I missing?
Have you set:
form1BindingSource.DataSource = this; // (or whatever the real data source is)
e.g. in the form's constructor, after InitializeComponent?
(This assumes that your Form1 instance is the data source, and you're binding the controls to it via a BindingSource.)
A few further detail suggestions:
Choosing the form itself as data source is somewhat unusual. IMHO it's better to separate all bound-to properties into a separate, non-UI data object. This then allows you to create a reusable base type for the INotifyPropertyChanged implementation.
As #rfmodulator says in his answer, the BindableAttribute is attached to the field:
[Bindable(true)]
private String cursorPosition;
public String CursorPosition
…
You probably meant to attach it to the property:
private String cursorPosition;
[Bindable(true)]
public String CursorPosition
…
Your setter should probably look like this:
set
{
if (!string.Equals(cursorPosition, value) // +
{ // +
cursorPosition = value;
NotifyPropertyChanged("CursorPosition");
} // +
}
That is, only raise the PropertyChanged event when the property value actually changes.
You probably want to change your NotifyPropertyChanged method to this:
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged; // +
if (handler != null) // ~
{
handler(this, new PropertyChangedEventArgs(propertyName)); // ~
}
}
This is because PropertyChanged could in theory change between the null check and the invocation. You can exclude this theoretical possibility by creating a local copy of the event delegate.
P.S.: To be precise, as Jeffrey Richter points out in his book "CLR via C#", a local variable is still not quite enough: Ideally, you'd assign Interlocked.CompareExchange(ref PropertyChanged, null, null) to handler (instead of simply PropertyChanged) because using that method will prevent the JIT code generator from optimizing away the local variable (IIRC).
I assume you want the PropertyChanged event to fire? You are setting the backing variable's value in Mouse_Move, not the property's value. As a result, the call to NotifyPropertyChanged won't get called.
You shouldn't implement INotifyPropertyChanged on components, controls and forms because databinding will rely on the XXXChanged event paradigm to listen for change notification. Internally, databinding uses property descriptors to listen for change events. They hint how the class detects changes in the documentation for the PropertyDescriptor.SupportsChangeEvents property. This has to do with the history of winforms databinding. XXXChanged was the way to do databinding & change notification before .NET 2.0. INotifyPropertyChanged was introduced in 2.0 in favor of the XXXChanged pattern.
The SupportsChangeEvents property indicates whether value change notifications for this property may originate from outside the property descriptor, such as from the component itself, or whether notifications will only originate from direct calls made to the SetValue method. For example, the component may implement the INotifyPropertyChanged interface, or may have an explicit nameChanged event for this property.
You're setting the Bindable attribute on the field but calling NotifyPropertyChanged with the property as the argument.
Typically we use this code:
private EventHandler _updateErrorIcons;
public event EventHandler UpdateErrorIcons
{
add { _updateErrorIcons += value; }
remove { _updateErrorIcons -= value; }
}
Is there a similar shortcut like with automatic properties?
Something like:
public event EventHandler UpdateErrorIcons { add; remove; }
Yep. Get rid of the { add; remove; } part and the backing delegate field and you're golden:
public event EventHandler UpdateErrorIcons;
That's it!
Let me just add that before you asked this question, I hadn't even thought about the fact that the auto-implemented version of events is inconsistent with that of properties. Personally, I would actually prefer it if auto-implemented events worked the way you first attempted in your question. It would be more consistent, and it would also serve as a mental reminder that events are not identical to delegate fields, just like properties are not identical to regular fields.
Honestly, I think you're the rare exception where you actually knew about the custom syntax first. A lot of .NET developers have no clue there's an option to implement your own add and remove methods at all.
Update: Just for your own peace of mind, I have confirmed using Reflector that the default implementation of events in C# 4 (i.e., the implementation that gets generated when you go the auto-implemented route) is equivalent to this:
private EventHandler _updateErrorIcons;
public event EventHandler UpdateErrorIcons
{
add
{
EventHandler current, original;
do
{
original = _updateErrorIcons;
EventHandler updated = (EventHandler)Delegate.Combine(original, value);
current = Interlocked.CompareExchange(ref _updateErrorIcons, updated, original);
}
while (current != original);
}
remove
{
// Same deal, only with Delegate.Remove instead of Delegate.Combine.
}
}
Note that the above utilizes lock-free synchronization to effectively serialize add and remove calls. So if you're using the latest C# compiler, you don't need to implement add/remove yourself even for synchronization.
public event EventHandler UpdateErrorIcons;
is just fine
you can use
yourObbject.UpdateErrorIcons += YourFunction;
add {} and remove {} are used only in special cases where you need to handle event hookups manually. Us mere mortals normally just use public event EventHandler UpdateErrorIcons; where "EventHandler" is the delegate of choice.
For instance:
public delegate void MyEventDelegate(object sender, string param1);
public event MyEventDelegate MyEvent;
Note that because MyEvent is null if it doesn't have any listeners you need to check if it is null before invoking it. A standard method for doing this check is:
public void InvokeMyEvent(string param1)
{
MyEventDelegate myEventDelegate = MyEvent;
if (myEventDelegate != null)
myEventDelegate(this, param1);
}
A key element in this check is to make a copy of the object in question first and then work only on the copy. If not you could get a rare race condition where another thread unhooks between your if and your call.
I'm using some CLR objects that use the INotifyPropertyChanged interface and use the PropertyChanged function to update in WPF bindings.
Pretty boilerplate:
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Then the property:
private double m_TotalWidgets = 0;
public double TotalWidgets
{
get { return m_TotalWidgets; }
set
{
m_TotalWidgets = value;
RaisePropertyChanged("TotalWidgets");
}
}
Is there a better way to update a derived value or even the whole class?
Say I had a calculated value:
public double ScaledWidgets
{
get
{
return TotalWidgets * CONSTANT_FACTOR;
}
}
I would have to fire ScaledWidget's PropertyChanged when TotalWidgets is updated, eg:
set
{
m_TotalWidgets = value;
RaisePropertyChanged("TotalWidgets");
RaisePropertyChanged("ScaledWidgets");
}
Is there a better way to do this? Is it possible "invalidate" the whole object, especially if there are a lot of derived values? I think it would be kind of lame to fire 100 PropertyChanged events.
You can raise the PropertyChangedEvent with the parameter string.empty or null. Then all properties of the object get "invalidated". See my post here
I don't know if there is a better way but I can suggest two things:
Create a class that encapsulates the firing of the PropertyChanged event so you don't have to write a lot of boilerplate code. You just have to declare the PropertyChanged event and use that class to invoke it.
If there are properties that are dependent from each other. Create a handler to the independent property so that every time it changes you can invoke the dependent properties. For example you can have an internal handler for TotalWidgets so that when it changes, you can change ScaledWidgets accordingly.
Why do you have to write 100 PropertyChanged events? Maybe it's not necessary and the problem is somewhere else.
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"