Windows Forms에서 메서드 이름 (예 button1_Click
:)을 다른 이름으로 변경하면 더 이상 작동하지 않습니다.
내가 기억하는 한 콘솔 응용 프로그램에서 원하는대로 메서드의 이름을 지정할 수 있기 때문에 이것이 이상합니다. 나는 무슨 일이 일어나고 있는지 이해하려고 노력하고 있습니다.
이러한 메서드의 이름 (예 :)을 어떻게 변경할 수button1_Click
있습니까?
이 질문은 메서드 이름을 바꾸면 양식 디자이너의 작동이 중지되는 경우에 대한 것입니다. 이벤트가 일반적으로 어떻게 작동하는지 (내가 생각할 수있는 모든 것) 다루었지만.
경험하는 것은 양식 디자이너의 아티팩트입니다.
물론 원하는 메서드 이름을 가질 수 있습니다. 문제는 양식 디자이너가 더 이상 작동하지 않는 이벤트에 메서드 이름을 연결 한 후 메서드 이름을 변경하면 양식 디자이너가 이러한 메서드를 백그라운드 이벤트에 바인딩한다는 것입니다. 방법을 찾을 수 없습니다).
Visual Studio에서 이벤트를 바인딩하려는 개체의 속성을 확인한 다음 패널 상단에서 이벤트를 선택합니다.
사용 가능한 이벤트 목록이 표시되며 기존 메서드를 바인딩하거나 새 메서드의 이름을 입력 할 수 있습니다.
이로 인해 디자이너가 나타나지 않으면 디자이너가 생성 한 코드 파일을 편집해야합니다. 디자이너가 생성 한 파일에는 양식의 이름과 그 뒤에 오는 .Designer.cs
(예 Form1.Designer.cs
:)이 있으며 솔루션 탐색기에서 찾을 수 있습니다.
참고 : 파일을 표시하려면 양식에 생성 된 하위 트리를 확장해야 할 수 있습니다.
여기에 다음과 같은 줄이 있습니다.
this.button1.Click += new System.EventHandler(this.button1_Click);
그리고 Visual Studio는 button1_Click
정의되지 않았 음을 알려줍니다 . 여기에서 메서드 이름을 새 이름으로 편집하거나 줄을 제거하여 디자이너가 다시 작업하도록하고 새 메서드를 바인딩 할 수 있습니다.
이름 바꾸기 대화 상자를 불러올 수 있습니다. 이것은 여러 가지 방법으로 수행 할 수 있습니다.
Edit
-> Refactor
->Rename
Refactor
->Rename
Alt + Shift + F10
한 다음Rename
F2
참고 : Visual Studio를 사용자 지정할 수 있으며 위의 메뉴 및 바로 가기 키는 변경 될 수 있습니다.
이름 바꾸기 대화 상자는 다음과 같습니다.
여기에 메서드의 새 이름을 입력 할 수 있으며 이렇게하면로드 된 프로젝트와 함께 해당 메서드에 대한 참조 또는 호출도 변경됩니다. 여기에는 Forms Designer에서 생성 한 코드가 포함됩니다.
양식 디자이너가 수행하는 모든 작업은 양식을 쉽게 편집하고 사용자를 대신하여 코드를 작성할 수있는 UI를 제공하는 것입니다. 스스로 코드를 작성할 수 없다고 생각하도록 속이지 마십시오.
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.
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== null) throw new ArgumentNullException("filter");
foreach (var item in array)
{
if (filter(item))
{
Console.WriteLine(item);
}
}
}
Outputs:
2
4
8
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);
}
}
}
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);
}
}
}
Outputs:
2
4
8
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);
Outputs:
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.
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);
}
}
Outputs:
74
79
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);
}
}
Outputs:
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;
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);
}
}
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 built-in 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 복사본을 구하는 것이 좋습니다 . 이를 통해 테스트를위한 새 프로젝트를 만드는 번거 로움을 줄일 수 있습니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다