![]()
4
Controls
Windows Forms or Winforms is a
contemporary Windows-based forms package that endows the Windows programmer
with an innovative methodology for creating aesthetic user interfaces and
interactive applications. We will not ramble on about the pros and cons of the
package, but commence the creation of the smallest GUI (Graphical User
Interface) application.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
System.Windows.Forms.Application.Run(z);
}
}
Run the compiler as
>csc a.cs
Compiler Error
a.cs(6,1): error CS1502: The best overloaded method match for
'System.Windows.Forms.Application.Run(System.Windows.Forms.Form)' has some invalid arguments
a.cs(6,38): error CS1503: Argument '1': cannot convert from 'zzz' to 'System.Windows.Forms.Form'
An error is generated because,
Run, which is a static function in the Application class of the
System.Windows.Forms namespace, requires a Form object. The error distinctly
states its inability to convert a zzz to System.Windows.Forms.Form, which
proves that, an object that looks like Form is mandatory here, and not zzz.
a.cs
using System.Windows.Forms;
public class zzz : Form
{
public static void Main()
{
Application.Run(new zzz());
}
}
|
|
|
Screen 4.1 |
This program is not very
dissimilar from the previous one. The using keyword is employed to avoid the
inevitability of writing namespace with every object. The object z has no
efficacy here, since we are passing the zzz object directly to the Run
function. As the class zzz is derived from Form, no error is generated.
When we run the program, a small
blank window is displayed. You can click on the 'x' symbol to close it. The
output is substantial enough for a single line of code.
a.cs
using System.Windows.Forms;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
zzz()
{
Text = "Vijay Mukhi";
}
}
|
|
|
Screen 4.2 |
In the constructor of the zzz
class, we have initialized a member called Text to the string value 'Vijay
Mukhi'. When we run the program, to our amazement, our window, which earlier
was without a title, now possesses the title 'Vijay Mukhi'.
This is the introductory concept
of Windows Forms programming. The class called Form has abundant properties
such as Text etc., which have specific relevance in a window. Any modifications
to the properties get reflected immediately in the window. The changes depend
upon the properties that we modify. In this case, the property of Text changes
the Caption, text displayed in the title bar.
A Form represents a window
displayed by an application. An application can have different types of
windows, such as a standard, tool bar, borderless or floating window. The Form
class is versatile enough to handle all the above types of windows, as it is
derived from innumerable classes. A Dialog Box, which is used to accept input
from the user, is available in two modes viz. modal and modeless. Our
trustworthy Form class can also handle such Dialog boxes with equal aplomb. We
normally use the Form class as the preliminary class for building WinForms
Applications.
The Main method calls the Run
function and gives it the Form object. In the constructor, we can modify the
properties of the Form Class to give the window a desired appearance. Since these properties are not static, they
cannot be altered in Main, but can be modified in the constructor or in any
other function.
a.cs
using System.Windows.Forms;
public class zzz
{
public static void Main()
{
Application.Run(new yyy());
}
}
class yyy : Form
{
public yyy()
{
Text = "Vijay Mukhi";
}
}
|
|
|
Screen 4.3 |
In this program, we have created
another class yyy that derives from the Form class. We have used this object as
a parameter to the function Run. The rules do not impel us to derive the class
zzz from Form. However, in all our programs, we shall follow the first approach
since we have decided to steer clear of controversy and stick to the rules.
a.cs
using System.Windows.Forms;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
zzz()
{
ClientSize = new System.Drawing.Size(300,600);
Size = new System.Drawing.Size(100,200);
}
}
|
|
|
Screen 4.4 |
The above example sets two
properties of the Form class. The first, which is called ClientSize, is used by
Windows.Forms to decide how large our initial window would be. This property
has a default value, which can be overwritten by specifying the width and
height. As we need to furnish two values, we use a class called Size in the
namespace System.Drawing, which accepts two values. This class does not insist
on receiving meaningful values. The constructor is passed the width and height
of the desired window in pixels.
A graphics screen is divided
into small dots or pixels. Depending upon the configuration of the monitor and
graphics card, a computer can handle and display a certain number of pixels and
colors. The higher the configuration, the larger are the number of pixels and
colors that are available.
The size of the client area of
the form is computed as the size of the form minus the borders and the title
bar placed by Windows. They are not of our concern, since we shall be placing
our own controls in our form. ClientSize is a property with a default value,
and it gets updated automatically whenever the form is resized.
The next property is Size. The
user enjoys the flexibility of altering the size of the window at run time.
Size is initialized in manner similar to ClientSize.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form
{
Button b;
public static void Main()
{
Application.Run(new zzz());
}
zzz()
{
b = new Button();
Controls.Add(b);
}
}
|
|
|
Screen 4.5 |
We now see a small button at the
top left hand corner of our Window. How did we create this button? To do so, we
first, create an object b, that looks like a Button class.
The Form class has a large
number of properties such as ClientSize, Size etc. One of them is called
Controls, which is a read-only property since it contains only a Get. This
property returns a Control.Collection object, whose Add function adds the
control to the Client area of the window. We shall be sprucing up our button
shortly.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form
{
Button b;
public static void Main()
{
Application.Run(new zzz());
}
zzz()
{
b = new Button();
b.Location = new Point(100,200);
b.Size = new Size(100,50);
b.Text = "Vijay Mukhi is smart";
Controls.Add(b);
}
}
|
|
|
Screen 4.6 |
Anything that is placed on a
form is called a Control or a Widget. Similar to a Form, a button control,
popularly known as a command button, has numerous properties. One of them is
the Location property, which decides the position on the Client area where the
button will be positioned.
Here, we use the Point class and
not Size, even though both are objects that represent two numbers. By
convention, a Size object represents a width and a height and a Point object
has an x and y co-ordinate system, starting from the upper left corner.
Most properties have a default
value. Since this fact about default values has been reiterated numerous times,
we shall not repeat it again. The Size property determines the initial size of
the window and the string assigned to the Text property is displayed on the
button.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form
{
Button b;
public static void Main() {
Application.Run(new zzz());
}
zzz()
{
b = new Button();
b.Location = new Point(100,200);
b.Size = new Size(100,50);
b.Text = "Vijay Mukhi is smart";
b.Click += new System.EventHandler(abc);
Controls.Add(b);
}
public void abc(object s, System.EventArgs e)
{
MessageBox.Show("Hi");
}
}
|
|
|
Screen 4.7 |
In the earlier example, clicking
on the button was an exercise in futility because the button did not achieve
anything. After augmenting the code of the program, when we click on the
button, we see a MessageBox that displays the greeting 'Hi'. The rationale
behind a button control is that, when we click on it, some code should get
executed, some action should take
place.
The button class has an event
object called Click, which accepts an object of type EventHandler. The syntax
for events uses the += symbol to add a function that is to be called when the
event handler gets activated. The function name is given through the
EventHandler delegate. This delegate has been specially created only to handle
events that a control will generate.
Thus, the function abc, which is
passed as a parameter to the EventHandler delegate, must have a certain
signature. The first parameter is the generic object that could represent any
entity identifying the caller. The second parameter is an EventArgs object,
which we will explain shortly. Thus, each time we click on the button, the
function abc gets called. This function in turn calls the static function Show
from the MessageBox class to display 'Hi'.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form {
Button b;
public static void Main()
{
Application.Run(new zzz());
}
zzz()
{
b = new Button();
b.Location = new Point(100,200);
b.Size = new Size(100,50);
b.Text = "Vijay Mukhi is smart";
b.Click += new System.EventHandler(abc);
b.Click += new System.EventHandler(pqr);
Controls.Add(b);
}
public void abc(object s, System.EventArgs e){
MessageBox.Show("Hi");
}
public void pqr(object s, System.EventArgs e)
{
MessageBox.Show("Bye");
}
}
|
|
|
Screen 4.8 |
This program reveals the
veritable power of events and delegates. Two functions, abc and pqr, are called
whenever the button is clicked. To achieve this, all that we need to do in the
code is to call the Click event again, using the += symbol, followed by the
name of the new function. The -= symbol is used if we change our minds. This is
a type safe way of calling code in response to an event.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form {
Button b;
TextBox t;
public static void Main() {
Application.Run(new zzz());
}
zzz()
{
b = new Button();
b.Location = new Point(100,200);
b.Size = new Size(100,50);
b.Text = "Vijay Mukhi is smart";
b.Click += new System.EventHandler(abc);
t = new TextBox();
t.Text = "Hell";
t.Location = new Point(10,20);
Controls.Add(b);
Controls.Add(t);
}
public void abc(object s, System.EventArgs e)
{
MessageBox.Show(t.Text + " " + ClientSize );
}
}
|
|
|
Screen 4.9 |
In the Forms Window, we now see
two controls: a Button and a TextBox object that lets us enter some text. The
textbox widget also has a large number of properties associated with it. We
shall not be repeating this obvious fact for all the other controls. The
properties Location and Size work in a similar manner when used with any
Control, but the property Text differs, depending upon the object in use. For a
button, it represents the caption, whereas for a text box, it represents the
text that is entered. Thus, some of the properties play different roles when
used in different controls.
|
|
|
Screen 4.10 |
Each time the button is clicked,
we would like to display the text that has been entered by the user in the text
box. In the eventhandler function abc, the property Text reveals the text
entered into the textbox. The
MessageBox class is used to display the value, along with the size of the
client area.
You can change the size of the
window or change the contents of the text box and observe the contents of the
MessageBox changing dynamically.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
public override void Dispose()
{
base.Dispose();
MessageBox.Show( "hi " + ClientSize );
}
}
This program displays our
ability to invoke code at a specific point in time, which in this case, is at
the stage when the user closes the window or when the application quits out. It
is akin to fulfilling the last wishes of the program.
|
|
|
Screen 4.11 |
As the application is quitting,
it calls a function called Dispose. So, if you ever want code to be called at
the point when an application is about to quit out, you must place it in the
Dispose function. This code could be used to close files or do anything else
that the programmer desires.
It is not mandatory to call
Dispose of the base class, but it is always a good programming practice to call
the base class function first, and then augment it with your own code. In this
particular case, it is inconsequential, but under different circumstances,
things may go out of hand if this advice is not heeded.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form {
public static void Main()
{
Application.Run(new zzz());
}
Brush b; int ii = 0;
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
b = new SolidBrush(Color.Blue);
ii++;
g.DrawString("Vijay Mukhi " + ii, Font, b ,1,25);
}
}
In this program, we are
overriding a function called OnPaint, which is present in the Form class. The
OnPaint function gets called each time the window has to be redrawn. Therefore,
all code that is to be written to the screen must be written in this function.
This code cannot be placed anywhere else in the program.
Our next endeavor is to create
an object that has functions which display text on the screen or draw a
picture. The class that contains these display functions is called Graphics.
Thus, we create an object g that looks like Graphics. As an object that looks
like Graphics cannot be instantiated, WinForms provides us with an object of
type PaintEventArgs with the OnPaint Function. This class contains members
required for graphical display. Hence, g is initialized to the Graphics member
in e.
As mentioned earlier, OnPaint
gets called whenever our window has to be redrawn. Whenever OnPaint gets
called, it creates an object that looks like PaintEventArgs and then passes it
as a parameter to the function. This object has a member called Graphics, which
contains functions used for drawing in our client area. The DrawString function
requires the text that is to be displayed and its Font.
The Form class provides us with
the object called Font. Thereafter, the text color, or to be more precise, the
brush is to be specified. Here, we want a solid Brush like object. So, we
create an object b, and give it a color in which the text should be displayed.
There is a static object Blue in the class Color that stands for the color
blue. The spelling of 'color' is as per the American usage. Finally, the x and
y co-ordinates on the screen are specified.
This positions the text in the
window at these specified co-ordinates.
Thus, the function has a total
of 5 parameters:-
• The text to be displayed.
• The font in which the text is to be displayed.
• The text color or the brush.
• The x co-ordinate.
• The y co-ordinate.
Here, we have specified certain
values, but every time we use DrawString, we can conveniently specify different
values for these parameters. Thus, the second DrawString function can display
different text and use a different font or brush. As the system does not have a
default brush or font, we call it a Stateless Model.
|
|
|
Screen 4.12 |
Along with 'Vijay Mukhi', we
have used a variable called ii, which has been initialized to 0. In the OnPaint
function, we increment this variable by 1. Before the window is displayed,
function OnPaint gets called. Thereafter, OnPaint gets called whenever the
'minimize' and 'maximize' buttons of the window are clicked.
The function OnPaint gets called
whenever our client area has to be redrawn due to any action carried out by the
user. This function has to be marked with the modifier named protected. This is
because the original function in the Form class is tagged with this modifier.
We can override a function of the base class, provided we do not change any of
the modifiers. By making OnPaint protected, only derived classes can use the
OnPaint function.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form {
public static void Main()
{
Application.Run(new zzz());
}
Brush b; int ii = 0;
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
b = new SolidBrush(Color.Blue);
ii++;
g.DrawString("Vijay Mukhi " + ii, Font, b ,1,25);
RectangleF r = new RectangleF(20, 60, 100, 25);
g.FillRectangle(new SolidBrush(Color.Gainsboro), r);
g.DrawString("Sonal Mukhi", Font, new SolidBrush(Color.Red), r);
StringFormat f = new StringFormat();
f.Alignment=StringAlignment.Center;
RectangleF r1 = new RectangleF(20, 100, 100, 25);
g.DrawString("Sonal Mukhi", Font, new SolidBrush(Color.Black), r1,f);
g.RotateTransform(-30);
g.TranslateTransform(0, 100);
g.DrawString("vijay mukhi", Font, new SolidBrush(Color.Orange), 20, 40);
g.ResetTransform();
}
}
|
|
|
Screen 4.13 |
The output of this program is a
window with text displayed haphazardly. This output is nothing to write home
about, but is useful in elucidating numerous concepts.
A rectangleF structure stores
two point objects i.e. it specifies a rectangular area of the window. We start
at one corner, where the x and y co-ordinates are 20 and 60, and the opposite
corner where the x and y co-ordinates are 100 and 25 respectively. The function
FillRectangle from the Graphics class is used to create and fill the above
rectangular portion of the screen with the color Gainsboro. The DrawString
function is overloaded to take not only x and y as the last two parameters, but
also a rectangular area into which it will draw a string.
We would now like to center the
above string in the rectangular area. This is easier said than done, because,
it entails creation of an object that looks like StringFormat with the
Alignment property set as Center. The documentation specifies many more options
that can be implemented. The StringFormat object is passed as the last
parameter to the DrawString function, resulting in the string being shown as
centered, instead of being Left aligned, which is the default setting.
If we want to rotate the image
by 30 degrees, we just have to call a function named RotateTransform from the
Graphics class and pass as a parameter, the amount of rotation that is
required. You can then watch the image get displayed at the specified angle.
Beware, too acute an angle may sprain your neck! The next function, named
TranslateTransform, is optional. It is used to move the text around in the
client area horizontally or vertically. Whenever we transform something, it
stays in the transformed position. But thereafter, if we do not want the other
objects to be in this form, we need to use the function ResetTranform to undo
the transform. However, it is optional.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
Brush b;
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
b = new SolidBrush(Color.FromArgb(180, Color.Black));
RectangleF r = new RectangleF(20, 20, 50, 50);
g.FillRectangle(b, r);
}
}
|
|
|
Screen 4.14 |
The topic of Brushes is so
exhaustive that a thesis can well be written on it. In this program, we use a
special brush to fill up a rectangular area on our screen.
Here, we specify not only a
color, but also a number, which is the alpha value and has a range from 0 to
255. The larger the value, the darker will be the color. To put it technically,
the larger the value, the lesser will be the translucence and vice-versa.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
Brush b;
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
b = new SolidBrush(Color.Black);
Font f = new Font("Times New Roman", 30);
g.DrawString("Vijay Mukhi " , f , b ,1,25);
}
}
|
|
|
Screen 4.15 |
In this program, we will shed
light on Fonts. When you read a newspaper or magazine, the style of the letters
looks different in each of them. This difference is due to the Font or the
Typeface used. There are numerous fonts in the world of letters.
While displaying text, we can be
very specific about the way in which the letters look. To enhance their visual
appeal, we create an object that looks like Font. Then, in the constructor, the
Name of the font is specified along with the Size in points. Remember that 72
points make an inch. Thus my name, Vijay Mukhi, now gets displayed in a size
that is bigger than normal.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Image i;
i = new Bitmap("sample.jpg");
g.DrawImage(i, 29, 20, 283, 212);
}
}
|
|
|
Screen 4.16 |
The above program merely
displays an image. A file with a jpg or a gif extension contains images or
pictures. To display images, we use a class called Image that can recognize
pictures. Even though i is an image object, we initialize it to an object that
looks like Bitmap. An Image class is an abstract class and the class Bitmap
derives from it.
An Image class could represent a
picture, which is not just an image, but could also be a cursor, icon etc. The
DrawImage function accepts an image object as the first parameter, followed by
the screen co-ordinates at which the image has to be positioned. The above .jpg
file is part of the samples offered while installing the .NET SDK. So, search
for the file and copy it to the current working directory. Like the text
sample, this picture can also be rotated, transformed etc.
a.cs
using System.Windows.Forms;
using System.Drawing;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Image i = new Bitmap("colorbars.jpg");
Brush b = new TextureBrush(i);
g.DrawString("Vijay Mukhi is very smart" , Font, b ,1,25);
}
}
|
|
|
Screen 4.17 |
By combining a Brush and an
image, we can create a multicolor brush. In one of the earlier programs, we had
used a Solid brush. Here, we are using a Texture brush. This brush fills the
interiors of a shape with a picture.
Thus, the text gets reflected in
a brush, which reminds us of a rainbow. You can enhance the aesthetic appeal of
your applications by using this facility.
a.cs
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public class zzz: Form {
public static void Main()
{
Application.Run(new zzz());
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen p = new Pen(Color.FromArgb(150, Color.Purple), 20);
p.DashStyle = DashStyle.Dash;
p.StartCap = LineCap.Round;
Point [] pp = new Point[] {new Point(200, 140),new Point(700, 240),new Point(500, 340)};
g.DrawCurve(p,pp);
}
}
|
|
|
Screen 4.18 |
The above program introduces
freehand drawing. A pen is like an artist's brush, which is used to draw any
shape that permeates the mind. In our program, we commence by creating a Pen object
p. It is initialized to a particular alpha color using FromArgb function from
the Color class, and to a specified width.
The constructor can also be
provided with other parameters, such as a brush. A pen is used to draw lines
and curves.
A Pen can also draw a line of a
specified width and style. The default DashStyle is Continuous. If we change
the DashStyle to Dash, the starting point becomes a rounded edge. The default
is a Straight Edge. The line drawn by a pen is very versatile, and can employ a
variety of fill styles, colors and textures. The DrawCurve function paints a
pen object that specifies how to draw a curve. It has an array of points with
the individual three point objects specifying where the curved line should be
drawn.
a.cs
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public class zzz: Form {
public static void Main() {
Application.Run(new zzz());
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Image i= new Bitmap("BoilingPoint.jpg");
Brush pb = new TextureBrush(i);
Pen p= new Pen(pb, 75);
g.DrawLine(p,1,5,150,200);
}
}
|
|
|
Screen 4.19 |
We can use a brush that looks
like an image and create a pen that will draw lines in the garb of a picture.
The DrawLine function accepts two sets of numbers, the x-y co-ordinates of the
starting point and the x-y co-ordinates of the ending point. It draws a thick
line joining these two points. Thus, we can use this function to draw any
possible shape.
a.cs
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
HatchBrush b = new HatchBrush(HatchStyle.ForwardDiagonal, Color.Green, Color.FromArgb(100, Color.Yellow));
g.FillEllipse(b, 250, 10, 100, 100);
Rectangle r = new Rectangle(300, 250, 100, 100);
LinearGradientBrush lb = new LinearGradientBrush(r, Color.Red, Color.Yellow,LinearGradientMode.BackwardDiagonal);
g.FillRectangle(lb, r);
}
}
|
|
|
Screen 4.20 |
On maximizing the screen, we see
two figures; one is a filled circle, while the other is a rectangular block. We
are equipped with a large number of brushes akin to those in the artistic
world. One of them is a HatchBrush. The constructor of HatchBrush accepts a
hatch style and two colors, viz. a background color and a foreground color.
The first parameter is the hatch
style, which can be one of six possible hatch styles. The foreground color, in
this case, green, defines the color of the lines to be drawn and the background
color defines the color for the gaps between the lines.
The FillEllipse function in
Graphics fills up the shape to display the effect of the brush. We could have
used the Rectangle function also, but as we are trying to be as akin as
possible to the samples provided by Microsoft, we have used the Ellipse
function.
A LinearGradientBrush can
represent color gradients and multi-color gradients. A gradient represents a
transformation from one color to another. A linear gradient is defined
alongside a line that is specified by the width of a rectangle or by any two
points. Thus, a two-color gradient will commence with a starting color and
conclude with the ending color. The blend from one color to the next can be
customized. First, we specify the object that is to be colored, which is a
rectangle in this case. The gradient starts with the left corner and ends at
the lower right corner. Thereafter, we follow with the starting color followed by
the ending color. Finally, the angle measured in degrees in the clockwise
direction is mentioned, starting from the x-axis. This defines the orientation
of the gradient. You can change the angle and witness the spectacular effects.
Menus
a.cs
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
MainMenu m;
public zzz()
{
m = new MainMenu();
MenuItem mi= m.MenuItems.Add("&File");
Menu = m;
}
}
|
|
|
Screen 4.21 |
Let us now build a menu. We have
created an object m, which symbolizes the MainMenu. MainMenu is called a
control and represents the menu structure for a Form. It is the root of the
menu.
A menu consists of various menu
items, which are displayed horizontally across the menu. We want to create a
menu item that displays the word 'File For this, we need another class called
MenuItem. A MenuItem can represent either an individual menu item depicting a
command, or it can cascade to another popup of menu items.
MenuItems is a read-only
property in MainMenu that gives a reference to all the MenuItems currently
available in the menu. We have none so far. This CollectionObject also has a
function called Add, which is used to add menu items. To do so, the text of the
item that is to be displayed must be stated as the parameter to the Add
function. We can also remove any menu item that has been previously added.
The variable mi stores the
MenuItem object returned by the Add function. Thereafter, Menu, which is an
object of type MainMenu, available in Form, is initialized to the menu that we
desire. The appearance of the menu depends upon the menu object stored in
Menu.
When we run the program, we see
the word File displayed in the top left corner. At this stage, nothing happens
when we click on it. On pressing the Alt key, F is displayed as underlined
since the symbol & underlines the character it is preceded with.
a.cs
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public class zzz: Form {
public static void Main() {
Application.Run(new zzz());
}
MainMenu m;
public zzz() {
m = new MainMenu();
MenuItem mi= m.MenuItems.Add("&File");
mi.MenuItems.Add("Hi");
mi.MenuItems.Add("-");
mi.MenuItems.Add("Bye");
Menu = m;
} }
|
|
|
Screen 4.22 |
Now things look more visually
attractive. When we click on File or use the accelerator Alt-F, a menu pops up
with the word 'Hi', followed by a separator and then finally by the word 'Bye'.
A separator is used to logically
group menus together. However, when we click on 'hi' or 'bye', nothing happens.
This situation needs to be redressed, since a menu should activate some code.
a.cs
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
MainMenu m;
public zzz()
{
m = new MainMenu();
MenuItem mi= m.MenuItems.Add("&File");
MenuItem m1;
m1 = new MenuItem("Hi", new System.EventHandler(abc), Shortcut.CtrlF11);
mi.MenuItems.Add(m1);
mi.MenuItems.Add("Bye");
Menu = m;
}
void abc(object sender, System.EventArgs e)
{
MessageBox.Show("hell");
}
}
|
|
Screen 4.24 |
|
Screen 4.23 |
|
Now, whether you either click on
File and then on the word 'Hi', or you press Control+F11, you will see a
message box with the word "hell" displayed in it.
The MenuItem constructor is
overloaded. The first parameter is the text to be displayed. The second
parameter is a delegate that encompasses the function to be called whenever
this menu item is activated. Just as the pen is mightier than the sword, under
some circumstances, the keyboard is certainly mightier than the mouse. At
times, it is faster to use a keyboard shortcut, instead of using the mouse.
Thus, the last parameter is the keyboard shortcut key, which is part of an
enumerator. This MenuItem object is passed to the Add function, that either
accepts a string or a MenuItem object.
a.cs
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
MainMenu m;
public zzz()
{
m = new MainMenu();
MenuItem mi= m.MenuItems.Add("&File");
MenuItem a = new MenuItem("One",new System.EventHandler(abc));
MenuItem b = new MenuItem("two",new System.EventHandler(abc));
mi.MenuItems.Add("hell",(new MenuItem[]{ a, b })
);
Menu = m;
}
void abc(object sender, System.EventArgs e)
{
MessageBox.Show("hell");
}
}
|
|
Screen 4.26 |
|
Screen 4.25 |
|
Here, we have a popup within a
popup. When you click on File, you will see the word 'hell' displayed. You will
also see an arrow pointing to the right, along with the menu item. If you move
the mouse over the arrow, a popup is displayed, containing the two menu items
'one' and 'two'. If we click on them, a message box with the word 'hell' gets
displayed.
In the program, with a single
statement, we have created two menu items, a and b, followed by an array of
menu items. This array is then passed as the last parameter to the Add
function. Thus, all the menus become sub-menus. In this case, the event handler
is associated with the submenu options, since clicking on the menu item
displays the sub-menu.
a.cs
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
MainMenu m;
public zzz()
{
m = new MainMenu();
MenuItem mi= m.MenuItems.Add("&File");
mi.MenuItems.Add( "hell",new System.EventHandler(abc));
mi.MenuItems.Add( "Bye",new System.EventHandler(abc));
Menu = m;
}
void abc(object s, System.EventArgs e)
{
MenuItem m = (MenuItem) s;
if ( m.Checked)
m.Checked = false;
else
m.Checked = true;
}
}
|
|
|
|
Screen 4.27 |
Screen 4.28 |
We add two menu items, 'hell'
and 'Bye' to our File menu and assign the same function abc to handle their
events. Clicking on any one of the menu options results in a call to the
function abc. This function takes two parameters. The first parameter s,
represents the menu item that was clicked on. If the first menu option 'hell'
is clicked, then the parameter s is not an object, but a menu item representing
'hell' and vice versa.
Every MenuItem has an option
called Checked, which if True, will display a tick mark on the menu item. Thus,
you can Check or Uncheck a menu option by clicking on it. You may click on each
menu option to observe this effect.
a.cs
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
public class zzz: Form
{
public static void Main()
{
Application.Run(new zzz());
}
MainMenu m;
public zzz()
{
m = new MainMenu();
MenuItem mi= m.MenuItems.Add("&File");
mi.MenuItems.Add( "hell",new System.EventHandler(abc));
mi.MenuItems.Add( "Bye",new System.EventHandler(abc));
Label l = new Label();
ContextMenu lm;
lm = new ContextMenu();
l.ContextMenu = lm;
l.Text = "Vijay Mukhi";
lm.MenuItems.Add(mi.CloneMenu());
Controls.Add(l );
Menu = m;
}
void abc(object s, System.EventArgs e)
{
MenuItem m = (MenuItem) s ;
if ( m.Checked)
m.Checked = false;
else
m.Checked = true;
}
}
The program output will display
the same menu - File, as seen in the earlier program. The text Vijay Mukhi will
also be visible. If you place the mouse on this text and right click the mouse,
you will see the same menu as seen with the File option.
|
|
|
|
Screen 4.29 |
Screen 4.30 |
This is called a Context
Sensitive Menu. These two menus, however, are different. If you Check a menu
option in this menu by clicking on it, it does not carry the tick mark to the
other menu.
We first create two objects:
• The first is lm, which looks like a ContextMenu.
• The second is l, which looks like a label.
|
|
|
Screen 4.31 |
Every label has a member called
ContextMenu, wherein we can specify a Context Sensitive menu. This member is
initialized to lm. As we have already created a menu item mi, we can reuse this
menu item.
However, a menu item cannot be used twice. Hence, calling the function CloneMenu off MenuItem creates a clone. This clone menu is then passed to the Add function of MenuItems in the ContextMenu.
Writing
Controls
Let us start by creating the
simplest control that money can buy. We create the following files:
c.cs
using System.Windows.Forms;
using System.Drawing;
public class yyy : Control
{
}
h.cs
using System.Windows.Forms;
public class zzz : System.Windows.Forms.Form
{
yyy a;
public zzz()
{
a = new yyy();
Controls.Add(a);
}
public static void Main() {
Application.Run(new zzz());
}
}
a.bat
del *.exe
del *.dll
csc.exe /t:library c.cs
csc.exe /r:c.dll h.cs
h
The file c.cs contains our very
first custom control. In order to create our own user-defined control, we
create a class yyy and derive it from the Control class. The Control class
implements the basic code required by classes to implement the behavior of a
control or a widget. This code can handle user input with a keyboard or a
pointing device, such as a mouse. Message handling and security features are
also supported.
At the end of the day, all
controls are merely child windows. The Control class defines the area or bounds
of a control along with the fonts, colors and images. This class allows
painting, context menus and anchoring with docking behavior.
Earlier, we had displayed scores
of controls in our containers, written by the Microsoft developers. All these
controls were derived from the Control class.
|
|
|
Screen 4.32 |
Thus, a user control like yyy is
an instance of a Control class, which is added to the Form using the Add
function off the Controls collection. It can't get any simpler. On running the
executable, we see no output. Yet, since no error was generated, we presume
that all went well.
The major difference between the
Microsoft controls and our controls is, the file in which the code for the
control is finally placed. We have placed our control code in assembly c.dll,
whereas, Microsoft controls are placed in System.Windows.Forms.dll.
c.cs
using System.Windows.Forms;
using System.Drawing;
public class yyy : Control
{
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawString(Text,Font, new SolidBrush(ForeColor), ClientRectangle);
}
}
h.cs
using System.Windows.Forms;
public class zzz : System.Windows.Forms.Form{
yyy a;
public zzz() {
a = new yyy();
a.Size = new System.Drawing.Size(600, 450);
a.Text = "Vijay Mukhi";
Controls.Add(a);
}
public static void Main() {
Application.Run(new zzz());
}
}
|
|
|
Screen 4.33 |
In the above example, we have
overridden a function called OnPaint in the Control class. This function gets
called whenever a control is to be redrawn on the screen. It is passed a
PaintEventArgs object as a parameter, from where we summon the DrawString
function to paint a string in a specified font and color, at a particular
location. The first parameter, Text, is a property, which refers to the string
to be displayed. The string 'Vijay Mukhi' is presently displayed in the window.
In the container h.cs, we have
initialized the property Text contained in the Control class to 'Vijay Mukhi'.
The Size property is also initialized, so that our control has a specific size
in the container.
h.cs
using System.Drawing;
using System.Windows.Forms;
public class zzz : Form {
Button b;
ccc c;
public zzz() {
b = new Button();
c = new ccc();
b.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
b.DialogResult = System.Windows.Forms.DialogResult.OK;
b.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
b.Size = new System.Drawing.Size(96, 24);
b.Text = "&Save";
b.Location = new System.Drawing.Point(8, 328);
b.Click += new System.EventHandler(abc);
Text = "Sonal Mukhi";
AcceptButton = b;
ClientSize = new System.Drawing.Size(400, 373);
c.Anchor=AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
c.AutoScrollMinSize = new System.Drawing.Size(0, 0);
c.Size = new System.Drawing.Size(400, 310);
c.Text = "Vijay Mukhi";
Controls.Add(b);
Controls.Add(c);
c.cust = ddd.rrr();
Size = new Size(400, (373 + SystemInformation.CaptionHeight));
}
void abc(object sender, System.EventArgs e)
{
c.aaa();
MessageBox.Show("vijay "+ c.cust);
}
public static void Main(string[] args)
{
Application.Run(new zzz());
}
}
c.cs
using System;
using System.Windows.Forms;
using System.Drawing;
public class ccc : UserControl
{
TextBox t;
TextBox ID;
Label l;
ddd c;
public ccc()
{
t = new TextBox();
l = new Label();
ID = new TextBox();
Text = "Vijay Mukhi";
Size = new System.Drawing.Size(384, 304);
t.Size = new System.Drawing.Size(88, 20);
t.Location = new System.Drawing.Point(88, 70);
l.Size = new System.Drawing.Size(64, 16);
l.Location = new System.Drawing.Point(8, 32);
l.Text = "ID:";
ID.ReadOnly = true;
ID.Size = new System.Drawing.Size(200, 20);
ID.Location = new System.Drawing.Point(88, 30);
ID.Enabled = false;
Controls.Add(t);
Controls.Add(ID);
Controls.Add(l);
}
public ddd cust
{
get
{
return c;
}
set
{
c=value;
ID.Text = c.ID;
t.Text = c.ti;
}
}
public void aaa()
{
c.ti = t.Text;
}
}
cc.cs
using System;
using System.ComponentModel;
using System.IO;
public class ddd : Component
{
string i ;
string t ;
public static ddd rrr()
{
ddd c = new ddd("111");
c.ti = "Vijay";
return c;
}
internal ddd(string s): base()
{
i = s ;
}
public string ID
{
get
{
return i ;
}
}
public string ti
{
get
{
return t ;
}
set
{
t = value ;
}
}
public override string ToString()
{
StringWriter sb = new StringWriter() ;
sb.WriteLine("Sonal \n");
sb.WriteLine(i);
sb.Write(t);
return sb.ToString();
}
}
a.bat
del *.exe
del *.dll
csc.exe /t:library /out:c.dll c.cs cc.cs
csc.exe /R:c.dll h.cs
h
This program is a rather
protracted one. As usual, we start with the container in h.cs. In the zzz
constructor, we first create a button b and an object c that is an instance of
our user control ccc. The control is present in the assembly c.dll. What this
class presently does is not significant. We begin by initializing a large
number of properties in the button.
The Anchor property decides as
to which edges of the control are to be anchored with the edges of the
container. Here, we have chosen the Bottom edge.
The DialogResult property is the
value that is returned to the parent form when we click on the button. The
value returned is OK.
The FlatStyle property belongs
to the ButtonBase class and is one of the numerous properties that influence
the flat style appearance of the button. Knowledge of GUI programming implies
cognizance and comprehension about all the facets of making your application more
comely and pleasing to the eye. The Size, Text and Location properties were
explained earlier.
Each time we click on the
button, the function abc gets called. The Text property decides on the title of
the windows. The AcceptButton property requires an object that represents a
button. Every form has a feature, which associates the Enter key with a button.
The resultant effect is that pressing the Enter key on the keyboard simulates a
click on the associated button. Thus, in the above form, pressing Enter or
clicking on the button would result in a call to the function abc. The
ClientSize property decides the size of the windows.
Our User Control too can
initialize properties since they belong to the Control class. In the program,
we have set the Anchor, AutoScrollMinSize, Size and Text properties of our
user-defined control class ccc, even though our control may not have
implemented these properties directly.
Using the Add function, we have
then added the button and the control ccc to the form. Finally, we have called a static function
rrr from the class ddd that initializes a property cust from our user-defined
control.
When we run this program, we see
two text boxes, a label and a button. It is obvious that other than the button,
the other widgets were created by the class ccc. This provides ample credence
to our belief that our user-defined control can do its biding.
|
|
Screen 4.35 |
|
Screen 4.34 |
|
We shall now endeavor to
comprehend what the constructor of class ccc in file c.cs is attempting to do.
The constructor contains two text boxes, one called ID to store the id of the
user, and the other called t to store the user's name. The label l is used to display a simple descriptive message. Since
we do not want the user to change the value contained in ID, we assign the
value true to its ReadOnly property and assign the value false to its Enabled
property. Thereafter, we add these three widgets to the form. Hence, we can now
see four widgets on the screen.
In the container h.cs, we call a
static function, rrr off class ddd. The ddd class is created in cc.cs and is
derived from Component. In the rrr function, we create an object c, which looks
like ddd and pass a value of 111 to the constructor. The constructor of class
ddd initializes an instance variable i to the value contained in s. The
variable i stands for the user id.
Class ddd has a property called
ti, which is initialized to my name, Vijay. This property ti sets another
instance variable t to the value 'Vijay'. Thus, we have initialized two instance
members of class ddd to specific values.
The value returned on calling
the rrr function is stored in the cust property of the control c. The class ccc
contains the property having type ddd.
The property Cust stores the ddd
object in the variable c for later use. It also initializes the Text property
of the text boxes to the ID and Name of the user that the two properties in the
class ddd were initialized to. Thus, we see '111' and 'Vijay' displayed in the
text boxes.
When we click on the button
labeled 'Save', the function abc gets called. This function first calls the
function aaa from class ccc using the object c. In aaa, we initialize the ti
property of the control to the value present in the textbox. The ID property is
dimmed out, and hence its value can never be changed. The object c represents
the ddd object in class ccc.
To display a string, an object,
whose data type is not a string, has to call the ToString function in the
datatype. The cust property in class ccc has the type of ddd, which contains
the ToString function. This function uses the StringWriter class to concatenate
the word 'Sonal' with the value of the instance variables i and t, which
eventually get displayed in the MessageBox.
The above program demonstrates
two crucial points:
(a) All the code that refers to the user has been encapsulated in class ddd.
(b) The user interface code is entered in class ccc.
The container is oblivious to
these classes and does not bother to verify whether there are two classes or
one. While the class ccc contains code pertaining to User Interface interaction
only, the class ddd contains code relating to the actual object.
h.cs
using System.Drawing;
using System.Windows.Forms;
public class zzz : Form {
RadioButton r1,r2;
GroupBox g1;
sss s;
public zzz()
{
r1 = new System.Windows.Forms.RadioButton();
r2 = new System.Windows.Forms.RadioButton();
r1.Location = new System.Drawing.Point(24, 24);
r1.Size = new System.Drawing.Size(128, 24);
r1.Text = "Vijay";
r1.Checked = true;
r1.CheckedChanged += new System.EventHandler(r1f);
r2.Location = new System.Drawing.Point(24, 64);
r2.Size = new System.Drawing.Size(128, 24);
r2.Text = "Mukhi";
r2.CheckedChanged += new System.EventHandler(r2f);
g1 = new System.Windows.Forms.GroupBox();
g1.Size = new System.Drawing.Size(192, 152);
g1.Text = "Sonal";
g1.Location = new System.Drawing.Point(320, 16);
s = new sss();
Text = "Control Example";
ClientSize = new System.Drawing.Size(528, 325);
s.Size = new System.Drawing.Size(304, 328);
s.TabIndex = 0;
s.Anchor = AnchorStyles.Left | AnchorStyles.Right;
s.Font = new System.Drawing.Font("TAHOMA", 16f, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.World);
s.Text = "Simple Control";
s.dmc += new System.EventHandler(sf);
Controls.Add(g1);
Controls.Add(s);
g1.Controls.Add(r2);
g1.Controls.Add(r1);
}
void r2f(object sender, System.EventArgs e)
{
if (r2.Checked)
{
s.dm = ddd.a2;
}
}
void r1f(object sender, System.EventArgs e)
{
if (r1.Checked)
{
s.dm = ddd.a1;
}
}
void sf(object sender, System.EventArgs e)
{
if (s.dm == ddd.a1)
MessageBox.Show("hi");
if (s.dm == ddd.a2)
MessageBox.Show("bye");
}
public static void Main()
{
Application.Run(new zzz());
}
}
c.cs
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
[DefaultProperty("dm"),DefaultEvent("dmc"),]
public class sss : Control
{
ddd d;
EventHandler eee;
public sss() :base()
{
d = ddd.a1;
ccc();
SetStyle(ControlStyles.ResizeRedraw, true);
}
[Category("Appearance"),Description("Controls how the control paints"),DefaultValue(ddd.a1),Bindable(true),]
public ddd dm
{
get
{
return d;
}
set
{
d=value;
ccc();
dmf(EventArgs.Empty);
}
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle);
Size textSize = e.Graphics.MeasureString(Text, Font).ToSize();
float x = (ClientRectangle.Width/2) - (textSize.Width/2);
float y = (ClientRectangle.Height/2) - (textSize.Height/2);
e.Graphics.DrawString(Text,Font,new SolidBrush(ForeColor),x, y);
}
protected override void OnTextChanged(EventArgs e) {
base.OnTextChanged(e);
Invalidate();
}
[Description("Raised when the DrawingMode changes")]
public event EventHandler dmc
{
add {
eee += value;
}
remove {
eee -= value;
}
}
protected virtual void dmf(EventArgs e)
{
Invalidate();
if (eee != null)
eee.Invoke(this, e);
}
void ccc()
{
if ( d == ddd.a1)
{
base.BackColor = Color.Yellow ;
base.ForeColor = Color.Green ;
}
if ( d == ddd.a2)
{
base.BackColor = Color.LightSlateGray ;
base.ForeColor = Color.White ;
}
}
}
public enum ddd
{
a1 = 0,
a2 = 1,
}
a.bat
del *.exe
del *.dll
csc /t:library c.cs
csc h.cs /r:c.dll
h
Let us now write a control that
is considerably intricate. In the container h.cs, we start with two radio
buttons r1 and r2. Each time we select a radio button, depending upon the
option selected, either of the functions r1f or r2f will get called.
Thereafter, we create a group box called g1. The radio buttons are added to the
group box, and the group box is added to the Controls collections. Apart from
these controls, one more user control named s, which is an instance of class
sss, is added to the Controls collection.
|
|
|
|
Screen 4.36 |
Screen 4.37 |
Prior to this, we initialize
various properties of this control such as TabIndex, Font, Size, Anchor, Text
etc. to some meaningful values. Besides these, a property called dmc in the
user control is initialized to an EventHandler that calls function sf. Thus,
whenever the event represented by dmc is triggered, the function sf gets
called.
The code implementing our user
control s, resides in the file c.cs. The user control s is an instance of sss
and is derived from the Control class. At the outset, the constructor of class
sss calls the constructor of the base class using the keyword base, even though
this is optional, because the base class constructor invariably gets called.
The class ddd is an enum, with
two members a1 and a2, having values 0 and 1 respectively. We could
conveniently have used numbers directly instead of an enum, but since the
original example used an enum, we have also done so. We set the object d to the
value 0 and call function ccc from class sss. The main objective of placing
code in a function is to enable the code to be called several times.
In the function ccc, we start by
checking the value of the object d. If it is a1 i.e. 0, we change the value of
the two properties BackColor and ForeColor to Yellow and Green respectively. If
the object has a value of a2, then another pair of colors is assigned to these
properties. The properties are changed in the base class using the keyword
base. The function SetStyle ensures that the form gets redrawn when it is
resized.
We have already learnt that the
OnPaint function is called whenever the window needs to be redrawn. In this function,
we first use the property BackColor to fill the form background. Next, we use
the width of the currently selected font, to calculate the midpoint of our
screen, and then, we write the value contained in the text property in the
center of the window.
When the second radio button is
selected, function r2f gets called. In this function, the program checks
whether the radio button is already checked. If so, it initializes the property
dm, whose data type is ddd, to a2.
Similarly, when the first radio
button is selected, function r1f gets called. This function first ascertains if
the radio button is already checked. If
so, it initializes the property dm to a1.
Now, we shall focus our
attention on the property dm. In the set accessor of property dm, the ddd
object named d is initialized to either a1 or a2. Following this action, a call
is made to function ccc, which changes the background and foreground color,
depending on the value contained in d. The effect is observed when the function
OnPaint gets called. A call is made to the function dmf with a parameter of an
Empty event.
In function dmf, we first call
Invalidate, which in turn, calls the OnPaint function. Just as life offers no
guarantees whatsoever, in much the same way, the calls made to the OnPaint
function are unpredictable. The Invalidate function instantly calls the OnPaint
function.
You may recall that in h.cs, the
property dmc was initialized with the name of the function sf. This property
dmc is an event type that stores the EventHandler or function sf in an instance
object eee. So, the value in the object eee is checked. If the value is not
null, the function Invoke is called off the object eee with two parameters. The
first parameter is a reference to itself, i.e. 'this', and the second parameter
is a null EventArgs object. The function Invoke, in turn, calls function sf in
the container, h.cs. The function sf displays a message box, depending upon the
value of the ddd object.
The main idea behind this
exercise is to demonstrate that clicking on a radio button in the container
initializes a property in the user control. This in turn, raises a property
changed event, thus resulting in a call to a function registered with a
property of the control. The function resides in the container and not in the
user control.
This is a circuitous route for
accomplishing results. The Invoke function is not aware of and could not care
less about the functions that it is calling.
All the other attributes in the
code can be safely ignored, since they are mainly meant for external tools or
programs that display the metadata.
h.cs
using System.Drawing;
using System.Windows.Forms;
public class zzz : Form
{
TextBox t;
Button b;
hhh h;
public zzz()
{
h = new hhh();
b = new Button();
t = new TextBox();
b.Size = new System.Drawing.Size(104, 40);
b.Text = "Vijay";
b.Location = new System.Drawing.Point(336, 56);
ClientSize = new System.Drawing.Size(448, 157);
t.Location = new System.Drawing.Point(80, 16);
t.Text = "Vijay Mukhi";
h.Dock = System.Windows.Forms.DockStyle.Bottom;
h.Size = new System.Drawing.Size(448, 40);
h.Location = new System.Drawing.Point(0, 117);
h.Text = "none";
h.ppp(t, "TextBox selected");
h.ppp(b, "Button Selected");
Controls.Add(t);
Controls.Add(b);
Controls.Add(h);
}
public static void Main(string[] args)
{
Application.Run(new zzz());
}
}
c.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
public class hhh : Control
{
Hashtable h;
Control a;
public hhh()
{
h = new Hashtable();
BackColor = SystemColors.Info;
}
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ]
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
private void ce(object s, EventArgs e)
{
a = (Control)s;
Invalidate();
}
private void cl(object s, EventArgs e)
{
if (s == a)
{
a = null;
Invalidate();
}
}
public void ppp(Control c, string v)
{
if (v == null)
{
v = string.Empty;
}
if (v.Length == 0)
{
h.Remove(c);
c.Enter -= new EventHandler(ce);
c.Leave -= new EventHandler(cl);
}
else
{
h[c] = v;
c.Enter += new EventHandler(ce);
c.Leave += new EventHandler(cl);
}
if (c == a)
{
Invalidate();
}
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
Rectangle rect = ClientRectangle;
Pen borderPen = new Pen(ForeColor);
pe.Graphics.DrawRectangle(borderPen, rect);
borderPen.Dispose();
if (a != null)
{
string te = (string)h[a];
if (te != null && te.Length > 0)
{
rect.Inflate(-2, -2);
Brush brush = new SolidBrush(ForeColor);
pe.Graphics.DrawString(te, Font, brush, rect);
brush.Dispose();
}
}
}
}
In this program, we have three
controls: a user control h, contained in class hhh, a TextBox t and a Button b.
The basic properties like Size, Text and Location for the Button and the Text
box are set to the specified initial values. Thereafter, the properties of the
user control h are initialized. The DockStyle for the Dock property is set to
the Bottom of the form, and the Size and the Location are specified. The Text property is also initialized.
The user control h has a property called ppp, which accepts two parameters, a control and a string. We c