I've kind of backed myself into a corner here.
I have a series of UserControls that inherit from a parent, which contains a couple of methods and events to simplify things so I don't have to write lines and lines of near-identical code. As you do. The parent contains no other controls.
What I want to do is just have one event handler, in the parent UserControl, which goes and does stuff that only the parent control can do (that is, conditionally calling an event, as the event's defined in the parent). I'd then hook up this event handler to all my input boxes in my child controls, and the child controls would sort out the task of parsing the input and telling the parent control whether to throw that event. Nice and clean, no repetitive, copy-paste code (which for me always results in a bug).
Here's my question. Visual Studio thinks I'm being too clever by half, and warns me that "the method 'CheckReadiness' [the event handler in the parent] cannot be the method for an event because a class this class derives from already defines the method." Yes, Visual Studio, that's the point. I want to have an event handler that only handles events thrown by child classes, and its only job is to enable me to hook up the children without having to write a single line of code. I don't need those extra handlers - all the functionality I need is naturally called as the children process the user input.
I'm not sure why Visual Studio has started complaining about this now (as it let me do it before), and I'm not sure how to make it go away. Preferably, I'd like to do it without having to define a method that just calls CheckReadiness. What's causing this warning, what's causing it to come up now when it didn't an hour ago, and how can I make it go away without resorting to making little handlers in all the child classes?
Declare the parent method virtual, override it in the child classes and call
base.checkReadyness(sender, e);
(or derevation thereof) from within the child class. This allows for future design evolution say if you want to do some specific error checking code before calling the parent event handler. You might not need to write millions of event handlers like this for each control, you could just write one, hook all the controls to this event handler which in turn calls the parent's event handler.
One thing that I have noted is that if all this code is being placed within a dll, then you might experience a performance hit trying to call an event handler from within a dll.
I've just come across this one as well, I agree that it feels like you're doing everything correctly. Declaring the method virtual is a work-around at best, not a solution.
What is being done is valid - a control which only exists in the derived class, and the derived class is attaching an event handler to one of that control's events. The fact that the method which is handling the event is defined in the base class is neither here nor there, it is available at the point of binding to the event. The event isn't being attached to twice or anything silly like that, it's simply a matter of where the method which handles the event is defined.
Most definitely it is not a virtual method - I don't want the method to be overridable by a derived class. Very frustrating, and in my opinion, a bug in dev-studio.
I too have experienced this issue because in earlier versions of VS, you could "inherit" the event handlers. So the solution I found without having to override methods is simply to assign the event handler somewhere in the initialization phase of the form. In my case, done in the constructor (I'm sure OnLoad() would work as well):
public MyForm()
{
InitializeComponent();
btnOK.Click += Ok_Click;
}
...where the Ok_Click handler resides in the base form. Food for thought.
I've just run into the exact problem Merus first raised and, like others who posted responses, I'm not at all clear why VS (I'm now using Visual C# 2010 Express) objects to having the event handler defined in the base class. The reason I'm posting a response is that in the process of getting around the problem by making the base class code a protected method that the derived classes simply invoke in their (essentially empty) event handlers, I did a refactor rename of the base class method and noticed that the VS designer stopped complaining. That is, it renamed the event handler registration (so it no longer followed the VS designer's convention of naming event handlers with ControlName_EventName), and that seemed to satisfy it. When I then tried to register the (now renamed) base event handler against derived class controls by entering the name in the appropriate VS event, the designer created a new event handler in the derived class which I then deleted, leaving the derived class control registered to the base class (event handler) method. Net, as you would expect, C# finds what we want to do legit. It's only the VS designer that doesn't like it when you following the designer's event handler naming convention. I don't see the need for the designer to work that way. Anywho, time to carry on.
If your event is already defined in your parent class, you do not need to rewire it again in your child class. That will cause the event to fire twice.
Do verify if this is what is happening. HTH :)
This article on MSDN should be a good starting points: Overriding Event Handlers with Visual Basic .NET. Take a look at the How the Handles Clause Can Cause Problems in the Derived Class section.
Why not declare the method as virtual in the parent class and then you can override it in the derived classes to add extra functionality?
Forget that it's an event handler and just do proper regular method override in child class.
Here's what I did to get base methods called in several similar looking forms, each one of them having a few extra features to the common ones:
protected override void OnLoad(EventArgs e)
{
try
{
this.SuspendLayout();
base.OnLoad(e);
foreach (Control ctrl in Controls)
{
Button btn = ctrl as Button;
if (btn == null) continue;
if (string.Equals(btn.Name, "btnAdd", StringComparison.Ordinal))
btn.Click += new EventHandler(btnAdd_Click);
else if (string.Equals(btn.Name, "btnEdit", StringComparison.Ordinal))
btn.Click += new EventHandler(btnEdit_Click);
else if (string.Equals(btn.Name, "btnDelete", StringComparison.Ordinal))
btn.Click += new EventHandler(btnDelete_Click);
else if (string.Equals(btn.Name, "btnPrint", StringComparison.Ordinal))
btn.Click += new EventHandler(btnPrint_Click);
else if (string.Equals(btn.Name, "btnExport", StringComparison.Ordinal))
btn.Click += new EventHandler(btnExport_Click);
}
The chance of an omission of using the right fixed button name looks the same to me as the chance of not wiring the inherited handler manually.
Note that you may need to test for this.DesignMode so that you skip the code in VS Designer at all, but it works fine for me even without the check.
Related
I'm using a class derived from UIElement class when writing a UWP program using C#, where I want to include handling input controls such as mouse and keyboard actions. Now I see there are already virtual methods that says OnSomeEvent() and clearly I can override these method to fit my handling process, or I can create new method handling the public events defined in the base class, and subscribe them to these input events in the constructor. I assume these two methods both work but I hope to know which is more professional or more advisable way of doing this, and why. Also it would help to explain why MS offers these two ways at the same time.
Here's the events and methods of UIElement class
https://msdn.microsoft.com/en-us/library/system.windows.uielement(v=vs.110).aspx#Examples
and a paragraph of quoting
UIElement provides a starting point for element layout
characteristics, and also exposes virtual methods that derived classes
can override, which can influence the layout rendering behavior of the
element and its child elements. Much of the input and focusing
behavior for elements in general is also defined in the UIElement
class. This includes the events for keyboard, mouse and stylus input,
and related status properties. Many of these events are routed events,
and many of the input-related events have both a bubbling routing
version as well as a tunneling version of the event. These paired
events are typically the events of greatest interest to control
authors.
In a derived class, I usually override the existing method.
Why? Event handler are less reliable than the override method. For example, external classes can clear event handlers, but they can't change the code in the override. You have to seal your own class though, or your method may be overridden.
Another point to consider is this: do I want to change the way the control works? Do I have to have control over the exact execution moment of the code (let's say before the code of the base class, after, or instead of)? If so, you have to use override.
I have a static event in a DLL that I use frequently - Toolkit.Dialogs.ExitConfirm
The only way I can use this event is by modifying the line that adds the event in form.Designer.cs. Example:
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
becomes
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(Toolkit.Dialogs.ExitConfirm);
If I try to add it via the Properties->Events page, it gives me this error: 'Toolkit.Dialogs.ExitConfirm' is not a valid identifier.
Is there a way to allow Form Designer to reference events from other classes/namespaces?
Edit: some people work better with visual cues, so here's some images to define the issue: http://imgur.com/a/RaLMg
The first image shows how I have to make it work in Visual Studio - an event that calls an event.
The second image shows what I'm actually trying to do.
The third image is the error that occurs when I key in the method name by hand.
I have a static event in a DLL
You don't, you just have a method. FormClosing is the event, your method can be the event handler method if it has the proper signature. The designer simply doesn't support what you try to do, you'll have to stop trying. There are two sane solutions, both involve writing code in the form class. First you can do it in the constructor:
public Form1() {
InitializeComponent();
this.FormClosing += Toolkit.Dialogs.ExitConfirm;
}
Or the sane one since it doesn't make sense for a class to listen to its own events:
protected override void OnFormClosing(FormClosingEventArgs e) {
Toolkit.Dialogs.ExitConfirm(this, e);
if (!e.Cancel) base.OnFormClosing(e);
}
Which has the great advantage of working properly when you derive another form from this one. Which is also a strong hint to what you are probably really should do. It looks like you are trying to write common code for dialogs. The "Toolkit" namespace suggests as much. Make it work well by having this toolkit implement a base form class instead. Now you can design your form class without any code or event handlers:
public partial class Form1 : Toolkit.Dialogs.BaseDialog {
// etc
}
With the assumption that Toolkit.Dialogs.BaseDialog is a class derived from Form that overrides OnFormClosing(). Maybe it should also have a public property named "ConfirmOnClose" of type bool. which enables the "ExitConfirm" logic. You can set that property in the designer without trouble.
The WinForms designer isn't designed to do that. I'm a little surprised it doesn't lose your event the next time you make a change in the designer.
A few ideas on other ways you could make this work:
You could make a Form class that hooks the event for you, and descend all your other forms from that base class. Then you'd get the behavior everywhere.
You could make a utility method that hooks the event for you, and call it from each form's constructor.
You could make an extension method that hooks the event and then shows the form, and call your extension method everywhere you show your forms (instead of calling Show).
The base class is probably the simplest solution, as long as you aren't already using form inheritance for some other purpose.
You can call this Method 'Toolkit.Dialogs.ExitConfirm' On form closing event of your application form and pass required param to Toolkit.Dialogs.ExitConfirm
I have a User Control that contains a list of items and I raise an event when the currentIndex changes, also, when it changes, I must call two other methods two verify and change the appearance of the Control (change an Image and block/unblock some buttons).
What I want to know, mostly out of curiosity because it is already working, is when is it more appropriate to call these two methods?
Should I call them within the CurrentIndex property per se? Should I call them within the OnCurrentIndexChanged(...)? Should I handle the event within the class and do it there?
I'll assume you've implemented the standard event generating pattern and made OnCurrentIndexChanged protected virtual so that a derived class can override the method and alter the event generation and/or handling.
Unfortunately that requires reading tea leaves, why would anybody want to override the method? And more seriously, how could overriding the method break your control when they do? That's awfully hard to guess at for anybody that doesn't know the code well, not exactly easy for you either. The principle to apply here, used in the .NET framework code as well, is to do as little as possible. Just raise the event, nothing else. Which minimizes the odds of breakage when the derived class does something silly, but entirely common, like not calling base.OnCurrentIndexChanged.
The behavior of your controls is an implementation detail of your UserControl. So change their properties in your CurrentIndex property setter, then call OnCurrentIndexChanged(). Anybody that derives from your class can override that behavior, if necessary. And nothing goes wrong when they forget to call your OnCurrentIndexChanged() method. But do note that you need to make the control variables protected instead of private. So they can override the behavior, if they need to.
And don't hesitate to just not use a virtual method at all if this is too spooky for you. It's not common to have to accommodate hundreds of thousands of programmers with your controls :)
In the user control, I would have a property that represents the selected item. Then, during the setter of the object, raise the event method to change your user control. That way, in the future, if you need to add more listeners, you just need to add another handler in the setter method. This is pretty common in MVVM applications and is pretty maintainable.
Because your UserControl acts as a ListControl, you need to implement two events and two properties.
public event System.EventHandler SelectedIndexChanged;
public event System.EventHandler SelectionChangeCommitted;
public int SelectedIndex {
get;
set;
}
public T SelectedItem { // Where T is whatever your type is
get;
set;
}
SelectedIndexChanged should always be used for actions that always need to be triggered when your selected index is changed. SelectionChangeCommitted should only be triggered when the user physically changes the selection. The separation between the two is an important distinction, and most controls in .NET follow this pattern (eg. ComboBox), but may not use the same name for the events.
Now, with that said, if the controls you need to change properties for are also within the same user control, then you should of course handle that within the user control code in the appropriate event. Otherwise, the code should be orphaned to whoever implements the user control (eg. a form or another user control) by subscribing to the event and doing the work there.
The order really depends on your requirements, but SelectedIndexChanged should always be raised (but not more than once per change as that would introduce strange behavior), and again SelectionChangeCommitted should only be raised by the user (eg. setting SelectedIndex or SelectedItem).
A good rule of thumb is if your internal stuff MUST happen before the user knows about it, call SelectedIndexChanged first, then SelectionChangeCommitted. If it doesn't matter, either or. Changing the order later on could result in breaking changes in whoever implements the control, so make sure your decision is solid.
The difference between the two is SelectedIndex and SelectedItem would be updated by things like clearing your list internally, adding new items, et cetera, but does not necessarily mean it was a physical user action that should result in both your events firing.
Hope this helps.
Question about the WinForms designer and how to customize behavior. What I've seen multiple times is that when you select a different event handler for a button it will remove the old one (as in ,the code) when it becomes unused.
I want to avoid this behavior but can't find configuration for this. Anyone a hint? Thanks!
Update
Since multiple comments question the actions that trigger this in the first place, I'd like to point out that it has mostly hit me during refactoring of an existing code base.
There is no configuration for this. The designer does the Right Thing, it only removes event handlers that have no code. As soon as you put something in the method body then it preserves what you've written and generates a new method. This ensures that you don't lose code and ensures that you don't have dead methods littering your code.
Beware that adding more than one event handler for a control's event in the same class (form) makes very little sense. You should just merge the code of the handlers. This also ensures that you won't have any surprises, the order in which multiple subscribers for the same event runs is fairly unpredictable. The designer only supports a single event handler, simply because it doesn't have any way to track more than one.
This is just the way the Designer works - you can't change it.
What you can do to work around your problem is to add your event handlers in code, rather than in the designer:
public Form1()
{
InitializeComponent();
this.button1.Click += new EventHandler(button1_Click);
this.button1.Click +=new EventHandler(button1_Click2);
}
I must point out that I question the need for two separate event handlers.
I am currently transitioning from VB to C# and am having some issues with regards to registering my interest in an event.
When using VB it was simply a case of specifying that a method Handles and event, often this was generated by using the object events list. While I can easily use the Class.event += delegate in C# I am unsure where the best place is to place the code to do this.
Am I best placing it inside of the InitializeComponent() as per the generated code (say if you select the event in the from designer) or should I place it inside the constructor for better readability/maintenance. If inside the constructor, should it be before or after the call to InitializeComponent()?
When you are doing WinForm development (judging from InitializeComponent() function mentioned), usually you assign the handler using Visual Studio. You look up the properties of your control, click on the lightning icon to get the list of all events, find your event, and either double click on it (to create a new handler), or select existing handler from the list. Visual Studio will add the wiring of this in the generated code, so you don't have to worry about it.
I always create a private method called Init() and place it there, and then call that method from the constructor or the Form_Load event handler. It's semantically better, IMO, than doing it within the constructor proper. And you don't want to place it within InitializeComponent(), because next time you change something in your designer it's likely to delete any manually-added code there.
Sometimes Visual Studio's designer can mess up the code, so adding the event handlers within InitializeComponent can create a headache, it would be better to do it something like this
public Form1(){
InitializeComponent();
WireUpEvents();
}
public void WireUpEvents(){
this.fooEvent += new EventHandler(foo_handler);
.... etc ....
}
And make sure that you remove the event handlers in the Form's Dispose function also...
public void UnWireEvents(){
this.fooEvent -= new EventHandler(foo_handler);
.... etc ....
}
As you design the form, Visual Studio will change the code within the InitializeComponent() method located in form.design.cs, so it is imperative that you do not manually edit this code..
It depends, but most of the time, yes.
Use InitializeComponent when you want the event to be hooked for the entire duration of the Form (I'm assuming you're talking about Forms/UserControls/etc.). In other cases, you'll want finer grained control of when the Event is handled.
Keep in mind that you'll want to unhook all of these events (using the -= syntax) when you're Disposing the Form, or no longer want to handle the event. Keeping the event handler delegates attached is one of the most common managed memory leaks around.
Do not manually add code to the InitializeComponent() method. This method is code generated, so as soon as you change your form, any logic that you've added manually to this method will be wiped out.
I usually add a method to handle the Form's Load event and put my event registrations there.
If you have the InitializeComponent() method you're using the designer so you can bind events directly in the designer if you like. To do this, click the lightning bolt icon in the properties window and you'll see a list of all the events for the selected object. You can just type the name of the event in there and it'll create the code for you.
If you're not a fan of the designer, bind them after your InitializeComponent call and make sure you detach them when you're done (in Dispose()).
2 ways of doing this. You can either create you own method which you call in your Constructor which in turn creates the Event Handler, or you can just place them in your Constructor. Probably a good idea to remove the Event Handlers in your Finalizer/Destructor code.
I would place it after InitializeComponent, since you might be registering events against a child control/object, like a button, and you will want to be sure the object has been created already.
There will be cases where you wire up to events dynamically/conditionally in other places, such as in response to some other event.