Events and delegatesIntroducing Windows Forms applications

In the we created our last C# console application. From now on, we are going to create Windows Forms applications.

Structure of a WF application

To create a new WF application, go to File > New > Project... and then in Templates > Visual C# choose Windows Forms Applications. Then, choose a name for it and a path to save it.

The first thing you see is, a tiny window appears in your coding zone! As mentioned in the introductory post, there are some IDE's that have the designer integrated, and some who don't. Visual Studio is one of the formers.

But fear not; to access the code, go to the right panel (the Solution Explorer. If you can't see it go to View > Solution Explorer). You will find there the Form1.cs, which is where the code for the functionality of your window goes.

There is also a Program.cs. You will see an [STAThread] attribute, which gives you extra information about the function below it (in this case, main()). Without going into too much detail, basically it tells the program that main() will work in a single thread, so it will work consistently with other Windows applications like ControlX, the clipboard, etc. This attribute is just another characteristic of the language.

The last line in main():


Application.Run(new Form1());
				

creates a new Form1 object which is where your window is defined. So let's learn a bit about Form1.

The form code

if you click the little triangle to the left of Form1.cs, it will display three files:

  • Form1.Designer.cs: This file is created automatically by Visual Studio. Don't edit it directly to avoid introducing inconsistencies, and anyway, it will be overwritten each time you hit the Save button.
  • Form1.resx: It's an XML file with resources, like audio/video clips, text strings, etc.
  • Form1.cs: The file you will be editing to add functionality to your form.
If you open Form1.Designer.cs, you will see a partial class Form1 {} declaration. In C#, a class can be broken down into partials. The other partial is in Form1.cs, where you will see the declaration public partial class Form1: Form {}. If you remember from the C# introductory post, if no identifier is used, the class is internal by default (like Form1.Designer.cs), and Form1 inherits from the Form class.

The first thing that the constructor of Form1.cs does is to call InitializeComponent(), which is defined in the Form1.Designer.cs file as private void InitializeComponent(). This function includes the objects that we add to the window graphically.

Properties

In the Solution Explorer, under your project, there is another folder called Properties. The dropdown will display an AssemblyInfo.cs file that contains meta information about your project. The Settings.settings file contains user configurations and the Resources.resx contains the settings of extra resources.

Adding elements to your window

Go back to your Form1.cs (design) file, where the window appears (or double click Form1.cs in the Solution Explorer). Click on the Toolbox to expand it. You can click on the pin icon in the upper right corner to keep the Toolbox open while you add elements. Choose for example a button. Drag it and drop it over the window.

Now check out your Form1.Designer.cs file. What do you see? First, there is a private System.Windows.Forms.Button button1 declaration, and if you check the InitializeComponents() function, the new Button object is instantiated with:


this.button1 = new Windows.System.Forms.Button();
				

and then, there are some configurations like size and location. These are default properties (they start with Uppercase). If you wanted to change these settings or add new ones, you can also do it from the code or from the window's visual designer. Finally, in the same file, where the configurations for the Form itself are, notice how a this.Controls.Add(this.button1). This will happen for every element we add to the window.

If you run your application now, you will get a window with a button that doesn't do anything (although it does change its state when you click it). Let's change that.

Adding functionality: Events and delegates

Below the Solutions Explorer, you will find the Properties tab. From there you can configure your element and overwrite the defaults generated in Form1.Designer.cs. If you don't see the tab, you can find Properties Window on the View menu. You can also open it by pressing F4 or by typing Properties in the Quick Launch window.

You can not only modify the visual properties, but the functionality too. If you click on the "thunder" icon, you will find a list of all the "events" that can be triggered by the selected element. In this case, you will get a list of a Button element associated events. For example, the click event.

Basically, the button will receive events, and then proceed to execute something, depending on the event received. For example, when the button receives a click, it generates an event (the click event). In other words, it generates a "message", and that message is sent to whom? To a "delegate".

A delegate is a prototype of a function. It's a class that defines how a certain function has to be defined or behave. It's like a mock-up of the behaviour. All functions who follow the prototype of the delegate subscribe to the event. When the delegate receives the event (the message), it notifies those functions, so that they go and trigger the behaviour defined by the delegate. Those functions could be defined anywhere and not only in the button, in this way, we can associate the clicking of the button to any action we desire.

Events and delegates in action

Let's add a click event to our button. For this, just go to the events list and double click the cell next to "Click". Automatically the system will create a function in the Form1class that has a very special syntax:


private void button1_Click(object sender, EventArgs e) {
}

where sender is the object who sent the event. Since we have added an action that affected the environment, the Form1.Designer.cs has been updated with the event we just added to the button, and if you go to the place where the default button properties where defined, you will find a new line:


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

At first, Click may look like just another property of button1, since the syntax is so similar, but it's an event, and the clue to differentiate them from normal properties is that they are instantiating an EventHandler, which is a delegate. The class EventHandler defines the behaviour of a function. What function? the one we pass it as an argument, button1_Click, which is the function we just created. So the delegate stored in the Click event of button1 is going to save the behaviour of <button1_Click in System.EventHandler.

In other words, we just made button1_Click subscribe to the delegate of button1.Click, so that when button1 receives a click, it will generate a Click event, and send it to System.EventHandler, which will notify our button1_Click function.

What does this function do? Still nothing! We haven't written anything for the function to do yet. Let's do that.


private void button1_Click(object sender, EventArgs e) {
	MessageBox.Show("Ouch! You just clicked me!");
}

Run the form again and click on the button, a pop-up will appear with our message =)

MessageBox.Show() is just one way of creating pop-ups in C#.

Subscribing to more than one event

Now that we have an event function defined, we can subscribe it to any other events. For example, drag and drop a Label element. If you go the events list for the Label, you will see it can also accept Click events. If you click the cell next to the Click event, our previous function will appear. Select it to assign it to the label and run the form. Now you have two ways of launching the pop-up, clicking on the button or clicking on the label!

The same function can be notified by events that occur in different types of widgets.

Comments

Have any questions? Spotted any typos? Want to showcase what you did? Found a better solution? Your feedback and suggestions are welcome! And don't forget to share =)