|
Windows
applications that we create fall under two major categoriesmanaged
and unmanaged. Unmanaged Windows applications can be created
using Microsoft Foundation Classes (MFC). Managed applications
run under Common Language Runtime (CLR) and are created using
.NET Framework Class Library. These applications are called
WinForm applications. WinForms (Windows Forms) create graphical
user interface (GUI) for Windows applications. A form is a
top-level window of an application. It can be a window, a
dialog or a multiple document interface (MDI) window.
Creating WinForms has many advantages over creating applications
using MFC. In unmanaged Windows-based programs, we need to
apply certain styles to the window only when it is created.
WinForms eliminate this quirk. If we apply styles that are
meant to be applied at creation time, .NET destroys the previous
window and creates a new one with new styles.
The .NET Framework Class Library is richer than MFC. Also,
the classes remain the same for all the .NET compliant languages.
All the classes that are used to create and design a WinForm
are encapsulated in System.Windows.Forms namespace. For example,
the System.Windows.Forms.Form class is used to create the
form, System.Windows.Forms.Button class is used to create
a button and so on. To create a GUI, we place components and
controls on the form and so, a form acts as a container of
components and controls. To create the GUI we drag and drop
the controls from Toolbox and change their properties to suit
our requirements. When we drag the control on form and change
properties, Visual Studio.NET generates suitable code for
us. However, only adding controls to create a user interface
is not enough. Our application must also respond when the
user interacts with the application. A user can interact with
the application by moving the mouse, clicking the mouse button,
pressing a key, etc. Whenever a user interacts with the GUI,
events are generated. Events are notifications sent to the
container, which can then respond to it. For example, when
we click a button, an event is generated notifying that the
user has clicked the button. In response, we can display a
message box or do something else. This job is done in methods
called event handlers. When a particular event
is generated, the corresponding event handler gets called
to process the event. For calling the event handlers, .NET
takes help of events and delegates.
An event in C# is a multicast delegate having a predefined
prototype. The prototype is such that every event returns
a void and always accepts two parameters. The first parameter
is always a reference to an object of System.Object class
and the second parameter is always a reference to an object
of the System.EventArgs class or a class derived from it.
The EventArgs object contains information about the event.
The object that raises an event is called an event raiser
and the object that receives an event is called an event
receiver. The event raiser class always declares the
event and the receiver class must have an event handler to
handle that event.
The first parameter collected by the event handler is always
a reference to an event raiser object. The addresses of event
handlers are stored inside the events (remember that events
are actually delegates) and hence they also must have the
same signature as the event. This kind of delegate is declared
using the keyword event. To understand events, consider the
following program.
using System;
namespace Sample
{
public class mouseeventargs
{
public int x,y;
public mouseeventargs (int x1, int y1)
{
x = x1;
y = y1;
}
}
public delegate void click (object m, mouseeventargs e);
public class MyForm
{
public event click c1;
public void mouseclick()
{
mouseeventargs m = new mouseeventargs (10,20);
c1 (this, m);
}
}
class MyForm1:MyForm
{
public MyForm1()
{
c1 += new click (button_click);
mouseclick();
}
public void button_click (object m, mouseeventargs e)
{
Console.WriteLine (Mouse Coordinates: + e.x + + e.y) ;
Console.WriteLine (Type is: + m.GetType().ToString()) ;
}
static void Main ( string[ ] args )
{
MyForm1 f = new MyForm1( ) ;
}
}
}
In this program we have declared three classesmouseeventargs,
MyForm and MyForm1 derived from MyForm. The mouseeventargs
class corresponds to the EventArgs class. This class contains
two int variables x and y and a constructor to initialise
them. We have declared a multicast delegate click whose signature
is the same as the event handler button_click( ). The MyForm
class here is an event raiser class and hence contains an
event as a data member called c1 of the type click. It also
contains a method called mouseclick( ). In this method we
have created an object of the mouseeventargs class. In the
statement,
c1 (this, m);
c1 encapsulates the button_click( ) method. this contains
the reference of MyForm object (event raiser) and m is a reference
of the mouseeventargs object. In the MyForm1 class, we have
defined the method button_click( ) (i.e. event handler). When
the compiler encounters the statement c1 += new click (button_click),
it creates an event object (delegate object) that wraps up
button_click( ) method. So whenever the c1 event is raised
(or called, as in this program), the method button _click(
) would get executed.
We could call a method even by using a delegate. Then what
are events for? Take a look at the following statement.
c1 = new click(button_click);
where,
c1 is a delegate reference. This statement has a bug. Here,
we are assigning a new delegate object to c1, and so, all
the delegates previously added are now lost. Instead of =
operator we should have used += operator to add a new delegate
object to the delegate list. This would never happen in case
of events. This is because we can perform only two operations
on events, namely, += and -=. We cannot use = operator with
events. As such, events provide a protection layer on delegates.
When we use the += and -= operators with delegates, they get
converted into a call to Delegate.Combine( ) and Delegate.Remove(
) methods. If we use += and -= operators with events they
get converted into a call to add_c1( ) and remove_c1( ) methods.
These methods in turn call the Delegate.Combine( ) and Delegate.Remove(
) methods.
WinForms also follow the same mechanism. Only thing is, we
dont need to invoke the event handlers ourselves. They
get called automatically when events occur. The compiler adds
suitable code such that when an event occurs our event handler
would get called. Let us now see what code gets generated
when we create a WinForm application and add an event handler.
The minimal code that is required to create a WinForm application
is given below:
using System;
using System.Windows.Forms;
namespace WinFormDemo
{
public class Form1 : Form
{
static void Main()
{
Application.Run ( new Form1( ) ) ;
}
}
}
Here, we have derived a class Form1 from the Form class and
passed a reference to its object to the Application.Run( )
method. This method creates the form, displays it on screen
and starts a message loop for it. Of course, we dont
need to write this code, Wizard generates it for us. If we
add a control to the form, a reference to the class representing
the control gets added to the class as a private member. If
we add a button the reference to the Button class would get
added as shown below.
private System.Windows.Forms.Button
button1;
The reference button1 would get initialised in the InitializeComponent(
) method as given below.
this.button1 = new System.Windows.Forms.Button();
On adding a handler for the Click event, the code generated
for it looks as given below.
this.button1.Click += new System.EventHandler(this.button1_Click);
The EventHandler is a delegate defined in the System namespace.
This statement wraps the EventHandler around the button1_Click(
) method so that when the Click event is fired, button1_Click(
) method would get called. The definition of button1_Click(
) method is shown below.
private void button1_Click (object sender, System.EventArgs e)
{
}
The first parameter passed to button1_Click( ) method identifies
the object that fired the event (button1 in this case). The
second parameter contains additional information about the
event.
The objects of every control class fire events. Now you can
appreciate how easy it is to handle these events under .NET
 |
Yashavant
Kanetkar, one of the first Express Computer columnists,
is an established software expert, speaker and author
with several best-sellers to his credit, including titles
like “Let Us C” and the “Fundas” series. Contact him at
kanet@nagpur.dot.net.in |
|