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 call it twice. When it is called for the first time, the first parameter passed is a text box control. The next time it is called, the first parameter passed is a button control. The button and the text box are displayed on the screen and a yellow colored label with the word 'TextBox selected' in the bottom pane.

 

Screen 4.38

 

When we click on the button, the words in the label change to 'Button selected'.

 

Screen 4.39

 

Thus, depending upon the control selected, our user control displays a help message which we have registered using the property ppp.

 

The user control h in file c.cs, has two instance variables, a HashTable h and a Control a. The HashTable class stores values based on a certain key. This helps in retrieving the value efficiently on being provided with the key. In the constructor of class hhh, an instance h of the HashTable is created. The property BackColor is initialized to a Read Only Color property from the class SystemColors. This property represents the tool tip background color. On our machine, it happens to be Yellow, and thus, we see a yellow color label.

 

There is a property called Text in the Control class. In order to implement our property, we must override the existing one. Presently, in our property, we are merely accessing the original Text in the base class.

 

The attributes make interesting reading even though they have little use in the current example. The Browsable attribute with a parameter of False prevents this property from showing up in the Property Browser. Also, the value assigned to the property is not saved on disk. We are not doing anything useful in the Text property at all.

 

The property ppp is called twice in the control, because the container has two property initialization statements. Good programming style incorporates comprehensive error checks.

 

We first check for a string value in the second parameter v. If it is null, we initialize the variable v to an empty string. If the string is not empty, we add the string contained in v to the hash table using the control parameter c as the key. Thus, in a hash table, any data type can be used as a key to insert or retrieve values.

 

The control class has Events called Enter and Leave. We use the += syntax to register the function ce whenever the Event Enter gets fired. In the same manner, the function cl gets called whenever the Event Leave gets fired.

 

If the user calls the property without a string, it signifies that the control has to be removed from the hash table and the list of functions in the events has to be called. Thus, we use the Remove function of the HashTable class to remove the key c from the hash table. The -= syntax of the Events and Delegates is employed to remove the functions registered with the Enter and Leave events.

In the OnPaint function, we have drawn a label and displayed some text in it. There is one rule that must never be violated: 'Call the function in the base class first.' This is because we are unaware of what the overridden function accomplishes.

 

A Pen is created from the color stored in ForeColor. This color is the default color stored in the Control class. The Background color is initialized in the constructor of hhh class. A rectangle that is drawn using this pen displays yellow as the background color, with the size defaulting to the value stored in the ClientRectangle property in the control class. The pen is then disposed off, to enable the system to retrieve the resources consumed by it. A check is thereafter performed on the value in the Control object. It should be null since the control has not been initialized in the beginning.

 

Before the OnPaint function gets called, a lot of activities get executed in the background, i.e. many functions get called and numerous events get triggered. On entering a field, the event OnControlEnter gets triggered and the function that is registered with the Enter event is called. In our case, function ce is called. The first parameter to this function is a handle to the control that caused the event. In our case, it is the Text Box. We initialize the object a to the Text Box control. Thus, in OnPaint, the value of control a is a Text Box.

 

We then retrieve the string stored in the hash table using the control as the key. We then ascertain that the value contained in the string te is not null and its length is greater than zero.

 

The Rect structure contains the size of the rectangle. We can then inflate or deflate the rectangle using the Inflate method. As a result, the position and the size of the Rectangle change. The X and Y properties are changed by the amount specified and the Width and Height are modified to twice the amount specified.  The final outcome is that the size of the rectangle is inflated without moving its geometric center. In our case, since the numbers are negative, the rectangular will be deflated. Following this, a solid brush is created using the property ForeColor. A string is drawn using the DrawString function in the Pen class. Thereafter, we dispose of the brush. 

 

When we leave the control, the event handler named Leave is called. The function associated with this eventhandler is cl, which ascertains whether the control in 'a' and the one passed as parameter i.e. 's' is the same. If so, then 'a' is initialized to a Null value and the OnPaint function is called. The value contained in 'a' is the deciding factor on whether we are inside a control or not.

 

h.cs

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form

{

fff f;

public zzz()

{

f = new fff();

f.Dock = System.Windows.Forms.DockStyle.Fill;

f.ForeColor = System.Drawing.Color.White;

f.BackColor = System.Drawing.Color.Black;

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

f.vvv = 73;

f.Text = "Vijay Mukhi";

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

Controls.Add(f);

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

c.cs

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

public class fff : Control {

int vvv1 = 0;

public int Min = 0;

public int Max = 100;

public bool ShowValue = false;

int dv = 0;

bool d = false;

public Color cs = Color.Red;

public  Color ce = Color.LimeGreen;

Brush bb = null;

Brush bd = null;

public int vvv

{

get

{

if (d)

{

return dv;

}

return vvv1;

}

set

{

vvv1 = value;

}

}

protected override void OnPaint(PaintEventArgs e)

{

base.OnPaint(e);

bb=new LinearGradientBrush(new Point(0, 0),

        new Point(ClientSize.Width, 0),cs,ce);

bd = new SolidBrush(Color.FromArgb(200, Color.Black));

e.Graphics.FillRectangle(bb, ClientRectangle);

Rectangle r = ClientRectangle;

float p = ((float)vvv / ((float)Max - (float)Min));

int a = (int)(p * (float)r.Width);

r.X += a;

r.Width -= a;

e.Graphics.FillRectangle(bd, r);

e.Graphics.Flush();

RectangleF r1 = new RectangleF();

SizeF ts = e.Graphics.MeasureString(Text, Font);

r1.Width = ts.Width;

r1.Height = ts.Height;

r1.X = (ClientRectangle.Width - r1.Width) / 2;

r1.Y = (ClientRectangle.Height - r1.Height) / 2;

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

}

protected override void OnMouseDown(MouseEventArgs e)

{

base.OnMouseDown(e);

Capture = true;

d = true;

sss(new Point(e.X, e.Y));

}

protected override void OnMouseMove(MouseEventArgs e)

{

base.OnMouseMove(e);

if ( !d)

return;

sss(new Point(e.X, e.Y));

}

protected override void OnMouseUp(MouseEventArgs e)

{

base.OnMouseUp(e);

if ( !d)

return;

Capture = false;

d = false;

vvv = dv;

}

void sss(Point l) {

Rectangle r = ClientRectangle;

float p = (float)l.X / (float)r.Width;

int a = (int)(p * (float)(Max - Min));

int o = dv;

dv = a;

float op = ((float)o / ((float)Max - (float)Min));

int ol = (int)(op * (float)r.Width);

float np = ((float)dv / ((float)Max - (float)Min));

int nl = (int)(np * (float)r.Width);

int mi = Math.Min(ol, nl);

int ma = Math.Max(ol, nl);

Rectangle r1 = new Rectangle(r.X + mi, r.Y, ma - mi, r.Height);

Invalidate(r1);

}

}

 

When we run the above program, we see a myriad of colors such as red, green and black. We also see 'Vijay Mukhi' displayed in the center. On clicking the mouse, more of the color black will be introduced upto the point of the mouse click on the screen, whereas dragging the mouse will extend or diminish the colors accordingly. This certainly is a sight for sore eyes!

 

Screen 4.40

Screen 4.41

 

We have to admit that the folks at Microsoft who worked on this sample did a truly marvelous job. Our task here is to explain the program that they have written, in absolute layman terms.

 

The h.cs file contains code that is already explained in the earlier program. As always, along with the properties that are present in Control, we have also initialized one of our own properties, vvv for control fff. The user-defined property vvv is assigned a value of 73. The value contained in the Text property is displayed at the center of the screen.

 

We now focus the spotlight on the actual code of our control fff that resides in the file c.cs.

 

The container sets the value of the property vvv to 73. Therefore, in the control, the property vvv gets called initially. The set accessor initializes the int variable vvv1 to 73.

 

The function OnPaint is responsible for drawing the rainbow of colors on the screen. In this function, we first call the original OnPaint function. Then, we create two brushes to obtain the desired effect. The first brush is a LinearGradientBrush, which can be accessed using the Brush object bb.  The last parameter to the constructor of this brush is the starting color cs, which has been initialized to Red, and an ending color ce that is initialized to a value of LimeGreen. The second brush is a SolidBrush bd, which is created to fill up any shape. The function FromArgb of the Color class creates a new color, where the first parameter is an alpha value 200 and the second parameter is the base color.

 

The Graphics property of PaintEventArgs merely returns a Graphics object when we call function FillRectangle. The two parameters to this function are : a Brush that represents a gradient and the Size of the window whose value is stored in the ClientRectangle property. If we stop at this stage, the red and the green gradients will stand out glaringly.

 

Now, let us draw the black rectangle separating the red and green colors. We create a temporary variable r to store the dimensions of the window.

Two instance variables, Max and Min having a value of 100 and 0 respectively, are used to decide the factor by which the property vvv should be divided. Since we need to retrieve the value for the property, the get accessor is called.  The value contained in d decides on the value to be assigned to the property. Initially d is false, so the value 73 contained in vvv1 is returned.

 

The float variable p now has a value of .73. The Width of the window in our case is 600 pixels. Thus, the variable 'a' is assigned a value of 438. You can use the WriteLine function to display these values. The Width of the rectangle is reduced by this amount and the X position is shifted by 438 pixels. Next, we draw the black rectangle using the function FillRectangle by providing it with a black brush and the new Rectangle object r. Modifying the value of the property vvv from 73 to 3 will result in displaying the entire screen in black color. Thus, the vvv parameter decides the size of the black rectangle; the smaller its value, the larger will be the size of the rectangle. The Flush function ensures that all pending graphic operations on the stack are executed immediately. If we stop here, the gradient and the rectangle will be displayed, but no text will be displayed in the center of the screen.

 

The Rectangular structure r1 stores the size and location of the rectangular region that our string requires. The MeasureString function takes two parameters:

     The first is a string value that is stored in our property Text.

     The second is a Font object.

 

The default Font property is supplied for the second parameter. The function returns a SizeF object, whose Width and Height determine the display region that the string requires. The width and height of rectangle r1 is initialized to the Width and Height of the string. As the string is to be displayed in the center of the form, objects X and Y, which are the members of rectangle r1, are given the following values:

     The Width of the form = the width of the string divided by 2.

     The Height of the form = the height of the string divided by 2.

 

We then use the DrawString function to display the text. This function takes the following parameters in the specified order:

     A string that is stored in the Text property.

     The default font.

     The color of the brush that is stored in the property ForeColor.

     The region, which is stored in object r1.

 

We have three mouse events that trigger off three functions. They are as follows:

     OnMouseMove: When we move the mouse.

     OnMouseDown: When we click the left mouse button, with the mouse cursor placed on the form.

     OnMouseUp: When we release the left mouse button.

 

 

If we move the mouse around, nothing happens. This is because the 'if' statement results in a value of true, as the variable d has a value of false. So, the function practically achieves nothing. Clicking the left mouse button toggles the value of the variable d to true. Thus, if the variable d is True, we know that the user has clicked the left mouse button, and when it reverts back to false, we know that the left mouse button has been released. The OnMouseMove function does something constructive and gainful only when the left mouse button is depressed.

 

The Capture property reveals whether the control has been able to capture the mouse or not. This property is optional. When we click within the window, the framework is informed about our victory over the mouse, and the variable d is set to true. The next most pertinent action is to call function sss with a Point object. This object stores the position at which the mouse has been clicked. The MouseEventArgs parameter e, has two members X and Y, which identify the current position of the mouse.

 

The function sss is called yet again when the mouse is moved. The OnMouseUp function changes the value of Capture, and sets the value of the boolean variable d to false. It also initializes the value of a property vvv to dv.

The net achievement of the function sss is that, it calls the function OnPaint using the Invalidate function. The OnPaint function does not draw or paint the entire form or window, but only the specific part that has been invalidated. This is done to achieve optimum efficiency, since it is preposterous to waste effort in redrawing areas that have not been invalidated. The Invalidate function is also passed a Rect object as a parameter, which takes a decision on the area of the client rectangle that is to be re-drawn.

 

Function sss is therefore, given a smaller Rect object as a parameter, which informs the OnPaint function about the specific part of the form that should be invalidated. The Point parameter is employed to determine the area of the form, that must be redrawn. 

The co-ordinates of the entire screen are stored in ClientRectangle. We also calculate a percentage p, depending upon our current position, i.e. l.X, divided by the Width of the screen. Next, we multiply this percentage by 100 (the difference between Max and Min). The value of dv is stored in variable o, since we are initializing dv to 'a' in the next line.

 

Two new percentage values, op and np have been calculated. They are merely the values of o and dv divided by 100. Thereafter, we calculate the minimum and maximum values of ol and nl. The new rectangle created is the Client Rectangle, where the minimum value is added to X, and Y is left untouched. The Width is set to the difference of ma and mi, and the Height is left unaltered.

 

The difference between variables ma and mi is negligible. Correspondingly, the width of the invalidated region too is insignificantly small. Further, mi is used to decide the position at which the X of the black rectangle should commence. Thus, if we replace r1 with ClientRectangle, everything would be hunky dory, but the screen will flicker a great deal, while the mouse is in motion. This is because the same screen has to be displayed when the movement ceases. Thus, to thwart this flicker, we redraw only the specific part of the screen that has been invalidated.

 

ScrollBar Control

 

a.cs

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form

{

VScrollBar v;

HScrollBar h;

PictureBox p;

Label l2;

Label l1;

bool  d = false;

int   ox, oy;

float vm;

float vp;

float hm;

float hp;

public zzz()

{

ClientSize = new System.Drawing.Size(520, 277);

l1 = new Label();

l1.Location = new System.Drawing.Point(408, 160);

l2 = new Label();

l2.Location = new Point(408, 184);

v = new VScrollBar();

v.Location = new Point(200, 24);

v.Size = new Size(16, 152);

v.Scroll += new ScrollEventHandler(vScroll);       

v.Minimum = -100;

h = new HScrollBar();

h.Location = new Point(16, 176);

h.Size = new Size(184, 16);

h.Scroll += new ScrollEventHandler(hScroll);

h.Minimum = -100;

p = new PictureBox();

Bitmap b = new Bitmap("water.bmp");

p.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;

p.Location = new Point(64, 32);

p.Size = new Size(96, 96);

p.Image = (Image)b;

p.MouseDown += new MouseEventHandler(pDown);

p.MouseUp += new MouseEventHandler(pUp);

p.MouseMove += new MouseEventHandler(pMove);

Controls.Add(l2);

Controls.Add(l1);

Controls.Add(h);

Controls.Add(v);

Controls.Add(p);

v.LargeChange = 20;

h.LargeChange = 20;

v.SmallChange = 1;

h.SmallChange = 1;

vmf();

hmf();

l1.Text = h.Value.ToString();

l2.Text = v.Value.ToString();

p.Cursor = Cursors.SizeAll;

}

void vmf()

{

float hsb = (float)(v.Height - p.Height);

float ticks = (float)(v.Maximum - v.Minimum);

vm = hsb / ticks;

}

void hmf()

{

float hsb = (float)(h.Width - p.Width) ;

float ticks = (float)(h.Maximum - h.Minimum) ;

hm = hsb / ticks ;

}

void vScroll(object sender, ScrollEventArgs e)

{

l2.Text = v.Value.ToString() ;

vp = (float)(v.Value- v.Minimum);

p.Top = v.Bottom - (int)(vm * vp) - p.Height;

}

void hScroll(object sender, ScrollEventArgs e)

{

l1.Text = h.Value.ToString() ;

hp = (float)(h.Value - h.Minimum);

p.Left = h.Right - (int)(hm * hp) - p.Width;

}

void pDown(object s, MouseEventArgs e)

{

d = true;

ox = e.X;

oy = e.Y;

}

void pMove(object s, MouseEventArgs e)

{

if (d)

{

int minY = v.Minimum;

int maxY = v.Maximum;

int minX = h.Minimum;

int maxX = h.Maximum;

int value = (int)(v.Value - (e.Y - oy)/vm);

if (value < minY)

{

v.Value = minY;

}

else if (value > maxY - v.LargeChange + 1)

{

v.Value = maxY - v.LargeChange + 1;

}

else

{

v.Value = value;

}

value = (int)(h.Value - (e.X - ox)/hm);

if (value < minX)

{

h.Value = minX;

}

else if (value > maxX - h.LargeChange + 1)

{

h.Value = maxX - h.LargeChange + 1;

}

else

{

h.Value = value;

}

l1.Text = h.Value.ToString() ;

l2.Text = v.Value.ToString() ;

value = p.Top + (e.Y - oy);

if (value < v.Top)

{

value = v.Top;

}

else if (value > v.Bottom - p.Height)

{

value = v.Bottom - p.Height;

}

p.Top = value;

value = p.Left + (e.X - ox);

if (value < h.Left)

{

value = h.Left;

}

else if (value > h.Right - p.Width)

{

value = h.Right - p.Width;

}

p.Left = value;

}

}

void pUp(object s, MouseEventArgs e)

{

d = false;

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

>csc a.cs

 

Before running the program, copy the file water.bmp into the current directory. This file is provided along with the samples in the installation program. The singular methodology to comprehend large programs is by first examining their output, because the output will motivate you to grasp the program.

 

We see two scrollbars, one vertical and the other horizontal, with a picture within them. Clicking on the scrollbars will move the picture in the direction of the scrollbar that is clicked. Let us understand how this is achieved.

 

Screen 4.42

 

We start by examining the constructor of class zzz. The default client size is modified to the desired size. Then, there are two labels l1 and l2, which display the current values of the two scroll bars. A vertical scrollbar v is an instance of a class VScrollBar, and a horizontal scrollbar h is an instance of a class HScrollBar. As is customary, we provide a Location and a Size to the scrollbar. The Minimum and Maximum properties decide the range of values that the user can select. Most controls such as a Text box or a Combo box already have a scroll bar built into them. Hence, they do not require this control. The Scroll event gets triggered whenever the scroll button moves. The button can be moved either by using a mouse or by the keyboard. Moving the vertical scroll button will call the vScroll function, whereas moving the horizontal scroll button will call the hScroll function.

 

After setting the properties of the Scroll Bar, we create an object p, which is an instance of a PictureBox class. Next, we create a Bitmap object, and in the constructor, we pass the file water.bmp to it. The SizeMode decides on the display of the image in the Picture Box. The enum value of StretchImage stretches the image to fit it into the PictureBox, whereas, the enum value of Normal places it in the upper left corner.

 

The image property is assigned to the picture or the bitmap that is to be displayed. Three event handlers are attached to the image:

     pDown : This is called each time we click in the picture.

     pUp : This is called when we release the mouse button.

     pMove : This is called when we move the mouse within the picture.

 

Finally, all the controls i.e. two labels, two scroll bars and one image, are appended to the Control class.

 

The property of LargeChange in the scrollbar control decides on the magnitude of the change in the value of the scroll bar, when the scroll bar is clicked. The SmallChange property is associated with the arrows on the scroll bar. We have set the LargeChange property to 20 and the SmallChange to 1.

 

To dispel the banality of the somber explanation given above, let us digress slightly to present a small, albeit important, elucidation on how Windows handles the art of scrolling.

 

Screen 4.43

 

The value of the scrollbar ranges between the minimum and the maximum. The maximum value is LargeChange+1. The rationale behind this is that, the scrollbar has a property called Value, which represents the position of the top of the thumb. The size of the scrollbar's thumb is equal to the page value or LargeChange. When we reach the last page, or when the thumb reaches the end of the scroll bar, whereupon, we cannot scroll any further, the value property will always be shown less than the maximum.

 

We shall now beguile you with the concepts of the vertical scroll bars. The explanation for the horizontal scroll bars is much the same.

 

The function vmf, which is called from the constructor, initializes 3 variables. The Height of the Vertical scroll bar is 152 pixels because the Size property of the scrollbar is initialized to this value. The Height of the picture is 96 pixels because the Size property of the picture is initialized to this value. The difference between these two heights, which is 56 pixels, is stored in the variable hsb.  The variable named ticks stores a value, which is within a range that the vertical scroll bar can handle.

 

A Tick happens to be the smallest increment that a scrollbar can make. In our case, as the Minimum is -100 and the Maximum is 100, the range of value stored in ticks becomes 200. This variable represents the ticks that the scroll bar needs in order to move from one end to the other. In the same vein, the variable hsb denotes the amount of pixels the image needs to be relocated from one end of the scroll bar to another. This is calculated by subtracting the height of the image from the height of the scroll bar.

 

Screen 4.44

 

Dividing 56 (the number of pixels to be moved) by 200 (the total number of ticks available), gives us a value of .28. Thus, every tick moved by the scroll bar moves the image by .28 pixels. This value, known as the 'pixels per tick', is stored in vm. A similar routine is followed for the hmf function while implementing a horizontal scroll bar.

 

The labels initially display zero, since this is the default value stored in the property value. The cursor property of the picture is changed to SizeAll. As a result of this, whenever the cursor moves into the picture, it's shape changes into a four-headed monster.

 

At the outset, we want to move the picture downwards. So, we click on the vertical scroll bar. This movement calls the vScroll function. In this function, we initialize the Text property of the label l2 to the Value property of the vertical scroll bar v. Then, variable vp is calculated, whose value is the current value of the scroll bar, i.e. the value of the Minimum property of the scroll bar.

 

Every downward movement of the scroll bar increments the Value property by 1. This is because, the value of property SmallChange has been initialized to 1. Thus, the value of vp will commence at 100 and will keep escalating thereafter. The value stored in the Bottom property of the scroll bar is 176 and the Height of the picture is 96 pixels. Thus, the picture will start at 52 pixels, and then, the distance between the picture and the bottom property will start reducing.

 

The formula for computing the top position where the picture should be placed is as follows: the Bottom of the scroll bar, minus the pixels per tick, multiplied by the current value of the property Value, minus the Height of the picture, plus the value of Minimum.

 

Now, let us do the reverse i.e. let us move the picture around and see how the scroll bars behave. Also, let us observe the corresponding transformation in the values. To move the picture, we first have to click on it with the left mouse button. This calls function pDown. In this function, the value of variable d, which is a boolean, is set to true. Simultaneously, in the function pUp, the value of variable d is set to false.

The parameter e of MouseEventArgs has two members X and Y, which furnish the co-ordinates of the mouse pointer with regard to the picture, rather than the window or form. We save these values in the variables ox and oy, which will be utilized to calculate the distance that the picture has been dragged.

 

The function pMove is the focus of all attention since, this is where the real excitement action lies! We place all the code in a large if statement, which results in true when the variable d is true. This occurs only when the mouse button is depressed. Each time the function is called, the minimum and maximum values of the scroll bars are stored in 4 variables. This is futile, since the values always remain constant.

 

We calculate a variable called Value as follows:

The initial Y position of the mouse before its dragging commenced, minus the current Y position of the mouse. The result is then divided by the multiplier to convert the pixels into scroll bar ticks. This simulates the scroll bar scrolling in the opposite direction.

 

If the new value of variable Value is less than the minimum value allowed for the scroll bar, the property of the scroll bar v is set to the minimum possible. If the value is larger than the maximum permissible value, then it is initialized to the largest possible value. This concept has been explained in the small note we earlier presented on scrolling. If none of the above hold true, then we merely change the property Value of the scroll bar to the variable Value. These are simple error checks. The labels l1 and l2 are also updated.

The above explanation is also relevant for the horizontal scroll bar.

 

The property Top of the picture has to be updated to take into account the new position. This is also stored in the variable value and computed as follows:

The original Top property value, plus the current position of the mouse, minus the position of the mouse before the dragging commenced.

 

Like before, we make sure that we do not exceed the Top and Bottom limits of the scroll bar. The picture has to be contained within the scroll bars. If it is smaller than the Top property of the scroll bar, we change the value to that of the Top property. The same holds true for the Bottom. Finally, we initialize the Top property of the picture to Value and do the same for the Left property.

 

Up Down Control

 

a.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form {

DomainUpDown u;

NumericUpDown n;

DomainUpDown a;

public zzz() {

ClientSize = new Size(504, 352);

n = new NumericUpDown();

n.Location = new Point(132, 132);

n.Maximum = new System.Decimal(100d);

n.Minimum = new System.Decimal(0d);

n.DecimalPlaces = 2;

n.Text = "0.00";

Controls.Add(n);

a = new DomainUpDown();

a.Location = new Point(152, 32);

a.Size = new Size(120, 23);

a.SelectedItemChanged += new EventHandler(abc);

Controls.Add(a);

u = new DomainUpDown();

u.Location = new Point(152, 64);

u.SelectedItemChanged += new EventHandler(pqr);

Controls.Add(u);

a.Items.Add(new yyy("Center",(int) HorizontalAlignment.Center));

a.Items.Add(new yyy("Left",(int)HorizontalAlignment.Left));

a.Items.Add(new yyy("Right",(int)HorizontalAlignment.Right));

u.Items.Add(new yyy("Left",(int)LeftRightAlignment.Left));

u.Items.Add(new yyy("Right",(int)LeftRightAlignment.Right));

u.SelectedIndex = 1;

}

void pqr(object s, EventArgs e)

{

yyy c = (yyy)(u.Items[u.SelectedIndex]) ;

n.UpDownAlign = (LeftRightAlignment)(c.i);

}

void abc(object s, EventArgs e)

{

yyy c = (yyy)(a.Items[a.SelectedIndex]) ;

n.TextAlign = (HorizontalAlignment)(c.i);

}

public static void Main()

{

Application.Run(new zzz());

}

class yyy

{

public string s;

public int i;

public yyy(string sz, int n)

{

s=sz;

i=n;

}

public override string ToString()

{

return s;

}

}

}

 

Screen 4.45

 

In the above program, we have a NumericUpDown control called n. A NumericUpDown control is used when we have a single numeric value that  needs to be incremented or decremented.

 

Screen 4.46

 

This can be done by clicking on the Up and Down buttons of the control respectively. This control has a property called ReadOnly, which has a value of False by default.

 

This allows entry of a value directly in the control if the user finds it too bothersome to click on the buttons to do so. From this perspective, it operates akin to a text box. The properties Minimum and Maximum specify the minimum and maximum values that the control can accept. Under no circumstances will the control permit us to exceed the range specified by the above two properties. Thus, error checking has been built into the control.

 

A NumericUpDown control has a large number of properties such as DecimalPlaces, Hexadecimal, ThousandsSeparator etc. These properties format the value that is displayed using the Text property.

Their roles are as follows:

     The DecimalPlaces property controls the number of decimal places.

     The Hexadecimal property displays numbers in hexadecimal format.

     The ThousandsSeparator property decides on the character to be used to separate the 1000s. This is because the comma is not the universally accepted separator.

 

The Increment property of the control which has a default value of 1 decides on the amount of increase/decrease in the number. each time we click on the up or down buttons. n.Increment=10; will increase the number by 10. The two functions ParseEditText and UpdateEditText get called with every change in the number.

 

Thereafter, two DomainUpDown controls, which behave in a manner similar to a NumericUpDown control, are created. These controls display a string instead of a number. Thus, the value passed to this control can be of any class, since all classes are derived from object. The user can also type in text directly. The value typed in must obviously match an item in the collection. The ToString function of the object is called, to display the value in the up-down control.

 

The DomainUpDown control has a property called Items, that returns a DomainUpDown.DomainUpDownItems object. This object represents the object collection. Thus, we can use the Add or Remove methods from the above collection to add or remove the items individually. The Sort property sorts the collection. We pass an object like yyy to the Add function, which accepts a string and an enum called HorizontalAlignment. The enum value is stored in the variable i to facilitate its retrieval at a later stage.

 

Each time we click on the control, function abc gets called. This is because the SelectedItemChanged event has been initialized to this function. In this function, we use the property SelectedIndex, which returns a number depicting the item that has been selected. The property is used as an array index. It returns the yyy object at that index. We store this object in c and then, access the variable i stored in the object yyy.

 

Thus, the entire object yyy is stored as part of the collection. The string and the int can both be accessed together. If the SelectedIndex property is not set, it will not display any value in the control.

 

DateTimePicker Control

 

a.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form {

DateTimePicker d;

public zzz() {

ClientSize = new Size(504, 293);

d = new DateTimePicker();

d.Location = new Point(24, 24);

d.CalendarFont = new Font("Times New Roman", 8f);

d.Size = new Size(200, 20);

d.CalendarForeColor = System.Drawing.SystemColors.WindowText;

d.ShowCheckBox = true;

d.ForeColor = System.Drawing.SystemColors.WindowText;

d.Format = System.Windows.Forms.DateTimePickerFormat.Custom;

d.BackColor = System.Drawing.SystemColors.Window;

d.CustomFormat = "\'Date : \'yy MM d - HH\':\'mm\':\'s ddd";

d.Anchor = AnchorStyles.Top|AnchorStyles.Right | AnchorStyles.Left;

DateTime now = DateTime.Now;

d.Value = now;

Controls.Add(d);

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

Screen 4.47

Screen 4.48

 

On running the above program, we see a listbox displaying a date. This date is the system date. You may wonder as to what is the big deal about displaying a simple date. But, when you click on the down arrow of the list box, a fine-looking calendar springs up! The current date is shown highlighted in red. If you click on any date, it will be instantly displayed in the list box. Once the date has been chosen, the calendar vanishes. You can change the month by clicking on the arrows, which are on the left and right of the month.

 

Now, let us get behind the scenes and unravel the mysteries of this program. We first create an object d as an instance of class DateTimePicker, thus encapsulating the standard Windows date time picker control. The control has all the standard properties like Location, Size, Color etc. The default size, in the case of the DateTimePicker, is a width of 200 pixels and a height of 23 pixels. The colors of the control can be changed using CalendarForeColor and ForeColor. The property CalendarFont decides on the font to be used to display the dates.

 

Screen 4.49

 

The ShowCheckBox property , which has the default value of false, is set to true, in order to display a check box on the extreme left of the date displayed in the control. The checkbox presently is checked. 

 

If the checkbox is checked, the date is valid. If not, the date is said to be unset.

 

The Format property of the control, which is initialized to Custom, decides the display format of the date and time in the control. This property can be initialized to any four of the following enums: Custom, Short, Long and Time. The last three values use the operating system's format options.

 

Since the Format property has been given the value of Custom, the property CustomFormat decides on the display format of the date in the control. This property is initialized as follows: The word Date:, followed by the year, month and day separated by a space, followed by a minus sign and finally, the time. The three ‘ddd’ represent the day of the week in words.

 

The Now property of the DateTime class, that returns the current day, is finally assigned to the Value property of the picker class, which then gets displayed in the control.

 

LinkLabel Control:

 

a.cs

using System;

using System.Windows.Forms;

using System.Drawing;

public class zzz : Form {

PropertyGrid pg;

LinkLabel l;

Panel p;

GroupBox g;

public zzz() {

l = new LinkLabel();

l.DisabledLinkColor = (Color)System.Drawing.Color.Blue;

l.ForeColor = (Color)System.Drawing.Color.Gainsboro;

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

l.BackColor = (Color)System.Drawing.Color.Transparent;

l.LinkArea = new LinkArea(13, 28);

l.Font = new System.Drawing.Font("Tahoma", 12f, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.World);

l.Text = "please click on sonal to see a message box";

l.Size = new System.Drawing.Size(136, 96);

l.LinkClicked += new LinkLabelLinkClickedEventHandler(abc);

ClientSize = new System.Drawing.Size(504, 445);

pg = new PropertyGrid();

pg.Dock = System.Windows.Forms.DockStyle.Fill;

pg.Location = new System.Drawing.Point(3, 16);

pg.CommandsVisibleIfAvailable = true;

pg.Text = "propertyGrid1";

pg.Size = new System.Drawing.Size(242, 405);

pg.SelectedObject = l ;

g = new GroupBox();

g.Location = new System.Drawing.Point(248, 16);

g.Anchor = AnchorStyles.Top|AnchorStyles.Right | AnchorStyles.Left;

g.Text = "LinkLabel Properties";

g.Size = new System.Drawing.Size(248, 424);

g.Controls.Add(pg);

Controls.Add(g);

p = new Panel();

p.Size = new System.Drawing.Size(200, 320);

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

p.BackgroundImage = (Bitmap) new Bitmap("hikingboot.bmp");

p.Controls.Add(l);

Controls.Add(p);

}

void abc(object sender, LinkLabelLinkClickedEventArgs  e)

{

MessageBox.Show("hi") ;

l.LinkVisited = true ;

}

public static void Main() {

Application.Run(new zzz());

}

}

 

To avoid any exception from being thrown, you should copy the file hikingboot.bmp to the current directory. Though the above program is not very sizeable, its achievements are substantial. The output of this program shows our screen divided into two parts as follows:

 

(a) The left pane has a picture in the background with some text that behaves like a hyperlink. Clicking on the hyperlink displays a message box with the message 'hi' and changes the color of the hyper link.

 

Screen 4.50

 

(b) On the right hand side, we come across a large number of properties. When we click on the plus sign in front of the font property, yet more properties get displayed. On varying the Font, the font of the text displayed in the window on the left side changes. You can experiment with the other properties too.

 

Thus, you can achieve a lot without writing tons of code!

 

l is an instance of LinkLabel, which relates to text that can be displayed as a hyper link. The DisabledLinkColor property of LinkLabel decides the color of the hyperlink when it is disabled. The ForeColor and the BackColor specify the foreground and the background color of the control respectively.

 

We may not intend to display the entire string as a hyperlink. So, we use the LinkArea property to ascertain the text that is to be hyper linked. The default position begins at 0,0.

 

The GraphicsUnit enumeration in the Font property specifies a unit of measurement. The value displayed uses 1/75 of an inch, since the unit of measure selected is World. We could also use any of the other 6 values in the enum, such as pixel, millimeter etc.

 

Screen 4.51

 

The Text property is the most significant property, as it decides the text to be displayed. The LinkClicked is the event property that will call function abc each time we click on the link. In this function, we display a Message Box and change the LinkVisited property to True, which results in a change in the color of the hyper link. You may observe that this LinkLabel instance l is not added to the form. Next, we create an instance of a PropertyGrid class and store it in pg.

 

The Dock property decides on the edge of the container that this property will be docked to. The usage of the Fill style allows docking of the property on all sides.

 

The property CommandsVisibleIfAvailable displays the command pane only for those objects that expose verbs. As always, the Text property is initialized to some text that does not get displayed. The property of SelectedObject is assigned the LinkLabel control, thereby linking it into the grid and facilitating browsing of the LinkLable control properties. This property also allows us to browse multiple objects. Finally, we add this object to the Form, thereby indirectly adding the hyperlink also.

 

A group box is merely an anthology of other controls. The Group box control is the one that contains the PropertyGrid object. We also create an instance of a Panel object, which, like a GroupBox class, contains other controls. If the Enabled property in the panels is set to False, all the controls within it will be disabled. The panel control, by default, is drawn without any Borders. The BorderStyle property provides us with two-dimensional or three-dimensional borders, to distinguish a panel from other areas of the form. The Panel class can also contain scrollbars.

 

The Panel class has a property called BackgroundImage that selects the picture to be displayed in the panel. This Panel class has the LinkLabel added to it, to which we add the Panel. Thus, we have two controls that have been added directly to the form, the Panel that has the LinkLabel and the GroupBox that has the PropertyGrid.

 

ListBox Control

 

a.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form

{

sss p;

ListBox l;

Button b;

ColorDialog c;

ImageList i;

public zzz()

{

b = new Button();

b.Location = new System.Drawing.Point(16, 200);

b.Text = "Color";

b.Size = new System.Drawing.Size(75, 23);

b.Click += new EventHandler(abc);

Controls.Add(b);

c = new ColorDialog();

p = new sss();

p.Location = new System.Drawing.Point(64, 90);

p.Size = new System.Drawing.Size(64, 168);

Controls.Add(p);

i = new ImageList();

i.ImageSize = new Size(24, 22);

i.Images.Add(new Bitmap("club.bmp"));

i.Images.Add(new Bitmap("diamond.bmp"));

i.Images.Add(new Bitmap("heart.bmp"));

i.Images.Add(new Bitmap("spade.bmp"));

Size = new System.Drawing.Size(512, 320);

l = new ListBox();

l.ForeColor = (Color)System.Drawing.SystemColors.WindowText;

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

l.IntegralHeight = false;

l.Size = new System.Drawing.Size(232, 60);

l.ColumnWidth = 144;

l.SelectedIndexChanged += new EventHandler(pqr);

l.Items.AddRange (new object[] {"a1", "a2", "a3", "a4"});

l.SelectionMode = SelectionMode.MultiSimple;

Controls.Add(l);

}

void abc(object sender, EventArgs e)

{

if (c.ShowDialog() == DialogResult.OK)

{

l.ForeColor = c.Color;

}

}

void pqr(object sender, EventArgs e)

{

p.ci();

int[] se = new int[l.SelectedIndices.Count];

l.SelectedIndices.CopyTo(se, 0);

for (int i=0; i<se.Length; i++)

{

int ind = se[i];

object it = l.Items[ind];

string s = it.ToString();

Image im = aaa(s);

p.ai(im);

}

p.Invalidate();

}

Image aaa(string b)

{

if (b.Equals("a1"))

{

return i.Images[0];

}

else if (b.Equals("a2"))

{

return i.Images[1];

}

else if (b.Equals("a3"))

{

return i.Images[2];

}

else if (b.Equals("a4"))

{

return i.Images[3];

}

else

{

return null;

}

}

public static void Main()

{

Application.Run(new zzz());

}

}

public class sss :  Panel

{

Image[] i = new Image[4];

int Cnt=0;

public virtual void ai(Image img)

{

i[Cnt++] = img;

}

protected override void OnPaint(PaintEventArgs pe)

{

base.OnPaint(pe);

for (int j=0; j< Cnt; j++)

{

pe.Graphics.DrawImage(i[j], new System.Drawing.Point(0, 30 * j + 5));

}

}

public virtual void ci()

{

Cnt = 0;

}

}

 

Before running the executable file, the 4 files with the 'bmp' extension must be copied from one of the sample directories into the current directory.

 

Screen 4.52

 

Screen 4.53

 

When we run the above program, we see a list box with 4 values and a button labeled 'Color'. When we click on the button, it displays a color dialog box, which permits us to choose from amongst a plethora of colors. We can select a color and then, click on the OK button. As a result, the color of the list box items changes to the selected color. We are also allowed to create our own custom colors in the dialog box.

 

Screen 4.54

Screen 4.55

 

Each time we select an item from the list box, an image representing the item is displayed. If we reselect an item that we had selected earlier, the currently selected image gets replaced by the latest selected one.

 

In the program, a Button b is created. It calls function abc whenever it is clicked. Then, an instance c of class ColorDialog is created. We have created the class sss. It is derived from the class Panel. It has a member i, which is an array of 4 images and an int variable named Cnt. The variable Cnt is used as an index to access the images in the array, and to store the count of the number of images. When we add the sss object to our Form, nothing gets displayed in the window. This is because, there are no controls in the panel.

 

Next, we create an ImageList object called i. This class stores a collection of images that can be used by other controls such as the Toolbar or ListView. Bitmaps or Icons are added to this class so that they are available for exploitation by other controls. Images is a property of type ImageList.ImageCollection, whose Add method is used to add a bitmap to the collection. We add a total of 4 bitmaps to our ImageList object.

 

The ListBox object l has a property ColumnWidth, that decides on the width, in pixels, of each column in the ListBox. In a multi-column ListBox, the ColumnWidth property refers to the width of each column. A value of zero is connotative of the fact that each column has a default width, taking into account the data displayed in them. The property IntegralHeight decides whether partial items can be displayed or not. If the value is set to true, then only complete items are displayed. In this case, the ListBox will be resized to ensure that partial items are not displayed.

 

Each time we select an item, the Event SelectedIndexChanged gets kick started. This event, in turn, calls function pqr. Every list box has a property called Items, which is a collection of items displayed in the list box. The data type of Items is ListBox.ObjectCollection. It has a method called AddRange, which accepts an array of Child Controls present in the list, sorted on the index. Since we want the list box to display the text a1, a2, a3 and a4, we initialize the object array to this array of strings.

 

The property SelectionMode decides on the number of items that can be chosen or selected concomitantly.

 

Four options available:

     None : This means that no items can be selected. In effect, it disables the ListBox.

     One : This allows us to select only one item at a time,

     MultiSimple : This lets us choose multiple items at the same time

     MultiExtended : This enables us to choose multiple items at a time and allows us to use keys like SHIFT and CONTROL combinations to select the multiple options.

 

We finally add the list box to the Form. Clicking on the button labeled Color calls ShowDialog from the ColorDialog class, which displays a dialog box with a zillion colors. Until we click on the OK button or the Cancel button, we cannot leave the dialog box.

 

The button returns one of two values:

     DialogResult.OK if we click on OK button.

     DialogResult.CANCEL if we click on the Cancel button.

 

If the user opts to click on the OK button, the color selected is assigned to the ForeColor property of the ListBox.

The function pqr is the vortex of action. The function is called when any item is selected or unselected from the list box. We first call function ci from the class sss, which initializes the variable Cnt to zero.

 

The SelectedIndices property returns a collection object named ListBox.SelectedIndicesCollection, which lists all the items that are currently selected in the list box. This is so because we can select multiple items from the list box. First, an array called se of type int is created, depending on the number of items selected.  Then, the selected indices are copied to the array. The variable ind in the for statement refers to each index selected. The Items array returns an object that stores the value of each index. As we need the string representation of the selected item, function ToString is used to convert the object into a string. This facilitates the possibility of having a list box containing pictures.

 

We then call the function aaa that accepts a string representing the ListBox item selected, which can be either a1, a2, a3 or a4. It returns an image representing the string. It then checks the value of the string passed and returns an image stored in the ImageList object. The function ai merely uses the variable Cnt to index the array of images with a new picture. Thus, if we select three images, the array i will contain three pictures. When the for loop terminates, we call the function Invalidate.

 

As we had learnt earlier, function OnPaint displays all the pictures. This is achieved using the Cnt variable that contains the number of images stored in the Array. Thus, we use the ImageList class to store the images and the array of images in the sss class to store the images that need to be displayed each time.

 

StatusBar control

 

a.cs

using System;

using System.Drawing;

using System.Windows.Forms;   

public class zzz : Form {

System.ComponentModel.Container c;

StatusBar s;

StatusBarPanel s1;

StatusBarPanel s2;

StatusBarPanel s3;

Timer t;

public zzz()  {

c = new System.ComponentModel.Container();

s2 = new StatusBarPanel();

t = new Timer(c);

s = new StatusBar();

s3 = new StatusBarPanel();

s1 = new StatusBarPanel();

s2.AutoSize = (StatusBarPanelAutoSize)2;

s2.Alignment = HorizontalAlignment.Right;

s2.Width = 76;

s3.AutoSize = StatusBarPanelAutoSize.Contents;

s3.Width = 20;

s1.BorderStyle = StatusBarPanelBorderStyle.None;

s1.Icon = new Icon("status.ico");

s1.AutoSize = StatusBarPanelAutoSize.Contents;

s1.Width = 62;

s1.Text = "sonal";

Size = new System.Drawing.Size(512, 320);

KeyUp += new KeyEventHandler(abc);

t.Interval = 1000;

t.Enabled = true;

t.Tick += new EventHandler(pqr);

s.Size = new Size(212, 20);

s.Location = new Point(0, 216);

s.BackColor = (Color)SystemColors.Control;

s.Text = "Vijay Mukhi";

s.ShowPanels = true;

s.Panels.AddRange((StatusBarPanel[])new StatusBarPanel[] {s1, s2, s3});

Controls.Add(s);

s3.Text = "OVR";

}

void abc(object se, KeyEventArgs e)

{

if (e.KeyCode == Keys.Insert)

{

string s = this.s3.Text;

if (s.Equals("INS"))

s3.Text = "OVR";

else

s3.Text = "INS";

}

}

void pqr(object se, EventArgs e)

{

DateTime t = DateTime.Now;

string s = t.ToLongTimeString() ;

s2.Text = s ;

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

Every Windows application displays a StatusBar control. This program is going to introduce the status bar in our window. In order to accomplish this, the file status.ico needs to be copied from one of the sample directories into the current directory.

 

The above program merely presents a blank window with a status bar at the bottom. The status bar has the following contents:

     an icon.

     the word 'sonal'.

     the time ticking away in the middle.

     the status of the Insert key.

 

Screen 4.56

Screen 4.57

 

Each time we use the insert key, the text toggles between INS and OVR.

 

The class Container encapsulates zero to one or more components. We create three status bar objects s1, s2 and s3, which are instances of class StatusBarPanel. This class, in turn, is derived from class Component. It stores the StatusBar control panel information. The object s is an instance of class StatusBar, which represents a Window status bar control. This control has no panels by default.

 

The property AutoSize in the StatusBarPanel class regulates the changes occurring in the panel of a status bar, whenever the status bar is resized. The values are obtained from the enumeration StatusBarPanelAutoSize that has the following three values:

     Contents: As is evident from the name, the contents of the status bar decide how its size will change.

     None: The status bar panel does not change whenever the status bar is resized.

     Spring: The panel shares the available space with all other panels having a setting of Spring, after yielding space to the panels having either the Contents or None settings.

 

Text can be aligned in a status bar panel in 3 ways with reference to the status bar, i.e. Left, Right and Center. The Alignment property, by default, has a value of Left. For the status bar panel s2, we have set it to Right. The default width is 100 pixels for a status bar panel. An Icon can be specified along with the text that is displayed. To do so, we initialize the Icon property to a .ico file. An .ico file is a small transparent bitmap image.

 

The KeyUp Event is fired whenever a key is released in the focused control. In our case, function pqr is called. The Timer class, that implements a Windows timer, merely activates an event at a particular time interval. This timer, which can be used in a window, is designed for a single threaded environment only, where User Interface threads are being executed. The Interval property decides the time, in milliseconds, between timer ticks. The Enabled property sets the timer On. The garbage collector does not interfere with the timer while it is running. The Event Tick will call the function pqr whenever the time interval set in the timer elapses. In our case, it is occurs after one second.

 

The StatusBar is made up of panels. The ShowPanels property displays all the panels that are added using the AddRange method, i.e. s1, s2 and s3. Finally, the text of the last panel is set to OVR and the StatusBar object is added to the form.

 

The function abc gets called each time we press a key in our form. This function is called with the parameter e of KeyEventArgs, which has a member KeyCode, which contains a number corresponding to the key pressed. As it is difficult to remember the numbers assigned to every key, the enumeration Keys is used to represent the keys. If the Insert key is pressed, the current Text displayed in the status bar panel s3 is retrieved and the value gets toggled from OVR to INS.

 

On completion of the timer interval, we use the Now property of the DateTime class to provide us with the current time. This is then supplied to s2.Text, which updates the status bar panel with the current time every second. What you can display in a status bar is limited by your imagination. It is common to display the status of keys and the time on the status bar.

 

Tab Control

 

a.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form {

GroupBox g2;

GroupBox g1;

ImageList i;

TabPage t1;

TabPage t2;

TabControl t;

public zzz()

{

ClientSize = new Size(546, 293);

g1 = new GroupBox();

g1.Location = new Point(12, 16);

g1.Text = "Sonal";

g1.Size = new Size(202, 144);

g2 = new GroupBox();

g2.Location = new Point(12, 16);

g2.Text = "Vijay mukhi";

g2.Size = new Size(202, 128);

i = new ImageList();

t1 = new TabPage();

t2 = new TabPage();

t = new TabControl();

t1.Text = "Mukhi";

t1.Size = new Size(224, 193);

t1.ImageIndex = 0;

t1.TabIndex = 0;

t2.Text = "Vijay";

t2.ImageIndex = 1;

t2.TabIndex = 1;

t.Location = new Point(24, 32);

t.Size = new Size(232, 220);

t.SelectedIndex = 0;

t.ImageList = i;

i.Images.Add((Bitmap)new Bitmap("calendar.bmp"));

i.Images.Add((Bitmap)new Bitmap("note.bmp"));

t.ImageList = i;

Controls.Add(t);

t1.Controls.Add(g1);

t2.Controls.Add(g2);

t.Controls.Add(t1);

t.Controls.Add(t2);

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

This program requires two bitmaps called note.bmp and calendar.bmp.

 

We start by creating two GroupBox controls named g1 and g2, and change the properties of Locations, Size and Text. The ImageList i stores a list of images. We next create two TabPage objects t1 and t2. A TabPage class implements a single page of the TabControl class. It is a panel class having the properties of a TabItem.

 

Screen 4.58

Screen 4.59

 

The string assigned to the Text property is displayed as the tab page text. We can set it to a certain size. The ImageIndex property is an index into the ImageList list object, which is associated with the TabControl. The TabPage objects are added to the TabControl using the Controls collection. The ImageList property is initialized to the list of images denoted by the ImageList class. Thus, the ImageIndex member decides on the image that will be displayed along with the Text in the TabControl.

 

A TabControl shows a list of TabPages. Clicking on the tab activates them. A TabControl, in other words, is like a series of Dialog boxes containing controls, organized in a logical fashion. The controls that would earlier have appeared in one large dialog box, are now placed in separate dialog boxes.

 

As mentioned earlier, we add the GroupBox to the Control collection of the individual TabPage objects t1 and t2. The controls that we require are added to the GroupBox. The GroupBox, in turn, gets added to the TabPage. From then on, the TabControl takes over and we can flick between TabPages with ease.

 

ToolTip Control

 

a.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form {

System.ComponentModel.Container c;

ToolTip t;

PictureBox p1;

PictureBox p2;

public zzz()

{

c = new System.ComponentModel.Container();

t = new ToolTip(c);

t.Active = true;

t.ShowAlways = true;

t.AutomaticDelay = 100;

t.AutoPopDelay = 100;

t.InitialDelay = 100;

t.ReshowDelay = 100;

p1 = new PictureBox();

p2 = new PictureBox();

Size = new Size(512, 300);

p1.Location = new Point(8, 7);

p1.Size = new Size(20, 20);

p1.Image = new Bitmap("open.bmp");

t.SetToolTip(p1, "vijay");

Controls.Add(p1);

p2.Location = new Point(28, 7);

p2.Size = new Size(20, 20);

p2.Image = new Bitmap("new.bmp");

t.SetToolTip(p2, "mukhi");

Controls.Add(p2);

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

All of us need help at some point in time. The ToolTip control is just the antidote for all maladies. Before running the executable, copy the two bitmaps open.bmp and new.bmp to the current directory.These two bitmaps will be displayed as two separate images on our form. Whenever we move our cursor close to them, a yellow colored help text comes into sight.

 

A ToolTip class gives us a small window that contains a single line of text that describes what a control stands for. The real productivity of Bitmaps and Icons can be fully harnessed only if we are able to discern their functionality. The Active property, if set to false, disables the ToolTip control. The ShowAlways property will show a ToolTip despite the fact that the control it is assigned to, is currently disabled.

 

Screen 4.60

Screen 4.61

 

The following four properties have been set for the Tooltip control:

     AutomaticDelay: It denotes the time in milliseconds that elapses before the tool tip appears. The default is 500 milliseconds.

     AutoPopDelay: It is the time in milliseconds for which the ToolTip remains visible to the user while the mouse is stationary on the control. Its default value is 10 times the value of the property AutomaticDelay.

     InitialDelay: It is the time in milliseconds for which the mouse must remain fixed within the control, before the tool tip becomes visible again. The default value is equal to that of property AutomaticDelay.

     ReshowDelay: It is the time in milliseconds that elapses, as the mouse moves from one control to another. The default is 1/5 of the property AutomaticDelay.

 

Any amendment made to the property AutomaticDelay has an impact on the above properties. Thus, it is advisable to first change the AutomaticDelay property, followed by the other properties.

 

Two PictureBox controls have been added in our window. We add these two pictures to the form. This class can display all types of images, including bitmaps, icons, JPEG, GIF and many more. The Image property decides on the image that gets displayed. By default, the image has no borders and can be clipped too.

The ToolTip class has a method called SetToolTip, which accepts a control as the first parameter and the text of the tool tip as the second. Henceforth, whenever the mouse is positioned on that control, the tool tip will be displayed.

 

TrackBar Control

 

a.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class zzz : Form

{

TrackBar t;

Label l;

public zzz() {

Size = new Size(512, 320);

t = new TrackBar();

t.Location = new Point(8, 24);

t.Size = new Size(200, 42);

t.ValueChanged += new EventHandler(abc);

t.TickFrequency = 5;

t.Minimum = 0;

t.Maximum = 100;

t.SmallChange = 5;

t.LargeChange = 25;

t.Orientation = Orientation.Horizontal;

t.TickStyle = TickStyle.Both;

l = new Label();

l.Location = new Point(112, 192);

l.Text = t.Value.ToString();

Controls.Add(t);

Controls.Add(l);

}

void abc(object source, EventArgs e)

{

l.Text = t.Value.ToString();

}

public static void Main() {

Application.Run(new zzz());

}

}

 

The TrackBar class resembles a scroll bar in many respects, but its interaction with the user is distinct. We can configure values that the TrackBar represents and also define increments for off-button clicks. The TrackBar can be positioned horizontally or vertically. The number of ticks that are displayed can also be configured.

 

Each time that the value of the TrackBar changes, the Event property ValueChanged calls the function abc. The property TickFrequency specifies the number of ticks that will be drawn. The lower limit of the range of the TrackBar is confined to a value decided by the Minimum property, which in our case is 0. The upper limit of the range of the TrackBar is confined to a value determined by the Maximum property, which in our case is 100.

 

Screen 4.62

Screen 4.63

 

We may not want to draw 100 ticks to represent all the values. So, we specify a value such as 5 as the TickFrequency. This will draw a tick mark at a spacing of 5, resulting in a total of 20 ticks. Thus, each tick will represent 5 units of the TrackBar's range of values.

 

The property SmallChange decides on the magnitude of change that occurs in the TrackBar, whenever the user presses the Up arrow key or the Down arrow key to move the TrackBar thumb. The value assigned to the LargeChange property is used whenever we click on the side, or use the Page Up and Page Down keys.

 

The Orientation property can take only two values i.e. horizontal or vertical. The TickStyle property uses an enumeration that can have four values:

     None: for no tick marks.

     Both: for tick marks on both sides of the control.

     BottomRight: for tick marks at the bottom for a horizontal control, and on the right for a vertical control .

     TopLeft: this is the reverse of BottomRight.

 

We then use a label to display the value of the track bar on the form. This value is stored in the property Value of the label class. Finally, we add the label and TrackBar. Function abc merely updates the label to depict the current position of the TrackBar.

 

Tree-Node Control

 

a.cs

using System;

using System.IO;

using System.Resources;  

using System.ComponentModel;

using System.Drawing;

using System.Windows.Forms;

using System.Runtime.InteropServices;

public class zzz : Form

{

TreeView d;

ImageList i;

public zzz()

{

ClientSize = new System.Drawing.Size(502, 293);

i = new ImageList();

i.Images.Add(new Bitmap("clsdfold.bmp"));

i.Images.Add(new Bitmap("openfold.bmp"));

d = new TreeView();

d.ImageList = (ImageList)i;

d.ForeColor = (Color)System.Drawing.SystemColors.WindowText;

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

d.AllowDrop = true;

d.Indent = 19;

d.Text = "treeView1";

d.SelectedImageIndex = 1;

d.Size = new System.Drawing.Size(200, 264);

d.AfterSelect += new TreeViewEventHandler(sss);

d.BeforeExpand += new TreeViewCancelEventHandler(eee);

Controls.Add(d);

string[] dr = Environment.GetLogicalDrives();

for (int ii = 0; ii < dr.Length; ii++)

{

if (PlatformInvokeKernel32.GetDriveType(dr[ii]) == PlatformInvokeKernel32.DRIVE_FIXED)

{

TreeNode c = new TreeNode(dr[ii]);

d.Nodes.Add(c);

ddd(c);

}

}

}

void ddd(TreeNode n)

{

DirectoryInfo dir = new DirectoryInfo(ppp(n));

DirectoryInfo[] e = dir.GetDirectories ();

for (int i = 0; i < e.Length; i++)

{

string na = e[i].Name;   

if (!na.Equals(".") && !na.Equals(".."))

{

n.Nodes.Add(new TreeNode(na));

}

}

}

 void sss(object source, TreeViewEventArgs e)

{

Text = "Windows.Forms File Explorer - " + e.Node.Text;

}

 void eee(object source, TreeViewCancelEventArgs e)

{

TreeNode n = (TreeNode)e.Node;

for (int i = 0; i < n.Nodes.Count; i++)

{

ddd(n.Nodes[i]);

}

}

string ppp(TreeNode node)

{

if (node.Parent == null)

{

return node.Text;

}

return Path.Combine(ppp(node.Parent), node.Text);

}

[STAThread]

public static void Main(string[] args) {

Application.Run(new zzz());

}

}

  public class PlatformInvokeKernel32

    {  

        [DllImport("KERNEL32", CharSet=System.Runtime.InteropServices.CharSet.Auto)]

        public static extern int GetDriveType(string lpRootPathName); 

        public const int DRIVE_FIXED = 3;

    }

 

Copy the two bmp files clsdfold.bmp and openfold.bmp required for this program, from the samples directory into your current directory.

 

Here, we first create a TreeView control called d. A TreeView displays a list of items or nodes in a hierarchical manner. A node consists of a caption and a bitmap; the bitmap is optional. The user can select a node and collapse it or expand it by clicking on the plus or minus sign, displayed alongside, respectively.

 

We have an ImageList control i that represents two images. We initialize the ImageList property of the TreeView control to the Image List. Thus, the TreeView can now use the images stored in the ImageList. The AllowDrop property facilitates the use of the control for events and Drag and Drop operations.

 

The property Indent decides the indent in the pixels. The SelectedImageIndex determines as to which picture is to be used from the ImageList when the user selects an item in the control.

There are a large number of events that get triggered off, but we are capturing only the following two:

     AfterSelect, that calls function sss

     BeforeExapnd, that calls functions eee.

 

The EventHandler functions use different delegate types. 

The class Environment has a static function GetLogicalDrives that returns an array of strings, which denotes the logical drives on our machine. If machine has three drives, C:\, D:\and E:\, the for loop is repeated thrice.

 

Using the Platform invoke API, we can call any code in any dll.  So, we first call the external function GetDriveType and compare it with the value retrieved for DRIVE_FIXED, which is 3 in this case.

If there are 3 fixed drives then the if statement is true for 3 of them, resulting in the display of 3 nodes only. If you have only one fixed drive i.e. C, you will see only one node on your screen.

 

Screen 4.64

Screen 4.65

 

A TreeNode object is created, which is used in the TreeView. The constructor is passed the drive name to enable it to be displayed as a label for the node. A TreeView is made up of Nodes. It has a property called Nodes, which is of type TreeNodeCollection. Using the Add member of the collection, we add the tree node. Thereafter, function ddd is called with this newly created and empty tree node object.

 

Function ppp is called with an empty tree node. If the node has no parent, the text associated with the node is returned. But if a parent exists, we use the static function Combine, of the Path class, to combine the two strings together. The Combine function calls the ppp function recursively.

 

To begin with, the value of ppp(n) is C:\. The object dir represents directories in the C drive. To obtain all the directories present on the drive, we call function GetDirectories, which returns an array of DirectoryInfo objects. The Name member returns the name of the directory. If the return value is not a . or .., we add the directory name to the list of Nodes.

 

In the AfterSelect event, function sss gets called. In this function, we change the Text of the form to the node selected. This is possible because the node label is passed as a parameter to TreeViewEventArgs. The function eee recovers the current active node from the parameter passed to it. Using the Count property, we calculate the number of nodes contained in the active node and thereafter, iterate through each of these nodes. The same function ddd is called with every node.

 

This is how we can display the sub-directories contained within directories and the files displayed within the directories. Data is sent to the control, which merely returns the same data in a tree like form. When we click on the first plus (+) sign of C:\, the function ppp(n) returns the entire path name of each directory in a recursive fashion.

 

Docking

 

a.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class zzz :Form {

Panel p1;

Panel p2;

GroupBox g;

Button b;

RadioButton r1;  

RadioButton r2;   

RadioButton r3;  

RadioButton r4;

RadioButton r5; 

RadioButton r6;   

RadioButton rs;

Splitter s;

public zzz () {

g = new GroupBox();

rs = new RadioButton();

r5 = new RadioButton();

s = new Splitter();

b = new Button();

r6 = new RadioButton();

r4 = new RadioButton();

r1 = new RadioButton();

r3 = new RadioButton();

p1 = new Panel();

p2 = new Panel();

r2 = new RadioButton();

Location = new Point(100, 100);

SizeGripStyle = SizeGripStyle.Show;

ClientSize = new Size(448, 400);

g.Location = new Point(16, 152);

g.TabStop = false;

g.Text = "&Dock";

g.Size = new Size(88, 176);

s.BackColor = Color.Blue;

s.Dock = DockStyle.Right;

s.Location = new Point(325, 0);

s.Size = new Size(3, 400);

b.BackColor = SystemColors.Control;

b.FlatStyle = FlatStyle.Popup;

b.Anchor = AnchorStyles.None;

b.Text = "Demo Button";

rs.Text = "rs";

rs.Size = new Size(100, 23);

r5.Location = new Point(8, 120);

r5.Text = "&Right";

r5.Size = new Size(72, 24);

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

r6.Location = new Point(8, 144);

r6.Text = "&Fill";

r6.Size = new Size(72, 24);

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

r4.Location = new Point(8, 96);

r4.Text = "&Bottom";

r4.Size = new Size(72, 24);

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

r1.Checked = true;

r1.Location = new Point(8, 24);

r1.Text = "&None";

r1.Size = new Size(72, 24);

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

r3.Location = new Point(8, 72);

r3.Text = "&Left";

r3.Size = new Size(72, 24);

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

p1.Text = "ButtonPanel";

p1.Size = new Size(325, 400);

p1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;

p1.Dock = DockStyle.Fill;

p1.BackColor = Color.Green;

p2.Location = new Point(328, 0);

p2.Text = "ControlsPanel";

p2.Size = new Size(120, 400);

p2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;

p2.Dock = DockStyle.Right;

r2.Location = new Point(8, 48);

r2.Text = "&Top";

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

Controls.Add(p2);

Controls.Add(s);

Controls.Add(p1);

p1.Controls.Add(b);

p2.Controls.Add(g);

g.Controls.Add(r4);

g.Controls.Add(r3);

g.Controls.Add(r1);

g.Controls.Add(r5);

g.Controls.Add(r6);

g.Controls.Add(r2);

rs = r1;

aaa();

}

void aaa() {

if (rs == r1)

b.Dock = DockStyle.None;

else if (rs == r2)

b.Dock = DockStyle.Top;

else if (rs == r3)

b.Dock = DockStyle.Left;

else if (rs == r4)

b.Dock = DockStyle.Bottom;

else if (rs == r5)

b.Dock = DockStyle.Right;

else

b.Dock = DockStyle.Fill;

}

protected void abc(object s, EventArgs e)

{

rs = (RadioButton)s;

aaa();

}

public static void Main() {

Application.Run(new zzz());

}

}

 

This program may be generously proportioned, however it is very simple to comprehend. We have created seven radio buttons, one GroupBox, two panels, one splitter and one button. The SizeGripStyle property can have three values:

     Show: that always shows the sizing grip.

     Hide : that always hides the sizing grip.

     Auto : that shows and hides the sizing grip as necessary.

 

The sizing handle is always shown at the right hand corner of the form.

 

The button has a FlatStyle of Popup that determines the appearance of the button. Clicking on any of the Radio Buttons results in a call to the function abc. This is as a consequence of the Event property Click having been set to abc. Next, we add all the controls to the Form. The repetitive code for adding the controls results in a lengthy program.

 

Screen 4.66

Screen 4.67

 

The function abc gets called. Its first parameter is an object type. The object stands for the control that calls the function. In function abc, the RadioButton that was clicked on, is represented by s. The value contained in s is stored in another RadioButton called rs. The function aaa is called thereafter. It compares the value contained in rs with the 5 radio buttons r1 to r5. Depending upon the value that matches, the Dock property of the button changes.

 

Screen 4.68

Screen 4.69

 

When a control is docked to the edge of a container, it will always take up a position flush against the edge, if the control is resized.

 

The Left, Right, Top and Bottom styles position the control towards the respective edge of the container. Fill will fill upto all the sides of the control.

 

The control is resized to fill the container's edges. The First dock style named None, ensures that the control does not get docked at all.