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.
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; }
}
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....
}
}
}
I have a class called TextBoxPlus that inherits from UserControl and holds multiple regular TextBox instances, while also exapnding on their functionality. I have an instace of TextBoxPlus in my Windows Form that is calling the TextChanged event. The issue I have is I want it to pass through to the TextChanged event of one of the TextBox instances within the TextBoxPlus. I don't want to make the TextBox public so I'm likely looking at an override but I'm not sure on how to do this as this does not work:
protected override void TextChanged(EventArgs e)
{
this.textbox.TextChanged(e);
}
obviously due to the TextChanged event not calling a method. How can I achieve this?
Define an event on the containing class whose implementation adds its handlers to the contained object's events:
public event EventHandler TextChanged
{
add
{
textbox.TextChanged += value;
}
remove
{
textbox.TextChanged -= value;
}
}
I am hoping someone can help me out. I have created a usercontrol in C# for use on a Winform. The control contains various controls including a monthCalendar control however the monthCalendar control is where my problem lies.
I want the parent form that holds my usercontrol to trigger a block of code to query a database using linq when the dateSelected event of the monthCalendar within the usercontrol is triggered. The idea being that the usercontrol should not be aware of the data access side of things so that the usercontrol can be used in other projects.
What I was hoping was that there was a way that I could make the dateSelected event available to the parent form; I have done this successfully with click events etc for other controls I just can't seem to make this work for the monthCalendar as DateSelected uses DateRangeEventHandler rather than the standard EventHandler.
I hope this is clear as i have been around the block with this one so i'm not sure what makes any sense any more :) Any help or advice in how I could go about coding this would be very much appreciated.
Same way you would with the Button events.
In your UserControl, it would look something like this:
public event DateRangeEventHandler DateChanged {
add { monthCalendar1.DateChanged += value; }
remove { monthCalendar1.DateChanged -= value; }
}
Then in your form, like all controls with events:
userControl11.DateChanged += userControl11_DateChanged;
void userControl11_DateChanged(object sender, DateRangeEventArgs e) {
// do something...
}
You can make MonthCalendar public in your UserControl and then in your Form just subscribe to the event using:
this.userControl.Monthcalender.DateSelected += new DateRangeEventHandler(Monthcalender_DateSelected)
or you can create a new event in your UserControl which will be raised on MonthCalendar.DataSelected. and in your Form subscribe to that event, something like:
UserControl:
public UserControl1()
{
InitializeComponent();
this.monthCalendar1.DateSelected += new DateRangeEventHandler(monthCalendar1_DateSelected);
}
public void monthCalendar1_DateSelected(object sender, DateRangeEventArgs e)
{
OnSeChanged(e);
}
public event DateRangeEventHandler SeChanged;
protected virtual void OnSeChanged(DateRangeEventArgs e)
{
if (SeChanged != null)
{
SeChanged(this, e);
}
}
Form:
userControl11.SeChanged += new DateRangeEventHandler(userControl11_SeChanged);
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.