4

 

Controls

 

Windows Forms or Winforms is a contemporary Windows-based forms package that endows the Windows programmer with an innovative methodology for creating aesthetic user interfaces and interactive applications. We will not ramble on about the pros and cons of the package, but commence the creation of the smallest GUI (Graphical User Interface) application.

 

a.cs

public class zzz

{

public static void Main()

{

zzz z = new zzz();

System.Windows.Forms.Application.Run(z);

}

}

 

Run the compiler as

>csc a.cs

 

Compiler Error

a.cs(6,1): error CS1502: The best overloaded method match for

        'System.Windows.Forms.Application.Run(System.Windows.Forms.Form)' has some invalid arguments

a.cs(6,38): error CS1503: Argument '1': cannot convert from 'zzz' to 'System.Windows.Forms.Form'

 

An error is generated because, Run, which is a static function in the Application class of the System.Windows.Forms namespace, requires a Form object. The error distinctly states its inability to convert a zzz to System.Windows.Forms.Form, which proves that, an object that looks like Form is mandatory here, and not zzz.

 

a.cs

using System.Windows.Forms;

public class zzz : Form

{

public static void Main()

{

Application.Run(new zzz());

}

}

 

Screen 4.1

 

This program is not very dissimilar from the previous one. The using keyword is employed to avoid the inevitability of writing namespace with every object. The object z has no efficacy here, since we are passing the zzz object directly to the Run function. As the class zzz is derived from Form, no error is generated.

 

When we run the program, a small blank window is displayed. You can click on the 'x' symbol to close it. The output is substantial enough for a single line of code.

 

a.cs

using System.Windows.Forms;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

zzz()

{

Text = "Vijay Mukhi";

}

}

 

Screen 4.2

 

In the constructor of the zzz class, we have initialized a member called Text to the string value 'Vijay Mukhi'. When we run the program, to our amazement, our window, which earlier was without a title, now possesses the title 'Vijay Mukhi'.

 

This is the introductory concept of Windows Forms programming. The class called Form has abundant properties such as Text etc., which have specific relevance in a window. Any modifications to the properties get reflected immediately in the window. The changes depend upon the properties that we modify. In this case, the property of Text changes the Caption, text displayed in the title bar.

 

A Form represents a window displayed by an application. An application can have different types of windows, such as a standard, tool bar, borderless or floating window. The Form class is versatile enough to handle all the above types of windows, as it is derived from innumerable classes. A Dialog Box, which is used to accept input from the user, is available in two modes viz. modal and modeless. Our trustworthy Form class can also handle such Dialog boxes with equal aplomb. We normally use the Form class as the preliminary class for building WinForms Applications.

 

The Main method calls the Run function and gives it the Form object. In the constructor, we can modify the properties of the Form Class to give the window a  desired appearance. Since these properties are not static, they cannot be altered in Main, but can be modified in the constructor or in any other function.

 

a.cs

using System.Windows.Forms;

public class zzz

{

public static void Main()

{

Application.Run(new yyy());

}

}

class yyy : Form

{

public yyy()

{

Text = "Vijay Mukhi";

}

}

 

Screen 4.3

 

In this program, we have created another class yyy that derives from the Form class. We have used this object as a parameter to the function Run. The rules do not impel us to derive the class zzz from Form. However, in all our programs, we shall follow the first approach since we have decided to steer clear of controversy and stick to the rules.

 

a.cs

using System.Windows.Forms;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

zzz()

{

ClientSize = new System.Drawing.Size(300,600);

Size = new System.Drawing.Size(100,200);

}

}

 

Screen 4.4

 

The above example sets two properties of the Form class. The first, which is called ClientSize, is used by Windows.Forms to decide how large our initial window would be. This property has a default value, which can be overwritten by specifying the width and height. As we need to furnish two values, we use a class called Size in the namespace System.Drawing, which accepts two values. This class does not insist on receiving meaningful values. The constructor is passed the width and height of the desired window in pixels.

 

A graphics screen is divided into small dots or pixels. Depending upon the configuration of the monitor and graphics card, a computer can handle and display a certain number of pixels and colors. The higher the configuration, the larger are the number of pixels and colors that are available.

 

The size of the client area of the form is computed as the size of the form minus the borders and the title bar placed by Windows. They are not of our concern, since we shall be placing our own controls in our form. ClientSize is a property with a default value, and it gets updated automatically whenever the form is resized.

 

The next property is Size. The user enjoys the flexibility of altering the size of the window at run time. Size is initialized in manner similar to ClientSize.

 

a.cs     

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form

{

Button b;

public static void Main()

{

Application.Run(new zzz());

}

zzz()

{

b = new Button();

Controls.Add(b);

}

}

 

Screen 4.5

 

We now see a small button at the top left hand corner of our Window. How did we create this button? To do so, we first, create an object b, that looks like a Button class.

 

The Form class has a large number of properties such as ClientSize, Size etc. One of them is called Controls, which is a read-only property since it contains only a Get. This property returns a Control.Collection object, whose Add function adds the control to the Client area of the window. We shall be sprucing up our button shortly.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form

{

Button b;

public static void Main()

{

Application.Run(new zzz());

}

zzz()

{

b = new Button();

b.Location = new Point(100,200);

b.Size = new Size(100,50);

b.Text = "Vijay Mukhi is smart";

Controls.Add(b);

}

}

 

Screen 4.6

 

Anything that is placed on a form is called a Control or a Widget. Similar to a Form, a button control, popularly known as a command button, has numerous properties. One of them is the Location property, which decides the position on the Client area where the button will be positioned.

 

Here, we use the Point class and not Size, even though both are objects that represent two numbers. By convention, a Size object represents a width and a height and a Point object has an x and y co-ordinate system, starting from the upper left corner.

 

Most properties have a default value. Since this fact about default values has been reiterated numerous times, we shall not repeat it again. The Size property determines the initial size of the window and the string assigned to the Text property is displayed on the button.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form

{

Button b;

public static void Main()  {

Application.Run(new zzz());

}

zzz()

{

b = new Button();

b.Location = new Point(100,200);

b.Size = new Size(100,50);

b.Text = "Vijay Mukhi is smart";

b.Click += new System.EventHandler(abc);

Controls.Add(b);

}

public void abc(object s, System.EventArgs e)

{

MessageBox.Show("Hi");

}

}

 

Screen 4.7

 

In the earlier example, clicking on the button was an exercise in futility because the button did not achieve anything. After augmenting the code of the program, when we click on the button, we see a MessageBox that displays the greeting 'Hi'. The rationale behind a button control is that, when we click on it, some code should get executed, some action  should take place.

 

The button class has an event object called Click, which accepts an object of type EventHandler. The syntax for events uses the += symbol to add a function that is to be called when the event handler gets activated. The function name is given through the EventHandler delegate. This delegate has been specially created only to handle events that a control will generate.

 

Thus, the function abc, which is passed as a parameter to the EventHandler delegate, must have a certain signature. The first parameter is the generic object that could represent any entity identifying the caller. The second parameter is an EventArgs object, which we will explain shortly. Thus, each time we click on the button, the function abc gets called. This function in turn calls the static function Show from the MessageBox class to display 'Hi'.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form {

Button b;

public static void Main()

{

Application.Run(new zzz());

}

zzz()

{

b = new Button();

b.Location = new Point(100,200);

b.Size = new Size(100,50);

b.Text = "Vijay Mukhi is smart";

b.Click += new System.EventHandler(abc);

b.Click += new System.EventHandler(pqr);

Controls.Add(b);

}

public void abc(object s, System.EventArgs e){

MessageBox.Show("Hi");

}

public void pqr(object s, System.EventArgs e)

{

MessageBox.Show("Bye");

}  

}

 

Screen 4.8

 

This program reveals the veritable power of events and delegates. Two functions, abc and pqr, are called whenever the button is clicked. To achieve this, all that we need to do in the code is to call the Click event again, using the += symbol, followed by the name of the new function. The -= symbol is used if we change our minds. This is a type safe way of calling code in response to an event.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form {

Button b;

TextBox t;

public static void Main() {

Application.Run(new zzz());

}

zzz()

{

b = new Button();

b.Location = new Point(100,200);

b.Size = new Size(100,50);

b.Text = "Vijay Mukhi is smart";

b.Click += new System.EventHandler(abc);

t = new TextBox();

t.Text = "Hell";

t.Location = new Point(10,20);

Controls.Add(b);

Controls.Add(t);

}

public void abc(object s, System.EventArgs e)

{

MessageBox.Show(t.Text + " " + ClientSize );

}

}

 

Screen 4.9

 

In the Forms Window, we now see two controls: a Button and a TextBox object that lets us enter some text. The textbox widget also has a large number of properties associated with it. We shall not be repeating this obvious fact for all the other controls. The properties Location and Size work in a similar manner when used with any Control, but the property Text differs, depending upon the object in use. For a button, it represents the caption, whereas for a text box, it represents the text that is entered. Thus, some of the properties play different roles when used in different controls.

 

Screen 4.10

 

Each time the button is clicked, we would like to display the text that has been entered by the user in the text box. In the eventhandler function abc, the property Text reveals the text entered into the textbox.  The MessageBox class is used to display the value, along with the size of the client area.

 

You can change the size of the window or change the contents of the text box and observe the contents of the MessageBox changing dynamically.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

public override void Dispose()

{

base.Dispose();

MessageBox.Show( "hi " + ClientSize );

}

}

 

This program displays our ability to invoke code at a specific point in time, which in this case, is at the stage when the user closes the window or when the application quits out. It is akin to fulfilling the last wishes of the program.

 

Screen 4.11

 

As the application is quitting, it calls a function called Dispose. So, if you ever want code to be called at the point when an application is about to quit out, you must place it in the Dispose function. This code could be used to close files or do anything else that the programmer desires.

 

It is not mandatory to call Dispose of the base class, but it is always a good programming practice to call the base class function first, and then augment it with your own code. In this particular case, it is inconsequential, but under different circumstances, things may go out of hand if this advice is not heeded.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form  {

public static void Main()

{

Application.Run(new zzz());

}

Brush b; int ii = 0;

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

b = new SolidBrush(Color.Blue);

ii++;

g.DrawString("Vijay Mukhi " + ii, Font, b ,1,25);

}

}

 

In this program, we are overriding a function called OnPaint, which is present in the Form class. The OnPaint function gets called each time the window has to be redrawn. Therefore, all code that is to be written to the screen must be written in this function. This code cannot be placed anywhere else in the program.

 

Our next endeavor is to create an object that has functions which display text on the screen or draw a picture. The class that contains these display functions is called Graphics. Thus, we create an object g that looks like Graphics. As an object that looks like Graphics cannot be instantiated, WinForms provides us with an object of type PaintEventArgs with the OnPaint Function. This class contains members required for graphical display. Hence, g is initialized to the Graphics member in e.

 

As mentioned earlier, OnPaint gets called whenever our window has to be redrawn. Whenever OnPaint gets called, it creates an object that looks like PaintEventArgs and then passes it as a parameter to the function. This object has a member called Graphics, which contains functions used for drawing in our client area. The DrawString function requires the text that is to be displayed and its Font.

 

The Form class provides us with the object called Font. Thereafter, the text color, or to be more precise, the brush is to be specified. Here, we want a solid Brush like object. So, we create an object b, and give it a color in which the text should be displayed. There is a static object Blue in the class Color that stands for the color blue. The spelling of 'color' is as per the American usage. Finally, the x and y co-ordinates on the screen are specified.

 

This positions the text in the window at these specified co-ordinates.

Thus, the function has a total of 5 parameters:-

 

     The text to be displayed.

     The font in which the text is to be displayed.

     The text color or the brush.

     The x co-ordinate.

     The y co-ordinate.

 

Here, we have specified certain values, but every time we use DrawString, we can conveniently specify different values for these parameters. Thus, the second DrawString function can display different text and use a different font or brush. As the system does not have a default brush or font, we call it a Stateless Model.

 

Screen 4.12

 

Along with 'Vijay Mukhi', we have used a variable called ii, which has been initialized to 0. In the OnPaint function, we increment this variable by 1. Before the window is displayed, function OnPaint gets called. Thereafter, OnPaint gets called whenever the 'minimize' and 'maximize' buttons of the window are clicked.

 

The function OnPaint gets called whenever our client area has to be redrawn due to any action carried out by the user. This function has to be marked with the modifier named protected. This is because the original function in the Form class is tagged with this modifier. We can override a function of the base class, provided we do not change any of the modifiers. By making OnPaint protected, only derived classes can use the OnPaint function.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form {

public static void Main()

{

Application.Run(new zzz());

}

Brush b; int ii = 0;

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

b = new SolidBrush(Color.Blue);

ii++;

g.DrawString("Vijay Mukhi " + ii, Font, b ,1,25);

RectangleF r = new RectangleF(20, 60, 100, 25);

g.FillRectangle(new SolidBrush(Color.Gainsboro), r);

g.DrawString("Sonal Mukhi", Font, new SolidBrush(Color.Red), r);

StringFormat f = new StringFormat();

f.Alignment=StringAlignment.Center;

RectangleF r1 = new RectangleF(20, 100, 100, 25);

g.DrawString("Sonal Mukhi", Font, new SolidBrush(Color.Black), r1,f);

g.RotateTransform(-30);

g.TranslateTransform(0, 100);

g.DrawString("vijay mukhi", Font, new SolidBrush(Color.Orange), 20, 40);

g.ResetTransform();

}

}

 

Screen 4.13

 

The output of this program is a window with text displayed haphazardly. This output is nothing to write home about, but is useful in elucidating numerous concepts.

 

A rectangleF structure stores two point objects i.e. it specifies a rectangular area of the window. We start at one corner, where the x and y co-ordinates are 20 and 60, and the opposite corner where the x and y co-ordinates are 100 and 25 respectively. The function FillRectangle from the Graphics class is used to create and fill the above rectangular portion of the screen with the color Gainsboro. The DrawString function is overloaded to take not only x and y as the last two parameters, but also a rectangular area into which it will draw a string.

 

We would now like to center the above string in the rectangular area. This is easier said than done, because, it entails creation of an object that looks like StringFormat with the Alignment property set as Center. The documentation specifies many more options that can be implemented. The StringFormat object is passed as the last parameter to the DrawString function, resulting in the string being shown as centered, instead of being Left aligned, which is the default setting.

 

If we want to rotate the image by 30 degrees, we just have to call a function named RotateTransform from the Graphics class and pass as a parameter, the amount of rotation that is required. You can then watch the image get displayed at the specified angle. Beware, too acute an angle may sprain your neck! The next function, named TranslateTransform, is optional. It is used to move the text around in the client area horizontally or vertically. Whenever we transform something, it stays in the transformed position. But thereafter, if we do not want the other objects to be in this form, we need to use the function ResetTranform to undo the transform. However, it is optional.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

Brush b;

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

b = new SolidBrush(Color.FromArgb(180, Color.Black));

RectangleF r = new RectangleF(20, 20, 50, 50);

g.FillRectangle(b, r);

}

}

 

Screen 4.14

 

The topic of Brushes is so exhaustive that a thesis can well be written on it. In this program, we use a special brush to fill up a rectangular area on our screen.

 

Here, we specify not only a color, but also a number, which is the alpha value and has a range from 0 to 255. The larger the value, the darker will be the color. To put it technically, the larger the value, the lesser will be the translucence and vice-versa.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

Brush b;

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

b = new SolidBrush(Color.Black);

Font f = new Font("Times New Roman", 30);

g.DrawString("Vijay Mukhi " , f , b ,1,25);

}

}

 

Screen 4.15

 

In this program, we will shed light on Fonts. When you read a newspaper or magazine, the style of the letters looks different in each of them. This difference is due to the Font or the Typeface used. There are numerous fonts in the world of letters.

 

While displaying text, we can be very specific about the way in which the letters look. To enhance their visual appeal, we create an object that looks like Font. Then, in the constructor, the Name of the font is specified along with the Size in points. Remember that 72 points make an inch. Thus my name, Vijay Mukhi, now gets displayed in a size that is bigger than normal.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

Image i;

i = new Bitmap("sample.jpg");

g.DrawImage(i, 29, 20, 283, 212);

}

}

 

Screen 4.16

 

The above program merely displays an image. A file with a jpg or a gif extension contains images or pictures. To display images, we use a class called Image that can recognize pictures. Even though i is an image object, we initialize it to an object that looks like Bitmap. An Image class is an abstract class and the class Bitmap derives from it.

 

An Image class could represent a picture, which is not just an image, but could also be a cursor, icon etc. The DrawImage function accepts an image object as the first parameter, followed by the screen co-ordinates at which the image has to be positioned. The above .jpg file is part of the samples offered while installing the .NET SDK. So, search for the file and copy it to the current working directory. Like the text sample, this picture can also be rotated, transformed etc.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

Image i = new Bitmap("colorbars.jpg");

Brush b = new TextureBrush(i);

g.DrawString("Vijay Mukhi is very smart" , Font, b ,1,25);

}

}

 

Screen 4.17

 

By combining a Brush and an image, we can create a multicolor brush. In one of the earlier programs, we had used a Solid brush. Here, we are using a Texture brush. This brush fills the interiors of a shape with a picture.

 

Thus, the text gets reflected in a brush, which reminds us of a rainbow. You can enhance the aesthetic appeal of your applications by using this facility.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

public class zzz: Form {

public static void Main()

{

Application.Run(new zzz());

}

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

Pen p = new Pen(Color.FromArgb(150, Color.Purple), 20);

p.DashStyle = DashStyle.Dash;

p.StartCap = LineCap.Round;

Point [] pp = new Point[] {new Point(200, 140),new Point(700, 240),new Point(500, 340)};

g.DrawCurve(p,pp);

}

}

 

Screen 4.18

 

The above program introduces freehand drawing. A pen is like an artist's brush, which is used to draw any shape that permeates the mind. In our program, we commence by creating a Pen object p. It is initialized to a particular alpha color using FromArgb function from the Color class, and to a specified width.

 

The constructor can also be provided with other parameters, such as a brush. A pen is used to draw lines and curves.

 

A Pen can also draw a line of a specified width and style. The default DashStyle is Continuous. If we change the DashStyle to Dash, the starting point becomes a rounded edge. The default is a Straight Edge. The line drawn by a pen is very versatile, and can employ a variety of fill styles, colors and textures. The DrawCurve function paints a pen object that specifies how to draw a curve. It has an array of points with the individual three point objects specifying where the curved line should be drawn.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

public class zzz: Form {

public static void Main() {

Application.Run(new zzz());

}

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

Image i= new Bitmap("BoilingPoint.jpg");

Brush pb = new TextureBrush(i);

Pen p= new Pen(pb, 75);

g.DrawLine(p,1,5,150,200);

}

}

 

Screen 4.19

 

We can use a brush that looks like an image and create a pen that will draw lines in the garb of a picture. The DrawLine function accepts two sets of numbers, the x-y co-ordinates of the starting point and the x-y co-ordinates of the ending point. It draws a thick line joining these two points. Thus, we can use this function to draw any possible shape.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

protected override void OnPaint(PaintEventArgs e)

{

Graphics g = e.Graphics;

HatchBrush b = new HatchBrush(HatchStyle.ForwardDiagonal, Color.Green, Color.FromArgb(100, Color.Yellow));

g.FillEllipse(b, 250, 10, 100, 100);

Rectangle r = new Rectangle(300, 250, 100, 100);

LinearGradientBrush lb = new LinearGradientBrush(r, Color.Red, Color.Yellow,LinearGradientMode.BackwardDiagonal);

g.FillRectangle(lb, r);

}

}

 

Screen 4.20

 

On maximizing the screen, we see two figures; one is a filled circle, while the other is a rectangular block. We are equipped with a large number of brushes akin to those in the artistic world. One of them is a HatchBrush. The constructor of HatchBrush accepts a hatch style and two colors, viz. a background color and a foreground color.

 

 

The first parameter is the hatch style, which can be one of six possible hatch styles. The foreground color, in this case, green, defines the color of the lines to be drawn and the background color defines the color for the gaps between the lines.

 

The FillEllipse function in Graphics fills up the shape to display the effect of the brush. We could have used the Rectangle function also, but as we are trying to be as akin as possible to the samples provided by Microsoft, we have used the Ellipse function.

 

A LinearGradientBrush can represent color gradients and multi-color gradients. A gradient represents a transformation from one color to another. A linear gradient is defined alongside a line that is specified by the width of a rectangle or by any two points. Thus, a two-color gradient will commence with a starting color and conclude with the ending color. The blend from one color to the next can be customized. First, we specify the object that is to be colored, which is a rectangle in this case. The gradient starts with the left corner and ends at the lower right corner. Thereafter, we follow with the starting color followed by the ending color. Finally, the angle measured in degrees in the clockwise direction is mentioned, starting from the x-axis. This defines the orientation of the gradient. You can change the angle and witness the spectacular effects.

 

Menus

 

a.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

MainMenu m;

public zzz()

{

m = new MainMenu();

MenuItem mi= m.MenuItems.Add("&File");

Menu = m;

}

}

 

Screen 4.21

 

Let us now build a menu. We have created an object m, which symbolizes the MainMenu. MainMenu is called a control and represents the menu structure for a Form. It is the root of the menu.

 

A menu consists of various menu items, which are displayed horizontally across the menu. We want to create a menu item that displays the word 'File For this, we need another class called MenuItem. A MenuItem can represent either an individual menu item depicting a command, or it can cascade to another popup of menu items.

 

MenuItems is a read-only property in MainMenu that gives a reference to all the MenuItems currently available in the menu. We have none so far. This CollectionObject also has a function called Add, which is used to add menu items. To do so, the text of the item that is to be displayed must be stated as the parameter to the Add function. We can also remove any menu item that has been previously added.

 

The variable mi stores the MenuItem object returned by the Add function. Thereafter, Menu, which is an object of type MainMenu, available in Form, is initialized to the menu that we desire. The appearance of the menu depends upon the menu object stored in Menu. 

 

When we run the program, we see the word File displayed in the top left corner. At this stage, nothing happens when we click on it. On pressing the Alt key, F is displayed as underlined since the symbol & underlines the character it is preceded with.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

public class zzz: Form {

public static void Main() {

Application.Run(new zzz());

}

MainMenu m;

public zzz() {

m = new MainMenu();

MenuItem mi= m.MenuItems.Add("&File");

mi.MenuItems.Add("Hi");

mi.MenuItems.Add("-");

mi.MenuItems.Add("Bye");

Menu = m;

}   }

 

Screen 4.22

 

Now things look more visually attractive. When we click on File or use the accelerator Alt-F, a menu pops up with the word 'Hi', followed by a separator and then finally by the word 'Bye'.

 

A separator is used to logically group menus together. However, when we click on 'hi' or 'bye', nothing happens. This situation needs to be redressed, since a menu should activate some code.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

MainMenu m;

public zzz()

{

m = new MainMenu();

MenuItem mi= m.MenuItems.Add("&File");

MenuItem m1;

m1 = new MenuItem("Hi", new System.EventHandler(abc), Shortcut.CtrlF11);

mi.MenuItems.Add(m1);

mi.MenuItems.Add("Bye");

Menu = m;

}

void abc(object sender, System.EventArgs e)

{

MessageBox.Show("hell");

}

}

 

Screen 4.24

Screen 4.23

 

 

Now, whether you either click on File and then on the word 'Hi', or you press Control+F11, you will see a message box with the word "hell" displayed in it.

 

The MenuItem constructor is overloaded. The first parameter is the text to be displayed. The second parameter is a delegate that encompasses the function to be called whenever this menu item is activated. Just as the pen is mightier than the sword, under some circumstances, the keyboard is certainly mightier than the mouse. At times, it is faster to use a keyboard shortcut, instead of using the mouse. Thus, the last parameter is the keyboard shortcut key, which is part of an enumerator. This MenuItem object is passed to the Add function, that either accepts a string or a MenuItem object.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

MainMenu m;

public zzz()

{

m = new MainMenu();

MenuItem mi= m.MenuItems.Add("&File");

MenuItem a = new MenuItem("One",new System.EventHandler(abc));

MenuItem b = new MenuItem("two",new System.EventHandler(abc));

mi.MenuItems.Add("hell",(new MenuItem[]{ a, b })

);

Menu = m;

}

void abc(object sender, System.EventArgs e)

{

MessageBox.Show("hell");

}

}

 

Screen 4.26

Screen 4.25

 

 

Here, we have a popup within a popup. When you click on File, you will see the word 'hell' displayed. You will also see an arrow pointing to the right, along with the menu item. If you move the mouse over the arrow, a popup is displayed, containing the two menu items 'one' and 'two'. If we click on them, a message box with the word 'hell' gets displayed.

 

In the program, with a single statement, we have created two menu items, a and b, followed by an array of menu items. This array is then passed as the last parameter to the Add function. Thus, all the menus become sub-menus. In this case, the event handler is associated with the submenu options, since clicking on the menu item displays the sub-menu.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

MainMenu m;

public zzz()

{

m = new MainMenu();

MenuItem mi= m.MenuItems.Add("&File");

mi.MenuItems.Add( "hell",new System.EventHandler(abc));

mi.MenuItems.Add( "Bye",new System.EventHandler(abc));

Menu = m;

}

void abc(object s, System.EventArgs e)

{

MenuItem m = (MenuItem) s;

if ( m.Checked)

m.Checked = false;

else

m.Checked = true;

}

}

 

Screen 4.27

Screen 4.28

 

We add two menu items, 'hell' and 'Bye' to our File menu and assign the same function abc to handle their events. Clicking on any one of the menu options results in a call to the function abc. This function takes two parameters. The first parameter s, represents the menu item that was clicked on. If the first menu option 'hell' is clicked, then the parameter s is not an object, but a menu item representing 'hell' and vice versa.

 

Every MenuItem has an option called Checked, which if True, will display a tick mark on the menu item. Thus, you can Check or Uncheck a menu option by clicking on it. You may click on each menu option to observe this effect.

 

a.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

public class zzz: Form

{

public static void Main()

{

Application.Run(new zzz());

}

MainMenu m;

public zzz()

{

m = new MainMenu();

MenuItem mi= m.MenuItems.Add("&File");

mi.MenuItems.Add( "hell",new System.EventHandler(abc));

mi.MenuItems.Add( "Bye",new System.EventHandler(abc));

Label l = new Label();

ContextMenu lm;

lm = new ContextMenu();

l.ContextMenu = lm;

l.Text = "Vijay Mukhi";

lm.MenuItems.Add(mi.CloneMenu());

Controls.Add(l );

Menu = m;

}

void abc(object s, System.EventArgs e)

{

MenuItem m = (MenuItem) s ;

if ( m.Checked)

m.Checked = false;

else

m.Checked = true;

}

}

 

The program output will display the same menu - File, as seen in the earlier program. The text Vijay Mukhi will also be visible. If you place the mouse on this text and right click the mouse, you will see the same menu as seen with the File option.

 

Screen 4.29

Screen 4.30

 

This is called a Context Sensitive Menu. These two menus, however, are different. If you Check a menu option in this menu by clicking on it, it does not carry the tick mark to the other menu.

 

We first create two objects:

     The first is lm, which looks like a ContextMenu.

     The second is l, which looks like a label.

 

Screen 4.31

 

Every label has a member called ContextMenu, wherein we can specify a Context Sensitive menu. This member is initialized to lm. As we have already created a menu item mi, we can reuse this menu item.

 

However, a menu item cannot be used twice. Hence, calling the function CloneMenu off MenuItem creates a clone. This clone menu is then passed to the Add function of MenuItems in the ContextMenu.

 

Writing Controls

 

Let us start by creating the simplest control that money can buy. We create the following files:

 

c.cs

using System.Windows.Forms;

using System.Drawing;

public class yyy : Control

{

}

 

h.cs

using System.Windows.Forms;

public class zzz : System.Windows.Forms.Form

{

yyy a;

public zzz()

{

a = new yyy();

Controls.Add(a);

}

public static void Main() {

Application.Run(new zzz());

}

}

 

a.bat

del *.exe

del *.dll

csc.exe /t:library c.cs

csc.exe /r:c.dll h.cs

h

 

The file c.cs contains our very first custom control. In order to create our own user-defined control, we create a class yyy and derive it from the Control class. The Control class implements the basic code required by classes to implement the behavior of a control or a widget. This code can handle user input with a keyboard or a pointing device, such as a mouse. Message handling and security features are also supported.

At the end of the day, all controls are merely child windows. The Control class defines the area or bounds of a control along with the fonts, colors and images. This class allows painting, context menus and anchoring with docking behavior.

 

Earlier, we had displayed scores of controls in our containers, written by the Microsoft developers. All these controls were derived from the Control class.

 

Screen 4.32

 

Thus, a user control like yyy is an instance of a Control class, which is added to the Form using the Add function off the Controls collection. It can't get any simpler. On running the executable, we see no output. Yet, since no error was generated, we presume that all went well.

 

The major difference between the Microsoft controls and our controls is, the file in which the code for the control is finally placed. We have placed our control code in assembly c.dll, whereas, Microsoft controls are placed in System.Windows.Forms.dll.

 

c.cs

using System.Windows.Forms;

using System.Drawing;

public class yyy : Control

{

protected override void OnPaint(PaintEventArgs e)

{

e.Graphics.DrawString(Text,Font, new SolidBrush(ForeColor), ClientRectangle);

}

}

 

h.cs

using System.Windows.Forms;

public class zzz : System.Windows.Forms.Form{

yyy a;

public zzz() {

a = new yyy();

a.Size = new System.Drawing.Size(600, 450);

a.Text = "Vijay Mukhi";

Controls.Add(a);

}

public static void Main() {

Application.Run(new zzz());

}

}

 

Screen 4.33

 

In the above example, we have overridden a function called OnPaint in the Control class. This function gets called whenever a control is to be redrawn on the screen. It is passed a PaintEventArgs object as a parameter, from where we summon the DrawString function to paint a string in a specified font and color, at a particular location. The first parameter, Text, is a property, which refers to the string to be displayed. The string 'Vijay Mukhi' is presently displayed in the window.

 

In the container h.cs, we have initialized the property Text contained in the Control class to 'Vijay Mukhi'. The Size property is also initialized, so that our control has a specific size in the container.

 

h.cs

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form {

Button b;

ccc c;

public zzz() {

b = new Button();

c = new ccc();

b.Anchor = System.Windows.Forms.AnchorStyles.Bottom;

b.DialogResult = System.Windows.Forms.DialogResult.OK;

b.FlatStyle = System.Windows.Forms.FlatStyle.Flat;

b.Size = new System.Drawing.Size(96, 24);

b.Text = "&Save";

b.Location = new System.Drawing.Point(8, 328);

b.Click += new System.EventHandler(abc);

Text = "Sonal Mukhi";

AcceptButton = b;

ClientSize = new System.Drawing.Size(400, 373);

c.Anchor=AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;

c.AutoScrollMinSize = new System.Drawing.Size(0, 0);

c.Size = new System.Drawing.Size(400, 310);

c.Text = "Vijay Mukhi";

Controls.Add(b);

Controls.Add(c);

c.cust = ddd.rrr();

Size = new Size(400, (373 + SystemInformation.CaptionHeight));

}

void abc(object sender, System.EventArgs e)

{

c.aaa();

MessageBox.Show("vijay "+ c.cust);

}

public static void Main(string[] args)

{

Application.Run(new zzz());

}

}

 

c.cs

using System;

using System.Windows.Forms;

using System.Drawing;

public class ccc : UserControl

{

TextBox t;

TextBox ID;

Label l;

ddd c;

public ccc()

{

t = new TextBox();

l = new Label();

ID = new TextBox();

Text = "Vijay Mukhi";

Size = new System.Drawing.Size(384, 304);

t.Size = new System.Drawing.Size(88, 20);

t.Location = new System.Drawing.Point(88, 70);

l.Size = new System.Drawing.Size(64, 16);

l.Location = new System.Drawing.Point(8, 32);

l.Text = "ID:";

ID.ReadOnly = true;

ID.Size = new System.Drawing.Size(200, 20);

ID.Location = new System.Drawing.Point(88, 30);

ID.Enabled = false;

Controls.Add(t);

Controls.Add(ID);

Controls.Add(l);

}

public ddd cust

{

get

{

return c;

}

set

{

c=value;

ID.Text = c.ID;

t.Text = c.ti;

}

}

public void aaa()

{

c.ti = t.Text;

}

}

 

cc.cs

using System;

using System.ComponentModel;

using System.IO;

public class ddd : Component

{

string i ;

string t ;

public static ddd rrr()

{

ddd c = new ddd("111");

c.ti = "Vijay";

return c;

}

internal ddd(string s): base()

{

i = s ;   

}

public string ID

{

get

{

return i ;

}

}

public string ti

{

get

{

return t ;

}

set

{

t = value ;

}

}

public override string ToString()

{

StringWriter sb = new StringWriter() ;

sb.WriteLine("Sonal \n");

sb.WriteLine(i);

sb.Write(t);

return sb.ToString();

}

}

 

 

a.bat

del *.exe

del *.dll

csc.exe /t:library /out:c.dll c.cs cc.cs

csc.exe /R:c.dll h.cs

h

 

This program is a rather protracted one. As usual, we start with the container in h.cs. In the zzz constructor, we first create a button b and an object c that is an instance of our user control ccc. The control is present in the assembly c.dll. What this class presently does is not significant. We begin by initializing a large number of properties in the button.

 

The Anchor property decides as to which edges of the control are to be anchored with the edges of the container. Here, we have chosen the Bottom edge.

 

The DialogResult property is the value that is returned to the parent form when we click on the button. The value returned is OK.

 

The FlatStyle property belongs to the ButtonBase class and is one of the numerous properties that influence the flat style appearance of the button. Knowledge of GUI programming implies cognizance and comprehension about all the facets of making your application more comely and pleasing to the eye. The Size, Text and Location properties were explained earlier.

 

Each time we click on the button, the function abc gets called. The Text property decides on the title of the windows. The AcceptButton property requires an object that represents a button. Every form has a feature, which associates the Enter key with a button. The resultant effect is that pressing the Enter key on the keyboard simulates a click on the associated button. Thus, in the above form, pressing Enter or clicking on the button would result in a call to the function abc. The ClientSize property decides the size of the windows.

 

Our User Control too can initialize properties since they belong to the Control class. In the program, we have set the Anchor, AutoScrollMinSize, Size and Text properties of our user-defined control class ccc, even though our control may not have implemented these properties directly.

 

Using the Add function, we have then added the button and the control ccc to the form.  Finally, we have called a static function rrr from the class ddd that initializes a property cust from our user-defined control.

 

When we run this program, we see two text boxes, a label and a button. It is obvious that other than the button, the other widgets were created by the class ccc. This provides ample credence to our belief that our user-defined control can do its biding.

 

Screen 4.35

Screen 4.34

 

 

We shall now endeavor to comprehend what the constructor of class ccc in file c.cs is attempting to do. The constructor contains two text boxes, one called ID to store the id of the user, and the other called t to store the user's name. The label l is used to display a simple descriptive message. Since we do not want the user to change the value contained in ID, we assign the value true to its ReadOnly property and assign the value false to its Enabled property. Thereafter, we add these three widgets to the form. Hence, we can now see four widgets on the screen.

 

In the container h.cs, we call a static function, rrr off class ddd. The ddd class is created in cc.cs and is derived from Component. In the rrr function, we create an object c, which looks like ddd and pass a value of 111 to the constructor. The constructor of class ddd initializes an instance variable i to the value contained in s. The variable i stands for the user id. 

 

Class ddd has a property called ti, which is initialized to my name, Vijay. This property ti sets another instance variable t to the value 'Vijay'. Thus, we have initialized two instance members of class ddd to specific values.

 

The value returned on calling the rrr function is stored in the cust property of the control c. The class ccc contains the property having type ddd.

 

The property Cust stores the ddd object in the variable c for later use. It also initializes the Text property of the text boxes to the ID and Name of the user that the two properties in the class ddd were initialized to. Thus, we see '111' and 'Vijay' displayed in the text boxes.

 

When we click on the button labeled 'Save', the function abc gets called. This function first calls the function aaa from class ccc using the object c. In aaa, we initialize the ti property of the control to the value present in the textbox. The ID property is dimmed out, and hence its value can never be changed. The object c represents the ddd object in class ccc.

 

To display a string, an object, whose data type is not a string, has to call the ToString function in the datatype. The cust property in class ccc has the type of ddd, which contains the ToString function. This function uses the StringWriter class to concatenate the word 'Sonal' with the value of the instance variables i and t, which eventually get displayed in the MessageBox.

 

The above program demonstrates two crucial points:

(a) All the code that refers to the user has been encapsulated in class ddd.

(b) The user interface code is entered in class ccc.

 

The container is oblivious to these classes and does not bother to verify whether there are two classes or one. While the class ccc contains code pertaining to User Interface interaction only, the class ddd contains code relating to the actual object.

 

h.cs

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form  {

RadioButton r1,r2;

GroupBox g1;

sss s;

public zzz()

{

r1 = new System.Windows.Forms.RadioButton();

r2 = new System.Windows.Forms.RadioButton();

r1.Location = new System.Drawing.Point(24, 24);

r1.Size = new System.Drawing.Size(128, 24);

r1.Text = "Vijay";

r1.Checked = true;

r1.CheckedChanged += new System.EventHandler(r1f);

r2.Location = new System.Drawing.Point(24, 64);

r2.Size = new System.Drawing.Size(128, 24);

r2.Text = "Mukhi";

r2.CheckedChanged += new System.EventHandler(r2f);

g1 = new System.Windows.Forms.GroupBox();

g1.Size = new System.Drawing.Size(192, 152);

g1.Text = "Sonal";

g1.Location = new System.Drawing.Point(320, 16);

s = new sss();

Text = "Control Example";

ClientSize = new System.Drawing.Size(528, 325);

s.Size = new System.Drawing.Size(304, 328);

s.TabIndex = 0;

s.Anchor = AnchorStyles.Left | AnchorStyles.Right;

s.Font = new System.Drawing.Font("TAHOMA", 16f, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.World);

s.Text = "Simple Control";

s.dmc += new System.EventHandler(sf);

Controls.Add(g1);

Controls.Add(s);

g1.Controls.Add(r2);

g1.Controls.Add(r1);

}

void r2f(object sender, System.EventArgs e)

{

if (r2.Checked)

{

s.dm = ddd.a2;

}

}

void r1f(object sender, System.EventArgs e)

{

if (r1.Checked)

{

s.dm = ddd.a1;

}

}

void sf(object sender, System.EventArgs e)

{

if (s.dm ==  ddd.a1)

MessageBox.Show("hi");

if (s.dm ==  ddd.a2)

MessageBox.Show("bye");

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

c.cs

using System;

using System.ComponentModel;

using System.Windows.Forms;

using System.Drawing;

[DefaultProperty("dm"),DefaultEvent("dmc"),]

public class sss : Control

{

ddd d;

EventHandler eee;

public sss() :base()

{

d = ddd.a1;

ccc();

SetStyle(ControlStyles.ResizeRedraw, true);

}

[Category("Appearance"),Description("Controls how the control paints"),DefaultValue(ddd.a1),Bindable(true),]

public ddd dm

{

get

{

return d;

}

set

{

d=value;

ccc();

dmf(EventArgs.Empty);

}

}

protected override void OnPaint(PaintEventArgs e)

{

e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle);

Size textSize = e.Graphics.MeasureString(Text, Font).ToSize();

float x = (ClientRectangle.Width/2) - (textSize.Width/2);

float y = (ClientRectangle.Height/2) - (textSize.Height/2);

e.Graphics.DrawString(Text,Font,new SolidBrush(ForeColor),x, y);

}

protected override void OnTextChanged(EventArgs e) {

      base.OnTextChanged(e);

      Invalidate();

}

[Description("Raised when the DrawingMode changes")]

public event EventHandler dmc

{

add {

              eee += value;

       }

remove {

              eee -= value;

      }

}

protected virtual void dmf(EventArgs e)

{

Invalidate();

if (eee != null)

eee.Invoke(this, e);

}

void ccc()

{

if ( d == ddd.a1)

{

base.BackColor = Color.Yellow ;

base.ForeColor = Color.Green ;

}

if ( d == ddd.a2)

{

base.BackColor = Color.LightSlateGray ;

base.ForeColor = Color.White ;

}

}

}

public enum ddd

{

a1 = 0,

a2 = 1,

}

 

a.bat

del *.exe

del *.dll

csc /t:library c.cs

csc h.cs /r:c.dll

h

 

Let us now write a control that is considerably intricate. In the container h.cs, we start with two radio buttons r1 and r2. Each time we select a radio button, depending upon the option selected, either of the functions r1f or r2f will get called. Thereafter, we create a group box called g1. The radio buttons are added to the group box, and the group box is added to the Controls collections. Apart from these controls, one more user control named s, which is an instance of class sss, is added to the Controls collection.

 

Screen 4.36

Screen 4.37

 

Prior to this, we initialize various properties of this control such as TabIndex, Font, Size, Anchor, Text etc. to some meaningful values. Besides these, a property called dmc in the user control is initialized to an EventHandler that calls function sf. Thus, whenever the event represented by dmc is triggered, the function sf gets called.

 

The code implementing our user control s, resides in the file c.cs. The user control s is an instance of sss and is derived from the Control class. At the outset, the constructor of class sss calls the constructor of the base class using the keyword base, even though this is optional, because the base class constructor invariably gets called.

 

The class ddd is an enum, with two members a1 and a2, having values 0 and 1 respectively. We could conveniently have used numbers directly instead of an enum, but since the original example used an enum, we have also done so. We set the object d to the value 0 and call function ccc from class sss. The main objective of placing code in a function is to enable the code to be called several times.

 

In the function ccc, we start by checking the value of the object d. If it is a1 i.e. 0, we change the value of the two properties BackColor and ForeColor to Yellow and Green respectively. If the object has a value of a2, then another pair of colors is assigned to these properties. The properties are changed in the base class using the keyword base. The function SetStyle ensures that the form gets redrawn when it is resized.

 

We have already learnt that the OnPaint function is called whenever the window needs to be redrawn. In this function, we first use the property BackColor to fill the form background. Next, we use the width of the currently selected font, to calculate the midpoint of our screen, and then, we write the value contained in the text property in the center of the window.

 

When the second radio button is selected, function r2f gets called. In this function, the program checks whether the radio button is already checked. If so, it initializes the property dm, whose data type is ddd, to a2.

 

Similarly, when the first radio button is selected, function r1f gets called. This function first ascertains if the radio button is already checked.  If so, it initializes the property dm to a1.

 

Now, we shall focus our attention on the property dm. In the set accessor of property dm, the ddd object named d is initialized to either a1 or a2. Following this action, a call is made to function ccc, which changes the background and foreground color, depending on the value contained in d. The effect is observed when the function OnPaint gets called. A call is made to the function dmf with a parameter of an Empty event.

 

In function dmf, we first call Invalidate, which in turn, calls the OnPaint function. Just as life offers no guarantees whatsoever, in much the same way, the calls made to the OnPaint function are unpredictable. The Invalidate function instantly calls the OnPaint function.

 

You may recall that in h.cs, the property dmc was initialized with the name of the function sf. This property dmc is an event type that stores the EventHandler or function sf in an instance object eee. So, the value in the object eee is checked. If the value is not null, the function Invoke is called off the object eee with two parameters. The first parameter is a reference to itself, i.e. 'this', and the second parameter is a null EventArgs object. The function Invoke, in turn, calls function sf in the container, h.cs. The function sf displays a message box, depending upon the value of the ddd object.

 

The main idea behind this exercise is to demonstrate that clicking on a radio button in the container initializes a property in the user control. This in turn, raises a property changed event, thus resulting in a call to a function registered with a property of the control. The function resides in the container and not in the user control.

 

This is a circuitous route for accomplishing results. The Invoke function is not aware of and could not care less about the functions that it is calling.

 

All the other attributes in the code can be safely ignored, since they are mainly meant for external tools or programs that display the metadata.

 

h.cs

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form

{

TextBox t;

Button b;

hhh h;

public zzz()

{

h = new hhh();

b = new Button();

t = new TextBox();

b.Size = new System.Drawing.Size(104, 40);

b.Text = "Vijay";

b.Location = new System.Drawing.Point(336, 56);

ClientSize = new System.Drawing.Size(448, 157);

t.Location = new System.Drawing.Point(80, 16);

t.Text = "Vijay Mukhi";

h.Dock = System.Windows.Forms.DockStyle.Bottom;

h.Size = new System.Drawing.Size(448, 40);

h.Location = new System.Drawing.Point(0, 117);

h.Text = "none";

h.ppp(t, "TextBox selected");

h.ppp(b, "Button Selected");

Controls.Add(t);

Controls.Add(b);

Controls.Add(h);

}

public static void Main(string[] args)

{

Application.Run(new zzz());

}

}

 

c.cs

using System;

using System.Collections;

using System.ComponentModel;

using System.Drawing;

using System.Windows.Forms;

public class hhh : Control

{

Hashtable h;

Control a;

public hhh()

{

h = new Hashtable();

BackColor = SystemColors.Info;

}

[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ]

public override string Text

{

get

{

return base.Text;

}

 set

{

base.Text = value;

}

}

private void ce(object s, EventArgs e)

{

a = (Control)s;

Invalidate();

}

private void cl(object s, EventArgs e)

{

if (s == a)

{

a = null;

Invalidate();

}

}

public void ppp(Control c, string v)

{

if (v == null)

{

v = string.Empty;

}

if (v.Length == 0)

{

h.Remove(c);

c.Enter -= new EventHandler(ce);

c.Leave -= new EventHandler(cl);

}

else

{

h[c] = v;

c.Enter += new EventHandler(ce);

c.Leave += new EventHandler(cl);

}

if (c == a)

{

Invalidate();

}

}

protected override void OnPaint(PaintEventArgs pe)

{

base.OnPaint(pe);

Rectangle rect = ClientRectangle;

Pen borderPen = new Pen(ForeColor);

pe.Graphics.DrawRectangle(borderPen, rect);

borderPen.Dispose();

if (a != null)

{

string te = (string)h[a];

if (te != null && te.Length > 0)

{

rect.Inflate(-2, -2);

Brush brush = new SolidBrush(ForeColor);

pe.Graphics.DrawString(te, Font, brush, rect);

brush.Dispose();

}

}

}

}

 

In this program, we have three controls: a user control h, contained in class hhh, a TextBox t and a Button b. The basic properties like Size, Text and Location for the Button and the Text box are set to the specified initial values. Thereafter, the properties of the user control h are initialized. The DockStyle for the Dock property is set to the Bottom of the form, and the Size and the Location are specified.  The Text property is also initialized.

 

The user control h has a property called ppp, which accepts two parameters, a control and a string. We c