I created a composite control using C# Windows Forms Control Library, when I use the new control in a test program, I want to find a way to detect when did the name of new control changed at design time, what should I do?
You can use the IComponentChangeService (in System.ComponentModel.Design) like in this example:
public class MyControl : UserControl
{
public event EventHandler NameChanged;
protected virtual void OnNameChanged()
{
EventHandler handler = NameChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
protected override void OnCreateControl()
{
base.OnCreateControl();
IComponentChangeService changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
if (changeService == null) return; // not provided at runtime, only design mode
changeService.ComponentChanged -= OnComponentChanged; // to avoid multiple subscriptions
changeService.ComponentChanged += OnComponentChanged;
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs e)
{
if (e.Component == this && e.Member.Name == "Name")
OnNameChanged();
}
}
This service is only provided in design mode, not at runtime.
I unsubscribe and subscribe again to the ComponentChanged event (to avoid multiple subscriptions).
In the event handler OnComponentChanged I check if my name has changed and raise the NameChanged event.
There is no event "OnNameChanged" and the Name-Property itself is not virtual and therefore not overrideable... I'm afraid, that there is no sure way...
Some suggestions:
Define your own Name-Property with "new", but inherited classes will not automatically overtake this and - even worse - casts to Control and usage of Control.Name will just work around your property...
Define a virtual "MyName"-Property and use this only to write through onto the Control's Name-Property. Still a call on Control.Name will fly by, but at least you can enforce the usage of MyName...
Puffer the Name within your constructor and define a Timer to look regularely if something has changed
Alltogether I must admit, that I would not use any of those... I don't know what exactly you want to achieve, but I'd try to find a different approach to reach this goal...
public MyControl : UserControl
{
public new string Name
{
get { return base.Name; }
set
{
base.Name = value;
// handle the name change here....
}
}
}
Related
Issue
I have created an event in the class LoginVM which looks like the following:
public class LoginVM : INotifyPropertyChanged
{
public event EventHandler<string> PasswordSet;
}
Also in this class I have a piece of code which fires this event:
public class LoginVM : INotifyPropertyChanged
{
public event EventHandler<string> PasswordSet;
private void PopulateLatestServer()
{
try
{
string SERVER_ID = Registry.GetValue(#"HKEY_CURRENT_USER\SOFTWARE\PODIA", "LATESTSERVER", null).ToString();
BDO_SERVERS latestserver = SavedServers.Where(a => a.Server_ID == SERVER_ID).FirstOrDefault();
setServerURL(latestserver.ServerURL, false);
Username = latestserver.Username;
PasswordSet(this, latestserver.Password);
}
catch (Exception)
{
Global.WriteLog("Could not find last logged in server.", EventLogEntryType.Warning);
}
}
}
I have another class which is called LoginV and in there I create an instance of the class and subscribe to the event:
public partial class LoginV : MetroWindow
{
public LoginV()
{
InitializeComponent();
LoginVM _loginVM = new LoginVM();
this.DataContext = _loginVM;
_loginVM.PasswordSet += new EventHandler<string> (_loginVM_PasswordSet);
}
private void _loginVM_PasswordSet(object sender, string e)
{
passwordBox.Password = e;
}
As you can probably tell I am trying to trigger an event from the ViewModel to the View but every time I trigger the event from the ViewModel, PasswordSet is null and errors.
An event is null when there's no listener to the event.
private void RaisePasswordSet(String pass) {
YourEventArgs args = new YourEventArgs(pass);
if(PasswordSet != null) PasswordSet(this, args);
}
Your issue is that when you try to raise the event no one listen to it yet.
It's a good idea to initialize the password in the constructor for LoginVM as you did. That's when initialization ought to happen. Ordinarily, you'd set a property and the binding in the XAML would take care of updating the control. No need for an event on the VM. But this is a password box, so you can't bind it, and the event you wrote is The Right Thing.
But in your implementation, that leaves you with this sequence of events:
Create VM
VM raises PasswordSet in its constructor -- without checking to see if there are any handlers.
View assigns VM to DataContext
View adds handler to PasswordSet event
And you get an exception at step 2, because you didn't check for handlers.
Here's what you do.
In the VM or anywhere, always use this pattern for raising events:
C# <= 5:
protected void OnPasswordSet(String e)
{
var handler = PasswordSet;
if (handler != null)
{
handler(this, e);
}
}
C#6
protected void OnPasswordSet(String e) => PasswordSet?.Invoke(this, e);
Either:
private void PopulateLatestServer()
{
try
{
string SERVER_ID = Registry.GetValue(#"HKEY_CURRENT_USER\SOFTWARE\PODIA", "LATESTSERVER", null).ToString();
BDO_SERVERS latestserver = SavedServers.Where(a => a.Server_ID == SERVER_ID).FirstOrDefault();
setServerURL(latestserver.ServerURL, false);
Username = latestserver.Username;
OnPasswordSet(latestserver.Password);
}
catch (Exception)
{
Global.WriteLog("Could not find last logged in server.", EventLogEntryType.Warning);
}
}
Can't crash now. Or at least not the same way as last time.
Problem number two: How do you update the view initially?
Easy: Take whatever's in the view's PasswordSet handler, move it into a protected method, and call that in both places. This looks a little verbose since it's only a one-liner, but it's nice to have things rolled into neatly labeled units. If that code were more complicated, you'd absolutely want not to be copying and pasting it. If it gets more complicated a year from now, you won't have to waste any time re-parsing your the old code.
public partial class LoginV : MetroWindow
{
public LoginV()
{
InitializeComponent();
LoginVM _loginVM = new LoginVM();
this.DataContext = _loginVM;
_loginVM.PasswordSet += new EventHandler<string> (_loginVM_PasswordSet);
UpdatePassword();
}
protected void UpdatePassword()
{
passwordBox.Password = e;
}
private void _loginVM_PasswordSet(object sender, string e)
{
UpdatePassword();
}
Option number two: Keep OnPasswordSet() as shown above, but instead of having the view manually update the password in the constructor, have the LoginVM require a PasswordSet handler as a parameter. This isn't the way I would do it; constructor parameters like this get on my nerves. But that may just be an irrational prejudice on my part. This way makes more clear the fact that the owner needs to handle that event to use the class, and "provide a suitable event handler" becomes the only thing the consumer needs to do in order to use the thing. The less a consumer needs to know about your class's internals, the better, for obvious reasons. Platonically ideal design would be when programmers who don't think at all can make casual glib assumptions about your class, and not end up on Stack Overflow begging somebody to read the documentation to them out loud. We'll never get there, though.
public class LoginVM : INotifyPropertyChanged
{
public LoginVM(EventHandler<string> passwordSetHandler)
{
if (passwordSetHandler != null)
{
PasswordSet += passwordSetHandler;
}
PopulateLatestServer();
}
// If the consumer doesn't want to handle it right way, don't force the issue.
public LoginVM()
{
PopulateLatestServer();
}
A third option is to set up explicit add/removes for the event, and raise the event when the handler comes in:
public class LoginVM : INotifyPropertyChanged
{
private event EventHandler<string> _passwordSet;
public event EventHandler<string> PasswordSet
{
add
{
_passwordSet += value;
// ...or else save latestServer in a private field, so here you can call
// OnPasswordSet(_latestServer.Password) -- but since it's a password,
// best not to keep it hanging around.
PopulateLatestServer();
}
remove { _passwordSet -= value; }
}
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.
I have UserControl which has two components
public System.Windows.Forms.ComboBox innerComboBox;
public System.Windows.Forms.TextBox innerTextBox;
and when i am using this UserControl i can not call Validating event like
myName.Validation += new System.ComponentModel.CancelEventHandler(myName_Validating);
becouse not working. I must call it like.
myName.innerTextBox.Validating += new System.ComponentModel.CancelEventHandler(myName_Validating);
can i override my UserControll that all events will be calling from innerTextBox of TextBox ?
In your UserControl, try adding it like this:
public new event CancelEventHandler Validating {
add { innerTextBox.Validating += value; }
remove { innerTextBox.Validating -= value; }
}
Then your myName.Validating should work like you want.
No, but you can "wrap" them in some ways, meaning you will need to expose the events from your top-level user control to which outsiders can subscribe, and then, subscribing to the inner-control even handlers, "inform" your top-level subscribers. For instance...
public class MyControl
{
public event CancelEventHandler Validating;
public System.Windows.Forms.TextBox innerTextBox;
public MyControl()
{
//post-instantiation stuff here
innerTextBox.Validating += myName_Validating;
}
void myName_Validating(oject sender, CancelEventArgs e)
{
if (Validating != null)
{
Validating(sender, e);
}
}
}
How exactly you want to wrap them is up to you; if you want your control to have some handling logic prior to publishing and executing the top-level subscriptions (can be handy in threading situations) then this will work, otherwise a more concise and practical approach for your might be what LarsTech suggests.
Someone gave me this code that works great. But I would really like to understand what is happening inside it. Could somebody explain please? What is the meaning of each part of the code? The code is inside a custom control which has two labels inside a panel.
Also I've seen some custom control events that use add/remove syntax, what is that for? What is the difference with what is happening here?
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public event EventHandler MyCustomClickEvent;
protected virtual void OnMyCustomClickEvent(EventArgs e)
{
// Here, you use the "this" so it's your own control. You can also
// customize the EventArgs to pass something you'd like.
if (MyCustomClickEvent != null)
MyCustomClickEvent(this, e);
}
private void label1_Click(object sender, EventArgs e)
{
OnMyCustomClickEvent(EventArgs.Empty);
}
}
See my comments below. Also for a more detailed event I blogged on this concept a while back where I go into more detail on the entire process.
public partial class UserControl1 : UserControl
{
//This is the standard constructor of a user control
public UserControl1()
{
InitializeComponent();
}
//This defines an event called "MyCustomClickEvent", which is a generic
//event handler. (EventHander is a delegate definition that defines the contract
//of what information will be shared by the event. In this case a single parameter
//of an EventArgs object.
public event EventHandler MyCustomClickEvent;
//This method is used to raise the event, when the event should be raised,
//this method will check to see if there are any subscribers, if there are,
//it raises the event
protected virtual void OnMyCustomClickEvent(EventArgs e)
{
// Here, you use the "this" so it's your own control. You can also
// customize the EventArgs to pass something you'd like.
if (MyCustomClickEvent != null)
MyCustomClickEvent(this, e);
}
private void label1_Click(object sender, EventArgs e)
{
OnMyCustomClickEvent(EventArgs.Empty);
}
}
I'd recommend reading up on Events for C# on MSDN. This is covered in detail.
Basically, MyCustomClickEvent is an event. The OnMyCustomClickEvent method is used to raise the event, but is being done in a way that subclasses can also raise this event if required.
When you click on "label1", the OnMyCustomClickEvent method runs, which raises the event. Any delegates subscribed to the event will execute at that point.
You mentioned seeing the add/remove syntax for events in some custom control examples. Most likely those examples are using the UserControl class' Events property to store event handlers, such as in the following example:
public event EventHandler MyEvent
{
add
{
Events.AddHandler("MyEvent", value);
}
remove
{
Events.RemoveHandler("MyEvent", value);
}
}
The idea there is that usually a consumer of a control is not going to want to handle every single event that the control exposes. If each event is defined as a "field" event (as in your example), then each event will take up a chunk of memory even if there are no subscribers for that event. When you have a complex page constructed of hundreds of controls, each of which may have dozens of events, the memory consumption for unused events is not insignificant.
This is why the System.ComponentModel.Component class (the base class of the System.Windows.Forms.Control class) has an Events property, which is basically a dictionary to store event handler delegates. This way each event is implemented more like a property than a field. The add/remove handlers for each event store or remove delegates from the Events dictionary. If an event is not used, then there just isn't an entry in the Events dictionary for it, and no additional memory is consumed for that event. It's a trade-off of doing slightly more work (having to look up the event handler) to save slightly more memory.
EDIT: fixed my answer to pertain to Windows Forms, rather than ASP.NET, although the concepts are the same.
Concerning the add/remove, this is a "manual" implementation of events. The following two snippets do the same thing.
Automatic implementation:
public event EventHandler MyEvent;
Manual implementation:
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add { _myEvent += value; }
remove { _myEvent -= value; }
}
This is exactly the same idea as automatic properties where:
public string Property { get; set; };
Does exactly the same as:
private string _property;
public string Property
{
get { return _property; }
set { _property = value; }
}
The difference between these snippets is that with the manual implementations, you get more control. Examples are:
Implement logic in the add/get and remove/set;
Get access to the fields which allows you to set e.g. [NonSerializable];
Put the values in e.g. a Dictionary.
The Form class e.g. does the latter to keep the number of fields in the Form class down.
I got a scenario like this
Class Parent
{
Property A;
}
Class A
{
Property X
}
How can I get a PropertyChangedNotification on Property A when X changes? I don’t want to refer ‘Parent’ in class A or any kind of event which spoils my decoupling. What I basically want is to make the Parent.IsDirty==true. This is a very simplified version of my story, I got tens of classes like Parent, so I am looking for some generic way to handle this.
Please note that this is not the actual code. I got all INotifyPropertyChanged implementation. I am just wondering any easy mechanism like RaisePropertyChanged("A.X")
You can try to register the propertychanged event in the parent class. In the constructor you can subribe to the event:
public Parent()
{
A.OnPropertyChanged += OnAPropertyChanged;
}
void OnAPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "X")
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("A"))
}
hope this helps...
I don't know if this is the best practice but:
what about something like this:
private Movie movie;
public Movie Movie
{
get { return movie; }
set
{
var oldProperties = typeof(Movie).GetProperties();
foreach (var property in oldProperties)
{
if (property.GetValue(movie).GetHashCode() != value.GetType().GetProperty(property.Name).GetValue(value).GetHashCode())
{
RaisePropertyChanged(property.Name);
}
}
movie = value;
}
}
surely you can take it outside to an external helper that take the 2 objects and the event handler