In WinForms, if you change the method name (for example button1_Click
) to something else, it doesn't work any more.
I find this strange because in Console Application as far as I remember you could name the methods as you wish. I'm trying to understanding what's happening.
How can I change the name of these methods (such as button1_Click
)?
This questions is about the case where renaming a method causes the forms designer to stop working. Although I have covered (all that I can think of) how events works in general.
What happened?
What you experience is an artifact of the forms designer.
Sure, you can have any method name you want, the matter is the forms designer is binding those methods to events behind the scenes, if you change the method name after the forms designer has linked it to the event it will no longer work (it can't find the method).
Giving proper names to your event handlers
In Visual Studio, look at the properties of the object you want to bind an event to, and then select events (on the top of the panel):
There you will see a list the available events and you will be able to bind an existing method or type the name for a new one:
If I have already screwed, How do I fix it?
If your designer is not appearing because of this you will have to edit the code file that is generated by the designer. The file generated by the designer has the name of the form followed by
.Designer.cs
(for example:Form1.Designer.cs
), you can find it with your solution explorer:Note: You may have to expand the sub-tree created on your form to reveal the file.
There you will find a line that looks something like this:
And Visual Studio will tell you that
button1_Click
is not defined. You can edit there the name of the method to the new name, or remove the line to have the designer work again, and bind a new method.Renaming an existing method without the hassle
You can summon the Rename dialog. This can be done by several ways:
Edit
->Refactor
->Rename
Refactor
->Rename
Alt + Shift + F10
and then selectRename
F2
Note: You can customise your Visual Studio, the above menus and keyboard shortcuts may be changed.
The Rename dialog looks like this:
There you can type a new name for the Method, and by doing so any reference or call to that method withing the loaded projects will be changed too. That includes the code generated by the Forms Designer.
Binding an event handler by hand
All that the forms designer does is present a UI that facilitates editing the form and write code on your behalf. Don't let it fool you into thinking that you can't write code yourself.
In fact, you can create your own methods and even bind them to the events of your Form. I have been saying "bind" because it is easier to understand at this level, although the accepted lingo is subscribe. So what we are going to do, is create a button and subscribe to its
Click
event.First let's take a look at the class of your form, it looks something like this:
Notice it says
partial
, that means that there could be more code for this class in another file, in fact that is the fileForm1.Designer.cs
where the forms designer has been adding code.Second notice it calls a method
InitializeComponent
inside the constructor of the form. This method has been created by the forms designer and it takes responsibility of initializing all the controls and components you have added using the forms designer (hence the name of the method).Now, let's say we want to add a button without the forms designer we can do it like this:
We have created a private field named
myButton
of typeButton
that will hold the new button. Then inside the constructor we add some new lines to create a newButton
and assign it tomyButton
and give it position (Location
),Size
andText
. And finally we have added the newly created button to theControls
of the form.Now we want to add an event handler for the Click event on this new button. Remember that this button is not in the forms designer, we we are going to have to do it "by hand".
In order to do so, add the new method (you can named whatever you want):
And then add it as an event handler for the Click event of the button (inside the constructor):
Notice we are not calling
WhenClick
. instead we are making a reference to it.Then we are creating a new
Delegate
of typeSystem.EventHandler
that will wrap the reference to the methodWhenClick
. I suggest to learn about Using Delegates.I repeat: we are not calling
WhenClick
. If we were to callWhenClick
we would do something like this:WhenClick(param1, param2)
. Please note that this is not what we are doing here, we haven't added parenthesis(/*...*/)
after the method name, so we are not doing a method call.You can also use some syntactic sugar to make all this easier:
You can even make the event handler an anonymous method:
What you see here is a C# Lambda expression (more info at MSDN Lambda expressions). Get used to these syntax, because you will see them more and more often.
Understanding events
You have already seen code like this:
As I told you we are passing a delegate object that has a reference to
button1_Click
. But that's not all that happens here... we are also giving it toClick
.Let's recapitulate and see how delegates behave. You can think about a delegate like an object that holds a method, or a pointer to a function if you prefer.
To understand this, I'll present some Examples you can run as Console Applications. The first one shows that you can change the method that a delegate points to during runtime:
Notice
myfunc
is typed asFunc<int, int>
this is a Generic Delegate type that takes anint
and returns anint
.Also notice that when I say
myfunc = Add2;
It is not callingAdd2
(there are no parenthesis there), it is passing a reference of the method itself. the same is true formyfunc = MultBy2;
: it is not callingMultBy2
, we are passing it.Using anonymous methods via lambda expressions you can write equivalent code is less lines:
Notice that we have two anonymous methods here:
x => x + 2
andx => x * 2
. The first one (x => x + 2
) is equivalent to the methodAdd2
we had before, and the second one (x => x * 2
) is equivalent to the methodMultBy2
we had before.In this example I want you to see that the same delegate can point to different methods along the time. This is accomplished by having a variable that points to the methods!
For the second example, I'll present the "callback" pattern. That is a common pattern in which you pass a delegate as a "callback", that is: something that will be called "back to you" from the code you are calling:
Outputs:
In this code we are having a variable
filter
that points to the methodIsPair
. I'll repeat this again and and again: in the lineFunc<int, bool> filter = IsPair;
we are not calling the methodIsPair
, instead we are taking a reference to it.Of course, it is possible to do the same without declaring the
filter
variable, you can pass the method reference directly:I cannot stress this hard enough: When I say
PrintFiltered(array, IsPair);
it is not callingIsPair
, it is passing it as a parameter toPrintFiltered
. Here effectively you have a Method (PrintFiltered
) that can take a reference to another method (IsPair
) as a reference.Of course you can write the same code using an anonymous method that replaces
IsPair
:Outputs:
In this example
x => x % 2 == 0
is an anonymous method that is equivalent to the methodIsPair
we had before.We have successfully filtered the array to only show the numbers that are pair. You can easily reuse the same code for a different filter. For example, the following line can be used to output only the items in the array that are less than 10:
Outputs:
With this example I want to show you that you can take advantage of the delegates to improve the reusability of your code, by having parts that change depending on the delegate you pass.
Now that - hopefully - we understand this, it is not hard to think that you could have a list of Delegate objects, and call them in succession:
Outputs:
You can see both anonymous methods (
x => Console.WriteLine(x)
andConsole.WriteLine(x + 5)
) has been called, one after the other... this happens inside theforeach
loop.Now, we can accomplish similar results with a multicast delegate:
Outputs:
Again, both anonymous methods has been called. And this is exactly how Events work. The default implementation of an Event uses a multicast delegated wrapped inside. Custom implementation of an event may use a list or similar structure to hold the delegates.
Now, if the event is just a list of delegates... that means the event is keeping a reference to all the methods it wants to call. And it also means that you could remove delegates from the list (or add more than one).
If you want to unsubscribe or unbind from an event, you can do so like this:
For a delegate object to an anoymous method it is a little bit more complicated, because you will need to keep the delegate in a variable to able to pass it back for removal:
Did you say Custom Implementation of an Event?
Does that means you can create your own events? Yes, and yes. If you want to publish an event in one of your classes you can declare it just like any other member.
This is an example that uses a multicast delegate:
For a custom implementation take this example:
Hopefully this not hard to read, now that you know how events work. The only details should be
lock
, it is there becauseList
is not thread-safe. You could also use a thread-safe data structure or a different locking mechanism, I kept this one for simplicity.Even if you don't write your own events, there are a few things to learn from here:
Notes:
While looking for example I found the series of articles "Delegates in C# - Attempt to Look Inside" by Ed Guzman (Part 1, Part 2, Part 3 and Part 4) very easy to read - although a bit outdated - you should check them out. You may want to read the Evolution of Anonymous Functions in C# for a grasp of what's missing.
Some commonly used Delegate Types buildin in .NET include:
Action<*>
(that is:Action<T>
,Action<T1, T2>
...)Func<*, TResult>
(that is:Func<T, TResult>
,Func<T1, T2, TResult>
...)EventHandler<TEventArgs>
Comparison<T>
Converter<TInput, TOutput>
Action
Predicate
EventHandler
ThreadStart
ParametrizedThreadStart
You may be interested in LINQ.
Threading and thread-safety it a even broader topic than delegates and events, don't rush to understand it all.
If you want play along with Lambda Expression and C# in general, I suggest to get a copy of LINQPad. This make reduce the hassle of creating new project to test things.
You can of course change the name of your event. it just sounds like you dont know about the designer file :) the designer file is just the result of your UI design, and if you dont update the name of your event in the UI it does not compile :)