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.