Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
129 views
in Technique[技术] by (71.8m points)

c# - How do I change the name of an existing event handler?

In Windows Forms, 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 a console application, as far as I remember, you could name the methods as you wished. I'm trying to understanding what's happening.

How can I change the name of these methods (such as button1_Click)?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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):

Open the events tab of the properties 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:

Select of create a new method to bind


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:

Find the code file generated by the designer

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:

this.button1.Click += new System.EventHandler(this.button1_Click);

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:

  • From the Menus: Edit -> Refactor -> Rename
  • By contextual Menu on the name of the Method: Refactor -> Rename
  • Put the cursor on the Method name and type Alt + Shift + F10 and then select Rename
  • Put the cursor on the Method name and press F2

Note: You can customise your Visual Studio, the above menus and keyboard shortcuts may be changed.

The Rename dialog looks like this:

Use the Rename dialog

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:

using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    }
}

Notice it says partial, that means that there could be more code for this class in another file, in fact that is the file Form1.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:

using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private Button myButton;

        public Form1()
        {
            InitializeComponent();

            // Create a new Button
            myButton = new Button();

            // Set the properties of the Button
            myButton.Location = new System.Drawing.Point(50, 12);
            myButton.Size = new System.Drawing.Size(100, 23);
            myButton.Text = "My Button";

            // Add the Button to the form
            this.Controls.Add(myButton);
        }
    }
}

We have created a private field named myButton of type Button that will hold the new button. Then inside the constructor we add some new lines to create a new Button and assign it to myButton and give it position (Location), Size and Text. And finally we have added the newly created button to the Controls 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):

        private void WhenClick(object sender, System.EventArgs e)
        {
            /* Code */
        }

And then add it as an event handler for the Click event of the button (inside the constructor):

            // Add an event handler
            myButton.Click += new System.EventHandler(WhenClick);

Notice we are not calling WhenClick. instead we are making a reference to it.

Then we are creating a new Delegate of type System.EventHandler that will wrap the reference to the method WhenClick. I suggest to learn about Using Delegates.

I repeat: we are not calling WhenClick. If we were to call WhenClick 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:

        public Form1()
        {
            InitializeComponent();

            // Create a new Button and set its properties
            myButton = new Button()
            {
                Location = new System.Drawing.Point(50, 12),
                Size = new System.Drawing.Size(100, 23),
                Text = "My Button"
            };


            // Add the Button to the form
            this.Controls.Add(myButton);

            // Add an event handler (the compiler infers the delegate type)
            myButton.Click += WhenClick;
        }

You can even make the event handler an anonymous method:

            // Add an event handler (the compiler infers the delegate type)
            myButton.Click += (sender, e) =>
            {
                /* code */
            };

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:

button1.Click += button1_Click;

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 to Click.

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:

// Console Application Example #1 ;)
static void Main()
{
    Func<int, int> myfunc = null;

    myfunc = Add2;
    Console.WriteLine(myfunc(7)); // This prints 9
    myfunc = MultBy2;
    Console.WriteLine(myfunc(7)); // This prints 14
}

static int Add2(int x)
{
    // This method adds 2 to its input
    return x + 2;
}

static int MultBy2(int x)
{
    // This method  multiplies its input by 2
    return x * 2;
}

Notice myfunc is typed as Func<int, int> this is a generic delegate type that takes an int and returns an int.

Also notice that when I say myfunc = Add2;, it is not calling Add2 (there are no parenthesis there), it is passing a reference of the method itself. the same is true for myfunc = MultBy2;: it is not calling MultBy2, we are passing it.

Using anonymous methods via lambda expressions you can write equivalent code is less lines:

// Console Application Example #1 ;)
static void Main()
{
    Func<int, int> myfunc = null;

    // This anonymous method adds 2 to its input
    myfunc = x => x + 2;
    Console.WriteLine(myfunc(7)); // This prints 9

    // This anonymous method  multiplies its input by 2
    myfunc = x => x * 2;
    Console.WriteLine(myfunc(7)); // This prints 14
}

Notice that we have two anonymous methods here: x => x + 2 and x => x * 2. The first one (x => x + 2) is equivalent to the method Add2 we had before, and the second one (x => x * 2) is equivalent to the method MultBy2 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:

// Console Application Example #2 ;)
static void Main()
{
    Func<int, bool> filter = IsPair;
    // An array with numbers
    var array = new int[]{1, 2, 3, 4, 5, 8, 9, 11, 45, 99};
    PrintFiltered(array, filter);
}

static bool IsPair(int x)
{
    // True for pair numbers
    return x % 2 == 0;
}

static void PrintFiltered(int[] array, Func<int, bool> filter)
{
    if (array == null) throw new ArgumentNullException("array");
    if (filter== n

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...