如何更改现有事件处理程序的名称? [英] How do I change the name of an existing event handler?

查看:74
本文介绍了如何更改现有事件处理程序的名称?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Windows窗体中,如果将方法名称(例如,button1_Click)更改为其他名称,它将不再起作用.

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.

如何更改这些方法的名称(例如button1_Click)?

推荐答案

此问题与重命名方法导致表单设计器停止工作的情况有关.尽管我已经介绍了(我能想到的)事件在一般情况下的工作原理.

您所体验到的是表单设计器的人工制品.

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

在Visual Studio中,查看要将事件绑定到的对象的属性,然后选择事件(在面板顶部):

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:

如果由于这个原因没有出现您的设计器,则必须编辑设计器生成的代码文件.设计器生成的文件的名称为表单的名称,后跟.Designer.cs(例如:Form1.Designer.cs),您可以使用解决方案资源管理器找到它:

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:

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

Visual Studio会告诉您未定义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.

您可以召唤重命名"对话框.这可以通过以下几种方法完成:

You can summon the Rename dialog. This can be done by several ways:

  • 从菜单中:Edit-> Refactor-> Rename
  • 通过上下文菜单上的方法名称:Refactor-> Rename
  • 将光标放在方法名称"上并键入Alt + Shift + F10,然后选择Rename
  • 将光标放在方法"名称上,然后按F2
  • 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

注意:您可以自定义Visual Studio,上述菜单和键盘快捷键可能会更改.

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

重命名"对话框如下:

您可以在其中键入该方法的新名称,这样一来,随装入的项目一起对该方法的任何引用或调用也将发生更改.其中包括表单设计器生成的代码.

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.

表单设计者所做的全部工作就是提供一个UI,该UI有助于编辑表单并代表您编写代码.不要让它欺骗您以为您自己不能编写代码.

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.

实际上,您可以创建自己的方法,甚至将它们绑定到Form的事件.我之所以说绑定",是因为虽然可以接受 subscribe 的行话,但在此级别上它更容易理解.因此,我们要做的是创建一个按钮,并 subscribe 为其Click事件.

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();
        }
    }
}

注意它说partial,这意味着在另一个文件中可以存在该类的更多代码,实际上是表单设计者一直在添加代码的文件Form1.Designer.cs.

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.

第二个注意,它在窗体的构造函数中调用方法InitializeComponent.此方法是由表单设计器创建的,它负责初始化使用表单设计器添加的所有控件和组件(因此该方法的名称).

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);
        }
    }
}

我们创建了一个名为myButton的私有字段,其类型为Button,将保留新按钮.然后在构造函数中,我们添加一些新行以创建新的Button并将其分配给myButton并为其指定位置(Location),SizeText.最后,我们在表单的Controls中添加了新创建的按钮.

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.

现在,我们要为此新按钮上的Click事件添加事件处理程序.请记住,此按钮不在表单设计器中,我们将不得不手动"完成该操作.

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 */
        }

然后将其添加为按钮的Click事件的事件处理程序(在构造函数内部):

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

请注意,我们不拨打WhenClick.相反,我们正在引用它.

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

然后,我们创建一个类型为System.EventHandler的新Delegate,它将包装对方法WhenClick的引用.我建议学习使用代理人

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.

我再说一遍:我们不打WhenClick.如果要调用WhenClick,我们将执行以下操作:WhenClick(param1, param2).请注意,这不是我们在这里所做的,我们没有在方法名称后面添加括号(/*...*/),因此我们没有在进行方法调用.

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 */
            };

您在这里看到的是 C#Lambda表达式(有关MSDN的更多信息, a href ="http://msdn.microsoft.com/en-us/library/bb397687.aspx" rel ="nofollow noreferrer"> Lambda表达式).习惯这些语法,因为您会越来越频繁地看到它们.

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.

您已经看过这样的代码:

You have already seen code like this:

button1.Click += button1_Click;

正如我告诉您的那样,我们正在传递一个对button1_Click的引用的委托对象.但这不是这里发生的全部...我们也将其提供给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;
}

通知myfunc键入为Func<int, int>,这是通用委托类型,它采用int并返回int.

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

还要注意,当我说myfunc = Add2;时,它没有调用Add2(那里没有括号),它正在传递方法本身的引用. myfunc = MultBy2;也是这样:它没有调用MultBy2,我们正在传递它.

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.

通过lambda表达式使用匿名方法,您可以编写等效代码的代码更少:

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
}

请注意,这里有两个匿名方法:x => x + 2x => x * 2.第一个(x => x + 2)等同于我们之前使用的方法Add2,第二个(x => x * 2)等同于我们之前使用的方法MultBy2.

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== null) throw new ArgumentNullException("filter");
    foreach (var item in array)
    {
        if (filter(item))
        {
            Console.WriteLine(item);
        }
    }
}

输出:

2
4
8

在此代码中,我们有一个变量 filter指向方法IsPair.我会一遍又一遍地重复:在Func<int, bool> filter = IsPair;行中,我们没有调用方法IsPair,而是在引用它.

In this code we are having a variable filter that points to the method IsPair. I'll repeat this again and and again: in the line Func<int, bool> filter = IsPair; we are not calling the method IsPair, instead we are taking a reference to it.

当然,可以在不声明filter 变量的情况下执行相同的操作,您可以直接传递方法引用:

Of course, it is possible to do the same without declaring the filter variable, you can pass the method reference directly:

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

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== null) throw new ArgumentNullException("filter");
    foreach (var item in array)
    {
        if (filter(item))
        {
            Console.WriteLine(item);
        }
    }
}

我无法对此施加足够的压力:当我说PrintFiltered(array, IsPair);时,它没有调用IsPair,而是将其作为参数传递给PrintFiltered.实际上,这里有一个方法(PrintFiltered),该方法可以引用另一个方法(IsPair)作为参考.

I cannot stress this hard enough: When I say PrintFiltered(array, IsPair); it is not calling IsPair, it is passing it as a parameter to PrintFiltered. Here effectively you have a method (PrintFiltered) that can take a reference to another method (IsPair) as a reference.

当然,您可以使用替代IsPair的匿名方法编写相同的代码:

Of course you can write the same code using an anonymous method that replaces IsPair:

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

static void PrintFiltered(int[] array, Func<int, bool> filter)
{
    if (array == null) throw new ArgumentNullException("array");
    if (filter== null) throw new ArgumentNullException("filter");
    foreach (var item in array)
    {
        if (filter(item))
        {
            Console.WriteLine(item);
        }
    }
}

输出:

2
4
8

在此示例中,x => x % 2 == 0是一个匿名方法,等效于我们之前使用的方法IsPair.

In this example x => x % 2 == 0 is an anonymous method that is equivalent to the method IsPair we had before.

我们已经成功过滤了数组以仅显示成对的数字.您可以轻松地将相同的代码重用于不同的过滤器.例如,以下行可以用于仅输出数组中小于10的项目:

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:

    PrintFiltered(array, x => x < 10);

输出:

1
2
3
4
7
8
9

在这个示例中,我想向您展示,您可以利用委托来提高代码的可重用性,方法是根据传递的委托来更改部分内容.

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.

现在-希望-我们理解了这一点,不难想到您可以拥有一个Delegate对象的列表,并依次调用它们:

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:

// Console Application Example #3 ;)
static void Main()
{
    // List<Action<int>> is a List that stores objects of Type Action<int>
    // Action<int> is a Delegate that represents methods that
    //  takes an int but does not return (example: void func(int val){/*code*/})
    var myDelegates = new List<Action<int>>();

    // We add delegates to the list
    myDelegates.Add(x => Console.WriteLine(x));
    myDelegates.Add(x => Console.WriteLine(x + 5));

    // And we call them in succesion
    foreach (var item in myDelegates)
    {
        item(74);
    }
}

输出:

74
79

您可以看到两个匿名方法(x => Console.WriteLine(x)Console.WriteLine(x + 5))都被调用,一个接一个地被调用……这发生在foreach循环内.

You can see both anonymous methods (x => Console.WriteLine(x) and Console.WriteLine(x + 5)) has been called, one after the other... this happens inside the foreach loop.

现在,我们可以使用多播委托来完成类似的结果:

Now, we can accomplish similar results with a multicast delegate:

// Console Application Example #3 ;)
static void Main()
{
    // This is a delegate... we haven't give it a method to point to:
    Action<int> myDelegates = null;

    // We add methods to it
    myDelegates += x => Console.WriteLine(x);
    myDelegates += x => Console.WriteLine(x + 5);

    // And we call them in succession
    if (myDelegates != null) // Will be null if we don't add methods
    {
        myDelegates(74);
    }
}

输出:

74
79

同样,两个匿名方法都已被调用.这正是事件的工作方式.事件的默认实现使用内部封装的多播委托.事件的自定义实现可以使用列表或类似的结构来保存委托.

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:

this.button1.Click -= button1_Click;

对于匿名方法的委托对象,它要复杂一些,因为您需要将委托保留在变量中,以便能够将其传递回以进行删除:

For a delegate object to an anonymous 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:

Action<int> myDelegates = null;

// Create the delegate
var myDelegate = x => Console.WriteLine(x);

// Add the delegate
myDelegates += myDelegate;

// ...

// Remove the delegate
myDelegates -= myDelegate;


您说的是事件的自定义实现吗?

这是否意味着您可以创建自己的事件?是的,是的.如果要在一个类中发布事件,则可以像其他任何成员一样声明它.


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:

// Event declaration:
public event EventHandler MyEvent;

// Method to raise the event (aka event dispatcher):
priavet void Raise_MyEvent()
{
    var myEvent = MyEvent;
    if (myEvent != null)
    {
        var e = new EventArgs();
        myEvent(this, e);
    }
}

对于自定义实现,请看以下示例:

For a custom implementation take this example:

// List to hold the event handlers:
List<EventHandler> myEventHandlers = new List<EventHandler>();

// Event declaration:
event EventHandler MyEvent
{
    add
    {
        lock (myEventHandlers)
        {
            myEventHandlers.Add(value);
        }
    }
    remove
    {
        lock (myEventHandlers)
        {
            myEventHandlers.Remove(value);
        }
    }
}

// Method to raise the event (aka event dispatcher):
private void Raise_MyEvent()
{
    var e = new EventArgs();
    foreach (var item in myEventHandlers)
    {
        item(this, e);
    }
}

希望这不难理解,因为您知道事件的工作原理.唯一的详细信息应该是 lock ,因为它是List不是线程安全的.您还可以使用线程安全的数据结构或其他锁定机制,为简单起见,我保留了这一结构.

Hopefully this not hard to read, now that you know how events work. The only details should be lock, it is there because List 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:

  1. 事件处理程序是连续执行的(默认情况下),因此,最好将事件处理程序快速执行(可能为了艰苦工作而采用异步操作),以防止阻塞"事件分发程序.
  2. 事件分派器通常不处理异常(可以做到),因此最好避免在事件处理程序中抛出异常.
  3. 事件的发布者"保留了事件处理程序列表,当您不再需要事件处理程序时,退订是个好主意.


注释:

在查找示例的同时,我发现了Ed Guzman撰写的系列文章"C#中的委托-试图向内看"(匿名函数的演变掌握丢失的内容.

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.

.NET中内置的一些常用的委托类型包括:

Some commonly used Delegate Types built-in in .NET include:

  • 通用
    • Action<*>(即:Action<T>Action<T1, T2> ...)
    • Func<*, TResult>(即:Func<T, TResult>Func<T1, T2, TResult> ...)
    • EventHandler<TEventArgs>
    • Comparison<T>
    • Converter<TInput, TOutput>
    • Generic
      • 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
      • Action
      • Predicate
      • EventHandler
      • ThreadStart
      • ParametrizedThreadStart

      您可能对 LINQ 感兴趣.

      与委托和事件相比,线程和线程安全是更广泛的主题,不要着急了解所有内容.

      Threading and thread-safety it a even broader topic than delegates and events, don't rush to understand it all.

      如果您想与Lambda Expression和C#一起使用,建议您复制一份 LINQPad LINQPad .这样可以减少创建新项目进行测试的麻烦.

      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.

      这篇关于如何更改现有事件处理程序的名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆