Cés*_*rim 5 c# visual-studio winforms
在WinForms中,如果将方法名称(例如button1_Click
)更改为其他名称,则它不再起作用.
我发现这很奇怪,因为在控制台应用程序中,据我记得你可以根据需要命名方法.我正在努力了解发生了什么.
如何更改这些方法的名称(例如button1_Click
)?
The*_*aot 26
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 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).
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
,您可以使用解决方案资源管理器找到它:
注意:您可能必须展开在表单上创建的子树以显示该文件.
在那里你会发现一条看起来像这样的行:
this.button1.Click += new System.EventHandler(this.button1_Click);
Run Code Online (Sandbox Code Playgroud)
Visual Studio会告诉您button1_Click
未定义的内容.您可以在那里编辑方法的名称到新名称,或删除该行以使设计器再次工作,并绑定一个新方法.
您可以召唤重命名对话框.这可以通过以下几种方式完成:
Edit
-> Refactor
-> Rename
Refactor
-> Rename
Alt + Shift + F10
and then select Rename
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.
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.
实际上,您可以创建自己的方法,甚至将它们绑定到表单的事件.我一直在说"绑定"因为在这个级别上更容易理解,尽管接受的术语是订阅的.所以我们要做的是创建一个按钮并订阅它的Click
事件.
首先让我们来看一下表单的类,它看起来像这样:
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意partial
,这意味着在另一个文件中可能有更多代码用于此类,实际上这是Form1.Designer.cs
表单设计者添加代码的文件.
第二个通知它调用InitializeComponent
表单构造函数中的方法.此方法由表单设计器创建,它负责初始化您使用表单设计器添加的所有控件和组件(因此方法的名称).
现在,假设我们想要添加一个没有表单设计器的按钮,我们可以这样做:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我们创建了一个名为myButton
type 的私有字段Button
,它将保存新按钮.然后在构造函数中添加一些新行来创建一个新行Button
并将其赋值给它myButton
并给它position(Location
)Size
和Text
.最后,我们将新创建的按钮添加到Controls
表单中.
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 */
}
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
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 */
};
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
Outputs:
2
4
8
Run Code Online (Sandbox Code Playgroud)
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.
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
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.
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
Outputs:
2
4
8
Run Code Online (Sandbox Code Playgroud)
In this example x => x % 2 == 0
is an anonymous method that is equivalent to the method IsPair
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:
PrintFiltered(array, x => x < 10);
Run Code Online (Sandbox Code Playgroud)
Outputs:
1
2
3
4
7
8
9
Run Code Online (Sandbox Code Playgroud)
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:
// 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);
}
}
Run Code Online (Sandbox Code Playgroud)
Outputs:
74
79
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
Outputs:
74
79
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
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:
Action<int> myDelegates = null;
// Create the delegate
var myDelegate = x => Console.WriteLine(x);
// Add the delegate
myDelegates += myDelegate;
// ...
// Remove the delegate
myDelegates -= myDelegate;
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
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:
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.
如果你想一般玩Lambda Expression和C#,我建议你得到一份LINQPad.这样可以减少创建测试项目的新项目的麻烦.
归档时间: |
|
查看次数: |
5900 次 |
最近记录: |