C++ Builder Programming 2nd Edition PDF
C++ Builder Programming 2nd Edition PDF
Table of Contents
Table of Contents
Table of Contents
PART I INTRODUCTION TO BORLAND C++ BUILDER ...................... 18
CHAPTER 1: THE BORLAND C++ BUILDER IDE .................................. 19
1.1
Windows Fundamentals........................................................................ 31
2.1.1 Introduction to the Win32 Library................................................ 31
2.1.2 C++ Builder Application Fundamentals ....................................... 31
2.1.3 Applications Instance .................................................................. 32
2.1.4 Introduction to User Interface Objects.......................................... 34
2.1.5 Design and Run Times.................................................................. 36
2.1 Techniques of Creating Controls .......................................................... 36
2.1.1 Overview ...................................................................................... 36
2.1.2 Control Design.............................................................................. 37
2.1.3 User Interface Design ................................................................... 39
2.1.4 Controls Selection......................................................................... 42
2.1.5 Controls Moving........................................................................... 44
2.1.6 Control Resizing ........................................................................... 47
2.1.7 Controls Navigation...................................................................... 48
2.2 Runtime Creation of Controls............................................................... 50
2.2.1 Window Creation.......................................................................... 50
2.2.2 VCL Control Creation .................................................................. 50
2.3 Windows Controls and Their Properties............................................... 50
2.3.1 Overview of Controls Properties .................................................. 51
2.3.2 Properties Categories .................................................................... 51
PART II APPLICATIONS PREREQUISITES ............................................ 54
CHAPTER 3: STRINGS ................................................................................. 55
Table of Contents
3.1
Table of Contents
Table of Contents
Table of Contents
Table of Contents
Table of Contents
Table of Contents
10
Table of Contents
11
Table of Contents
Table of Contents
13
Table of Contents
Table of Contents
15
Table of Contents
16
17
Table of Contents
PART I
Introduction to Borland C++ Builder
This section introduces the Borland C++ Builder programming environment with the
necessary information you need in order to navigate around the screen, create
applications and files, use the menus and other accessories that can ease your experience
with this product.
18
1.1.1 Overview
Borland C++ Builder offers a practical and easy means of creating computer applications
for the Microsoft Windows operating systems. It uses the C++ computer language as its
core syntax and programming logic, adhering to ANSI Standard with a lot of
improvements of customized items of the Win32 library.
There are various ways you can launch the program. The most common way consists of
clicking.
To create a shortcut on the desktop, in Microsoft Windows higher than Win95, you can
click Start -> Programs -> Borland C++ Builder, right-click C++ Builder 6 and click
Send To -> Desktop (Create Shortcut)
To start Borland C++ Builder, click Start -> Programs -> Borland C++ Builder 6 ->
C++ Builder 6
19
20
1.
2.
3.
4.
The main section of the title bar displays the C++ Builder 6 name of the application,
and the name of the program that is running. A C++ Builder program is called a
project. When Bcb starts, it creates a starting project immediately, and it names the
starting project, Project1. If or when you add other projects, they take subsequent
names such as Project2, Project3, etc
This main section of the title bar is also used to move, minimize, maximize the top
section of the IDE, or to close Bcb. On the right section of the title bar, there are
three system buttons with the following roles
Button
Role
Minimizes the window
Maximizes the window
Restores the window
Closes the window
Click File. There are four main types of menus you will encounter.
3.
4.
5.
6.
7.
8.
9.
21
10. Notice that on the main menu (and any menu), there is one letter underlined on each
word. Examples are F in File, E in Edit, etc. The underlined letter is called an access
key. It allows you to access the same menu item using the keyboard. In order to use
an access key, the menu should have focus first. The menu is given focus by pressing
either the Alt or the F10 keys.
To see an example, press Alt.
11. Notice that one of the items on the menu, namely File, has its border raised. This
means the menu has focus.
12. Press p and notice that the Project menu is expanded.
13. When the menu has focus and you want to dismiss it, press Esc.
For example, press Esc.
14. Notice that the Project menu has collapsed but the menu still has focus.
15. Press f then press o. Notice that the Open dialog displays.
16. On most or all dialog boxes that have either an OK, Open, or Save buttons, when you
press Enter, the OK, Open, or Save button is activated. On the other hand, most of
those dialog boxes also have a Cancel button. You can dismiss those dialogs by
clicking Cancel or pressing Esc.
As an example, press Esc to dismiss the Open dialog.
17. On some menu items, there is a combination of keys we call a shortcut. This key or
this combination allows you to perform the same action on that menu using the
keyboard.
If the shortcut is made of one key only, you can just press it. If the shortcut is made
of two keys, press and hold the first one, while you are holding the first, press the
second key once and release the first key. Some shortcuts are a combination of three
keys.
To apply an example, press and hold Ctrl, then press S, and release Ctrl.
18. Notice that the Save As dialog box opens. To dismiss it, press Esc.
Means
Press the t key
Press and release Alt. Then press G
Press and hold Ctrl. While you are still holding Ctrl, press H
once. Then release Ctrl
Press and hold Ctrl. Then press and hold Shift. Then press E
once. Release Ctrl and Shift
main menu, but a toolbar equipped with the New button allows you to proceed a little
faster.
By default, Bcb displays or starts with 6 toolbars. Every toolbar has a name. One way
you can find out the name of a toolbar is to click and hold the mouse on its gripper bar
and drag it away from its position
1.
2.
3.
Borlands toolbars can be positioned anywhere on the screen. To position the toolbar
back or to somewhere else, drag its title bar to the desired location.
4.
You can get a list of the toolbars that are currently displaying if you right-click any
button on any toolbar or menu.
For example, right-click a toolbar and notice the list of toolbars:
5.
6.
A toolbar is equipped with buttons that could be unpredictable. Just looking at one is
not obvious. The solution into knowing what a button is used for is to position the
mouse on top of it. A tool tip also called a hint will come up and display for a few
seconds.
As an example, position the mouse (do not click) on the button that looks like a piece
of paper bent on its top right corner
7.
Without clicking, move the mouse to another button and to other buttons. Notice that
some buttons hints also display a shortcut that would lead to the same action:
8.
To use a toolbars button, you click it. For example, click the New button
Notice that the action calls the New Items dialog box.
9.
10. Some buttons present an arrow on their right side. This arrow represents a menu.
To see an example, position the mouse on the Open button. Click the arrow that
appears and observe the menu.
11. Press Esc to dismiss the menu.
The Component Palette holds many objects that you will be using to create your
applications. The Component Palette, like a toolbar, can be moved to any location on the
screen; but it is a good idea, if possible, to always keep it at its default location.
The Component Palette is made of objects categorized in different tabs. To display a
particular tab, you click it.
1.
2.
3.
4.
Once again, it is not obvious to predict what a button is used for. The alternative is to
position the mouse on a button for a few seconds until a hint appears.
Position the mouse on any button on the Component Palette and observe the hint that
appears.
From now on, each button of a toolbar or the Component Palette will be referred to
by its hint. The hint will represent the name of the button. Therefore, Click New
means, Click the New button.
24
1.1.7
To see what the current application looks like, on the main menu, click Run -> Run.
2.
Notice that a dialog box displays showing the evolution of compiling the application:
3.
4.
5.
A form is made of two parts, its physical part called the form and its code section
called a unit.
6.
7.
25
form, such as the one we have now, is built from at least two files: a header file and a
source file. By default, Bcb does not display this file at startup; you have to request it.
To display the header file of the form, you can right-click the source file and click Open
Source/Header File. Indeed, this action is used to toggle both displays. Since the source
and the header file go in pair (when using classes), they hold the same name but different
extensions.
2.
3.
4.
5.
Click Unit1.cpp
6.
Click Unit1
7.
26
1.2
2.
1.
2.
3.
Click the arrow of the Save In combo box and click the (C:) drive.
4.
5.
6.
7.
Click Save
8.
9.
Click Save.
27
1.3
1.3.1 Overview
There are two main sources of help available for Borland C++ Builder. The first source
of help is provided with the compiler, this is from the Borland Company. This help is
mainly electronic and hardly printed. Fortunately, you have access to this help even if
you are not designing an application. Everything considered, this is the closest and the
highest documentation that the compiler provides.
To access C++ Builder help, on the task bar, you can click Start -> Program -> Borland
C++ Builder -> Help, and make your choice from the categories.
Another place you can find information is on the Internet. Fortunately, most of that help
is free. On the companys web site, you can access http://bdn.borland.com.
Although the best challenger to Microsoft Visual C++, C++ Builder does not enjoy the
number of (independent) books that are published for MSVC. Fortunately, since there is a
good presence of Win32 API in C++ Builder, it is very important that you have access to
the Microsoft Developer Network documentation. It is available free from
http://msdn.microsoft.com and on CD-ROM or DVD.
In the Contents tab, double-click Programming With C++ Builder to expand it.
Notice that the help window displays, click the advance button.
Type OnMo
28
Log on to http://www.borland.com
After finding some information and reading, change the address in the Address box
to http://msdn.microsoft.com and press Enter.
29
30
Windows Fundamentals
If you start a default graphical application, a source file with the name of the project
would be created and it would have the parameter-less syntax of the WinMain()
function:
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
31
For a Win32 application, you must declare a variable of one of these structures. Then you
should provide or at least control the value of each of the member variables. Fortunately,
to simplify this process, the VCL provides two fundamental classes used to create an
application. They are TApplication and TScreen.
TApplication is used to create, or get access to, an application. It is the main central
point of an application as a Windows entity.
The TScreen class is used to access the computers desktop, its size, the monitor your are
using, and other pieces of information related to a screen as an object or related to the
objects of an application.
To give you ample access to the application and to the screen, every GUI application you
develop using the VCL declares a TApplication and a TScreen variables. This means
that you will hardly, if ever, need to declare these variables since they are already
available to your application.
To create an application, the TApplication class provides the Initialize() method. Its
syntax is simply:
void __fastcall Initialize(void);
Start Borland C++ Builder and, on the Standard toolbar, click the New button
2.
3.
Click OK
4.
In the Console Wizard dialog box, click the C++ radio button and click the Use VCL
check box
5.
Click OK
6.
33
//--------------------------------------------------------------------------#pragma argsused
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow)
{
Application->Initialize();
return 0;
}
//---------------------------------------------------------------------------
7.
To save the application, on the Standard toolbar, click the Save All button
8.
9.
Type Exercise1 and press Enter twice to display it in the Save combo box
on the
After creating the forms resource, you must let the application know that you have a
have. To do this, you must call the CreateForm() method of the TApplication class. Its
syntax is:
void __fastcall CreateForm(System::TMetaClass* InstanceClass, void *Reference);
The name of the form is passed as pointer as the second argument. Because the form is
being added to the application, giving the application access to it for further operations
and processing, the CreateForm() method requires that the class that controls the form
be specified. This is normally done by calling the __classid() operator. Once you have
specified the form to the application, you must also specify the name of the source file
that holds the implementation of the form. This is done by calling the USEFORM macro.
Its syntax is:
USEFORM(FileName, FormName)
This macro takes two arguments. The first is a string that specifies the source file. The
second specifies the name of the form as it appears in the project.
In future lessons, we will see that various object controls spend their time sending
messages to the application. As the application receives them, it processes them and acts
34
appropriately. For example, the application can be made aware that the user wants to
close it. To process such messages, the TApplication class uses a method called Run().
Its syntax is:
void __fastcall Run(void);
Therefore, an application should (must) call this method to process its assignment. If
Run() is not called, the application will compile and execute fine but because it cannot
process messages, it will not display anything (displaying something on the screen is a
message by itself and must be processed).
To add a form to the application, on the main menu, click File -> New -> Form
2.
To save the new form, on the Standard toolbar, click the Save All button and click
Save
3.
4.
Click the Unit1.cpp tab and change the content of the file as follows:
//--------------------------------------------------------------------------#include <vcl.h>
#include <windows.h>
#pragma hdrstop
USEFORM("Unit2.cpp", Form2);
//--------------------------------------------------------------------------#pragma argsused
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
Application->Initialize();
Application->CreateForm(__classid(TForm2), &Form2);
Application->Run();
return 0;
}
//---------------------------------------------------------------------------
5.
35
6.
2.1
2.1.1 Overview
A Windows control is a graphical object that allows the user to interact with the
computer. The controls are as varied as the needs and goals are. Because there are so
many controls for various purposes, their design and creation are left to the computer
programmer.
When you start Borland C++ Builder, it creates and displays an empty form. It also
provides its various controls on the Component Palette so you can choose which ones are
appropriate for your particular application. Otherwise, to create a graphical application,
you can click File -> New -> Application from the main menu. You can also click the
New button
on the Standard toolbar. From the New Items dialog box, also called the
Object Repository, you can click the type of application or file you would like to create.
36
Controls can be set by categories based on their function or role. A container is a control
whose main purpose is to host other controls. To design it, you pick up objects from the
Component Palette and drop them where desired. The most common and the most widely
used container is the form. In Bcb, a form can be configured to display like a regular
dialog box, to become a Single Document Interface (SDI) or to participate in a Multiple
Document Interface (MDI) application.
All of the applications we will create in this book qualify as graphical user interface
(GUI). A GUI application is one that displays Windows controls to the user who acts on
them to interact with the computer.
There are various categories of controls and objects on the Component Palette, some of
which you will hardly use. The controls are represented each by a specific button. Some
of the buttons display an appearance that easily suggests their role. Some others may not
be obviously identified. In any case, to find out what control a button represents, you can
position your mouse on it. A small yellow box called a tool tip or a hint would display.
With experience, you will find out that the hint reflects the actual name of the control:
From now on, each button on the Component Palette will be called by its hint.
The controls are organized in tabs that do not strictly follow a specify rule. During your
development, you will simply find out the tab that holds a control you need.
In this book, we will study the controls as they are needed, in a cumulative fashion. We
will not follow the categories as set on the Component Palette. For this same reason,
although controls in Microsoft Windows are sometimes classified as Standard Controls
(those that were already available before Windows 95) and Common Controls (those
that were released with Windows 95 and added subsequently), in this book, unless
specified otherwise, the Windows controls will be considered regardless of their release
date.
Rapid Application Design (RAD) consists of selecting the controls that are necessary for
your application and use them as you see fit and appropriate. There are various
techniques you can use to add a control to a container.
37
If you did not yet, start Borland C++ Builder with its default form
2.
To add your first control, on the Component Palette, click the Standard tab
3.
4.
5.
6.
Notice that the second control is positioned on top of the first because whenever you
double-click a control from the Component Palette, it gets positioned in the middle
of the form
Actually when you double-click a control on the Component Palette, it gets
positioned in the middle of the selected container. If the selected control on the form
is not a container, the control you double-click would be positioned in the middle of
the form.
7.
8.
To draw a Memo on the form, position the mouse in the top middle section of the
form, then click and hold the mouse. Drag to the center right section of the form:
9.
Release the mouse. Notice that a memo has been added to the form.
10. There are other controls that do not use dimensions. You just click them and click on
the form. Their presence is more important on a form than their physical location.
11. To see an example, on the Component Palette, click the Dialogs tab.
12. Click the ColorDialog button
13. Click and drag on the form to draw a big rectangular box and release the mouse.
14. Notice that the icon keeps its set width and height.
15. To display the code for the form, press F12. Notice that although we have added
controls, none of their code has been written.
38
16. To display the header file for the form, on the lower section of the Code Editor, click
the unit1.h tab
17. Notice the list of the objects that were added and their names set by Bcb.
18. To display the form, press F12 and click on an unoccupied area on the form.
To start a new program, on the main menu, click File -> New -> Application. If you
are asked whether you want to save your project, click No.
2.
3.
4.
Click on the center-top section of the form (it is not important how it is positioned):
39
5.
6.
This time, click on the middle left section of the form (again, the position is not
important):
7.
8.
This time, click in the lower-left section of the form and hold the mouse down.
9.
13. Click and drag on the form from the lower-right section to the upper-center section
of the form and release the mouse.
14. Notice that this time, the memo keeps the drawn dimensions.
40
15. To add another control, on the Standard tab, click the ActionList control
16. Click and drag on the form to draw a tall and wide rectangle. Then release the
mouse.
17. Notice that the new control keeps a constant shape.
18. To add another form, on the View toolbar, click New Form
19. While Form2 has focus, on the Standard tab, double-click the Edit control to place it
on Form2
20. On the Standard tab, click the Panel control
21. On the form, click in the lower right section of the form and drag to the upper-center
section of the form to draw a rectangle that partially covers the edit box. Release the
mouse:
22. Make sure the new panel is still selected. From the Standard tab, double-click the
Edit control:
23. Notice that the new edit box is positioned in the center of the panel and not in the
center of the form. This is because the Panel control is a container and can hold its
own controls.
24. On the Standard tab, click the Edit control.
Copyright 2003 FunctionX, Inc.
41
25. Click inside the panel but just above the Edit2 edit box.
42
1.
To display one of the forms, on the main menu, click View -> Forms
2.
3.
Click OK
4.
To select one of the controls, click the memo box. Also notice the Name field in the
Object Inspector:
5.
6.
7.
To select another control, click the arrow on the upper section of the Object
Inspector and click Edit2
8.
9.
10. While one of the controls, namely Edit1 is still selected, press the up, down, left, and
right arrow keys on the keyboard to select other controls on the form.
11. Click anywhere on the form and not on a control. Notice that the form is selected.
You can verify this by checking the name on the top section of the Object Inspector.
12. On the form, click Memo1 to select it.
13. To dismiss the selected control, press Esc. Notice that the form is now selected.
14. To hide the form, press F12.
15. To display the form again, press F12.
Copyright 2003 FunctionX, Inc.
43
21. Then release the mouse. Notice that the touched controls are selected.
22. Press Shift + F12 to display the View Form dialog box.
23. Press the down arrow key to select Form2 and press Enter. Notice that Form2
displays.
44
The Alignment Palette is a window that can be used to deal with both alignments and
control spacing. To access it, on the main menu, you can click View -> Alignment
Palette. To find out what a button on that window is used for, position the mouse on a
particular button for a few seconds until a hint displays.
To move the form with regard to its position on the screen, click and hold its title
bar. Then drag left.
2.
While you are still holding the mouse, drag right and release the mouse.
3.
Here is another technique you can use. Make sure the form has focus; this means its
title bar is blue (or the system color of your computer as set in Control Panel). Press
Alt + Shift + Space (this is equivalent to clicking the system menu of the form).
Notice that a menu comes up.
4.
5.
Press the left arrow key 4 times and notice that the form is moving.
6.
Press the right arrow key 6 times and press Enter. Notice the new position of the
form.
7.
To move the Edit1 edit box on Form2, click and hold the mouse on Edit1; then drag
to the upper left section of the form and release the mouse. Notice that the control
has moved.
8.
9.
Drag to the left section of the form to move the panel. Release the mouse.
10. Notice that the panel has moved with its children.
11. On the main menu, click View -> Forms
12. Double-click Form1.
13. Notice that the controls selected earlier are still highlighted. Otherwise, select them
(Edit2, Edit3, and Memo1).
14. Since some controls are selected, click and hold your mouse on one of the selected
controls; for example click and hold Memo1. Then drag to the upper section of the
form and release the mouse.
15. To deselect the group, press Esc.
16. To use another technique of moving controls, click Edit2 to select it.
17. Press and hold Shift. Then click Edit1 and Edit3 and release the mouse.
18. Once you have selected those controls, on the main menu, click Edit -> Align
19. On the Alignment dialog box, in the Horizontal section, click the Right Sides radio
button:
45
28. Click OK
29. While the controls are still selected, on the main menu, click View -> Alignment
Palette.
30. On the Align window, click Align Left Edges
31. Notice all selected controls share the same left alignment.
32. Close the Alignment Palette
33. To deselect, click anywhere on the form
46
Role
Moves the seized border in the North-West <-> South-East direction
Shrinks or heightens the control
Moves the seized border in the North-East <-> South-West direction
Narrows or enlarges the control
To resize the control by one grid unit, click one of the handles and drag in the desired
location. Once satisfied, release the mouse.
To resize a control by the small possible unit, select the control. Press and hold Alt. Then
click the desired handle and drag in the desired direction. Once satisfied, release the
mouse and Alt.
To resize the form, position your mouse on its right border until the mouse turns into
double horizontal arrow
2.
3.
4.
5.
6.
7.
8.
Position the mouse on its lower left corner until the mouse turns into a double arrow.
9.
Click and drag in the lower left direction and release the mouse.
10. Notice that the controls dimensions did not change but the icon moved.
11. Click Memo1.
12. Position your mouse on its left border until the pointer turns into a double horizontal
line.
13. Click and drag to the left to enlarge it. Then release the mouse.
14. Click anywhere on the form to select it.
47
To start another project, on the main menu, click File -> New -> Other
2.
From the New Items dialog, make sure Application is selected and click OK.
3.
4.
5.
6.
7.
8.
9.
to add a button
48
20. Rearrange the list on the Edit Tab Order dialog as follows:
49
21. Click OK
22. To test the form, press F9.
23. On the form, Press Tab a few times and notice the new sequence of navigation.
24. Close the form.
2.2
2.3
50
51
The box on the right side of each name represents the value of the property that you can
set for an object. There are various kinds of fields you will use to set the properties. To
know what particular kind a field is, you can click its name. But to set or change a
property, you use the box on the right side of the propertys name: the property's value,
also referred to as the field's value.
Empty fields:
By default, these fields have nothing
in their properties. Most of these properties are dependent on other settings of your
program. For example, you can set a menu property for a form only after you have
created a menu.
To set the property on such a field, you can type in it or select from a list. If you type an
invalid value and press Enter, you would receive an "Invalid Property Value" error:
You can click OK to dismiss the error dialog and type a new valid value.
52
Expandable Fields:
Some fields have a + button.
This indicates that the property has a set of sub-properties that actually belong to the
same property and are set together. To expand such a field, click its + button and a
button will appear:
To collapse the field, click the button. Some of the set properties are created from an
enumerator. Since the field was created as a Pascal-style set, this allows the property to
have more than one value. For example, the BorderIcons property can have the
minimize and the maximum buttons. Some other fields that have a + button are actually
complete classes or a combination of classes.
Boolean Fields:
Some fields can have only a true or
false value. To change their setting, you can either select from the combo box or doubleclick the property to toggle to the other value.
Action Fields: Some fields would require a list of items and need to be controlled by an
intermediary action. Such fields display an ellipsis button . When you click the button,
a dialog box would come up and you can set the value for the field. You can also doubleclick the field value to call the dialog.
Selection Fields:
To change the value of some of the
fields, you would use their combo box to display the available values. After clicking the
arrow, a list would display.
There are various types of selection fields. Some of them display just two items. To
change their value, you can just double-click the field. Some other fields have more than
two values in the field. To change them, you can click their arrow and select from the list.
You can also double-click a few times until the desired value is selected. On some other
properties, you can double-click the field and a dialog box would come up.
53
PART II
Applications Prerequisites
The second part of this book was inserted after various hesitations. Logically, we would
have started studying Windows controls that the VCL is rich of, but these controls use
some characteristics, properties, techniques and methods that must be reviewed prior to
actually studying the controls. Therefore, in this section, we will encounter such aspects
as strings, files, message boxes, mathematic functions, primary dialog boxes, etc. Most
controls featured in the VCL take so high advantage of these aspects that we judged it
necessary to first show what their related properties are dealing with.
54
Chapter 3: Strings
Chapter 3: Strings
3.1
Since AnsiString is a class with its own constructor, you can also declare its variable
with empty parentheses, which would be calling the class constructor. Here is an
example:
AnsiString City();
You can also initialize the string variable when declaring it, again, using the assignment
operator and providing the desired value. Here is an example:
AnsiString Province("British Columbia");
55
Chapter 3: Strings
Once you have defined the string you can use it as you see fit. You can use it to change a
controls caption:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Country;
Country = "Madagascar";
Panel1->Caption = Country;
}
//---------------------------------------------------------------------------
3.2
The AnsiString class provides its own function used to check whether a string is empty.
Its syntax is:
bool __fastcall IsEmpty() const;
This member function can be used to check whether a text-based control contains nothing
but it cannot empty a text control. The following example shows two ways of using the
AnsiString::IsEmpty() method:
56
Chapter 3: Strings
3.2.2
To get the length of an AnsiString variable, use the AnsiString::Length() method. Its
syntax is:
int __fastcall Length() const;
This function returns the length of the string as an integer. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString S = Edit1->Text;
Edit2->Text = S.Length();
}
//---------------------------------------------------------------------------
The AnsiString class allows you to impose the number of characters of a string. It uses
the following method:
AnsiString& __fastcall SetLength(int NewLength);
This method takes one argument which is the length you want the AnsiString variable to
have. If the NewLength argument is less than the length of the string, the resulting string
would be the first NewLength characters of the original string. If the NewLength value is
57
Chapter 3: Strings
less than the length of the string, the original would be preserved and assigned to the
resulting string:
//--------------------------------------------------void __fastcall TForm1::Button1Click(TObject
*Sender)
{
AnsiString S = Edit1->Text;
Edit2->Text = S.SetLength(7);
}
//---------------------------------------------------
If the original string has space on its left, this function would remove it and return a string
that is like the original without the leading space. If the original does not have any
leading space, the function would return the same string:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit2->Text = Edit1->Text.TrimLeft();
}
//---------------------------------------------------------------------------
Another function used to perform the same operation is the TrimLeft(). Its syntax is:
AnsiString _fastcall TrimLeft(const AnsiString S);
58
Chapter 3: Strings
To remove any space on the right side of a string, you can use the
AnsiString::TrimRight() method. Its syntax is:
AnsiString __fastcall TrimRight() const;
If the original string has space on its right, this function would remove it and return the
same string without any trailing space. Otherwise, the original string would be returned:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit2->Text = Edit1->Text.TrimRight();
}
//---------------------------------------------------------------------------
Another function used to perform the same operation is the TrimRight(). Its syntax is:
AnsiString _fastcall TrimRight(const AnsiString S);
The (global) TrimRight() function requires one argument as the string that needs to be
trimmed. The function returns the original string without the trailing space:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit2->Text = TrimRight(Edit1->Text);
}
//---------------------------------------------------------------------------
Other functions allow you to combine the last two operations into one. You can use the
AnsiString::Trim() method to remove spaces on both sides of a string. Its syntax is:
AnsiString __fastcall Trim() const;
Alternatively, you can use the global Trim() function to perform the same operation. Its
syntax is:
AnsiString _fastcall Trim (const AnsiString S);
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit2->Text = Trim(Edit1->Text);
}
//---------------------------------------------------------------------------
59
Chapter 3: Strings
3.3
String Conversions
A character:
An integer:
AnsiString Int = 120;
A long integer:
A floating-point value:
A double-precision number:
A string:
Any of these variables can be declared using their equivalent constructors: AnsiString
Symbol('H');
AnsiString Int(120);
AnsiString GirlFriend("Micheline Phoon");
AnsiString WeeklyEarnings(675.15);
AnsiString Longer(-73495745);
AnsiString Silver(2.15e28);
Based on the configurations of the AnsiString constructors, you can convert any value
and make it available to a control that uses an AnsiString property. For example, you can
convert and display:
60
A character:
An interger:
A long integer:
A floating-point value:
A double-precision number:
A string:
Chapter 3: Strings
Based on this constructor, you can declare a C string, manipulate its value and use it in
your application. To convert an AnsiString value to a null-terminated string, use the
AnsiString::c_str() method. The syntax of this function is:
char* __fastcall c_str() const;
This function is very valuable because it can help you perform various types of string
operations.
61
Chapter 3: Strings
This member function considers an AnsiString variable and examines each one of its
characters. If a character is an alphabetic character in lowercase, it would be converted to
uppercase. If the character is either an alphabetical character in uppercase or it is not an
alphabetic character, it would be kept as is. This method also considers the Regional
Settings of the computer being used, as set in Control Panel.
If you want to convert a single character to uppercase, after initializing or getting, call
this method. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
String S = 's';
Edit1->Text = S.UpperCase();
}
//---------------------------------------------------------------------------
You can use this same method to convert a string to uppercase as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
String S1 = "James N. Fame!";
String S2 = S1.UpperCase();
Edit1->Text = S2;
}
//---------------------------------------------------------------------------
Besides the AnsiString method, you can use the UpperCase() function to convert a
character or string to uppercase. Its syntax is:
AnsiString __fastcall UpperCase(const AnsiString S);
This function uses the same algorithm as the AnsiString::UpperCase() method. Here is
an example of using it:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
String S1 = "James N. Fame!";
String S2 = UpperCase(S1);
Edit2->Text = S2;
}
//---------------------------------------------------------------------------
The AnsiUpperCase() function uses the same syntax as the UpperCase() function and
applies the same algorithm as the AnsiString::UpperCase() method. Unlike the
UpperCase() function, AnsiUpperCase() considers the Regional Settings of the
computer being used. Look at how these two functions convert the same French string:
Using UpperCase()
//--------------------------------------------------void __fastcall TForm1::Button1Click(TObject
*Sender)
62
Chapter 3: Strings
{
}
//---------------------------------------------------
//---------------------------------------------------
Using the Regional Settings, this function examines each character of the provided
AnsiString variable. If a character is an alphabetic character in uppercase, it would be
converted to lowercase. The case of all the other characters would be ignored.
If you want to convert a single character to uppercase, after initializing or getting, call
this method. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnConvertClick(TObject *Sender)
{
String String1 = "[Borland C++ Builder]";
Edit1->Text = String1.LowerCase();
}
//---------------------------------------------------------------------------
You can also use the LowerCase() function to convert a character or string to lowercase.
Its syntax is:
AnsiString __fastcall LowerCase(const AnsiString S);
This function uses the same algorithm as the AnsiString::UpperCase() method. Here is
an example of using it:
//--------------------------------------------------------------------------void __fastcall TForm1::btnConvertClick(TObject *Sender)
{
String String1 = "[Borland C++ Builder]";
Edit1->Text = LowerCase(String1);
}
//---------------------------------------------------------------------------
If the local settings you are using or distributing your program to are a factor, you should
use the AnsiLowerCase() function. It processes the string in the same way as the
Copyright 2003 FunctionX, Inc.
63
Chapter 3: Strings
3.4
Strings Addition
You can then use the addition operation to add as many strings as you see fit.
This function takes two arguments as strings. The source is added to the destination. The
function returns the destination already changed by the operation. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Destination = "Paul ";
AnsiString Source = "Lombard";
AppendStr(Destination, Source);
Edit1->Text = Destination;
}
//---------------------------------------------------------------------------
64
3.5
Chapter 3: Strings
3.5.1 Introduction
String comparison allows you to find out which one of two strings is longer or whether
both strings are equal. When comparing two strings, the compiler sometimes checks
lowercase and uppercase characters. Depending on the function you are using, you can
ask the compiler to consider the case of the characters; this is referred to as casesensitivity. Some of the functions, when performing their comparison on Dates or
currency values, would refer to the Regional Settings of your computer as set in the
Control Panel.
The function requires two AnsiString variables and compares them. The comparison is
performed without case-sensitivity. If the strings are the same, the result is true (the
integer equivalent is 1). Otherwise the comparison yields false (0). You can use the
SameText() function on a validation dialog like this one:
Then you can implement the OnClick() event of the OK button as follows:
//--------------------------------------------------------------------------void __fastcall TOKBottomDlg::OKBtnClick(TObject *Sender)
{
String Password1 = edtPassword1->Text;
String Password2 = edtPassword2->Text;
Boolean Result = SameText(Password1, Password2);
if(Result == False)
{
Panel1->Caption = "Your passwords do not match!";
edtPassword1->SetFocus();
}
65
Chapter 3: Strings
else
{
Panel1->Caption = "Your account has been setup.";
Close();
}
}
//---------------------------------------------------------------------------
Alternatively, you can use the CompareText() function to compare strings. Unlike the
AnsiString::AnsiCompareIC() method, this function does not care about the Windows
Regional Settings. The syntax of this function is:
int __fastcall CompareText(const AnsiString First, const AnsiString Second);
The function takes two string arguments and examines their characters incrementally.
After the comparison, the function returns:
3.5.3
66
Chapter 3: Strings
Edit3->Text = "False";
else
Edit3->Text = "Equal";
}
//---------------------------------------------------------------------------
The function considers its own string and compares it to the string argument it takes. This
function returns:
To compare strings, you can also use the CompareStr() function. Unlike the
AnsiString::AnsiCompare() method, this function does not care about the Windows
Regional Settings. The syntax of this function is:
int __fastcall CompareStr(const AnsiString First, const AnsiString Second);
The function takes two string arguments and examines their characters incrementally.
After the comparison, the function returns:
67
Chapter 3: Strings
comparison renders a Boolean value of true or false. Both libraries can perform any sort
of comparison.
When you have two strings and would like to find out whether both are equal, you can
use the (overloaded) == operator. If both strings are equal, the conditional comparison
would return true.
You can also use the AnsiSameStr() function. Its syntax is:
bool __fastcall AnsiSameStr(const AnsiString First, const AnsiString Second);
The function takes the Windows Regional Settings into consideration when comparing
the First and the Second strings with case-sensitivity. If both strings are the same, the
function return true. If they are not the same, the result is false. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnSendClick(TObject *Sender)
{
String EMail1 = edtEMail1->Text;
String EMail2 = edtEMail2->Text;
if(AnsiSameStr(EMail1, EMail2))
{
frmCongratulations->ShowModal();
Close();
}
}
//---------------------------------------------------------------------------
Alternatively, to compare two strings, you can use the AnsiSameText() function. Both
functions use the same syntax. Like the AnsiSameStr() function, the AnsiSameText()
considers the Regional Settings. Unlike the AnsiSameStr() function, the
AnsiSameText() function does not consider the case of the characters in the strings.
If the strings you want to compare are captions, such as those you see on a form, it would
be cumbersome to write a comparison function that would examine them. This is because
the captions on a form usually have an ampersand used to underline one of their
characters. Examples include First Name, Address, City, Full Name or Department.
Fortunately, Borland provides the AnsiSameCaption() function. Its syntax is:
bool __fastcall AnsiSameCaption(const AnsiString First, const AnsiString Second);
This function takes two captions and compares them considering the Regional Settings .
Regardless of where the ampersand is positioned, the other characters of the captions
would be examined. If both captions are the same, the function would return true. In the
following example, two captions are set as First Name and First Name respectively. A
regular comparison would find them different, but the AnsiSameCaption() function
finds that both strings are the same:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
lblCaption1->Caption = "&First Name";
lblCaption2->Caption = "First &Name";
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnCompareCaptionsClick(TObject *Sender)
68
Chapter 3: Strings
}
//---------------------------------------------------------------------------
Besides all available comparison methods of the AnsiString class and comparison
functions throughout the VCL, you can use the Boolean operators to perform any type of
comparisons on strings. These operators can be used the same way you would proceed
with regular numeric variables. Therefore, the operators are:
3.6
Equal: ==
Not Equal: !=
You can use this method to find out the last character of a given string. In the following
example, the last character of the string in the Edit1 edit box displays in the Edit2 edit
box when the user clicks the Button1 control:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
String S = Edit1->Text;
Edit2->Text = S.AnsiLastChar();
}
//---------------------------------------------------------------------------
If the AnsiString is used on a console application, you can use this same method to find
the last character of an AnsiString variable:
Copyright 2003 FunctionX, Inc.
69
Chapter 3: Strings
This member function takes two integer arguments. The first argument specifies the
position where the compiler would start considering the deletion. The second argument
specifies the number of characters that should be deleted from the AnsiString variable.
If you declare an AnsiString variable called Country. You can use Country.Delete(14, 11)
to delete 11 characters starting at the 14th character:
AnsiString Country("United States of America");
AnsiString After;
puts(Country.c_str());
After = Country.Delete(14, 11);
puts(After.c_str());
This method takes two integer arguments. From the original string, the first argument
specifies the position of the character where the new string would start. The second
argument specifies the number of characters that would be considered when creating the
new string. Here is an example:
//--------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Original = Edit1->Text;
AnsiString SubOne = Original.SubString(9, 4);
Edit2->Text = SubOne;
}
//---------------------------------------------------
3.6.4
70
Chapter 3: Strings
In many cases, you can also use the AnsiString::AnsiPos() method). Its syntax is:
extern PACKAGE int __fastcall AnsiPos(const AnsiString Substr, const AnsiString S);
The StringReplace() function will look for the LookFor character or substring in the
Original string. If it finds it, then it will replace the LookFor character or sub string with
the ReplaceWith character or substring. You control how this operation is performed by
using the FlagToUse argument. The values you can use are to replace all occurrences of
LookFor with ReplaceWith. The flag used would be rfReplaceAll. You can also ask the
compiler not to take the character(s) case into consideration, which is done with
rfIgnoreCase. Once the TReplaceFlags argument is a set, you can use one or both of the
flags. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit1->Text = StringReplace(Edit1->Text, " ", "",
TReplaceFlags() << rfReplaceAll);
}
//---------------------------------------------------------------------------
3.7
String Quotations
This function takes one string, the Source argument, and returns it added the Quote
characters on both sides of the string. Here is an example:
71
Chapter 3: Strings
This function takes one string and returns it after adding a single-quote on both sides.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::edtQuotedExit(TObject *Sender)
{
char *BookTitle = edtQuoted->Text.c_str();
AnsiString Quoted = QuotedStr(BookTitle);
edtBookTitle->Text = Quoted;
}
//---------------------------------------------------------------------------
This function takes two arguments. The Source parameter is a null-terminated string that
is returned as an AnsiString value. When using the function, you must specify what
character or symbol is used as Quote. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::edtQuotedExit(TObject *Sender)
{
char *BookTitle = edtQuoted->Text.c_str();
char Quote = '"';
72
Chapter 3: Strings
73
74
4.1.1 Overview
A message box is a relatively small dialog box used to display a message and provide one
or more buttons. Here is an example of a message box:
A message box is used to provide information to the user or to request a decision (from
the user). By clicking one of the buttons, the user makes a decision and the program
continues.
Message boxes are created from built-in functions from the VCL and the Win32 library.
The necessary functions shipped with the compiler.
2.
3.
From the Standard tab of the Component Palette, click the Edit control and click on
the form.
4.
Change the name of the edit control to edtMessage and delete the contents of its Text
field.
75
A message box created with the ShowMessage() function uses the name of the project as
its caption. The message to display is a string that can be provided by the developer. Here
is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::ButtonClick(TObject *Sender)
{
ShowMessage("Welcome to the Sellers Bank.");
}
//---------------------------------------------------------------------------
The string can also derive from another control such as the contents of an edit box, a
memo, or any text control. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnMsgFromEditClick(TObject *Sender)
{
ShowMessage(edtMessage->Text);
}
//---------------------------------------------------------------------------
As with other message boxes that we will study here, to display the message on various
lines of text, you can separate lines using the C/C++ new line constant represented by '\n'.
The VCL provides another alternative. To separate lines of text, you can use the
sLineBreak constant. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString strMessage = "Your record has been registered";
AnsiString strCountry = "Country Name: Australia";
AnsiString strCity = "City to visit: Melbourne";
AnsiString strFinal = "Have a nice strip.";
ShowMessage(strMessage + sLineBreak + strCountry + sLineBreak +
strCity + sLineBreak + strFinal);
}
//---------------------------------------------------------------------------
76
From the Standard tab of the Component Palette, click Button and click on the form
2.
Change the caption of the new button to Show &Msg and change its name to
btnShowMsg
3.
4.
5.
6.
7.
8.
To save the project, on the main menu, click File -> Save All
9.
10. Click the Create New folder button. Type Message Boxes and press Enter twice to
display the new folder in the Save In combo box.
11. Click Save to save the Unit.
12. Type Messages to replace the name of the project and press Enter.
13. To display the message on more than one line, change the event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnShowMsgClick(TObject *Sender)
{
ShowMessage("Please fill out your Time Sheet before leaving.\n"
"Make sure you sign and send it to Human Resources.");
}
//---------------------------------------------------------------------------
14. Test the form to verify the new caption of the message box:
Copyright 2003 FunctionX, Inc.
77
The MessageBox() function takes three arguments. The first argument, Message, is a
null-terminated string representing the message that the user would read. The Message
string could be a static sentence. It could be constructed from another control. Or it could
be a combination of different strings appended using C/C++ string functions and
operations.
You can create a simple message box similar to one implemented using the
ShowMessage() function to display a simple message with an OK button. In this case,
you would provide only the Message argument. Set the other two arguments as NULL.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBox( "This operation can only be "
"performed by an administrator.", NULL, NULL);
}
//---------------------------------------------------------------------------
The second argument, also a string, is the caption that would display on the title bar of
the dialog box. You can also set it when creating the message box or you can build it
from what would be available at runtime. If you do not have a caption, you can set the
value of this argument as NULL. In that case the title bar would display Error. Therefore,
to create a less boring message box, provide the Caption argument. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBox("Make sure the music is playing.",
"CD PLayer Instructions", NULL);
}
//---------------------------------------------------------------------------
The third argument specifies the flags that would display on the dialog box: one or more
buttons and an optional picture. You can create a simple message box with OK as the
only button. In that case, set the third argument as MB_OK. Here is an example:
78
To display more than one button, use a constant integer that represents a group of the
available buttons. Here are the constants and their buttons:
Constant
Buttons
MB_OK
MB_OKCANCEL
MB_ABORTRETRYIGNORE
MB_YESNOCANCEL
MB_YESNO
MB_RETRYCANCEL
MB_HELP
For example, to create a message box that displays the Yes and No buttons, you could
write:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBox("Do you hear any music now or any sound at all?",
"CD Player Instructions", MB_YESNO);
}
//---------------------------------------------------------------------------
If you provide the MB_HELP as the only button, the message box would display with an
OK and a Help buttons.
To enhance your dialog and accentuate your message, you can display an icon using one
of the Win32 defined integer constants. Although you can use any icon with any button,
you should be tactful and make sure that the appearance of the icon you use is in
accordance with the message. The values and icons are:
Value
Icon
Suited when
MB_ICONEXCLAMATION
MB_ICONWARNING
MB_ICONINFORMATION
MB_ICONASTERISK
79
MB_ICONQUESTION
MB_ICONSTOP
MB_ICONERROR
MB_ICONHAND
The icons are used in conjunction with the buttons constant. To combine these two flags,
use the bitwise OR operator |. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBox("Do you hear any music now or any sound at all?",
"CD Player Instructions",
MB_YESNOCANCEL | MB_ICONQUESTION);
}
//---------------------------------------------------------------------------
When a message box is configured to display more than one button, the operating system
is set to decide which button is the default. The default button has a thick border that sets
it apart from the other button(s). If the user presses Enter, the message box would behave
as if the user had clicked the default button. Fortunately, if the message box has more
than one button, you can decide what button would be the default. To specify the default
button, use one of the following constants:
Value
MB_DEFBUTTON1
MB_DEFBUTTON2
MB_DEFBUTTON3
MB_DEFBUTTON4
To specify the default button, use the bitwise OR operator to combine the constant
integer of the desired default button with the button's constant and the icon. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBox("Do you hear any music now or any sound at all?",
"CD Player Instructions",
MB_YESNOCANCEL | MB_ICONQUESTION | MB_DEFBUTTON2);
}
//---------------------------------------------------------------------------
Since the combination of these buttons is using the OR bitwise operator to construct the
Flags argument, it does not make a difference which constant appears first:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBox("Do you hear any music now or any sound at all?",
"CD Player Instructions",
80
After reading the message displaying on the dialog box, the user would click one of the
buttons and the dialog would be closed. Each one of the buttons has a constant integer
number that is assigned and recognized by the compiler. You can use this number to find
out what button the user had clicked. This means that the MessageBox() function returns
an integer value as in the following table:
Displayed Button(s)
If the user
clicked
The return
value is
IDOK
IDOK
IDCANCEL
IDABORT
IDRETRY
IDIGNORE
IDYES
IDNO
IDCANCEL
IDYES
IDNO
IDRETRY
IDCANCEL
Therefore, you can use one of these integers to act depending on the button clicked:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
if( Application->MessageBox(
"Do you hear any music now or any sound at all?",
"CD Player Instructions",
MB_YESNOCANCEL | MB_ICONQUESTION) == IDNO )
Panel1->Caption = "We will stop these tests now. "
"Let the machine rest!";
}
//---------------------------------------------------------------------------
81
2.
Change the Caption of the new button to Simple &1 and its name to btnSimpleMsg
3.
4.
5.
6.
Click the Simple 1 button to view the message box. Notice that the message box has
an OK button and a caption of Error:
7.
8.
9.
10. Change its caption to Simple &2 and its name to btnSimple2
11. Double-click the Simple 2 button.
12. Press Tab and type:
Application->MessageBox("Make sure the music is playing.", "CD PLayer
Instructions", MB_OK);
22. Double-click the Default button and implement its event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDefaultClick(TObject *Sender)
{
Application->MessageBox(
"The file you are trying to copy is being "
"used by someone else.\n"
"Would you like to try later? If you click\n"
"Yes: You will be reminded when the file is ready.\n"
"No: Copy the file anyway. You will get only the source file\n"
"Cancel: Cancel the operation.", "Copying Files",
MB_YESNOCANCEL | MB_ICONQUESTION | MB_DEFBUTTON2);
}
//---------------------------------------------------------------------------
4.2
The first argument, Message, is the message addressed to the user. It can be a simple
static sentence, a paragraph or any combination of strings. The IconType is an icon used
to enhance the dialog box. The icon is set using a constant integer as follows:
83
Value
Icon
mtWarning
mtError
mtInformation
mtConfirmation
mtCustom
None
The Buttons argument is used to specify the type(s) of button(s) to display on the dialog
box. The buttons are defined using the TMsgDlgButtons set as follows:
Value
Button
Value
mbYes
mbRetry
mbNo
mbIgnore
mbOK
mbAll
mbCancel
mbNoToAll
mbAbort
mbYesToAll
Button
mbHelp
The last argument is used if there is a help file available, in which case you would specify
the particular index related to this message box. This message box uses the name of the
project as its caption.
2.
3.
4.
5.
6.
7.
8.
84
9.
85
This function takes three arguments similar to those of the MessageDlg() function. You
construct it by specifying the string message, the icon type, and the button type. To create
this dialog box, assign its construction to a dynamic form. To do this, you could create a
local form in a function or event, but since a local dynamic control is accessible only in
the event or function in which it is created, this would deceive the purpose of using the
CreateMessageDialog() function. Therefore, declare an instance of the TForm class in
the private or public sections of the unit or form that would use the message box:
//--------------------------------------------------------------------------#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------------------------#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TButton *Button2;
TPanel *Panel1;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
void __fastcall FormCreate(TObject *Sender);
void __fastcall Panel1Click(TObject *Sender);
private: // User declarations
TForm* Mine;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------------------------#endif
Next, use the new operator to specify the owner of the form:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
86
: TForm(Owner)
{
Mine = new TForm(this);
}
//---------------------------------------------------------------------------
To create the custom message box, assign the return value of the
CreateMessageDialog() function by creating it. The message box can be as simple as a
single string, an icon, and a button:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Mine = new TForm(this);
Mine = CreateMessageDialog("Custom Dialog Box", mtWarning,
TMsgDlgButtons() << mbYes);
}
//---------------------------------------------------------------------------
The message could also comport any of the available icons combined with any common
buttons:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Mine = new TForm(this);
Mine = CreateMessageDialog("Is this the best song or what?",
mtInformation,
TMsgDlgButtons() << mbYes << mbAbort
<< mbCancel << mbNo);
}
//---------------------------------------------------------------------------
With the message box created, you can call it from any section or control that needs it in
your application:
#include <vcl.h>
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Mine = new TForm(this);
Mine = CreateMessageDialog("Is this the best song or what?",
mtInformation,
TMsgDlgButtons() << mbYes << mbAbort
<< mbCancel << mbNo);
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
87
Mine->ShowModal();
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
Mine->ShowModal();
}
//--------------------------------------------------------------------------void __fastcall TForm1::Panel1Click(TObject *Sender)
{
Mine->ShowModal();
}
//---------------------------------------------------------------------------
The Caption argument specifies the string that would display on the title bar of the dialog
box. The Prompt is a sentence that would display to the user as to what to type in the
provided edit box. The Default argument is a suggested value you can display in the edit
box to guide the user as the type of value expected. If you do not want to specify a default
value, you can set its string value to empty. Here is example:
procedure TForm1.Button1Click(Sender: TObject);
begin
InputBox('Distance and Measurement',
'Enter the distance in kilometer:',
'');
end;
If you specify the Default argument and the user clicks OK without changing the content
of the edit box, the compiler would consider the default value as valid. After using the
dialog box, the user would click OK, press Enter, click Cancel, or press Esc. If the user
clicks OK or presses Enter, the function returns the value that the user would have typed
88
in the edit box. This allows you to write a conditional statement that would consider the
new value returned by clicking OK on the InputBox dialog:
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := InputBox('Distance and Measurement',
'Enter the distance in kilometer:',
'');
end;
If the user clicks Cancel or presses Esc, whatever the edit box was displaying would be
ignored. But the function would still return the default value. If the returned value is
intended for mathematical, date, or time calculations, you should convert it accordingly.
Add a button
to the form
2.
3.
Press F9 to test the form. Click the new button. Notice that the content of its edit box
is empty
4.
This takes three strings. The Caption parameter is a string that displays on the title bar of
the dialog box. The Prompt parameter is the sentence that indicates to the user what to
type in the edit box. Like the InputBox() function, the Value parameter provides a
default and sample value to the user. Like the InputBox() function, the user can type a
new value. Here is an example of using the InputQuery() function:
procedure TForm1.Button1Click(Sender: TObject);
var
Value: string;
begin
InputQuery('Exiting Application',
'Are you sure you want to exist (y=Yes/n=No)?',
Value);
end;
Unlike the InputBox() function that returns a string, the InputQuery() function returns
two values. By its declaration, this function returns a Boolean value of true or false. If the
user clicks OK or presses Enter after using the dialog box, the function returns true. If the
user presses Esc or clicks Cancel, the function returns false.
89
If the user clicks OK (or presses Enter), like the InputBox() function, whether the user
had changed the value of the edit box or not, the content of the edit box, provided as the
Value argument, would be returned. Because Value is passed by reference, the function
can return two values. Unlike the InputBox() function, if the user clicks Cancel (or
presses Esc) after dealing with the dialog box, the value of the Value argument would be
ignored.
You can validate the returned value of Value by writing a conditional statement that
examines whether the user had clicked OK or Cancel. In the following example, when the
user clicks a button on the form, the compiler finds out if the user had clicked OK; in
which case it would display the returned value of the Value argument in an Edit control
of the form:
procedure TForm1.Button1Click(Sender: TObject);
var
Answer: string;
begin
if InputQuery('Exiting Application',
'Are you sure you want to exist (Y=Yes/Y=No)?',
Answer) = True then
Edit1.Text := Answer;
end;
90
5.1.1 Introduction
The controls on your applications will receive strings of various kinds either supplied by
the user or gotten from other controls. Some of the values on these controls will be
involved in mathematical operations. The C++ language provides a rich set of functions
to help you quickly perform different types of calculations. The functions range from
arithmetic to geometry, from trigonometry to algebra, etc. To compensate for the areas
where C++ does not expand, instead of writing your own functions, The Visual
Component Library (VCL) is equipped with various functions that, besides geometry and
algebra, deal with finance, statistics, random number generation, etc. Because there are so
many of these functions and they get added with each new release of the library, we will
review only the most common used.
By default, the content of a text control, such as an edit box, is a string, which is an array
of characters. If you want the value or content of such a control to participate in a
mathematical operation, you must first convert such a value to a mathematically
compatible value.
If the control whose string needs to be converted is displaying an invalid integer, the
program would throw an error. The AnsiString provides a viable alternative. The
91
A function used to convert a string is the StrToInt() function. Its syntax is:
int __fastcall StrToInt(const AnsiString S);
This function takes as an argument the string that you are trying to convert. In the
following example, the strings of two edit boxes are converted to integers and an addition
is performed on their values:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Number1 = StrToInt(Edit1->Text);
int Number2 = StrToInt(Edit2->Text);
int Addition = Number1 + Number2;
Edit3->Text = Addition;
}
//---------------------------------------------------------------------------
92
}
//---------------------------------------------------------------------------
On the other hand, you can use this function to format a floating-point number. In the
following example, the values of the dimensions of a sphere are calculated and display in
appropriate edit boxes:
//--------------------------------------------------------------------------void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
double Radius, Diameter, Circumference, Area, Volume;
Radius = edtRadius->Text.ToDouble();
Diameter = Radius * 2;
Circumference = Radius * 2 * M_PI;
Area = Radius * Radius * M_PI;
93
}
//---------------------------------------------------------------------------
When the same values are configured using the AnsiString::sprintf() method, the
diameter and the circumference can be set to display two decimal values while the area
and the volume display three, as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
double Radius, Diameter, Circumference, Area, Volume;
Radius = edtRadius->Text.ToDouble();
Diameter = Radius * 2;
Circumference = Radius * 2 * M_PI;
Area = Radius * Radius * M_PI;
Volume = Radius * Radius * Radius * 4.00 * M_PI / 3;
edtDiameter->Text = edtDiameter->Text.sprintf("%.2f", Diameter);
edtCircumference->Text = edtCircumference->Text.sprintf("%.2f", Circumference);
edtArea->Text = edtArea->Text.sprintf("%.3f", Area);
edtVolume->Text = edtVolume->Text.sprintf("%.3f", Volume);
}
//---------------------------------------------------------------------------
5.2
Arithmetic Functions
considered as neutral. In some operations, the number considered will need to be only
positive even if it is provided in a negative format. The absolute value of a number x is x
if the number is (already) positive. If the number is negative, its absolute value is its
positive equivalent. For example, the absolute value of 12 is 12, while the absolute value
of 12 is 12.
To get the absolute value of a number, you can use one of the C/C++ abs() function. Its
syntax is:
int abs(int x);
This function takes an integer as the argument and returns its absolute value equivalent.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnAbsoluteClick(TObject *Sender)
{
int Number = Edit1->Text.ToInt();
Edit2->Text = abs(Number);
}
//---------------------------------------------------------------------------
95
In arithmetic, the ceiling of a number is the closest integer that is greater or higher than
the number considered. In the first case, the ceiling of 12.155 is 13 because 13 is the
closest integer greater than or equal to 12.155. The ceiling of 24.06 is 24.
}
//---------------------------------------------------------------------------
96
//--------------------------------------------------------------------------#pragma argsused
int main(int argc, char* argv[])
{
Extended Value1 = 312.44;
Extended Value2 = -4002.35;
cout << "The ceiling of " << Value1 << " is" << Ceil(Value1) << endl;
cout << "The ceiling of " << Value2 << " is" << Ceil(Value2);
cout << "\n\nPress any key to continue...";
getchar();
return 0;
}
//---------------------------------------------------------------------------
97
The result returned, Mantissa, is a double (frexp) or a long double (frexpl) number in the
range 0.5 (included) to 1 (excluded). The Exp argument, passed as a pointer, is returned
as
Number = Mantissa * 2Exp
For the following example, a form is equipped with three Edit controls named
edtNumber, edtMantissa, and edtExponent. It also has a Button control named
btnCalculate with the Default property set to true. The user must type a number in the
Number edit box and press Enter. Then the OnClick event of the button executes to
perform the frexp() function which leads to displaying the results in the appropriate edit
boxes :
98
99
100
101
//---------------------------------------------------------------------------
Therefore, the value of the argument should be between these two extremes. For a larger
number, use the expl() function:
long double expl(long double x);
As opposed to an 8-byte value, this version of the function takes a 10-byte variable,
calculates its exponent, and returns a long double.
The ldexp() function works on double-precision numbers while the ldexpl() uses long
doubles.
102
LnXP1
The LnXP1() function is used to calculate the natural logarithm of a number that is being
incremented to 1. The syntax of this function is
Extended __fastcall LnXP1(Extended X);
When executing, this function takes one argument, X, adds 1 to X, and then calculates the
natural logarithm, also called the Napierian logarithm, of the new number. The formula
used is Result = ln(X+1). Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Extended X, Result;
X = StrToFloat(Edit1->Text);
Result = LnXP1(X);
Edit2->Text = FloatToStr(Result);
}
//---------------------------------------------------------------------------
Log10
The Log10() function calculates the base 10 logarithm of a number. The syntax of this
function is:
Extended __fastcall Log10(Extended X);
The number to be evaluated is passed as the argument X. The function returns the
logarithm on base 10 using the formula:
y = log10x which is equivalent to x = 10y
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Extended X, Result;
X = StrToFloat(Edit1->Text);
Result = Log10(X);
Edit2->Text = FloatToStr(Result);
}
//---------------------------------------------------------------------------
103
Log2
The Log2() function is used to calculate the logarithm of a number on base 2. The syntax
of the function is:
Extended __fastcall Log2(Extended X);
The variable whose logarithmic value will be calculated is passed as argument X to the
function. The function uses the formula:
Y = log2x. This is the same as x = 2y
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Extended X, Result;
X = StrToFloat(Edit1->Text);
Result = Log2(X);
Edit2->Text = FloatToStr(Result);
}
//---------------------------------------------------------------------------
LogN
The LogN() function is used to calculate the logarithmic value of a number to the desired
base. Its syntax is:
Extended __fastcall LogN(Extended Base, Extended Number);
This function takes two arguments. The second, Number, is the variable whose value will
be evaluated. The first argument, Base, is used as the base of the logarithm. The formula
used by this function is:
y = logbx which is the same as x = by
For the LogN() function, this formula would be:
LogN(Base, Number) = NumberBase;
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Extended Base, Number, Result;
Base = StrToFloat(Edit1->Text);
Number = StrToFloat(Edit2->Text);
Result = LogN(Base, Number);
Edit3->Text = FloatToStr(Result);
}
//---------------------------------------------------------------------------
104
For a large or larger number, you can use the sqrtl() function. Its syntax is:
long double sqrtl(long double x);
This second form takes a long double number as a variable and returns a long double
number as the square root of x.
After the calculation, if the function succeeds, it would return the square root. If it fails, it
would throw an error.
5.3
Business Functions
5.3.1 Introduction
An asset is an object of value. It could be a person, a car, a piece of jewelry, or a
refrigerator, etc. Anything that has a value is an asset. In the accounting world, an asset is
a piece of/or property whose life span can be projected, estimated, or evaluated. As days,
months or years go by, the value of such an asset degrades.
When an item is acquired for the first time as brand new, the value of the asset is
referred to as its Cost. The declining value of an asset is referred to as its Depreciation.
At one time, the item will completely lose its worth or productive value. Nevertheless,
Copyright 2003 FunctionX, Inc.
105
the value that an asset has after it has lost all its value is referred to its Salvage Value. At
any time, between the purchase value and the salvage value, accountants estimate the
value of an item based on various factors including its original value, its lifetime, its
usefulness (how the item is being used), etc.
//--------------------------------------------------------------------------#include <vcl.h>
#include <math.hpp>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
106
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended Cost = StrToFloat(edtCost->Text);
Extended Salvage = StrToFloat(edtSalvage->Text);
Integer Life = StrToInt(edtLife->Text);
Integer Period = StrToInt(edtPeriod->Text);
edtDepreciation->Text = FloatToStr(Depreciation);
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnExitClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
107
Cost = StrToFloat(edtCost->Text);
Salvage = StrToFloat(edtSalvage->Text);
Life = StrToInt(edtLife->Text);
Depreciation = SLNDepreciation(Cost, Salvage, Life);
edtDepreciation->Text = FloatToStrF(Depreciation, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------
This is equivalent to 1. As you can see, the first year would have the lowest dividend
(1/15 0.0067) and the last year would have the highest (5/15 0.33).
To calculate the depreciation for each year, the fractions (1/15 + 2/15 + 3/15 + 4/15 +
5/15) are reversed so that the depreciation of the first year is calculated based on the last
fraction (the last year divided by the common denominator). Then the new fraction for
each year is multiplied by the original price of the asset. This would produce:
Year
1
2
3
4
5
Fraction
5/15
4/15
3/15
2/15
1/15
*
Amount
*
$18,000.00
*
$18,000.00
*
$18,000.00
*
$18,000.00
*
$18,000.00
Total Depreciation
=
=
=
=
=
=
=
Depreciation
$6,000.00
$4,800.00
$3,600.00
$2,400.00
$1,200.00
$18,000.00
The VCL function used to calculate the depreciation of an asset using the sum of the
years is called SYDDepreciation() and its syntax is:
Extended __fastcall SYDDepreciation(constExtended Cost, const Extended Salvage,
int Life, int Period);
The Cost parameter is the original value of the item. In our example, this would be
$18,000. The Salvage parameter is the value the asset would have (or has) at the end of
its useful life. The Life is the number of years of the asset would have a useful life. The
108
Period is the particular period or rank of a Life portion; for example, if the Life of the
depreciation is set to 5 (years), the Period could be any number between 1 and 5. If set to
1, the depreciation would be calculated for the first year. If the Period is set to 4, the
depreciation would be calculated for the 4th year. You can also set the Period to a value
higher than Life. For example, if Life is set to 5 but you pass 8 for the Period, the
depreciation would be calculated for the 8th year. If the asset is worthless in the 8th year,
the depreciation would be 0.
Here is an example:
}
//---------------------------------------------------------------------------
5.4
Finance Functions
5.4.1 Introduction
The Visual Component Library provides a series of functions destined to perform various
types of financially related operations. These functions use common factors depending on
the value that is being calculated. Many of these functions deal with investments or loan
financing.
The Present Value is the current value of an investment or a loan. For a savings account,
a customer could pledge to make a set amount of deposit on a bank account every month.
Copyright 2003 FunctionX, Inc.
109
The initial value that the customer deposits or has in the account is the PresentValue as
referenced in the VCL functions. The sign of the variable, when passed to a function,
depends on the position of the customer. If the customer is making deposits, this value
must be negative. If the customer is receiving money (lottery installment, family
inheritance, etc), this value should be positive.
The Number Of Periods is the number of periods that make up a full cycle of a loan or
an investment. This period could be the number of months of a year, which is 12; but it
could be another length. This variable is passed as NPeriods. Suppose a customer is
getting a car loan that would be financed in 5 years. This is equivalent to 5 * 12 = 60
months. In the same way, a cash loan can stretch from 0 to 18 months, a carpenter truck
loan can have a life financing of 40 months, and a condominium can be financed for 15
years of 12 months plus an additional 8 months; this is equivalent to (15 * 12) + 8 = 188
months.
The Interest Rate is a fixed percent value applied during the life of the loan or the
investment. The rate does not change during the length of the NPeriods. For deposits
made in a savings account, because their payments are made monthly, the rate is divided
by the number of periods (the NPeriods) of a year, which is 12. If an investment has an
interest rate set at 14.50%, the Rate would be 14.50/12 = 1.208. Because the Rate is a
percentage value, its actual value must be divided by 100 before passing it to the
function. For a loan of 14.50% interest rate, this would be 14.50/12 = 1.208/100 = 0.012.
The Payment is the amount the customer will be paying. For a savings account where a
customer has pledged to pay a certain amount in order to save a set (goal) amount, this
would be the amount the customer would pay every month. If the customer is making
payments (car loan, mortgage, deposits to a savings account), this value must be negative.
If the customer is receiving money (lottery installment or annuity, family inheritance,
etc), this value must be positive.
The Payment Time specifies whether the payment is made at the beginning or the end of
the period. For a monthly payment, this could be the beginning or end of every month.
The PaymentTime uses one of the values of the TPaymentTime enumerator. When
passing this variable, select one of the members of the enumerator:
enum TPaymentTime { ptEndOfPeriod, ptStartOfPeriod };
110
111
In the following examples, a customer is applying for a car loan. The car costs $15500. It
will be financed at 8.75% for 5 years. The dealer estimates that the car will have a value
of $2500 when it is paid off. The dialog box is used to calculate the monthly payment
(the Payments edit box) that the customer will make every month:
112
113
Here is an example:
114
The PresentValue parameter is the current value of the item. It could be the marked value
of the car, the current mortgage value of a house, or the cash amount that a bank is
lending. The NPeriods is the number of periods that occur during a yearly cycle of the
loan. The Rate argument is a fixed percent value applied during the life of the loan. The
Period argument represents the payment period. The FutureValue is the total amount that
the customer will have paid when the loan is paid off. The PaymentTime specifies
whether the periodic (such as monthly) payment of the loan is made at the beginning or
end of the period.
Here is an example:
Copyright 2003 FunctionX, Inc.
115
All of the arguments are the same as described for the InterestPayment() function. Here
is an example:
116
The CashFlows is an array of cash amounts that a customer has made on an investment.
For example, a customer could make monthly deposits in a savings or credit union
accounts. Another customer could be running a business and receiving different amounts
of money as the business is flowing (or losing money). The cash flows do not have to be
the same at different intervals but they should (or must) occur at regular intervals such as
weekly (amount cut from a paycheck), bi-weekly (401k directly cut from paycheck,
monthly (regular investment), or yearly (income). The CashFlows argument must be
passed as an array and not an amount; otherwise you would receive an error.
The Guess is an estimate interest rate of return of the investment.
The CashFlow_Size is the dimension of the array 1.
Here is an example:
Copyright 2003 FunctionX, Inc.
117
118
119
Month6 = edtMonth6->Text.ToDouble();
Rate = StrToFloat(edtRate->Text) / 100;
double Months[] = { Month1, Month2, Month3, Month4, Month5, Month6 };
Periods = (sizeof(Months) / sizeof(double)) - 1;
}
//---------------------------------------------------------------------------
5.5
Measure-Based Functions
5.5.1 Introduction
A
R
C
Note
Equidistant
means same
distance
A circle is a group or series of distinct points drawn at an exact same distance from
another point referred to as the center. The distance from the center C to one of these
equidistant points is called the radius, R. The line that connects all of the points that are
equidistant to the center is called the circumference of the circle. The diameter is the
distance between two points of the circumference to the center; in other words, a
diameter is double the radius.
To manage the measurements and other related operations, the circumference is divided
into 360 portions. Each of these portions is called a degree. The unit used to represent the
degree is the degree, written as . Therefore, a circle contains 360 degrees, that is 360.
The measurement of two points A and D of the circumference could have 15 portions of
the circumference. In this case, this measurement would be represents as 15.
The distance between two equidistant points A and B is a round shape geometrically
defined as an arc. An angle, , is the ratio of the distance between two points A and B of
the circumference divided by the radius R. This can be written as:
Therefore, an angle is the ratio of an arc over the radius. Because an angle is a ratio and
not a physical measurement, which means an angle is not a dimension, it is
independent of the size of a circle. Obviously this angle represents the number of portions
included by the three points. A better unit used to measure an angle is the radian or rad.
120
B
A cycle is a measurement of the rotation around the circle. Since the rotation is not
necessarily complete, depending on the scenario, a measure is made based on the angle
that was covered during the rotation. A cycle could cover part of the circle in which case
the rotation would not have been completed. A cycle could also cover the whole 360 of
the circle and continue there after. A cycle is equivalent to the radian divided by 2 * Pi.
The VCL ships with functions used to perform conversions of values between different
units. To use any of these functions, you must include the VCL math header file as:
#include <math.hpp>
#include <vcl.h>
#pragma hdrstop
121
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnConversionClick(TObject *Sender)
{
Extended Cyc = StrToFloat(edtCycle->Text);
Extended Rad = CycleToRad(Cyc);
edtRadian->Text = FloatToStr(Rad);
}
//---------------------------------------------------------------------------
122
123
5.6
Statistics
The MaxIntValue() function calculates the maximum value of an array of integers. The
first parameter of the function, Data, represents the name of the array. The second
argument, Data_Size is the number-1 of members of the array.
To get the maximum value of a group of integers, declare an integral array of numbers.
You can initialize such a variable or request the values of its members from the user. The
value of the Data_Size argument must be 1 less than the total number of the array
members.
Here is an example that uses the MaxIntValue() function:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Integer n, MaxInteger;
Integer Numbers[] = { 15, 408, 72, 995, 32 };
MaxInteger = MaxIntValue(Numbers, 4);
Edit1->Text = IntToStr(MaxInteger);
}
//---------------------------------------------------------------------------
The MaxValue() function is a numeric value that represents the maximum number of
items of an array. This function takes two arguments. The first argument, Data, represents
an array of integers or double-precision numbers. The second argument is the number-1
of the items of the array; for example, if the considered array has 4 members, the
Data_Size argument would be 3.
To use the MaxValue() function, declare an array that involves the necessary numbers.
You can initialize such a variable or request the numbers from the user. To calculate the
maximum value of a range, supply the array and its size. If you do not know the
dimension of the array, you can use the sizeof operator to find it out. Here is an example
of using the MaxValue() function:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
double Values[] = { 12.55, 10.15, 980.22, 50.50, 280.12 };
int Size = (sizeof(Values)/sizeof(double)) - 1;
double Maximum = MaxValue(Values,Size);
Edit1->Text = Maximum;
}
//---------------------------------------------------------------------------
125
}
//---------------------------------------------------------------------------
}
//---------------------------------------------------------------------------
<<
<<
<<
<<
127
cout
cout
cout
cout
cout
cout
cout
cout
cout
<<
<<
<<
<<
<<
<<
<<
<<
<<
}
//--------------------------------------------------------------------------Company Inventory
Type the number of items of each category
Desktops:
12
Laptops:
2
Printers:
8
Fax Machines: 2
Chairs:
18
Tables:
14
Book Shelves: 10
Books:
20
Trash Cans: 15
Pens:
120
Pencils:
144
Others:
212
Total Number of Items: 577
Press Enter to send the inventory...
128
Here is example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Double Grades[] = { 12.50, 14.00, 16.00, 15.50,
12.00, 10.50, 14.50, 17.50 };
int Size = (sizeof(Grades)/sizeof(double)) - 1;
Double Total = SumOfSquares(Grades, Size);
Edit1->Text = FloatToStr(Total);
}
//---------------------------------------------------------------------------
}
//---------------------------------------------------------------------------
5.7
Trigonometric Functions
129
130
Consider AB the length of A to B, also called the hypothenuse to point A. Also consider
CB the length of C to B, which is the opposite side to point A. The sine represents the
ratio of CB/AB; that is, the ratio of the opposite side, CB over the hypothenuse AB.
The sin() function takes a double-precision number and returns one between 1 and 1.
The sinl() function is used for 10-byte values.
Example: A form contains an Edit control named edtValue. After the user has typed a
value and presses Enter, the OnKeyPress event retrieves the number typed in the edit box,
calculates its sine and displays the result in the same Edit control:
//--------------------------------------------------------------------------void __fastcall TForm1::edtValueKeyPress(TObject *Sender, char &Key)
{
if( Key == VK_RETURN )
{
double Value = edtValue->Text.ToDouble();
double Sinus = sin(Value);
edtValue->Text = Sinus;
}
}
//---------------------------------------------------------------------------
5.7.3 Tangents
The C/C++ tan Functions
double tan(double x);
long double tanl(long double x);
The tan() function calculates the tangent of a number.
In geometry, consider AC the length of A to C. Also consider BC the length of B to C.
The tangent is the result of BC/AB; that is, the rario of BC over AB.
Example: A form contains an Edit control named edtValue. After the user has typed a
value and presses Enter, the OnKeyPress event retrieves the number typed in the edit box,
calculates its tangent and displays the result in the same Edit control:
//--------------------------------------------------------------------------void __fastcall TForm1::edtValueKeyPress(TObject *Sender, char &Key)
131
{
if( Key == VK_RETURN )
{
double Value = edtValue->Text.ToDouble();
double Tangent = tan(Value);
edtValue->Text = Tangent;
}
}
//---------------------------------------------------------------------------
This function takes a long double argument and returns a long double.
132
Files
6.1.1 Introduction
A file is a series of bits of data that are arranged in a particular way to produce a usable
document. For easy storage, location, and management, the bits are stored on a medium
such as a hard disc, a floppy disc, a compact disc, or any valid and support type of
storage. When these bits belong to a single but common entity, the group is referred to as
a file. For even greater management, files can be stored in a parent object called a
directory or a folder. Since a file is a unit of storage and it stores information, it has a size
which is the number of bits it contains. To manage it, a file also has a location also called
a path that specifies where and/or how the file can be retrieved. Also, for better
management, a file has attributes that indicate what can be done on a file or that provide
specific information that the programmer or the operating system can use when dealing
with the file.
File processing consists of creating, storing, and/or retrieving the contents of a file from a
recognizable medium. For example, it is used to save word-processed files to a hard
drive, to store a presentation on floppy disk, or to open a file from a CD-ROM. To
perform file processing on VCL applications, you have four main choices, two derived
from C and C++ languages, one or a few classes provided by the Visual Component
Library, or use the Win32 API.
133
The most fundamentatal piece of information a file must have is a name. Once the user
has created a file, whether the file is empty or not, the operating system assigns basic
pieces of information to it. Once a file is created, it can be opened, updated, modified,
renamed, etc.
6.2
1.
Start Borland C++ Builder or create a new project with its default form
2.
3.
4.
134
Save As Type
This allows the
user to set an
extension
The primary role of the Save As dialog box is to allow users to store a file on the hard
drive of the computer, on a portable media such as a floppy disk, or on a network drive.
To make this efficient and complete, the user must supply two valuable pieces of
information: the location and the name of the file. The location of a file is also known as
its path.
The name of a file follows the directives of the operating system. On MS DOS and
Windows 3.X, it had to be in an 8.3 format. The actual name had to have a maximum of 8
characters with restrictions on the characters that could be used. The user also had to
specify three characters after a period. The three characters, known as the file extension,
were used by the operating system to classify the file. That was all necessary for those 8bit and 16-bit operating systems.
Various rules have changed. For example, the names of folders and files on Microsoft
Windows >= 95 can have up to 255 characters. The extension of the file is mostly left to
the judgment of the programmer but the files are still using extensions. Applications can
also be configured to save different types of files; that is, files with different extensions.
To use the Save As dialog box, users usually click an item under the File menu. Here is
how it works for most regular applications. The user creates a new file. If the user wants
to save the file, she can click File -> Save. If the file was not previously saved, the
application would call the Save As dialog box. If a file is displaying, whether it was
saved previously or not, the user can also click File -> Save As... which also would call
the Save As dialog box.
Two objects are particularly important on the Save As dialog box: The Save In combo
box and the File Name edit box or combo box (the File Name box is made of a combo
box to make it user-friendly but over all, users hardly use the list side of this combo box).
Since Windows 95, the user does not have to specify an extension if the programmer
makes it easy. To help with this, the Save As dialog box is equipped with a Save As Type
combo box. This combo box allows the user to select one of the extensions. The available
extensions have to be created by the programmer so the user can select from this preset
list. If the programmer neglects this, the user would have no extension to select from.
Although the file can still be saved, the operating system would not associate it with a
known type of file. Therefore, if you specify a series of extensions, the user can select
one of these and, in the File Name box, she can simply type a name for the file. If the
user does not specify an extension, the operating system would allocate the extension of
the Save As Type combo box. Users of regular commercial applications, such as word
processors, spreadsheet programs, or databases, etc, are usually trained not to care about
135
the extensions and let the application deal with that detail. In some other circumstances,
the users must pay close attention to the extension they give a file (this is common on
web development or graphics design).
After working on a Save As dialog box, the user can click Save or press Enter, which
would validate her entries. To change her mind, regardless of what she did on the Save
As dialog box, she can click Cancel or press Esc, which would dismiss the dialog box and
ignore what she did (in reality, some actions cannot be ignored, such as creating a new
file or folder inside of the Save As dialog box, deleting, cutting, or pasting files, etc; but
if the user clicked Cancel or pressed Esc, the new file would not be saved).
Alternatively, if you cannot add a SaveDialog control at design time, you can create one
at run time when you need it in an event or a function. If you want the dialog box to be
accessible to more than one event or function, you can declare a pointer to a
TSaveDialog class. Here is an example:
private:
AnsiString CurrentFile;
TSaveDialog * dlgSave;
// User declarations
public:
// User declarations
__fastcall TForm1(TComponent* Owner);
};
To make the control available to the form, you can initialize it in the constructor of the
form as follows:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
dlgSave = new TSaveDialog(Form1);
}
//---------------------------------------------------------------------------
Eventually, when the form closes, you can make sure the memory occupied by the
control is freed by deleting the dynamic control. This can be done in the OnDestroy
event of the form:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDestroy(TObject *Sender)
{
delete dlgSave;
dlgSave = NULL;
}
//---------------------------------------------------------------------------
136
The Prompt is a section that defines what the user would see in the Save As Type combo
box. An example would be 24-bit Bitmap. Such a string does not let the user know what
actual extension the file would use. Therefore, as a courtesy, you can specify, between
parentheses, the extension that would be applied if this extension is used. Therefore, the
Prompt can be 24-bit Bitmap (*.bmp). In this case, the extension used would be bmp.
The asterisk * lets the user know that whatever is provided as the file name would be
used in place of the asterisk. The period indicates the separation from the file to its
extension. This means that the characters on the left of the period would be the file name,
the characters on the right side of the period would be used as the actual file extension.
To specify the extension that the operating system would use to associate to the file, you
provide a second part of the string as Extension. In Microsoft Windows, most extensions
are made of three characters. Some applications use a 2-letter extensions (for example
Perl files have a pl extension) and some others use 4 letters (such as html for some
HTML files). This depends on the programmer (or the company that is publishing the
application). An example of a string the species an extension is:
24-bit Bitmap (*.bmp)|*.bmp
If you want to provide various extensions to your Save dialog box, you can separate them
with a | symbol. An example would be:
HTML Files (*.htm)|*.htm|Active Server Pages (*.asp)|*.asp|Perl Script (*.pl)|*.pl
To make the exensions available to the SaveDialog control, you can assign the string to
the Filter property. Here is an example:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
dlgSave = new TSaveDialog(Form1);
dlgSave->Filter = "HTML Files (*.htm)|*.htm|"
"Active Server Pages (*.asp)|*.asp|"
"Apache Files (*.php)|*.php|"
137
Once you know the types of files that your application will be dealing with, you can
make your dialog box friendly by displaying the most likely extension for a document
created using your application. For example, if you create a Memo-based application,
users are more likely to create a text file with it. If you create a RichEdit-based
application, users are more likely to create a Rich Text Format file with it. This most
likely extension is known as the default extension, it allows the user not to specify an
extension. By simply providing a file name and clicking Save, the operating system
would associate the file with the default extension. Of course, if you create a filter, the
user can specify a desired allowed extension.
To specify the default extension for your SaveDialog object, type the desired extension in
the DefaultExt field of the Object Inspector.
If you had created a Filter and if you provide a default extension for a SaveDialog
object, make sure it is one of the file extensions specified in the Filter list.
Microsoft Windows operating systems, especially since Windows 9X, are configured to
have a default folder in which users are most likely to save their files. On Windows 9X, it
is C:\My Documents. On Windows NT, it is usually specified by the network
administrator. On Windows 2000 and Windows XP, it uses a more customized scenario.
These settings are known to the operating system and you will usually not be concerned
with them. In a rare circumstance, if you want to specify in which folder the users should
save their files by default, you can provide it in the InitialDir property. This directory
usually ends with \My Documents. If you want to find the path to the My Documents for
a user, you can call the SHGetFolderPath(). Its syntax is:
138
HRESULT SHGetFolderPath(
HWND hwndOwner,
int nFolder,
HANDLE hToken,
DWORD dwFlags,
LPTSTR pszPath
);
Here is an example:
//--------------------------------------------------------------------------#include <vcl.h>
#include <shfolder.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
char strBuffer[MAX_PATH];
SHGetFolderPath(NULL,
CSIDL_PERSONAL,
NULL,
NULL,
strBuffer);
Edit1->Text = strBuffer;
}
//---------------------------------------------------------------------------
Once again, most of the time, you will not be concerned with this issue if you are creating
an application for any user.
Probably the most important issue users care about, as far as they are concerned, is a
name for the file they are trying to save. Users know that they can set the name of the file
in the File Name box. To make their saving a little faster, you can provide a default name
for a file in case a user does not want to specify a file name. This is done by typing a
name in the FileName field of the Object Inspector. In practicality, the FileName value
is the string that displays in the File Name box of the Save As dialog box.
139
On the Dialogs tab of the Component Palette, click the SaveDialog button
click the form
and
2.
While the Save Dialog1 button is still selected on the dialog, in the Object Inspector,
click the DefaultExt field and type rtf
3.
4.
5.
Click OK
6.
7.
8.
In the Object Inspector, click the Events tab and double-click OnDblClick
9.
10. Execute the application. To test the Save As dialog box, double-click anywhere on
the form
140
6.3
6.3.1 Introduction
One of the most usual involvements with computer files consists of opening them for
review or for any other reason the user judges appropriate. Microsoft Windows provides
a convenient dialog box to handle the opening of files. The job is performed by using the
Open File dialog box:
141
142
1.
and click
2.
While the OpenDialog1 icon is still selected on the form, in the Object Inspector,
click DefaultExt and type rtf
3.
4.
5.
Click OK
6.
7.
Click an unoccupied area on the form to select it and, in the Object Inspector, click
the Events property page
8.
9.
143
6.4
6.4.1 Introduction
The Visual Component Library ships with other dialog boxes useful for tasks that involve
files, such as letting the user browse the hard drive to select a folder. This can be done
using the Browse For Folder dialog box:
144
The VCL provides a convenient dialog box used to browse the drives of the users
computer or a computer on the network to locate a folder or a mapped drive.
This function takes three arguments and returns two values. The Caption parameter
displays under the title bar but above the tree view of the dialog box. The Root value is a
string that represents the name of the root drive. The Directory string is the default path
folder. This function returns a Boolean value upon exiting. Here is an example of using
the SelectDiretory() function:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDirectoryClick(TObject *Sender)
{
AnsiString Caption = "Select a Directory";
const WideString Root = "C:\"";
AnsiString Directory = "C:\\Program Files";
SelectDirectory(Caption, Root, Directory);
}
//---------------------------------------------------------------------------
When the dialog box opens, it displays two buttons: OK and Cancel. The OK button is
disabled because no directory would have been selected. To use the Browse For Folder
dialog box, the user clicks the + button to expand a folder, and the (if any) button to
collapse a folder. Once the user locates the desired folder, he must click it to highlight it,
which enables the OK button.
After using the Browse For Folder dialog box, if the user clicks Cancel or presses Esc,
whatever change was made would be dismissed and the function would return false. If
the user clicks OK or presses Enter, the function would return the full path of the folder
the user had selected. This path is the returned Directory argument. You can use a
conditional statement to find out what button the user had clicked then use the returned
Directory string as you see fit. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDirectoryClick(TObject *Sender)
{
AnsiString Caption = "Select a Directory";
const WideString Root = "C:\"";
AnsiString Directory = "C:\\Program Files";
if( SelectDirectory(Caption, Root, Directory) == True )
edtInstallation->Text = Directory;
}
//---------------------------------------------------------------------------
145
6.5
6.5.1 Introduction
An overloaded version of the SelectDirectory() function allows performing a different
type of folder selection:
Like the other SelectDirectory() function, this version returns two values: a Boolean type
and a string. Once again, the Directory argument is required, although it is used as a
sample that the user can change. Since the root drive is not a directory, you cannot set its
value as C:, C:\, A:, A:\ or the likes. The Directory value must be a valid path
of an existing folder. When the dialog displays, the string of the Directory is selected,
which enables the OK and the Cancel buttons. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
AnsiString Directory = "F:\\Corel\\WordPerfect Office 2002\\Graphics";
SelectDirectory(Directory, TSelectDirOpts(), 0);
}
//---------------------------------------------------------------------------
146
If the user wants to use a directory that does not exist, if the directory can be created, add
the sdPerformCreate option to the set as follows:
TSelectDirOpts() << sdAllowCreate << sdPerformCreate;
If the user types a directory that does not exist, it would be created transparently. If you
want the user to confirm that he wants to create a new folder, add the sdPrompt option to
the set. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
AnsiString Directory = "C:\\Program Files\\Borland\\Delphi4\\Projects";
SelectDirectory(Directory, TSelectDirOpts() << sdAllowCreate
<< sdPerformCreate
<< sdPrompt, 0);
}
//---------------------------------------------------------------------------
The last option allows you to specify a help file to provide context sensitive help to the
user. To use it, associate an integer that is mapped to the appropriate identifier in the Help
file. Once you set this argument and the application has a help file, a Help button would
appear on the dialog box. Here is an example of using the help argument:
147
6.6
The print dialog box allows a user to select a printer if more than one are available. The
user can decide either to print the whole document, to print a range of pages, or to print a
148
portion of the document that was previously selected. The user can also decide on the
number of copies to print from the document, the range specified, or the selected portion.
Furthermore, the user can access the particular characteristics of the selected printer and
specify how the printer should perform the job. For example, if the selected printer can
print in color and the document is in color but the user wants to print in black and white,
she can specify this using the Properties button.
If you want to allow your users to customize or control their printing, you can provide
them with the Print dialog box:
In the VCL, the Print dialog box is represented through the TPrintDialog class. To add
printing during the design of your application, on the Dialogs property sheet of the
Component Palette, you can click the PrintDialog button and click on the form.
Copyright 2003 FunctionX, Inc.
149
If you want to programmatically provide a Print dialog box, you can declare a pointer to
TPrintDialog class. The compiler will need to know "who" owns the printer. This can be
done as follows:
// Adding a Print dialog box at run time
TPrintDialog *dlgPrint = new TPrintDialog(Form1);
Using a menu item or a toolbar button, you can call the Execute() method of the
TPrintDialog class. As a Boolean function, you should first make sure that the user was
able to open the Print dialog box, then you can execute printing. Suppose you have a
RichEdit control whose content you want to print and suppose you have added a menu
item called mnuPrint and a PrintDialog control named PrintDialog1, you can perform
printing with the following code:
//--------------------------------------------------------------------------void __fastcall TForm1::mnuPrintClick(TObject *Sender)
{
if( PrintDialog1->Execute() )
RichEdit1->Print("");
}
//---------------------------------------------------------------------------
The TPrintDialog class provides all the options to customize the behavior of the Print
dialog box.
One of the first choices people make when printing is whether to print the whole
document or just sections of it. By default, the Print dialog box always allows users to
print the document completely. This is represented by the All radio button. Most other
characteristics of the Print dialog box are not singly set. A particular characteristic
usually works in connection with another effect of the same Print dialog box.
When customizing the Print dialog box, you will mostly set its options using the Options
property with one or more other related properties. The Options property is a Set; this
means that it allows you to combine the options you want. If you want users to be able to
select a portion of text and print only the selected portion, you must make sure the
Selection radio button is available. This is done by setting the poSelection option to true.
If you want the dialog box to appear with the Selection radio button selected, you can set
the PrintRange property to prSelection. Usually this would not be a standard good idea.
If you want this radio button to be selected when the dialog box comes up, you should
first make sure that a portion of the document to be printed has been selected. For
example, if you are (or the user is) trying to print from a RichEdit control, you can check
whether there is already selected text before displaying the dialog box as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnPrinterClick(TObject *Sender)
{
if( RichEdit1->SelLength )
PrintDialog1->PrintRange = prSelection;
else
PrintDialog1->PrintRange = prAllPages;
if( PrintDialog1->Execute() )
RichEdit1->Print("");
}
//---------------------------------------------------------------------------
150
By default, the range of pages to print from the document is not available to the users. If
you want the users to be able to set the range, you can set the poPageNums option to true.
Consequently, on a document that has 12 pages, users can print for example pages from 4
through 8.
6.7
When using the Print Setup dialog box, the user must first select a printer. This is usually
done already by the operating system that selects the default printer of the computer that
called this dialog box. Otherwise, if there is more than one printer, the user can change it
using the Name combo box.
The options of the Print Setup dialog box depend on the driver of the printer selected in
the Name combo box.
The Print Setup dialog box allows the user to select a printer, if there is more than one,
and to customize the appearance of the paper on which the document would be printed.
On the Print Setup, the user can click the arrow of the Size combo box and select one of
the configured sizes of paper:
151
If the printer has many trays, as indicated by the driver of the selected printer, the user
can select which tray would be used when printing. As it happens, one printer can have
only one tray while another printer can have 3, 5, or more:
If the desired printer is on a network, the user can click the Network button to locate it.
She also has the option to print the document in Portrait (vertical) or in Landscape
(horizontal) position.
152
C File Processing
After instantiating the structure, you can define what to do with it, using one of the
provided functions. Because FILE was created as a C structure, it does not have member
functions. The functions used to perform its related operations on files are also declared
in the stdio.h header file.
Open the StdGrades project from the Students Grades1 folder from the resources
that accompany this book and display the main form
153
The first argument, FileName, must be a valid name of a file. If the user is creating or
saving a new file, you can let him specify the name of the file, following the rules of the
operating system. If the user is opening an existing file, you can make sure the file really
exists, retrieve its name and pass it to the fopen() function.
Because the fopen() function is used to save a new file, to open an existing one, or to
save a file that was only modified, the second argument, Mode, actually allows you to
decide what operation the function will be used to perform. This argument is a short
string of one or two characters and can be one of the following:
Mode
Role
Opens an existing
file, saves new file,
or saves a existing
file that has been
modified
Opens an existing file
r+
w+
a+
If the operation performed using the fopen() function is successful, the function returns a
pointer to the FILE instance that was declared. The FILE structure is usually used in C
and C++ console programs that must conform to console applications. However, when
used in VCL applications, because applications are created in a visual development, you
should let the users use the Save and Open common dialog boxes that they are used to. In
this case, if the user is opening a file, you can pass the FileName member variable of the
common dialog box to the fopen() function. Because the fopen() function takes a pointer
to char while the Save and Open dialog boxes use AnsiString members, you should
convert The TOpenDialog::FileName or the TSaveDialog::FileName to a C string.
After using a file, you should/must close its stream. This is done using the fclose()
function. Its syntax is:
int fclose(FILE *stream);
154
To use this function, you must let it know what instance of the FILE object you are
closing.
From the Dialogs tab of the Component Palette, click the OpenDialog button
and click anywhere in the form
2.
3.
4.
From the Dialogs tab of the Component Palette, click the SaveDialog button
and click anywhere in the form
5.
On the Object Inspector, change its DefaultExt to rcd and, in its Filter field, type
Student Record (*.rcd)|*.rcd|All Files
6.
On the top section of the Main.cpp file, include the stdio library file as follows:
//--------------------------------------------------------------------------#include <vcl.h>
#include <cstdio>
using namespace std;
#pragma hdrstop
#include "Main.h"
//---------------------------------------------------------------------------
7.
To perform the opening of a record, on the form, double-click the Open button and
implement its OnClick event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnOpenClick(TObject *Sender)
{
FILE *FOpen;
if( OpenDialog1->Execute() )
155
}
fclose(FOpen);
}
//---------------------------------------------------------------------------
8.
To perform the saving of a record, on the form, double-click the Save button and
implement its OnClick event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnSaveClick(TObject *Sender)
{
FILE *FSave;
if( SaveDialog1->Execute() )
{
FSave = fopen(SaveDialog1->FileName.c_str(), "w");
if( FSave == NULL )
{
ShowMessage("The file could not be opened");
return;
}
}
fclose(FSave);
}
//---------------------------------------------------------------------------
9.
Each one of these functions takes a few arguments depending on how it is used. The first
argument, stream, must be an instance of a FILE structure.
The second argument is a string that specifies how data will be formatted and possibly
positioned in the stream instance.The string typically starts with the % symbol followed
by one or more characters that represents a format. Different formats are used depending
on the type of data of the variable that is being written. You can use one the following
characters:
156
Character
Used for
c
d
e
f
g
h
i
o
s
u
x
A single character
An integer
A floating-point number
A floating-point number
A floating-point number
A short integer
A decimal, a hexadecimal, or an octal integer
An octal integer
A string followed by a white space character
An unsigned decimal integer
A hexadecimal integer
After specifying the format, you can type the name of the variable that is being saved.
You can repeatedly use the fprintf() function for each variable you want to save. If you
have opened a file and want to retrieve data stored from it, you can use the fscanf() or the
fwscanf() function. Their syntaxes are:
int fscanf(FILE *stream, const char *format[, address, ...]);
int fwscanf(FILE *stream, const wchar_t *format[, address, ...]);
The first argument, stream, must be a valid instance of a FILE structure. The second
argument, format, follows the same rules as for the fprintf() and the fwprintf() functions.
After typing the format, type the name of the variable that is being retrieved.
In the Main.cpp file, change the OnClick event of the Open button as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnOpenClick(TObject *Sender)
{
FILE *FOpen;
char FirstName[30], LastName[30], DOB[40];
int Gender;
char English[6], Language2[6], History[6],
Geography[6], Sciences[6], Sports[6];
if( OpenDialog1->Execute() )
{
FOpen = fopen(OpenDialog1->FileName.c_str(), "r+");
if( FOpen == NULL )
{
ShowMessage("The file could not be opened");
return;
}
fscanf(FOpen,
fscanf(FOpen,
fscanf(FOpen,
fscanf(FOpen,
"%s", FirstName);
"%s", LastName);
"%s", DOB);
"%d", &Gender);
157
fscanf(FOpen,
fscanf(FOpen,
fscanf(FOpen,
fscanf(FOpen,
fscanf(FOpen,
fscanf(FOpen,
"%s",
"%s",
"%s",
"%s",
"%s",
"%s",
English);
Language2);
History);
Geography);
Sciences);
Sports);
edtFirstName->Text = FirstName;
edtLastName->Text = LastName;
edtDOB->Text
= DOB;
cboGender->ItemIndex = Gender;
edtEnglish->Text
= English;
edt2ndLanguage->Text = Language2;
edtHistory->Text
= History;
edtGeography->Text = Geography;
edtSciences->Text = Sciences;
edtSports->Text
= Sports;
fclose(FOpen);
}
//---------------------------------------------------------------------------
2.
In the Main.cpp file, change the OnClick event of the Save button as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnSaveClick(TObject *Sender)
{
FILE *FSave;
char FirstName[30], LastName[30], DOB[40];
int Gender;
double English, Language2, History, Geography, Sciences, Sports;
strcpy(FirstName, edtFirstName->Text.c_str());
strcpy(LastName, edtLastName->Text.c_str());
strcpy(DOB, edtDOB->Text.c_str());
Gender = cboGender->ItemIndex;
English = StrToFloat(edtEnglish->Text);
Language2 = StrToFloat(edt2ndLanguage->Text);
History = StrToFloat(edtHistory->Text);
Geography = StrToFloat(edtGeography->Text);
Sciences = StrToFloat(edtSciences->Text);
Sports = StrToFloat(edtSports->Text);
if( SaveDialog1->Execute() )
{
FSave = fopen(SaveDialog1->FileName.c_str(), "w");
if( FSave == NULL )
{
ShowMessage("The file could not be opened");
return;
}
fprintf(FSave,
fprintf(FSave,
fprintf(FSave,
fprintf(FSave,
158
"%s\n", FirstName);
"%s\n", LastName);
"%s\n", DOB);
"%d\n", Gender);
7.2
3.
4.
7.2.1 Overview
File processing in C++ is performed using the fstream class. Unlike the FILE structure,
fstream is a complete C++ class with constructors, a destructor and overloaded operators.
To perform file processing, you can declare an instance of an fstream object. If you do
not yet know the name of the file you want to process, you can use the default
constructor.
Unlike the FILE structure, the fstream class provides two distinct classes for file
processing. One is used to write to a file and the other is used to read from a file.
159
Description
ios::app
ios::ate
ios::in
ios::out
ios::trunc
ios::nocreate
ios::noreplace
Image you have a form with three edit boxes whose job is to get the first name, the last
name, and the age of a student, you can save its data using a SaveDialog as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnSaveClick(TObject *Sender)
{
char FirstName[30], LastName[30];
int Age;
strcpy(FirstName, edtFirstName->Text.c_str());
strcpy(LastName, edtLastName->Text.c_str());
Age = edtAge->Text.ToInt();
if( SaveDialog1->Execute() )
{
160
The default constructor, ofstream(), can be used to create an empty stream if you do not
yet have enough information about the file you intend to deal with and what type of
operation you will perform. This constructor is used if you plan to call member methods
to perform the desired file processing.
After declaring an instance of the ofstream class, you can use the ofstream::open()
method to create the file. The syntax of the open() method is:
void open( const char* FileName, int FileMode);
This method behaves exactly like, and uses the same arguments as, the constructor we
described above. The first argument represents the name of the file you are dealing with
and the FileMode argument follows the modes of the above table.
Because the fstream class in this case is declared as ofstream, the compiler is aware that
you want to save a file (in reality, the use of ofstream means that you want to write to a
file, in other words you want the FileMode with a value of ios::out), you can use the first
constructor with just the FileName as argument or you can call the open() method with
only the name of the file. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnSaveClick(TObject *Sender)
{
char FirstName[30], LastName[30];
int Age;
strcpy(FirstName, edtFirstName->Text.c_str());
strcpy(LastName, edtLastName->Text.c_str());
Age = edtAge->Text.ToInt();
if( SaveDialog1->Execute() )
{
ofstream Students;
Students.open(SaveDialog1->FileName.c_str());
Students << FirstName << "\n" << LastName << "\n" << Age;
}
}
//---------------------------------------------------------------------------
After using a file, you should close it. This is taken care by using the ofstream::close()
method whose syntax is:
void close();
Open the BodyTag1 application you created in the previous lessons. If you do not
have it, open the BodyTag2 project from the exercises that accompany this book
161
2.
Display the main form, frmMain. From the Dialogs tab of the Component Palette,
double-click the SaveDialog button
3.
While the SaveDialog button is still selected on the form, on the Object Inspector,
change the DefaultExt to btd
4.
5.
6.
7.
From the Additional tab of the Component Palette, double-click the BitBtn button
8.
Set its Glyph as the Floppy1 bitmap from the Bitmaps folder that accompanies this
ebook
9.
Change the buttons Name to btnSave and change its Caption to &Save
10. Double-click the new Save button to access its OnClick() event
11. On the top section of the source file, include the fstream library:
//--------------------------------------------------------------------------#include <vcl.h>
#include <fstream>
using namespace std;
#pragma hdrstop
162
BlueBG
= GetBValue(mmoPreview->Color);
RedText = GetRValue(edtPreviewText->Font->Color);
GreenText = GetGValue(edtPreviewText->Font->Color);
BlueText = GetBValue(edtPreviewText->Font->Color);
RedLink = GetRValue(edtPreviewLink->Font->Color);
GreenLink = GetGValue(edtPreviewLink->Font->Color);
BlueLink = GetBValue(edtPreviewLink->Font->Color);
RedALink = GetRValue(edtPreviewALink->Font->Color);
GreenALink = GetGValue(edtPreviewALink->Font->Color);
BlueALink = GetBValue(edtPreviewALink->Font->Color);
RedVLink = GetRValue(edtPreviewVLink->Font->Color);
GreenVLink = GetGValue(edtPreviewVLink->Font->Color);
BlueVLink = GetBValue(edtPreviewVLink->Font->Color);
if( SaveDialog1->Execute() )
{
ofstream FormatToSave(SaveDialog1->FileName.c_str(), ios::out);
if( !FormatToSave )
{
ShowMessage("There was a problem saving the file.");
return;
}
Caption = "HTML Body Tag Formatter - " +
ExtractFileName(SaveDialog1->FileName);
FormatToSave << RedBG << "\n" << GreenBG << "\n"
<< BlueBG << "\n" << RedText << "\n"
<< GreenText << "\n" << BlueText << "\n"
<< RedLink << "\n" << GreenLink << "\n"
<< BlueLink << "\n" << RedALink << "\n"
<< GreenALink << "\n" << BlueALink << "\n"
<< RedVLink << "\n" << GreenVLink << "\n"
<< BlueVLink << "\n";
FormatToSave.close();
}
}
//---------------------------------------------------------------------------
163
The first argument of the constructor, FileName, is a constant string that represents the
file that you want to open. The FileMode argument is a natural number that follows the
table of modes as we described above.
If necessary, you can also declare an empty instance of the ifstream class using the
default constructor:
ifstream();
After declaring this constructor, you can use the ifstream::open() method to formally
open the intended file. The syntax of the open() method is:
open( const char* FileName, int FileMode);
This method uses the same arguments as the above constructor. By default, when
declaring an instance of the ifstream class, it is assumed that you want to open a file; that
is, you want to use the FileMode attribute with a value of ios::in. Therefore, the second
argument is already set to ios::in value. This allows you to call the open() method with
just the FileName value.
After using the ifstream class, you can close it using the ifstream::close() method. Here
is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnOpenClick(TObject *Sender)
{
char FirstName[30], LastName[30];
int Age;
if( SaveDialog1->Execute() )
{
ifstream Students;
Students.open(SaveDialog1->FileName.c_str());
Students >> FirstName >> LastName >> Age;
Students.close();
edtFirstName->Text = FirstName;
edtLastName->Text = LastName;
edtAge->Text = Age;
}
}
//---------------------------------------------------------------------------
Display the main form, frmMain. From the Dialogs tab of the Component Palette,
double-click the OpenDialog button
164
2.
While the OpenDialog button is still selected on the form, on the Object Inspector,
change the DefaultExt to btd
3.
4.
5.
6.
From the Additional tab of the Component Palette, double-click the BitBtn button
7.
Set its Glyph as the Open1 bitmap from the Bitmaps folder that accompanes this
ebook
8.
Change the buttons Name to btnOpen and change its Caption to &Open
9.
Double-click the Open button and implement its OnClick() event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnOpenClick(TObject *Sender)
{
DWORD RedBG, GreenBG, BlueBG,
RedText, GreenText, BlueText,
RedLink, GreenLink, BlueLink,
RedALink, GreenALink, BlueALink,
RedVLink, GreenVLink, BlueVLink;
ifstream FormatToOpen;
if( OpenDialog1->Execute() )
{
FormatToOpen.open(OpenDialog1->FileName.c_str(), ios::in);
if( !FormatToOpen )
{
ShowMessage("There was a problem opening the file.");
return;
}
Caption = "HTML Body Tag Formatter - " +
ExtractFileName(OpenDialog1->FileName);
FormatToOpen >> RedBG >> GreenBG >> BlueBG
>> RedText >> GreenText >> BlueText
>> RedLink >> GreenLink >> BlueLink
>> RedALink >> GreenALink >> BlueALink
165
}
}
//---------------------------------------------------------------------------
166
7.3
7.3.1 Introduction
The Visual Component Library (VCL) provides various built-in classes to perform file
processing. Most of the features are provided through the TFileStream class. To perform
file streaming using this class, first declare its instance using its constructor whose syntax
is:
__fastcall TFileStream(const AnsiString FileName, Word Mode);
The first argument of this constructor is the name (or path) of the file you are dealing
with. If the file does not exist or it cannot be accessed (opened or saved) for any reason,
the compiler would throw an error and stop the action.
The second action, Mode, specifies what you are trying to do with the file.
The TFileStream class is conceptually designed to deal with the contents of one or more
controls. Therefore, instead of concerning yourself with the values of controls,
TFileStream would consider the change that affect a control on behalf of the user, which
mostly is its contents. When saving a file, TFileStream faithfully gets the contents of all
controls as you wish and saves them in one file. If opening a file, TFileStream locates
the content of each file and restores it. Based on this, TFileStream is appropriate for
VCL objects and usually its files should not mixed with non-VCL controls.
167
When saving a file, the Mode argument of the constructor can be passed as one of the
following constants:
Write Mode
fmCreate
fmOpenWrite
fmOpenReadWrite
Description
If the user is saving a new file, this is used to save it as new
This is used to save a file as new. If the file was previously saved
and reopened, this mode would erase its previous contents and fill it
with the new data
If the file already existed, this can be used to save a file that has
been modified. Otherwise it can be used to create a new file
When it is possible that other people or application would try accessing the same file at
the same time, the following constants can be used to manage the sharing of files:
Share Mode
Description
fmShareExclusive
fmShareDenyWrite
fmShareDenyRead
fmShareDenyNone
To combine these two mode for the Mode argument, you use the bitwise OR operator |
Imagine you create a form equipped with a Memo and an Edit controls:
168
}
//---------------------------------------------------------------------------
Description
fmOpenRead
This is used to open a file but the user cannot modify and then save
it.
This allows opening an existing file, modifying, and saving it.
fmOpenReadWrite
You can combine this mode with one of the above share modes using the bitwise OR
operator. Here is an example from the same above form design:
//--------------------------------------------------------------------------void __fastcall TForm1::btnOpenClick(TObject *Sender)
{
TFileStream *FStream;
if( OpenDialog1->Execute() )
{
try {
FStream = new TFileStream(OpenDialog1->FileName,
fmOpenRead | fmShareExclusive);
FStream->ReadComponent(Memo1);
FStream->ReadComponent(Edit1);
169
}
__finally
{
delete FStream;
}
}
}
//---------------------------------------------------------------------------
7.4
7.4.1 Introduction
The Visual Component Library supports another technique of file processing. Instead of
saving the components as streamable objects, it gives you the option of saving the
contents of controls. These contents are taken as data and not as VCL components. We
will refer to this technique as file buffering.
To process the contents of controls as streamable values, the value of each object of the
application is taken in its C/C++ context, as a variable created from known data types.
The application itself is created like any other:
The WriteBuffer() method is used when you must let the compiler know the amount of
memory space you want to use for a particular variable. It requires two arguments. The
first, Buffer, is the value that needs to be saved. The Count parameter specifies the
number of bytes that the value will need to the stored.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnSaveClick(TObject *Sender)
{
TFileStream
*Streamer;
char
FullName[40];
TDateTime DOB;
170
Integer
Gender;
strcpy(FullName, edtFullName->Text.c_str());
DOB
= StrToDate(edtDOB->Text);
Gender
= cboGender->ItemIndex;
if( SaveDialog1->Execute() )
{
try
{
Streamer = new TFileStream(SaveDialog1->FileName, fmCreate);
Streamer->WriteBuffer(&FullName, 40);
Streamer->WriteBuffer(&DOB, 40);
Streamer->WriteBuffer(&Gender, 20);
}
__finally
{
delete Streamer;
}
}
}
//---------------------------------------------------------------------------
The ReadBuffer() method also requires two pieces of information. The Buffer parameter
is the value that needs to be read. The Count parameter is used to specify the number of
bytes that need to be read for the Buffer value.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnOpenClick(TObject *Sender)
{
TFileStream *Streamer;
char FullName[40];
TDateTime DOB;
Integer
Gender;
if( OpenDialog1->Execute() )
{
try
{
Streamer = new TFileStream(OpenDialog1->FileName, fmOpenRead);
edtFullName->Text.Delete(0, 40);
edtDOB->Text.Delete(0, 40);
cboGender->ItemIndex = 2;
Streamer->ReadBuffer(&FullName, 40);
Streamer->ReadBuffer(&DOB, 40);
Streamer->ReadBuffer(&Gender, 20);
171
edtFullName->Text = FullName;
edtDOB->Text = DateToStr(DOB);
cboGender->ItemIndex = StrToInt(Gender);
}
__finally
{
delete Streamer;
}
}
}
//---------------------------------------------------------------------------
7.5
The lpFileName argument is a null-terminated string that respresents the name of the file.
You can explicitly specify it as a double-quoted string. If you want the user to save the
current file or to open an existing file by the user specifying the name of the file, you can
use an OpenDialog or a SaveDialog objects. In this case the lpFileName can be obtained
with the TOpenDialog::FileName or the TSaveDialog::FileName mamber variables.
The dwDesiredAccess argument specifies the type of operation that will be performed on
the file. It can have one or a combination of the following values:
172
0: Information about a device (floppy, CD, DVD, hard disk, etc) will be
retrieved without a file being accessed
READ_CONTROL: The read rights of the file can be accessed or read from
FILE_EXECUTE: The file can be executed. For example, if the file being
accessed is an application, it can be launched
Omitting the 0 value, the above flags can be combined using the bitwise OR operator.
Alternatively, you can pass one of the following values for a combination of flags:
STANDARD_RIGHTS_REQUIRED:
Combines
READ_CONTROL, WRITE_DAC, and WRITE_OWNER
GENERIC_EXECUTE:
combines
FILE_READ_ATRIBUTES,
STANDARD_RIGHTS_EXECUTE, and SYNCHRONIZE
GENERIC_READ:
combines
FILE_READ_ATRIBUTES,
FILE_READ_DATA, FILE_READ_EA, STANDARD_RIGHTS_READ,
and SYNCHRONIZE
GENERIC_WRITE:
combines
FILE_APPEND_DATA,
FILE_WRITE_ATRIBUTES, FILE_WRITE_DATA, FILE_WRITE_EA,
STANDARD_RIGHTS_WRITE, and SYNCHRONIZE
DELETE,
READ_CONTROL,
The dwShareMode argument controls how the file can be shared. Its possibles values are:
The lpSecurityAttributes argument specifies some security attributes used when creating
the file. Such attibutes are defined as SECURITY_ATTRIBUTES value. You can pass
this argument as NULL, in which case the security attributes would be ignored.
173
The dwCreationDisposition argument specifies the behavior to adopt whether the file
already exists or is just being created. It can have one of the following values:
CREATE_ALWAYS: If the file does not exist, it will be created. If the file
exists already, it will be destroyed and replaced by a new one with an attribute
of Archive
CREATE_NEW: If the file does not exist, it will be created. If the file already
exists, the CreateFile() function would fail
OPEN_ALWAYS: If the file does not exist, it will be created. If the file already
exists, it would be opened
OPEN_EXISTING: If the file does not exist, the CreateFile() function would
fail. If the file already exists, it would be opened
The dwFlagsAndAtributes argument specifies the attribute(s) to apply to the file. It can
have one or a combination of the following values:
The above attributes can also be combined with the following values:
FILE_FLAG_BACKUP_SEMANTICS
FILE_FLAG_DELETE_ON_CLOSE
FILE_FLAG_NO_BUFFERING
FILE_FLAG_OPEN_NO_RECALL
FILE_FLAG_OPEN_REPARSE_POINT
FILE_FLAG_OVERLAPPED
FILE_FLAG_POSIX_SEMANTICS
FILE_FLAG_RANDOM_ACCESS
FILE_FLAG_SEQUENTIAL_SCAN
FILE_FLAG_WRITE_THROUGH
The hTemplateFile argument is a handle to a template file. If you do not have or cannot
use a file template (for example they are not supported on home operating systems), pass
this argument as NULL.
174
If the CreateFile() function succeeds, it returns a handle to the file and you can use it as
you see fit, to analyze it, to read its contents if allowed, or to write data to it if permitted.
If this function fails, it returns INVALID_HANDLE_VALUE.
Open the FastFood1 application from the exercises that accompany this book
2.
In the header file of the main form, create a structure called TCustomerOrder and
declare its instance in the private section of the forms class:
//--------------------------------------------------------------------------struct TCustomerOrder
{
char
strClerk[20];
TDateTime dteOrderDate;
Integer
iBread;
Integer
iMeat;
Boolean
bLettuce;
Boolean
bOnion;
Boolean
bTomato;
Boolean
bPickles;
Integer
iIngredients;
Boolean
bCheese;
Boolean
bBacon;
};
//--------------------------------------------------------------------------class TfrmMain : public TForm
{
__published: // IDE-managed Components
...
private:
void __fastcall EvaluatePrice();
TCustomerOrder CustOrder;
// User declarations
public:
// User declarations
__fastcall TfrmMain(TComponent* Owner);
};
//---------------------------------------------------------------------------
3.
Save All
175
The hFile argument is the handle to the file. It is typically the return value of a call to the
CreateFile() function. Since in this case we are trying to save a file, the file should/must
have been created with at least the GENERIC_WRITE flag for the dwDesiredAccess
argument of the CreateFile() function.
The lpBuffer argument is the object that will be saved. As you can see, it is defined as a
pointer to VOID, meaning that its type is not known to the function, which leaves it up to
you to decide what type of value is being saved. It can be a C/C++ generic type (integer,
character, floating-point value, or their variants). It can be a VCL or a Win32 type of
object. It can also be a completely new type that you create as a simple C structure or a
more elaborate C++, VCL, or Win32 class.
The nNumberOfBytesToWrite argument is the number of bytes to save. As the DWORD
type indicates, it must be a positive integer.
The lpNumberOfBytesWritten argument is a positive integer returned by the function as
the number of bytes that were written.
The lpOverlapped argument is necessary only if the file was created with the
FILE_FLAG_OVERLAPPED flag for the dwFlagsAndAtributes argument of the
CreateFile() function.
From the Dialogs tab of the Component Palette, click the SaveDialog button
and click on the form
2.
3.
On the form, double-click the Save button and implement its OnClick() event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnSaveClick(TObject *Sender)
{
HANDLE hFile;
DWORD dFileSize, dBytesWritten;
BOOL bResult;
if( SaveDialog1->Execute() )
{
__try
{
hFile = CreateFile(SaveDialog1->FileName.c_str(),
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_FLAG_RANDOM_ACCESS, NULL);
if( hFile == INVALID_HANDLE_VALUE )
{
ShowMessage("There was a problem saving the customer order");
return;
}
176
dFileSize = sizeof(CustOrder);
if( dFileSize == 0xFFFFFFFF )
{
ShowMessage("Invalid file size");
return;
}
strcpy(CustOrder.strClerk, edtClerk->Text.c_str());
CustOrder.dteOrderDate = edtOrderDate->Text;
if( rdoBun->Checked )
CustOrder.iBread = 0;
else if( rdoRoll->Checked )
CustOrder.iBread = 1;
if( rdoBeefPatty->Checked )
CustOrder.iMeat = 0;
else if( rdoGrilledChicken->Checked )
CustOrder.iMeat = 1;
else if( rdoChickenBreast->Checked )
CustOrder.iMeat = 2;
CustOrder.bLettuce = dlgIngredients->chkLettuce->Checked;
CustOrder.bOnion = dlgIngredients->chkOnion->Checked;
CustOrder.bTomato = dlgIngredients->chkTomato->Checked;
CustOrder.bPickles = dlgIngredients->chkPickles->Checked;
CustOrder.bCheese = chkCheese->Checked;
CustOrder.bBacon = chkBacon->Checked;
if( rdoMayonnaise->Checked )
CustOrder.iIngredients = 0;
else if( rdoKetchup->Checked )
CustOrder.iIngredients = 1;
else if( rdoMustard->Checked )
CustOrder.iIngredients = 2;
bResult = WriteFile( hFile, &CustOrder, dFileSize,
&dBytesWritten, NULL );
if( bResult == FALSE )
{
ShowMessage("The file could not be saved");
return;
}
}
__finally
{
if( hFile != INVALID_HANDLE_VALUE )
CloseHandle(hFile);
}
}
}
//---------------------------------------------------------------------------
4.
177
5.
6.
Save All
The hFile parameter is a handle to the file to be opened. It can be the return value of a
previous call to the CreateFile() function.
The lpBuffer parameter is the object or value to be saved. As its type suggests, it can be
anything and it is up to you to define what type of value is being opened.
The nNumberOfBytesToRead parameter is the number of bytes to read.
The lpNumberOfBytesRead parameter is a returned value of the function, indicating the
number of bytes that were read.
The lpOverlapped parameter is used if the file is being opened as overlapped, in which
case the lpOverlapped argument of the CreateFile() function would have received the
FILE_FLAG_OVERLAPPED flag.
178
Display the main form. From the Dialogs tab of the Component Palette, click the
OpenDialog button
2.
3.
On the form, double-click the Open button and implement its OnClick event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnOpenClick(TObject *Sender)
{
HANDLE hFile;
DWORD dFileSize, dBytesRead;
BOOL bResult;
if( OpenDialog1->Execute() == True )
{
__try
{
hFile = CreateFile(OpenDialog1->FileName.c_str(), GENERIC_READ,
0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE )
{
ShowMessage("There was a problem opening the file");
return;
}
dFileSize = GetFileSize(hFile, NULL);
if( dFileSize == 0xFFFFFFFF )
179
{
}
180
{
}
}
//---------------------------------------------------------------------------
4.
Test the application and open the previous order saved to file
5.
6.
Save All
181
182
Introduction to Lists
8.1.1 Overview
A list is a series of items of the same category and considered as an entity. By default, a
list is made of a single type of items easily identified, but an advanced list is made of
objects that are of the same type although each object can be made of sub-objects.
The first concern of a list is the composition of its members. The members of a list could
be simple text fields, such is the case of the Animation list of the Microsoft Word Font
dialog box:
A list could also be structured as a tree made of words or groups of words. An example is
the list of fonts in WordPerfect 2002:
183
Another list could be made of graphics or pictures only. Examples are the Solitaire and
the FreeCell games. A list can also combine graphics and text.
A list does not have to be made of items uniformly aligned. A list does not necessarily
display its items all at once. For example, the items of a ListView are usually configured
to change their display mode. Such is the case for the right pane of Windows Explorer.
Another category of list of this kind is implemented for a multiple-choice question exam
or text where only one question as a member of the list of questions would display.
184
Another type of list also provides a static list but allows the user to select or retrieve an
item from the list. A popular type of list is available on database applications. These lists
allow the user to select items from one list to create a new one. An example is the Form
Expert from Corel Paradox:
When creating a list, you will decide how you would like users to interact with that part
of your application, how they can use the list, what they can do, and what they should be
prevented from doing. The less interaction users have with a list, the least difficult it is to
create and maintain. Depending on your intentions, you will need to provide just as much
usefulness as possible to the user.
Many controls use lists as their main assignment. Such controls are list views, combo
boxes, rich texts, tree views, list boxes, color fields, radio button groups, text memos,
checked list boxes, etc. There are various classes C++ Builder provides to create such
lists. The primary and the most regularly used class to create a list is the TStrings class.
8.2
8.2.1 Introduction
The TStrings class is used to provide most of the list-based controls with the properties
and methods they need for their functionality. Because of this, such controls are equipped
to take advantage of all (or most) properties of this class. Such controls need to be filled
out with their main items, usually strings.
Since the TStrings class does not lead to a specific Windows control, the control that
needs it has to call it. Put it in reverse order, the TStrings class is declared in each class
that needs to create a list based on a TStrings class. For this reason, it is free to be named
anyway a class wants it. For example, in the TMemo class, the TStrings variable is
declared as Lines because a memo is just a list of paragraphs. On the other hand, the
TStrings class is called Items in the TListBox and the TComboBox classes. For a
TStringGrid object, the TStrings variable is called Cells.
185
To implement a list based on the TStrings class from any of these objects, call the
TStrings property which in turn would give access to its properties and methods.
This member function takes one argument as an AnsiString object and adds it to the end
of the target list. If the list is empty, the Add() method would initiate the list with the new
item. The Source argument could be a locally defined string. For example, the following
would add the Borland C++ Builder is Fun!!! string to a Memo control when the user
clicks Button1:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Add("Borland C++ Builder is fun!!!");
}
//---------------------------------------------------------------------------
You can also use a string held by another control. In this case you would call the control
field that holds the string. For example, the following would add the content of the Edit1
edit box, or the Text property of the Edit control, to a Memo control when the user clicks
Button1:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Add(Edit1->Text);
}
//---------------------------------------------------------------------------
You could also use the name of a string variable to add its value to a TStrings variable:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
String Address = "4812 Lockwood Drive #D12";
Memo1->Lines->Add(Address);
}
//---------------------------------------------------------------------------
If the addition of the Source string to the TStrings variable was successful, Add() returns
the position where the Source string was added. The position is zero-based. If Source was
set as the first item of the list, its position would be 0. If it were added as the 2nd item of
186
the list, it would assume position 1, etc. If you need to, you can find out the position the
argument is occupying after being added. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
String Address = "4812 Lockwood Drive #D12";
int Success = Memo1->Lines->Add(Address);
if( Success != -1)
ShowMessage(Address + " was added at position " +
String(Success + 1));
}
//---------------------------------------------------------------------------
If you are not interested in the position that Source occupied once it was added, you can
use the TStrings:: Append() method. Its syntax is:
void __fastcall Append(const AnsiString Source);
For a text-based control such as Memo or Rich Edit, the only thing you want to know is
whether the item was added to the list. This makes the TStrings:: Append() method
useful. This method also takes one argument as the AnsiString object to be added. Here is
an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Append("The magazine is on the table");
}
//---------------------------------------------------------------------------
While the Add() and the Append() methods are used to add a string to the end of a list,
the Insert() method allows you to add a string to a position of your choice in the target
list. The syntax of the Insert() method is:
void __fastcall Insert(int Index, const AnsiString Source)
This member function needs two pieces of information. The Index argument specifies the
intended position to insert the item. The positions are zero-based. If you want to add the
new item on top of the list, set the Index to 0, for the second place, set the Index to 1, etc.
The string to be added is the Source argument. In the following example, the Easy
Listening string is added to the 3rd position on a list:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
ListBox1->Items->Insert(2, "Easy Listening");
}
//---------------------------------------------------------------------------
The Index value applies whether the list is sorted or not. If you would like to add a new
string to a sorted list and insert it to the right alphabetical sequence, use the Add() or the
Append() methods.
187
To use this function, provide the string you are looking for. This string is an AnsiString
object. If the string exists in the list, the IndexOf() method returns its position in the list.
The following event is used to examine the items of a combo box looking for a Printer
string:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFindClick(TObject *Sender)
{
cbxNetwork->Items->IndexOf("Printer");
}
//---------------------------------------------------------------------------
If the IndexOf() method finds that the Source string exists in the target list, it returns the
position of Source. You can then use this information as you see fit. For example you can
insert a new string on top of the found string. Here is example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnInsertClick(TObject *Sender)
{
int Found = cbxNetwork->Items->IndexOf("Printer");
188
if( Found )
cbxNetwork->Items->Insert(Found, "Digital Camera");
}
//---------------------------------------------------------------------------
You could also look for Source in a target list and delete it if it exists. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDeleteClick(TObject *Sender)
{
int Found = cbxNetwork->Items->IndexOf("Printer");
if( Found )
cbxNetwork->Items->Delete(Found);
}
//---------------------------------------------------------------------------
Another type of operation you will perform in a list is to retrieve the text of a particular
string in the group; this usually happens when you want to transfer an item to another
control or display a message about the item. The Strings property allows you to get an
item based on its position in the list. This property is an array that represents the items of
the list. To locate a string, use the square brackets to specify its position. Because the list
is zero-based, the first item is 0 and would be represented by Strings[0], the second is 1
and would be called with Strings[1], etc.
For the following example, when the user clicks the list box, regardless of the item
selected, a label on the form would retrieve the third item of the list, provided the list has
at least 3 items:
//--------------------------------------------------------------------------void __fastcall TForm1::ListBox1Click(TObject *Sender)
{
Label1->Caption = ListBox1->Items->Strings[2];
}
//---------------------------------------------------------------------------
One of the most effective ways to use the Strings property is to find out the item that the
user would have selected in a list and act accordingly. In the following example, when
the user selects a radio button from a RadioGroup control, the caption of the button
selected displays on a label:
//--------------------------------------------------------------------------void __fastcall TForm1::RadioGroup1Click(TObject *Sender)
{
// Find the position of the item selected
int ItemPos = RadioGroup1->ItemIndex;
// Get the text of the item selected
String strSelected = RadioGroup1->Items->Strings[ItemPos];
// Using a label, display a message associated with the item selected
Label1->Caption = "You selected " + strSelected;
}
//---------------------------------------------------------------------------
One of the most regular reasons for this operation is to make sure that a string is not
duplicated and added to a list that already has the Source argument. In the following
Copyright 2003 FunctionX, Inc.
189
example, when the user types a string in an edit box and clicks somewhere else (that is,
when the Edit control looses focus), the compiler checks to see if the string in the edit
box already exists in the list. If the list of the combo box does not have the string in the
edit box, then this string is added to the list:
//--------------------------------------------------------------------------void __fastcall TForm1::edtLaptopExit(TObject *Sender)
{
String Laptop = edtLaptop->Text;
int Exists = cbxNetwork->Items->IndexOf(Laptop);
if( Exists == -1 )
cbxNetwork->Items->Append(Laptop);
}
//---------------------------------------------------------------------------
Some operating system configuration files contain lines with the = symbol as part of a
string. There is no strict rule on what those files are or what they do. The company or the
person who creates such a file also decides what the file is used for and when. For
example, a music program would use such a file to configure the keyboard keys as related
to the associated software. A communication program could use another type of those
files as a list or table of ports. Programmers have to deal with those files for various
reasons. Some of those files have strings made of three parts: Left=Right. The value
called Right has to be assigned to the value on the left. Sometimes the Left value is called
a Key; sometimes it is called a Name. The value on the right of equal is also called a
Value. Therefore, a string on this file would have the form Name=Value. Some of these
files have the .INI extension but can perfectly have any extension the programmer
wanted.
Depending on how the file is structured, programmers have to find a certain key or name
in the list. The TStrings class is equipped with a function that helps with this operation.
The method is IndexOfName() and its syntax is:
int __fastcall IndexOfName(const AnsiString Name);
This method takes an AnsiString object as argument. The compiler scans the list looking
for a string that has the form Name=Value. If the left part of a string matches the Name
argument, the IndexOfName() method returns the first position where such a string was
found. The following dialog box is equipped with an edit box and a memo. To add a new
key to the list of keys in the memo, the user types the key in the edit box and clicks the
Add Key button (the Edit control is named edtNewKey, the Memo control is named
mmoConfigure, the button is named btnAddKey, the bottom Edit control is named
edtFound):
190
Here is an example of implementing the IndexOfName() method from the OnClick event
of the Add Key button:
//--------------------------------------------------------------------------void __fastcall TForm1::btnAddKeyClick(TObject *Sender)
{
AnsiString NewKey = edtNewKey->Text;
int LookFor = mmoConfigure->Lines->IndexOfName("HelpDir");
if(LookFor == -1)
mmoConfigure->Lines->Append(NewKey);
else
edtFound->Text = LookFor;
}
//---------------------------------------------------------------------------
When the user clicks the Add Key button, the Name part, which is the string on the left of
the = symbol of the edit box, is checked for each string in the memo. If no name matches
the Name part of the edit box, the new key is added to the memo. If that Name part is
already in the list, the bottom edit box displays the position of the first string that contains
Name.
Probably the best way to do this, Live, is to retrieve the Name part from the string that
the user has typed in the top edit box. The following commented code would accomplish
that:
//--------------------------------------------------------------------------void __fastcall TForm1::btnAddKeyClick(TObject *Sender)
{
// Get the content of the edit box
AnsiString NewKey = edtNewKey->Text;
// Find the position of the first occurrence in the edit box int
EqualPos = NewKey.AnsiPos("=");
// Create a string from the beginning to the first occurrence of =
AnsiString NamePart = NewKey.Delete(EqualPos, NewKey.Length());
// Find out if the Name part of the key is already in the list
int LookFor = mmoConfigure->Lines->IndexOfName(NamePart);
// if it is not, add the new key to the file
if(LookFor == -1)
191
mmoConfigure->Lines->Append(edtNewKey->Text);
}
//---------------------------------------------------------------------------
Again, depending on how the list is structured, you can ask the compiler to retrieve the
name part of a string provided its position. This operation is performed using the Names
property. This property is an array of the strings of this type of file. When needed, you
can ask the compiler to give you the name part of a certain line. For example, to retrieve
the name part of the 5th line, you could write Names[4]. If the item at that position does
not have = as part of the string, the compiler would consider that the line is not a valid
IndexOfName string and would avoid it. In the following example, the 2nd line of
mmoConfigure is examined. If that line is a valid IndexOfName, its Name part displays
in the Found At edit box:
//--------------------------------------------------------------------------void __fastcall TForm1::btnGetNameClick(TObject *Sender)
{
edtFound->Text = mmoConfigure->Lines->Names[1];
}
//---------------------------------------------------------------------------
By contrast, to retrieve the Value part of an IndexOfName string, use the Values
property. This also is an array of the valid = strings of the list. It uses the following
syntax:
AnsiString Values[AnsiString NamePart];
This time, you must (or should) provide the string you are looking for as if it were the
position of the string. This string, which is an AnsiString object, should be the Name part
of a string you are looking for. The compiler would scan the list of strings looking for
valid IndexOfName strings. If it finds a string that encloses an = symbol, then the
compiler would check its Name part. If this Name matches the NamePart of the Values
property, then it would return the Value. This is useful if you want to know whether a
certain key has already been defined in the file.
When providing the NamePart as the string to look for, make sure you do not include =
as part of the string
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnGetValueClick(TObject *Sender)
{
edtFound->Text = mmoConfigure->Lines->Values["Directory"];
}
//---------------------------------------------------------------------------
Another usefulness of items of a list is to switch their positions as related to each other.
The primary method used to swap items is the Move() method. Its syntax is:
void __fastcall Move(int CurrentPos, int NewPos);
This function takes two strings. The first string is considered for its position. When
executing, the function moves the first item from the CurrentPos position to the position
specified by NewPos. The following event would move the 2nd item of a CheckListBox
control to the 5th position:
192
After the move, the item at CurrentPos is moved to NewPos. If the item is moved just one
position, all of the items whose positions are between CurrentPos and NewPos are
affected. If the item moved up, the items that were above it would be moved down. The
opposite occurs if the item has moved down.
While the Move() method is used to move an item from one position to another, the
Exchange() method is used to switch two items. Its syntax is:
void __fastcall Exchange(int Index1, int Index2);
The compiler takes the item at Index1, moves it to Index2, takes the item that was at
Index2 and moves it to Index1. In the following example, the items at the 4th and the 1st
positions are switched:
//--------------------------------------------------------------------------void __fastcall TForm1::btnExchangeClick(TObject *Sender)
{
CheckListBox1->Items->Exchange(3, 0);
}
//---------------------------------------------------------------------------
193
This function takes an AnsiString object as argument, which is the FileName. If you
want to open a file whose path you know, you can provide this path as the argument.
Here is an example that fills out a Memo control of a form with the contents of a text file:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->LoadFromFile("C:\\Windows\\WINHELP.INI");
}
//---------------------------------------------------------------------------
If you provide the path of a file but the file does not exist, when the user clicks the
button, the application would throw an uncomfortable error. The best alternative is to let
the user select a file using an appropriate object such the Open dialog box. In this case,
the FileName argument would be matched to the content of the dialog box. Following
this code, a Memo control named Memo1 would be filled with the content of a file
opened from an OpenDialog1 and a Button1 controls placed on the form:
//--------------------------------------------------------------------------void __fastcall TForm1::btnOpenFileClick(TObject *Sender)
{
if(OpenDialog1->Execute())
Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
}
//---------------------------------------------------------------------------
On the other hand, the TStrings class is also equipped with a special method that can be
used to save a file from a Memo or a RichEdit controls. Its syntax is:
void __fastcall SaveToFile(const AnsiString FileName);
This method takes an AnsiString object as the argument, called FileName. This argument
specifies the default name of the file being saved by the user. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnSaveClick(TObject *Sender)
{
if( SaveDialog1->Execute() )
Memo1->Lines->SaveToFile(SaveDialog1->FileName);
}
//---------------------------------------------------------------------------
Besides the Memo and RichEdit controls, you can also fill out a list of a control from the
strings of another list. This is mainly performed using the AddStrings() method whose
syntax is:
void __fastcall AddStrings(TStrings* Str);
194
The argument provided to this string must be a valid string object. Therefore, you should
make sure that the list originates from another list-based control or from another valid
source. The Str argument could come from a known control. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnTransferFileClick(TObject *Sender)
{
Memo2->Lines->AddStrings(Memo1->Lines);
}
//---------------------------------------------------------------------------
Since the TStrings class is a descendent of the TPersistent class, you can also use the
Assign() method. Its syntax is:
void __fastcall Assign(TPersistent* Source);
At any time you can find out how many items compose a list using the Count property.
Not only does this property provide an integral count of the members of the list but also
you can use it when scanning the list using a for loop. The fundamental way of using the
Count property is to get the number of items of a list. In the following example, when the
user clicks the Count button, the number of strings in the RadioGroup1 control displays
in the Count edit box:
//--------------------------------------------------------------------------void __fastcall TForm1::btnCountClick(TObject *Sender)
{
edtCount->Text = RadioGroup1->Items->Count;
}
//---------------------------------------------------------------------------
An alternative is to use the Capacity property which fundamentally also provides the
number of items of a list. Its main role is to deal with memory allocation especially when
writing a component that uses a list of items.
If you have two lists and want to compare them, use the Equals() method whose syntax
is:
bool __fastcall Equals(TStrings* Strings);
195
The items of a TStrings list are usually text-based although they can also be of different
kinds of objects. When the items are made of strings, sometimes you will need to convert
them to a single text. Such is the case when transferring the items of a ListBox or a
ComboBox to a text document or a rich text file. To convert a TStrings list of strings to
text, you should translate the list to a C-based string of objects. This can be done using
the GetText() method whose syntax is:
char * __fastcall GetText(void);
When this method is called by a TStrings variable, it returns a null-terminated string that
represents all of the items of the text. To assign a C-based string to a TStrings list of
strings, use the SetText() method. Its syntax is:
void __fastcall SetText(char * Text);
196
This method takes one argument as a C string and converts it to TStrings. You can use
these two methods to perform the same transfer or transaction we used to pass a list of
strings from one list to another. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnGetTextClick(TObject *Sender)
{
char *WholeList = Memo1->Lines->GetText();
Memo2->Lines->SetText(WholeList);
}
//---------------------------------------------------------------------------
Even if the controls are of different kinds, you can use the same transaction. For example,
the following event transfers the contents of a ListBox to a memo:
//--------------------------------------------------------------------------void __fastcall TForm1::btnTransToLBXClick(TObject *Sender)
{
char *WholeList = ListBox1->Items->GetText();
Memo3->Lines->SetText(WholeList);
}
//---------------------------------------------------------------------------
197
As you can see, this transfer is not properly interpreted by the Edit control because this
control does not have a multiple line capability while the WordWrap property helps to
manage the Label control.
Another technique used to most effectively transfer a list from a strictly list-based
control, such as a ListBox, a ComboBox or a RadioGroup control to a text-based control
such as a memo or a RichText control, is to use the CommaText property. When called to
use this property, the compiler would scan the list of items. If an item is a one-word
string, the compiler would write a comma , on its right and start adding the next item. If
the item is a string made of more than one word, to delimit it, the compiler would enclose
it with double-quotes. This is done until the last string:
In the following example, when the user clicks the Transfer button, the list of items from
the ListBox is transferred to both a memo and an edit box using the CommaText
property:
//--------------------------------------------------------------------------void __fastcall TForm1::btnTransferClick(TObject *Sender)
{
Memo1->Lines->Add(ListBox1->Items->CommaText);
Edit1->Text = ListBox1->Items->CommaText;
}
//---------------------------------------------------------------------------
If you decide to dismiss a whole list, use the Clear() method. Its syntax is:
void __fastcall Clear();
Unlike the Delete() method, the Clear() function completely empties the list of items.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnEmptyTheListClick(TObject *Sender)
{
Memo1->Lines->Clear();
}
//---------------------------------------------------------------------------
8.3
198
This method requires a name for the file. When executed, it saves the contents of the list
to disc.
Alternatively, you can use the TStrings::SaveToStream() method to save a list of
strings to a stream.
Open the Notice2 project from the exercises that accompany this book
2.
On the main menu of the application, click File -> Save and implement the event as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Save1Click(TObject *Sender)
{
if( SaveDialog1->Execute() )
Memo1->Lines->SaveToFile(SaveDialog1->FileName);
}
//---------------------------------------------------------------------------
3.
4.
On the toolbar, click the Save button and save the file with a recognizable name
5.
199
Like the SaveToFile() method, LoadFromFile() requires a name for the file that needs to
be opened.
Alternatively, you can use the TStrings::LoadFromStream() method to open a list of
strings from a stream.
On the main menu of the form, click File -> Open and implement its event as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Open1Click(TObject *Sender)
{
if( OpenDialog1->Execute() )
Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
}
//---------------------------------------------------------------------------
8.
Execute the application and open the file saved in the previous exercise
9.
8.4
8.4.1 Introduction
The TStrings class appears to provide enough methods to perform any operation on any
list of strings. Unfortunately, because TStrings is an abstract class, you cannot declare an
instance of it, which means that you cannot create a dynamic list of strings using the
TStrings class. That is one of the reasons you will use alternate classes to accomplish
such a task.
The TStringList is derived from the TStrings class and adds new properties and
methods for operations not possible on its parent. This is because the TStrings class can
only fill out a list. It cannot independently manage the items of its own list; it relies on
the control that uses it.
One of the strengths of the TStringList class is that, unlike the TStrings class, it is not
associated with any control, not even a control that creates a list. Therefore, you are in
charge of using it as you see fit. This also allows a TStringList object to accommodate
almost any control that uses a list. It also provides the primary means of exchanging
items among controls of various kinds.
operator to declare an instance of a TStringList class. The compiler does not need to
know the parent or owner of the list. For the same reason, you have the responsibility of
deleting the list when you do not need it anymore. Although lists are usually difficult to
create and maintain, Borland did a lot of work behind the scenes so that the compiler can
tremendously help you with your list.
To dynamically create a list, you must declare an instance of a TStringList class using
the new operator. If you are planning to use the list inside of only one function or event,
you can initiate the object as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::CreateAlist()
{
TStringList *Categories = new TStringList;
}
//---------------------------------------------------------------------------
Such a list cannot be accessed outside of the event or function in which you created it. If
you want the list to be available to more than one event or function, create it globally.
This could be done on top of the source file. Here is an example:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
TStringList *Categories = new TStringList;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
This time, the list would be available to any event or function of the same source file.
Other objects, events, or functions that are part of other units (for example if you call this
form from another form) other than this one cannot access the list. The alternative,
sometimes the best one, is to declare the list variable in the header file of the primary unit
that would manipulate or use it. This is done in the private, public or protected sections of
the unit. If only one unit will use this list, declare the variable in the private section. By
contrast, if more than one unit will need it, then declare it in the public section. This time,
since you cannot initialize a variable in a class, only declare a pointer to a TStringList
class. Here is an example:
//--------------------------------------------------------------------------#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------------------------#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//--------------------------------------------------------------------------class TForm1 : public TForm
201
{
__published: // IDE-managed Components
private:
TStringList * Categories; // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------------------------#endif
After declaring such a variable, you should initialize it in a function or event that would
be called prior to any other event or function using the list. Although this can be done in
the OnCreate() event of a form, probably the safest place to initialize the list is the
constructor of the form. You will use the new operator to initialize the variable. Here is
an example:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
Categories = new TStringList;
}
//---------------------------------------------------------------------------
Once you have created the list locally or globally, you can call any of its properties or
methods to manipulate it.
Once again, each item is added to the list as an AnsiString object. If the addition is
successful, the method returns the position occupied by the Source argument in the list. If
the list was empty, Source would occupy the first position, which is 0 because the list is
zero-based. If the list already had at least one item, the Source argument would be added
to the end of the existing items.
Probably the most difficult of the lists are those that, either the users would create or the
user would be allowed to edit the items or the list itself. The same advice would be given
here: only provide the necessary tools to the user, not more not less.
As you know already, the VCL provides various classes for creating lists. It is very likely
that one class would not be enough to handle all of the functionality you expect from
your application. Therefore, you should know how and when to combine classes to assign
the needed actions the user would need to perform.
The TStrings and the TStringList classes exchange information fairly easily. It is likely
that you will have to use the TStringList class whenever you need a dynamic list. Then
use the TStrings class to actually fill a list on a control.
203
204
205
PART II
The Device Context and its Usefulness
Because Microsoft Windows is a graphical operating system, the controls used on its
applications for user interaction perform and process a lot of drawing. This section
studies the various pieces of information you need in order to draw, not only on rough
objects like forms but also on smaller controls. This section also was judged a valuable
prerequisite to Windows controls studying.
206
207
9.2
9.2.1 Lines
A line is a junction of two points. This means that a line has a beginning and an end:
The beginning and the end are two distinct points. In real life, before drawing, you should
define where you would start. To help with this, the TCanvas class provides the
MoveTo() method. Its syntax is:
void __fastcall MoveTo(int X, int Y);
208
The X argument represents the horizontal distance of the line beginning from the (0, 0)
origin.
The Y value is the vertical distance from the (0, 0) origin.
To end the line, you use the TCanvas::LineTo() method. Its syntax is:
void __fastcall LineTo(int X, int Y);
The X argument represents the horizontal end of the line from the (0, 0) origin.
The Y value is the vertical end of the line from the (0, 0) origin.
Here is an example that draws a line starting at a point defined as (20, 15) coordinates
and ending at (255, 82):
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->MoveTo(20, 15);
Canvas->LineTo(255, 82);
}
//---------------------------------------------------------------------------
We have mentioned that the TCanvas::MoveTo() method is used to set the starting
position of a line. When using LineTo(), the line would start from the MoveTo() point to
the LineTo() end. As long as you do not call MoveTo(), any subsequent call to LineTo()
would draw a line from the previous LineTo() to the new LineTo() point. You can use
this property of the LineTo() method to draw various lines. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->MoveTo(60, 20);
Canvas->LineTo(60, 122);
Canvas->LineTo(264, 122);
Canvas->LineTo(60, 20);
}
//---------------------------------------------------------------------------
209
9.2.2 Polylines
A polyline is a series of connected lines. The lines are stored in an array of TPoint
values. To draw a polyline, you use the TCanvas::Polyline() method. Its syntax is:
void __fastcall Polyline(const Types::TPoint* Points, const int Points_Size);
The Points argument is an array of TPoint values. The Points_Size argument specifies the
number of members of the array. When executing, the compiler moves the starting point
to Points[0]. The first line is drawn from Points[0] to Points[1] as in:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[] = { Point(60, 20), Point(60, 122) };
Canvas->MoveTo(Pt[0].x, Pt[0].y);
Canvas->LineTo(Pt[1].x, Pt[1].y);
}
//---------------------------------------------------------------------------
To draw a polyline, you must have at least two points. If you define more than two
points, each line after the first would be drawn from the previous point to the next point
until all points have been included. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[7];
Pt[0]
Pt[1]
Pt[2]
Pt[3]
Pt[4]
Pt[5]
Pt[6]
=
=
=
=
=
=
=
Point(20, 50);
Point(180, 50);
Point(180, 20);
Point(230, 70);
Point(180, 120);
Point(180, 90);
Point(20, 90);
Canvas->Polyline(Pt, 7);
}
//---------------------------------------------------------------------------
210
Besides the Polyline() method, the Win32 API provides the PolylineTo() function. Its
syntax is:
BOOL PolylineTo(HDC hdc, CONST POINT *lppt, DWORD cCount);
The hdc argument is a handle to the canvas on which you are drawing.
The lppt argument is the name of an array of POINT or TPoint objects.
The cCount argument specifies the number of points that would be included in the figure.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[7];
Pt[0]
Pt[1]
Pt[2]
Pt[3]
Pt[4]
Pt[5]
Pt[6]
=
=
=
=
=
=
=
Point(20, 50);
Point(180, 50);
Point(180, 20);
Point(230, 70);
Point(180, 120);
Point(180, 90);
Point(20, 90);
While the Polyline() method starts the first line at lppt[0], the PolylineTo() function does
not control the beginning of the first line. Like the LineTo() method, it simply starts
Copyright 2003 FunctionX, Inc.
211
drawing, which would mean it could starts at the origin (0, 0). For this reason, if you
want to control the starting point of the PolylineTo() drawing, you can use the MoveTo()
method:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[7];
Pt[0]
Pt[1]
Pt[2]
Pt[3]
Pt[4]
Pt[5]
Pt[6]
=
=
=
=
=
=
=
Point(20, 50);
Point(180, 50);
Point(180, 20);
Point(230, 70);
Point(180, 120);
Point(180, 90);
Point(20, 90);
}
//---------------------------------------------------------------------------
The hdc argument is a handle to the canvas on which you want to draw.
Like the Polyline() method, the lppt argument is an array of POINT or TPoint values.
The PolyPolyline() function needs to know how many polylines you want to draw. Each
polyline will use the points of the lppt value but when creating the array of points, the
values must be incremental. This means that PolyPolyline() will not access their values at
random. It will retrieve the first point, followed by the second, followed by the third, etc.
Therefore, your first responsibility is to decide where one polyline starts and where it
ends. The good news (of course depending on how you see it) is that a polyline does not
212
start where the previous line ended. Each polyline has its own beginning and its own
ending point.
The lpdwPolyPoints argument is an array or positive integers (unsigned long). Each
member of this array specifies the number of vertices (lines) that its corresponding
polyline will have. For example, imagine you want to draw M, followed by L, followed
by Z. The letter M has 4 lines but you need 5 points to draw it. The letter L has 2 lines
and you need 3 points to draw it. The letter Z has 3 lines so 4 points are necessary to
draw it. You can store this combination of lines in an array defined as { 5, 3, 4 }.
Unlike Polyline(), here, the cCount argument is actually the number of shapes you want
to draw and not the number of points (remember that each polyline "knows" or controls
its beginning and end).
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[15];
DWORD lpPts[] = { 4, 4, 7 };
// Left Triangle
Pt[0] = Pt[3] = Point(50, 20);
Pt[1] = Point(20, 60);
Pt[2] = Point(80, 60);
// Second Triangle
Pt[4] = Pt[7] = Point(70, 20);
Pt[5] = Point(100, 60);
Pt[6] = Point(130, 20);
// Hexagon
Pt[8] = Pt[14] = Point(145, 20);
Pt[9] = Point(130, 40);
Pt[10] = Point(145, 60);
Pt[11] = Point(165, 60);
Pt[12] = Point(180, 40);
Pt[13] = Point(165, 20);
HDC hDC = Canvas->Handle;
PolyPolyline(hDC, Pt, lpPts, 3);
}
//---------------------------------------------------------------------------
213
9.2.4 Polygons
The polylines we have used so far were drawn by defining the starting point of the first
line and the end point of the last line. There was no relationship or connection between
these two extreme points. A polygon is a closed polyline. In other words, it is a polyline
defined so that the end point of the last line is connected to the start point of the first line.
To draw a polygon, you can use the TCanvas::Polygon() method. Its syntax is:
void __fastcall Polygon(const TPoint * Points, const int Points_Size);
This member function uses the same types of arguments as the Polyline() method. The
only difference is on the drawing of the line combination. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[7];
Pt[0]
Pt[1]
Pt[2]
Pt[3]
Pt[4]
Pt[5]
Pt[6]
=
=
=
=
=
=
=
Point(20, 50);
Point(180, 50);
Point(180, 20);
Point(230, 70);
Point(180, 120);
Point(180, 90);
Point(20, 90);
Canvas->Polygon(Pt, 7);
}
//---------------------------------------------------------------------------
The hdc argument is a handle to the canvas on which you want to draw.
Like the Polygon() method, the lpPoints argument is an array of POINT or TPoint
values. The PolyPolygon() function needs to know the number of polygons you would be
214
drawing. Each polygon uses the points of the lpPoints value but when creating the array
of points, the values must be incremental: each polygon has its own set of points.
The lpPolyCounts argument is an array or integers. Each member of this array specifies
the number of vertices (lines) that its polygon will have..
Unlike Polygon(), the nCount argument of PolyPolygon() is the number of polygons you
want to draw and not the number of points.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[12];
int lpPts[] = { 3, 3, 3, 3 };
// Top Triangle
Pt[0] = Point(125, 10);
Pt[1] = Point( 95, 70);
Pt[2] = Point(155, 70);
// Left Triangle
Pt[3] = Point( 80, 80);
Pt[4] = Point( 20, 110);
Pt[5] = Point( 80, 140);
// Bottom Triangle
Pt[6] = Point( 95, 155);
Pt[7] = Point(125, 215);
Pt[8] = Point(155, 155);
// Right Triangle
Pt[9] = Point(170, 80);
Pt[10] = Point(170, 140);
Pt[11] = Point(230, 110);
HDC hDC = Canvas->Handle;
PolyPolygon(hDC, Pt, lpPts, 4);
}
//---------------------------------------------------------------------------
215
The drawing of a rectangle typically starts from a point defined as (X1, Y1) and ends at
another point (X2, Y2). To draw a rectangle, you can use the TCanvas::Rectangle()
method. Its syntax is:
void __fastcall Rectangle(int X1, int Y1, int X2, int Y2);
As seen on the figure and the formula, a rectangle spans from coordinates (x1, y1) to (x2,
y2). Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Rectangle(20, 20, 226, 144);
}
//---------------------------------------------------------------------------
216
When drawing a rectangle, if the value of x2 is less than that of x1, then the x2 coordinate
would mark the left beginning of the figure. This scenario would also apply if the y2
coordinate were lower than y1.
To draw a rectangle, you can also use a RECT or a TRect object. The syntax you would
use is:
void __fastcall Rectangle(TRect Rect);
In this case, you must have defined a RECT or a TRect value and pass it as a pointer to
the Rectangle() method. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
RECT Recto;
Recto.left = 328;
Recto.top = 125;
Recto.right = 48;
Recto.bottom = 25;
Canvas->Rectangle(Recto);
}
//---------------------------------------------------------------------------
A square is a rectangle whose sides are all equal. Therefore, to draw a square, when
specifying the arguments of the Rectangle() method, make sure that |x1 - x2| = |y1 - y2|.
217
The hdc argument represents a handle of the canvas on which you want to draw.
The qrc argument is passed as a pointer to a RECT or TRect, which is the rectangle that
would be drawn.
The edge value specifies how the interior and the exterior of the edges of the rectangle
would be drawn. It can be a combination of the following constants:
Value
BDR_RAISEDINNER
BDR_SUNKENINNER
BDR_RAISEDOUTER
BDR_SUNKENOUTER
Description
The interior edge will be raised
The interior edge will be sunken
The exterior edge will be raised
The exterior edge will be sunken
These values can be combined using the bitwise OR operator. On the other hand, you can
use the following constants instead:
Value
EDGE_DUMP
EDGE_ETCHED
EDGE_RAISED
EDGE_SUNKEN
Used For
BDR_RAISEDOUTER | BDR_SUNKENINNER
BDR_SUNKENOUTER | BDR_RAISEDINNER
BDR_RAISEDOUTER | BDR_RAISEDINNER
BDR_SUNKENOUTER | BDR_SUNKENINNER
The grfFlags value specifies what edge(s) would be drawn. It can have one of the
following values:
Value
BF_RECT
BF_TOP
BF_LEFT
BF_BOTTOM
BF_RIGHT
BF_TOPLEFT
BF_BOTTOMLEFT
BF_TOPRIGHT
BF_BOTTOMRIGHT
BF_DIAGONAL_ENDBOTTOMLEFT
BF_DIAGONAL_ENDBOTTOMRIGHT
218
Description
The entire rectangle will be drawn
Only the top side will be drawn
Only the left side will be drawn
Only the bottom side will be drawn
Only the right side will be drawn
Both the top and the left sides will be
drawn
Both the bottom and the left sides will be
drawn
Both the top and the right sides will be
drawn
Both the bottom and the right sides will be
drawn
A diagonal line will be drawn from the topright to the bottom-left corners
A diagonal line will be drawn from the topleft to the bottom-right corners
Copyright 2003 FunctionX, Inc.
BF_DIAGONAL_ENDTOPLEFT
BF_DIAGONAL_ENDTOPRIGHT
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TRect Recto(20, 20, 225, 115);
HDC hDC = Canvas->Handle;
DrawEdge(hDC, &Recto, BDR_RAISEDOUTER | BDR_SUNKENINNER, BF_RECT);
}
//---------------------------------------------------------------------------
Because an ellipse can fit in a rectangle, in GDI programming, an ellipse is defined with
regards to a rectangle it would fit in. Therefore, to draw an ellipse, you specify its
rectangular corners. The syntax used to do this is:
void __fastcall Ellipse(int X1, int Y1, int X2, int Y2);
219
The arguments of this method play the same roll as those of the Rectangle() method:
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Ellipse(20, 20, 226, 144);
}
//---------------------------------------------------------------------------
Like the rectangle, you can draw an ellipse using a RECT or TRect object it would fit in.
The syntax you would use is:
void __fastcall Ellipse(TRect Rect);
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TRect Recto(328, 125, 28, 8);
Canvas->Ellipse(Recto);
}
//---------------------------------------------------------------------------
220
A circle is an ellipse whose all points have the same distance with regards to a central
point.
To draw such a rectangle, you can use the TCanvas::RoundRect() method. Its syntax is:
void __fastcall RoundRect(int X1, int Y1, int X2, int Y2, int X3, int Y3);
When this member function executes, the rectangle is drawn from the (x1, y1) to the (x2,
y2) points. The corners are rounded by an ellipse whose width would be x3 and the
ellipse's height would be x3.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->RoundRect(20, 20, 275, 188, 42, 38);
}
//---------------------------------------------------------------------------
221
9.2.10 Pies
A pie is a fraction of an ellipse delimited by two lines that span from the center of the
ellipse to one side each. It can be illustrated as follows:
To draw a pie, you can use the TCanvas::Pie() method whose syntax is:
void __fastcall Pie(int X1, int Y1, int X2, int Y2, int X3, int Y3, int X4, int Y4);
The (X1, Y1) point determines the upper-left corner of the rectangle in which the ellipse
that represents the pie fits. The (X2, Y2) point is the bottom-right corner of the rectangle.
The (X3, Y3) point specifies the starting corner of the pie in a default counterclockwise
direction.
The (X4, Y4) point species the end point of the pie.
To complete the pie, a line is drawn from (X3, Y3) to the center and from the center to
the (X4, Y4) points.
Here is an example:
222
9.2.11 Arcs
An arc is a portion or segment of an ellipse. This means that an arc is a non-complete
ellipse. Because an arc must confirm to the shape of an ellipse, it is defined as it fits in a
rectangle and can be illustrated as follows:
To draw an arc, you can use the TCanvas::Arc() method whose syntax is:
void __fastcall Arc(int X1, int Y1, int X2, int Y2, int X3, int Y3, int X4, int Y4);
Besides the left (X1, Y1) and the right (X2, Y2) borders of the rectangle in which the arc
would fit, an arc must specify where it starts and where it ends. The additional points are
set as the (X3, Y3) and (X4, Y4) points of the figure. Based on this, the above arc can be
illustrated as follows:
223
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Arc(20, 20, 226, 144, 202, 115, 105, 32);
}
//---------------------------------------------------------------------------
Besides the Arc() method, the Win32 library provides the ArcTo() function used to draw
an arc. Its syntax is as follows:
BOOL ArcTo(HDC hdc,
int nLeftRect, int nTopRect, int nRightRect, int nBottomRect,
int nXRadial1, int nYRadial1, int nXRadial2, int nYRadial2);
The hdc argument is a handle to the canvas on which you want to draw.
This method uses the same arguments as Arc(). The difference is that while Arc() starts
drawing at (x3, y3), ArcTo() does not inherently control the drawing starting point. It
refers to the current point, exactly like the LineTo() (and the PolylineTo(). methods.
Therefore, if you want to specify where the drawing should start, you can call
TCanvas::MoveTo() before ArcTo(). Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
HDC hDC = Canvas->Handle;
224
You may wonder why the arc is drawn to the right side of a vertical line that would cross
the center of the ellipse instead of the left. This is because the drawing of an arc is
performed from right to left or from bottom to up, in the opposite direction of the clock.
This is known as the counterclockwise direction. To control this orientation, the Win32
library provides the SetArcDirection() function. Its syntax is:
Copyright 2003 FunctionX, Inc.
225
This function specifies the direction the TCanvas::Arc() method should follow from the
starting to the end points. The argument passed as ArcDirection controls this orientation.
It can have the following values:
Value
AD_CLOCKWISE
AD_COUNTERCLOCKWISE
Orientation
The figure is drawn clockwise
The figure is drawn counterclockwise
}
//---------------------------------------------------------------------------
After calling SetArcDirection() and changing the previous direction, all drawings would
use the new direction to draw arcs using Arc() or ArcTo() and other figures (such as
chords, ellipses, pies, and rectangles). Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
HDC hDC = Canvas->Handle;
SetArcDirection(hDC, AD_COUNTERCLOCKWISE);
Canvas->Arc(20, 20, 226, 144, 202, 115, 105, 32);
Canvas->Arc(10, 10, 250, 155, 240, 85, 24, 48);
}
//---------------------------------------------------------------------------
226
If you want to change the direction, you must call SetArcDirection() with the desired
value. Here is an example;
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
HDC hDC = Canvas->Handle;
SetArcDirection(hDC, AD_COUNTERCLOCKWISE);
Canvas->Arc(20, 20, 226, 144, 202, 115, 105, 32);
SetArcDirection(hDC, AD_CLOCKWISE);
Canvas->Arc(10, 10, 250, 155, 240, 85, 24, 48);
}
//---------------------------------------------------------------------------
At any time, you can find out the current direction used. This is done by calling the
GetArcDirection() function. Its syntax is:
int GetArcDirection(HDC hdc);
227
The hdc argument represents a handle to the canvas on which you want to draw. This
function draws a line and an arc connected. The arc is based on a circle and not an ellipse.
This implies that the arc fits inside a square and not a rectangle. The circle that would be
the base of the arc is defined by its center located at C(X, Y) with a radius of dwRadius.
The arc starts at an angle of eStartAngle. The angle is based on the x axis and must be
positive. That is, it must range from 0 to 360. If you want to specify an angle that is
below the x axis, such as -15, use 360-15=345. The last argument, eSweepAngle, is
the angular area covered by the arc.
The AngleArc() function does not control where it starts drawing. This means that it may
start at the origin, unless a previous call to MoveTo() specified the beginning of the
drawing.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
HDC hDC = Canvas->Handle;
Canvas->MoveTo(52, 28);
AngleArc(hDC, 120, 45, 142, 345, -65);
}
//---------------------------------------------------------------------------
9.2.14 Chords
The arcs we have drawn so far are considered open figures because they are made of a
line that has a beginning and an end (unlike a circle or a rectangle that do not). A chord is
an arc whose two ends are connected by a straight line. In other words, a chord is an
ellipse that is divided by a straight line from one side to another:
228
To draw a chord, you can use the TCanvas::Chord() method. It is defined as follows:
void __fastcall Chord(int X1, int Y1, int X2, int Y2, int X3, int Y3, int X4, int Y4);
The X1, Y1, X2, and Y2 are the coordinates of the rectangle in which the chord of the
circle would fit.
The X3 and Y3 coordinates specify where the arc that holds the chord starts.
The X4 and Y4 arguments specify the end of the arc.
To complete the chord, a line is drawn from (X3, Y3) to (X4, Y4).
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Chord(20, 20, 226, 144, 202, 115, 105, 32);
}
//---------------------------------------------------------------------------
229
To draw this line (with four points), the compiler would draw a curve from the first to the
fourth points. Then it would bend the curve by bringing each middle (half-center) side
close to the second and the third points respectively, of course without touching those
second and third points. For example, the above bzier curve could have been drawn
using the following four points:
PolyBezier(): To draw a bzier curve, the TCanvas provides the PolyBezier() method. Its
syntax is:
void __fastcall PolyBezier(const TPoint* Points, const int Points_Size);
The Points argument is an array of POINT or TPoint values. The Points_Size argument
specifies the number of points that will be used to draw the line minus 1. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[4] = { Point(20, 12), Point(88, 246),
Point(364, 192), Point(250, 48) };
Canvas->PolyBezier(Pt, 3);
}
//---------------------------------------------------------------------------
230
In the same way, you can draw a series of complicated subsequent lines. This is done by
adding reference points to the array. To do this, you must add points in increments of
three. After drawing the first curve based on the first four points, to draw the next line,
the function would use the fourth point as the starting point. Since the bzier line requires
4 points, you must add three more. You can continue adding points by three to draw the
bzier curve. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[] = { Point(20, 12), Point(88, 246),
Point(364, 192), Point(250, 48),
Point(175, 38), Point(388, 192), Point(145, 125) };
Canvas->PolyBezier(Pt, 6);
}
//---------------------------------------------------------------------------
231
The PolyBezierTo() method draws a bzier curve. Its first argument, Points, is a pointer
to an array of POINT or TPoint values. This member function requires at least three
points. It starts drawing from the current line to the third point. If you do not specify the
current line, it would start at the origin (0, 0). The first and the second lines are used to
control the curve. The Points_Size argument is the number of points that would be
considered minus 1. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint Pt[] = { Point(320, 120), Point(88, 246), Point(364, 122) };
Canvas->PolyBezierTo(Pt, 2);
}
//---------------------------------------------------------------------------
9.3
The TextOut() method is used to create an display a piece of text on the screen. The X
and Y arguments are the point coordinates of the top-left corner of the string being
displayed. The Text argument is the text that needs to be displayed. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->TextOut(10, 10, "Walter Bells");
}
//---------------------------------------------------------------------------
232
The TextRect() method draws text in a rectangle. A portion of the text that is larger than
the allocated rectangle would be hidden. The Rect argument is the rectangle that will
contain the text. The X and Y argument are the coordinates of the text, in screen
coordinates. This means that, to show the beginning of the text, the value of X must be
greater than or equal to the Left member variable of the Rect argument. If you want to
show the top section of the text, the value of Y must be greater than or equal to the Top
member variable of the Rect argument. The Text argument is the string that needs to be
displayed. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->TextRect(Rect(40, 20, 120, 60), 40, 20, "Walter Bells");
}
//---------------------------------------------------------------------------
The Text argument is the string that is displaying or needs to be shown. After this method
has executed, it returns the width of the string. On the other hand, if you want to find out
the height of text that is drawn or needs to be drawn, you can call the
TCanvas::TextHeight() method whose syntax is:
nt __fastcall TextHeight(const AnsiString Text);
Like TextWidth(), the TextHeight() method takes as argument the string that is
displaying or needs to be displayed. TextHeight() returns the height of the string.
If you need to find both the width and the height occupied by a string, you can use a
single TCanvas method called TextExtent. Its syntax is:
TSize __fastcall TextExtent(const AnsiString Text);
Like the previous two methods, TextExtent() takes as argument the string that needs to be
considered. After this method has executed, it return both the width and the height of the
Text string stored in a TSize variable.
233
The lpString argument is the string that needs to be drawn. The nCount is the number of
characters that compose the string. The string will be positioned in the lpRect rectangle.
The uFormat argument specifies how the text will be formatted.
234
Green
Blue
22
21
20
19
18
17
16
15
14
Blue
13
12
11
Green
10
Red
Converted to decimal, this number has a value of 255 * 255 * 255 = 16581375. This
means that we can have approximately 16 million colors available.
235
The 32-bit numeric value used by the Win32 library to characterize a color is defined as
the COLORREF data type. You can use it to declare a color variable. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
COLORREF ClrMine;
Color = ClrMine;
}
//---------------------------------------------------------------------------
When or after declaring such a variable, you can initialize it with a 32-bit decimal value.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
COLORREF ClrMine = 1637623;
}
//---------------------------------------------------------------------------
The VCL itself defines a color as a member of the TColor enumerator. Although you can
use the COLORREF data type to declare or use a color, you should always cast your
color variables to TColor. Otherwise, most of the time, you will receive a nevertheless
and somewhat harmless warning. For example, the above COLORREF value can be
used to colorize the client area of a form after being cast to TColor as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
COLORREF ClrMine = 1637623;
Color = TColor(ClrMine);
}
//---------------------------------------------------------------------------
The RGB macro behaves like a function and requires three numeric values separated by a
comma. Each value must range between 0 and 255 both included. Using RGB, the above
initialization can be done as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
COLORREF ClrMine = RGB(247, 252, 24);
Color = TColor(ClrMine);
}
//---------------------------------------------------------------------------
You can also declare a color using the TColor enumerator as a data type. Like any other,
the variable can have any valid C++ name. After declaring the variable, you can initialize
236
it. To do this, you can assign it any long integer value. You can also use the RGB macro
to create the color. Whether using a constant long or the RGB macro, you should always
cast the value to TColor. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TColor FirstColor = TColor(723873);
TColor SecondColor = TColor(RGB(247, 252, 24));
}
//---------------------------------------------------------------------------
You can also initialize a TColor variable using a color name as we will review below.
Each macro takes a 32-bit value as argument, rgb. The GetRValue() macro returns the
red value of the rgb parameter. The GetGValue() macro returns the green value of the
rgb number. The GetBValue() macro returns the blue value of rgb.
237
Just like you, because users are able and free to change these colors to their liking, it is
almost impossible to predict the appearance of these colors on someone elses computer.
Fortunately, if you want to use one of these colors, you can ask your application to check
its value on the users computer. To do this, you can call the GetSysColor() function. Its
syntax is:
DWORD GetSysColor(int nIndex);
This function receives a constant value that is defined in the operating system
representing one of the appearances colors and returns the 32-bit value of that color. The
colors defined in Control Panel and/or the VCL and can be passed as the nIndex
argument of the GetSysColor() function are:
System Color Role: Color of
3D Objects Background
COLOR_3DFACE
clBtnFace
238
COLOR_BTNFACE
COLOR_3DHILIGHT
COLOR_3DHIGHLIGHT
COLOR_BTNHILIGHT
COLOR_BTNHIGHLIGHT
COLOR_3DDKSHADOW
clBtnHighlight
cl3DDkShadow
COLOR_3DLIGHT
cl3DLight
COLOR_3DSHADOW
COLOR_BTNSHADOW
clBtnShadow
COLOR_BTNFACE
clBtnFace
COLOR_BTNTEXT
COLOR_WINDOWTEXT
COLOR_ACTIVECAPTION
COLOR_ACTIVEBORDER
COLOR_INACTIVEBORDER
COLOR_BACKGROUND
COLOR_DESKTOP
COLOR_BACKGROUND
COLOR_INACTIVECAPTION
clBtnText
COLOR_INACTIVECAPTIONTEXT
COLOR_APPWORKSPACE
COLOR_SCROLLBAR
COLOR_HIGHLIGHT
COLOR_HIGHLIGHTTEXT
COLOR_INFOBK
COLOR_INFOTEXT
COLOR_CAPTIONTEXT
COLOR_WINDOW
COLOR_WINDOWFRAME
COLOR_WINDOWTEXT
COLOR_GRADIENTACTIVECAPTION
clInactiveCaptionText
clAppWorkSpace
clMenuBar
clMenu
clMenuText
clMenuHighlight
clScrollBar
clHighlight
clHighlightText
clInfoBk
clInfoText
clCaptionText
clWindow
clWindowFrame
clWindowText
clGradientActiveCaption
COLOR_GRADIENTINACTIVECAPTION
clGradientInactiveCaption
COLOR_HOTLIGHT
clHotLight
COLOR_MENU
COLOR_MENUTEXT
clActiveCaption
clActiveBorder
clInactiveBorder
clBackground
clBackground
clBackground
clInactiveCaption
Besides the system colors defined in the right column, the VCL provides various color
names whose values are constant and can be predicted for an application. These colors
Copyright 2003 FunctionX, Inc.
239
are clBlack, clMaroon, clGreen, clOlive, clNavy, clPurple, clTeal, clGray, clSilver,
clRed, clLime, clYellow, clBlue, clFushsia, clAqua, clLtGray, clDkGray, clWhite,
clMoneyGreen, clSkyBlue, clCream, clMedGray, clNone, and clDefault. Remember that
you can create any color you want by providing its red, green, and blue value then
initialize a TColor variable with it.
This function takes as argument a color value which is crColor. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
SetTextColor(Canvas->Handle, clRed);
Canvas->TextRect(Rect(40, 20, 120, 60), 40, 20, "Walter Bells");
}
//---------------------------------------------------------------------------
240
As you will learn from now on concerning the device context, once you change one of its
characteristics or tool, that characteristic or tool remains in the device context until you
change it again. This means that, after the SetTextColor() function has been called to
change the color of text, any text drawing performed on the device context would be
made using the crColor color. If you want a different color, you would have to select a
new one using either SetTextColor() or some other function or a TCanvas method.
You must provide the color you want to use as the crColor argument. If this function
succeeds, it changes the background of the next text that would be drawn and it returns
the previous background color, which you can restore at a later time. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
SetTextColor(Canvas->Handle, RGB(255, 25, 2));
Canvas->TextOut(50, 42, "Johnny Carson");
SetBkColor(Canvas->Handle, RGB(0, 0, 128));
SetTextColor(Canvas->Handle, RGB(128, 255, 255));
Canvas->TextOut(50, 60, "The once king of late-night");
}
//---------------------------------------------------------------------------
If you want to know the background color applied on the text drawn, you can call the
GetBkColor() function. Its syntax is:
COLORREF GetBkColor(HDC hdc);
This function returns the color used to highlight the text, if the text is highlighted. The
highlighting of text is actually controlled by the SetBkMode() function whose syntax is:
int SetBkMode(HDC hdc, int iBkMode );
This function specifies whether the background color should be applied or not. This is set
by the iBkMode argument. It can have one of two values. If it is:
241
If you want to find out what background mode is applied to the object(s) drawn, you can
call the GetBkMode() function. It is declared as follows:
int GetBkMode(HDC hdc);
You can also draw text and include it in a (colored) rectangle. This can be done using the
ExtTextOut() function. Its syntax is:
BOOL ExtTextOut(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc,
LPCTSTR lpString, UINT cbCount, CONST INT *lpDx);
The X and Y values specify the location of the first character of the text to be drawn.
The fuOptions parameter holds a constant that determines how the rectangle will be
drawn. It can be:
ETO_CLIPPED: The lpString string will be clipped to the lprc rectangle. For
example, the color previously specified by SetBkColor() will only highlight the text
The lprc parameter is a rectangle that will be drawn behind the text.
The lpString string is the text to be drawn.
The cbCount value is the number of characters of lpString.
The lpDx parameter is an array of integers that specifies the amount of empty spaces that
will be used between each combination of two characters. Unless you know what you are
doing, pass this argument as 0, in which case the regular space used to separate characters
would be used.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
RECT Recto = { 20, 28, 188, 128 };
SetTextColor(Canvas->Handle, RGB(25, 55, 200));
SetBkColor(Canvas->Handle, RGB(128, 255, 255));
ExtTextOut(Canvas->Handle, 50, 42, ETO_OPAQUE,
&Recto, "Johnny Carson", 13, NULL);
}
242
//---------------------------------------------------------------------------
243
The most important and most obvious property of the Color dialog box is the selected
color once the user has made a choice. When the user opens the dialog you can set the
default color on the Object Inspector using the Color property. You can also set this color
programmatically as follows:
//--------------------------------------------------------------------------void __fastcall TfrmFoodOrders::FormCreate(TObject *Sender)
{
ColorDialog1->Color = clRed;
}
//---------------------------------------------------------------------------
When the user has finished using the Color dialog box and clicked OK, you can find out
what color was selected by using the TColorDialog::Color property.
244
You can control the regular or full size of the dialog using the Options property. At
design time, to manipulate the options, on the Object Inspector, click the + button on the
Options field to expand it. Since the options are controlled by the TColorDialogOption
is a set, you can specify as many options as you want:
//--------------------------------------------------------------------------void __fastcall TfrmFoodOrders::FormCreate(TObject *Sender)
{
ColorDialog1->Color = clRed;
ColorDialog1->Options << TColorDialogOption()
<< cdFullOpen << cdAnyColor;
}
//---------------------------------------------------------------------------
If you want to supply the user with a set of colors of your choice, you can do this using a
list of custom colors. To create this list, click the CustomColor field to reveal its ellipsis
button, then click that button to display the String List Editor dialog box. You can specify
up to 16 colors. The colors are named ColorA, ColorB, ColorC, and so on up to ColorP.
To create the list, type the ordinal name and assign it an integer number. Here is an
example:
245
}
//---------------------------------------------------------------------------
The most efficient approach is to make sure that the dialog was opened and closed
successfully, then retrieve the color if the user clicked OK to close the dialog. This is
done through a conditional call, as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
if( ColorDialog1->Execute() )
Color = ColorDialog1->Color;
}
//---------------------------------------------------------------------------
After creating it, you can use it as a regular control. For example, you can change the
color of an Edit control on the form as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnCreateColorClick(TObject *Sender)
{
TColorDialog* Dlg = new TColorDialog(this);
if( Dlg->Execute() )
edtFullName->Color = Dlg->Color;
}
//---------------------------------------------------------------------------
To make a dynamically created Color dialog available to more than one event or
function, declare an instance of the TColorDialog class in the private or public sections
of the header file of a form or the unit that would use it:
246
1.
2.
On the Object Inspector, click the Caption field and type Color Changer
3.
Click the Color field to reveal its combo box. Then click the arrow of the combo box
and select clBackground
4.
5.
Test the application to see the result. Then close it and return to Bcb
6.
On the Object Inspector, double-click the right field to Color to display the Color
7.
Click Define Custom Colors and set the color values to Red: 22, Green: 125, and
Blue: 190
8.
Click OK and test the application. Then close it and return to Bcb
9.
On the Object Inspector, click the Events tab and double-click the right side of the
OnDblClick field
10.4 Fonts
247
248
You can also create a font using one of the many Win32 font related functions.
To use a particular font, assign its name to the Name member variable of the TFont
class. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
TFont *NewFont = new TFont;
NewFont->Name = "Garamond";
Canvas->Font = NewFont;
Canvas->TextOut(20, 18, "Christine");
}
//---------------------------------------------------------------------------
If you are specifying a font other than the default to use in your application, you should
use only the most popular font that are more likely to be found on your users computers.
Otherwise, the result may be unpredictable.
The height of a font is a measure of the height used to represent its characters. It is
represented by the Height property of the TFont class.
249
The font size is the dimension of characters used to represent the font on a device
context. It is specify using the TFont::Size member variable. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Font->Size = 120;
Canvas->Font->Name = "Garamond";
Canvas->TextOut(26, 24, "Christine");
}
//---------------------------------------------------------------------------
The Style of a font controls how the font displays, in normal, italicized, underlined,
stroke out, some of these characteristics or all of them. The VCL manages these
properties as a set; meaning you can build them, add those you want or retract those you
do not need. The available characteristics are as follows:
Characteristic
Bold
Italic
Underline
Strikeout
Value
fsBold
fsItalic
fsUnderline
fsStrikeOut
Example
This text is bold
Italicized section
The words are underlined
Stroke out but happy
Font styles are implemented through the TFontStyles property. To control the Style of
font, you must call TFontStyles and use the extraction operators to add or subtract a
style. To add a style, you can use the << operator. For example, suppose you want to
apply a Bold style to a Memo control. You can use the << operator as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Memo1->Font->Name = "Verdana";
Memo1->Font->Size = 10;
Memo1->Font->Style = TFontStyles() << fsBold;
}
//---------------------------------------------------------------------------
In the same way, you can add other styles using the << operator:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Memo1->Font->Name = "Verdana";
250
Memo1->Font->Size = 10;
Memo1->Font->Color = clBlue;
Memo1->Font->Style = TFontStyles() << fsBold << fsUnderline
<< fsItalic << fsStrikeOut;
}
//---------------------------------------------------------------------------
Unlike the Win32 functions as we will see, the TFont class provide support for colors.
Therefore, to draw text using a color of your choice, assign its value to the TFont::Color
property. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Font->Size = 120;
Canvas->Font->Color = clSkyBlue;
Canvas->Font->Name = "Garamond";
SetBkMode(Canvas->Handle, TRANSPARENT);
Canvas->TextOut(26, 24, "Christine");
Canvas->Font->Color = clBlue;
Canvas->TextOut(20, 18, "Christine");
}
//---------------------------------------------------------------------------
251
The nHeight parameter is the height of a small rectangle in which a character of this font
would fit.
The nWidth value is the average width of characters of this font. If you know the width to
apply, then you can pass it as this argument. If not, pass it as 0. In this case, the system
will choose the closest value to be applied on the text.
The nEscapement parameter is the angle used to orient the text. The angle is calculated as
a multiple of 0.1, oriented counterclockwise and provided in degrees.
The nOrientation parameter is the angular orientation of the text with regards to the
horizontal axis.
The fnWeight parameter is used to attempt to control the font weight of the text because it
is affected by the characteristics of the font as set by the designer. It holds values that
displays text from thin to heavy bold. The possible values are:
Constant
FW_DONTCARE
FW_EXTRALIGHT
FW_LIGHT
FW_NORMAL
FW_MEDIUM
FW_SEMIBOLD
FW_BOLD
FW_EXTRABOLD
FW_BLACK
Value
0
200
300
400
500
600
700
800
900
Constant
FW_THIN
FW_ULTRALIGHT
Value
100
200
FW_REGULAR
400
FW_DEMIBOLD
600
FW_ULTRABOLD
FW_HEAVY
800
900
The fdwItalic value specifies whether the font will be italicized (TRUE) or not (FALSE).
The dwbUnderline value is used to underline (TRUE) or not underline (FALSE) the text.
The fdwStrikeOut value is specifies whether the text should be stroke out (TRUE) or not
(FALSE) with a (horizontal) line.
The fdwCharSet parameter specifies the character set used. The possible values are:
ANSI_CHARSET, BALTIC_CHARSET, CHINESEBIG5_CHARSET,
DEFAULT_CHARSET, EASTEUROPE_CHARSET, GB2312_CHARSET,
GREEK_CHARSET, HANGUL_CHARSET, MAC_CHARSET, OEM_CHARSET,
RUSSIAN_CHARSET, SHIFTJIS_CHARSET, SYMBOL_CHARSET,
TURKISH_CHARSET, HEBREW_CHARSET, and THAI_CHARSET.
252
The fdwOutPrecision parameter controls the amount of precision used to evaluate the
numeric values used on this function for the height, the width, and angles. It can have one
of the following values: OUT_CHARACTER_PRECIS, OUT_DEFAULT_PRECIS,
OUT_DEVICE_PRECIS, OUT_OUTLINE_PRECIS, OUT_RASTER_PRECIS,
OUT_STRING_PRECIS, OUT_STROKE_PRECIS, OUT_TT_ONLY_PRECIS,
and OUT_TT_PRECIS.
The fdwClipPrecision parameter is used to specify how some characters may be drawn
outside of the area in which they are intended. The possible values used are
CLIP_DEFAULT_PRECIS,
CLIP_CHARACTER_PRECIS,
CLIP_STROKE_PRECIS, CLIP_MASK, CLIP_EMBEDED, CLIP_LH_ANGLES,
and CLIP_TT_ALWAYS.
The fdwQuality parameter specifies how the function will attempt to match the font's
characteristics. The possible values are ANTIALIASED_QUALITY,
DEFAULT_QUALITY, DRAFT_QUALITY, NONANTIALIASED_QUALITY, and
PROOF_QUALITY.
The fdwPitchAndFamily parameter specifies the category of the font used. It combines
the pitch and the family the intended font belongs to. The pitch can be specified with
DEFAULT_PITCH, VARIABLE_PITCH, or FIXED_PITCH. The pitch is combined
using the bitwise OR operator with one of the following values:
Value
FF_DECORATIVE
FF_DONTCARE
FF_MODERN
FF_ROMAN
FF_SCRIPT
FF_SWISS
Description
Used for a decorative or fancy fonts
Let the compiler specify
Modern fonts that have a constant width
Serif fonts with variable width
Script-like fonts
Sans serif fonts with variable width
253
Remember that once a device context object, such as a font, has been selected, it remains
there until further notice. For example, if you have created and selected a font, any text
you draw would follow the characteristics of that font. If you want another font, you must
change the previously selected font.
The CreateFont() function is used to specify all characteristics of a font in one step.
Alternatively, if you want to specify each font property, you can declare a LOGFONT
variable and initialize it. It is defined as follows:
typedef struct tagLOGFONT {
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT, *PLOGFONT;
This time, you do not have to provide a value for each member of the structure and even
if you do, you can supply values in the order of your choice. For any member whose
value is not specified, the compiler would use a default value but you may not like some
of the default values. Therefore, you should specify as many values as possible. The
member variables can be initialized with their equivalent values we reviewed for the
CreateFont() function.
After initializing the LOGFONT variable, call the CreateFontIndirect() function. Its
syntax is:
HFONT CreateFontIndirect(CONST LOGFONT *lplf);
When calling this member function, pass the LOGFONT variable as a pointer, lplf. Like
CreateFont(), the CreateFontIndirect() function returns an HFONT value. After
calling this function, you can retrieve its return value and initialize the handle of the
254
TCanvas::Font member variable. After that assignment, the font is ready for you. Here
is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
HFONT font;
LOGFONT LogFont;
LogFont.lfStrikeOut = 0;
LogFont.lfUnderline = 0;
LogFont.lfHeight = 42;
LogFont.lfEscapement = 0;
LogFont.lfItalic = TRUE;
LogFont.lfWidth = 22;
font = CreateFontIndirect(&LogFont);
Canvas->Font->Handle = font;
Canvas->TextOut(20, 18, "James Kolowski");
}
//---------------------------------------------------------------------------
After this initialization, your variable can provide you with any type of valid information
related to the currently selected font on the device context, such as the fonts name, its
size, its style(s), character set, etc.
255
If the system or you have created or selected a font, you can use it to initialize another
font variable. To support this, the TFont class is equipped with the Assign() method. Its
syntax is:
virtual void __fastcall Assign(Classes::TPersistent* Source);
The Source parameter is the returned new font. After the font variable calls it, it would
hold the same characteristics of the existing font. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
TFont *CurFont = new TFont;
Canvas->Font->Assign(CurFont);
Caption = CurFont->Name;
}
//---------------------------------------------------------------------------
As seen on this example, the TFont::Assign() method can be used to retrieve the current
font selected in the device context.
256
To use the Font dialog box, the user should first have text that needs to, and can, be
formatted. Users usually call the Font dialog box using a menu item or a popup menu
from right clicking. Once the dialog box displays, a user can select a font by its name, its
style, its size, one or both effects (Underline or Strikeout), and a color. After making the
necessary changes, the user can click OK to apply the changes or click Cancel to ignore
the selected attributes.
If you cannot add a FontDialog object at design time for any reason, you can get a Font
dialog box by declaring a pointer to TFontDialog. This is can be done in the function or
event where the dialog box would be needed. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TFontDialog *dlgFont = new TFontDialog(Form1);
}
//---------------------------------------------------------------------------
If you want the dialog box to be available to all functions and events of a unit, you can
declare a pointer to a TFontDialog class in the class of the form where the object would
be needed.
At design time, the Font dialog box hardly needs any change of properties to work. The
only time you would set its properties is if you judge that its default properties are not
conform to your particular scenario. For example, if you are providing font formatting for
a RichEdit control and you want users to control the font characteristics of individual
letters or paragraph, there should be nothing to change at design time. Otherwise, the
default properties can be changed using the Object Inspector.
Copyright 2003 FunctionX, Inc.
257
The Object Inspector presents the same options the user would need to set when
displaying the Font dialog box. Imagine that you want to change the default font
attributes of any control that descends from the TControl class, for example a memo. At
design time, when the object is selected on the form, on the Object Inspector, you can
expand the Font property and the Style set if necessary then change the properties as you
see fit:
At run time, you can still set the characteristics as you wish. For example, you can
change them in response to some intermediate action. On the other hand, you can use the
TFontDialog object to let the user customize the font characteristics of text.
Once, and however, you have a TFontDialog instance, you can display the Font dialog
box by calling the Execute() method. The font dialog box is equipped with two primary
buttons: OK and Cancel. After using it, if the user clicks OK, this implies that if there
were changes of font, size, color, etc, the user wants them committed to the document. If
the user clicks Cancel, this means that you should ignore any actions that were performed
on the dialog box. The Execute() method is Boolean. It returns true if the user clicks OK.
Otherwise, if the user clicks Cancel, it would return false. Therefore, after the user has
used it, you should find out if she clicked OK or Cancel before applying her changes.
This inquiry is usually performed with an if conditional statement as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
if( dlgFont->Execute() )
{
// What to do if the user clicked OK
}
}
//---------------------------------------------------------------------------
The Name of the selected font is an AnsiString value that indirectly belongs to the TFont
class. After the user has clicked OK, you can find out what font was selected and assign it
to the Font object you are trying to change. Here is an example:
258
The styles can be managed using the Font dialog box as one object. After the user has
clicked OK on the dialog box, you can simply assign whatever style was set to the
TFont::Style property of the object that needs the change. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
if( dlgFont->Execute() )
{
Memo1->Font->Name = dlgFont->Font->Name;
Memo1->Font->Size = dlgFont->Font->Size;
Memo1->Font->Color = dlgFont->Font->Color;
Memo1->Font->Style = dlgFont->Font->Style;
}
}
//---------------------------------------------------------------------------
259
260
A pen is referred to as cosmetic when it can be used to draw only simple lines of a
fixed width, less than or equal to 1 pixel
A pen is geometric when it can assume different widths and various ends.
261
After calling this function, make sure you retrieve its return value so you can assign it to
the Handle member variable of TCanvas::Pen.
The Win32 API also provides the LOGPEN structure that you can use to individually
specify each characteristics of a logical pen. The LOGPEN structure is created as
follows:
typedef struct tagLOGPEN {
UINT
nStyle;
POINT nWidth;
COLORREF nColor;
} LOGPEN, *PLOGPEN;
To use this structure, declare a variable of LOGPEN type or a pointer. Then initialize
each member of the structure. If you do not, its default values would be used and the line
may not be visible. After initializing the LOGPEN variable, call the
CreatePenIndirect() function to create a pen. The syntax of the CreatePenIndirect()
function is:
HPEN CreatePenIndirect(CONST LOGPEN *lplgpn);
The LOGPEN value is passed to this method as a pointer. After this call, the new pen is
available and can be selected into a device context variable for use.
262
Although most lines are drawn continuously, the VCL and the Win32 library support
non-continuous lines. This characteristic is controlled by the style of the pen. For a TPen
object, this would be the Style property. For the CreatePen() function or the LOGPEN
structure, this would be the fnPenStyle argument or member variable. The possible values
of the style are:
TPen::Style
psSolid
psDash
PS_DASH
psDot
PS_DOT
psDashDot
PS_DASHDOT
psDashDotDot
PS_DASHDOTDOT
psClear
PS_NULL
psInsideFrame
PS_INSIDEFRAME
Illustration
Description
A continuous solid line
A continuous line with dashed
interruptions
A line with a dot interruption at
every other pixel
A combination of alternating dashed
and dotted points
A combination of dash and double
dotted interruptions
No visible line
A line drawn just inside of the
border of a closed shape
The Width or nWidth of a pen specifies how wide its line(s) would be. Its value should
be greater than or equal to 1. If you specify a width less than 1, it would be restored to 1.
The value of the width cannot be applied to all types of pens. This property is directly
influenced by the style. A cosmetic pen can have a width of only 1 pixel. If you specify a
higher width, it would be ignored. A geometric pen can have a width of 1 or more pixels
but the line can only be solid or null. This means that, if you specify the style as psDash,
PS_DASH, psDot, PS_DOT, psDashDot, PS_DASHDOT, psDashDotDot, or
PS_DASHDOTDOT but set a width higher than 1, the line would be drawn as
PS_SOLID.
If you are using the CreatePen() function, to specify the type of pen you are creating, as
cosmetic or geometric, use the bitwise OR operator to combine one of the above styles
with one of the following:
If you are creating a cosmetic pen, you can also add (bitwise OR) the PS_ALTERNATE
style to set the pen at every other pixel.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
263
Canvas->Pen->Style = psDashDotDot;
Canvas->Pen->Width = 1;
Canvas->Pen->Color = TColor(RGB(255, 25, 5));
Canvas->Rectangle(20, 22, 250, 125);
}
//---------------------------------------------------------------------------
Once a pen has been selected, any drawing performed and that uses a pen would use the
currently selected pen. If you want to use a different pen, you can either create a new pen
or change the characteristics of the current pen.
Here is an example that uses the HPEN:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
HPEN NewPen;
LOGPEN LogPen;
LogPen.lopnStyle = PS_SOLID;
LogPen.lopnWidth = Point(1, 105);
LogPen.lopnColor = RGB(235, 115, 5);
NewPen = CreatePenIndirect(&LogPen);
Canvas->Pen->Handle = NewPen;
Canvas->Ellipse(60, 40, 82, 80);
Canvas->Ellipse(80, 20, 160, 125);
Canvas->Ellipse(158, 40, 180, 80);
Canvas->Ellipse(100, 60, 110, 70);
Canvas->Ellipse(130, 60, 140, 70);
Canvas->Ellipse(100, 90, 140, 110);
}
//---------------------------------------------------------------------------
264
11.2 Brushes
11.2.1 Introduction
A brush is a drawing tool used to fill out closed shaped or the interior of lines. It is
similar to picking up a bucket and pouring its contents somewhere. In the case of
computer graphics, the area where you position the brush is called the brush origin. The
color (or picture) that the brush holds would be used to fill the whole area until the brush
finds a limit set by some rule. A brush can be characterized by its color (if used), its
pattern used to fill the area, or a picture (bitmap) used as the brush.
The VCL provides support for brushes through the TBrush class. The TCanvas class has
a TBrush member variable. This means that, any object that can receive drawing, that is,
every control that provides a Canvas member variable, already provides a TBrush
variable ready for use. If you want to explicitly create a brush, you can declare a TBrush
variable and use it to initialize the TCanvas::Brush member variable.
265
Once a brush has been selected, it would be used on all shapes that are drawn under it,
until you delete or change it. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Brush->Color = static_cast<TColor>(RGB(255, 2, 5));
TPoint Pt[3];
// Top Triangle
Pt[0] = Point(125, 10);
Pt[1] = Point( 95, 70);
Pt[2] = Point(155, 70);
Canvas->Polygon(Pt, 2);
// Left Triangle
Pt[0] = Point( 80, 80);
Pt[1] = Point( 20, 110);
Pt[2] = Point( 80, 140);
Canvas->Polygon(Pt, 2);
// Bottom Triangle
Pt[0] = Point( 95, 155);
Pt[1] = Point(125, 215);
Pt[2] = Point(155, 155);
266
Canvas->Polygon(Pt, 2);
// Right Triangle
Pt[0] = Point(170, 80);
Pt[1] = Point(170, 140);
Pt[2] = Point(230, 110);
Canvas->Polygon(Pt, 2);
}
//---------------------------------------------------------------------------
If you want to use a different brush, you must change the characteristic(s) of the currently
selected brush or create a new one. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Brush->Color = static_cast<TColor>(RGB(255, 2, 5));
TPoint Pt[3];
// Top Triangle
Pt[0] = Point(125, 10);
Pt[1] = Point( 95, 70);
Pt[2] = Point(155, 70);
Canvas->Brush->Color = clGreen;
Canvas->Polygon(Pt, 2);
// Left Triangle
Pt[0] = Point( 80, 80);
Pt[1] = Point( 20, 110);
Pt[2] = Point( 80, 140);
Canvas->Brush->Color = clRed;
Canvas->Polygon(Pt, 2);
// Bottom Triangle
Pt[0] = Point( 95, 155);
Pt[1] = Point(125, 215);
267
To support solid brushes, the Win32 API provides the CreateSolidBrush() function. Its
syntax is:
HBRUSH CreateSolidBrush(COLORREF crColor);
Therefore, to create a brush, you can call this function and pass it a COLORREF color
value. Retrieve the return value of this function and use it to initialize the Handle
member variable of the TBrush class.
268
The fnStyle parameter specifies the hatch pattern that must be used to fill the area. The
possible values to use are HS_BDIAGONAL, HS_CROSS, HS_DIAGCROSS,
HS_FDIAGONAL, HS_HORIZONTAL, or HS_VERTICAL. The clrref argument
specifies the color applied on the drawn pattern.
2.
3.
4.
269
5.
The lbStyle member variable specifies the style applied on the brush.
The lbColor is specified as a COLORREF value.
The lbHatch value represents the hatch pattern used on the brush. . It takes the same value
as the fnStyle parameter of the CreateHatchBrush() function.
After initializing the LOGBRUSH variable, pass it to the CreateBrushIndirect()
function. Its syntax is:
HBRUSH CreateBrushIndirect(CONST LOGBRUSH *lplb);
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
LOGBRUSH LogBrush;
LogBrush.lbStyle = BS_HATCHED;
LogBrush.lbColor = RGB(255, 0, 255);
LogBrush.lbHatch = HS_DIAGCROSS;
HBRUSH NewBrush = CreateBrushIndirect(&LogBrush);
Canvas->Brush->Handle = NewBrush;
Canvas->Rectangle(20, 12, 250, 175);
}
270
//---------------------------------------------------------------------------
If you had started C++ Builder, to start a graphic, on the main menu of C++ Builder,
you can click Tools -> Image Editor
Image Editor is installed in the same group as C++ Builder. To start Image Editor at
anytime, from the taskbar, you can click Start -> (All) Programs -> Borland C++
Builder -> Image Editor
271
272
To find out what a button is used for, position the mouse on top. A tool tip,
or hint, would display. The buttons are used for various goals and exhibit
different behaviors.
Some
suchthe
as the
Pencil,
andofthea geometric
If
youtools
change
default
linelines,
width
tool, the selected width or
shapes (rectangle,
rectangle,
thickness
stays in round
memory
and canand
be ellipse)
applied allow
to a tool that does not
you to specify
a width
their lines.
obviously
display
thisfor
option.
This happens if you a great width for a
rectangle and then decide to use a Filled Rectangle tool, the last width
would apply to the new tool. Therefore, if you do not want to use the same
width, select the default before using another tool.
Some tools such as the Brush or the Spray allow you to
select a thickness for the dot or mark they would apply to a
graphic. To access this change, first select the Brush or
Spray, then, in the lower section of the Tools Palette, select
the the thickness you want.
The main area of the application is made of a wide black rectangle that is used to host the
graphics you will be using.
Like the top section, the bottom area of the application contains two objects. The Color
Palette displays a list of 16 colored buttons (by default)
When closing Image Editor, if you had a modified document that needs to be saved, you
would be prompted to save it.
Graphics used in the Windows operating system are divided in categories. Probably the
most popular of the graphics natively used in the operating system is called a bitmap.
11.4 Icons
11.4.1 Introduction
Like a bitmap, an icon is used to display graphics on window objects. While a bitmap can
have any dimension the window needs, the size of an icon must be limited. This is
because icons assume different roles on an application.
Copyright 2003 FunctionX, Inc.
273
274
1.
2.
On the main menu of Image Editor, click File -> New -> Icon File (.ico)
3.
On the Icon Properties dialog box, click the 32 x 32 (Standard Icon) radio button. In
the Colors section, click the 16 Color radio button.
4.
Click OK.
5.
6.
7.
On the Color Palette, click the gray button (2nd column, 1st row)
8.
9.
10. On the Color Palette, click the red color (3rd column, 2nd row)
11. Click inside of the drawn gray rectangle
12. On the Tools Palette, click the Filled Rectangle button
13. On the Color Palette, click the yellow color (4th column, 2nd row).
14. Using the drawing tools and the colors on the Color Palette, draw a rectangle as
follows:
15. To associate an equivalent smaller icon, under the title bar of the child window, click
the New button:
16. Notice that the 16 x 16 (Small Icon) and 16 Color radio buttons are already selected.
Therefore, click OK.
17. Press Ctrl + I five times to zoom
18. Using the drawing tools on the Tools Palette and the colors on the Colors Palette,
draw the flag as follows:
Copyright 2003 FunctionX, Inc.
275
276
277
33. Notice that the executable file uses the the right icon for each display.
34. Return to Bcb
11.5 Cursors
11.5.1 Introduction
Cursors are another type of application accessory you can design using pens and brushes
in Image Editor. A cursor is a small graphic that represents the position of the mouse on a
Windows screen. Because Windows is a graphic-oriented operating system, when it
installs, it creates a set of standard or regularly used cursors. These can be seen by
opening the Control Panel window and double-clicking the Mouse icon. This opens the
Mouse Properties dialog box where you can click the Pointers tab to see a list of standard
cursors installed by Windows:
278
279
280
1.
On the main menu of Image Editor, click File -> New -> Cursor File (.cur)
2.
3.
4.
In the line width section, make sure the top line is selected. In the Color Palette,
make sure the black color is selected
5.
Draw a vertical line from the pixel on the 6th column and 2nd row from top
6.
Draw a diagonal line at 45 from the top border of the new line to the lower-right
until the line is at 5 pixels from the right border of the drawing area
7.
Draw a horizontal line from the lower border of the dialog line to half-left
8.
Draw a diagonal line from the lower border of the vertical line to the left border of
the horizontal line:
9.
Draw another diagonal line from the top corner of the current shape to the
intersection of horizontal and left diagonal line:
15. To test the cursor, on the main menu, click Cursor -> Test
16. Draw a curved line. After previewing the cursor, click Close
17. On the Color Palette, right-click the button with a green background and a red S
18. On the drawing area, right-click outside of the shape to apply the necessary
background
19. In the Color Palette, click the black color and click inside the left triangle
20. In the Color Palette, click the white color and click inside the right triangle
21. To set the position of the cursor pointer, on the main menu, Cursor -> Set Hot
Spot
22. Change the Horizontal (X) value to 5 and change the Vertical (Y) value to 1
281
23. Click OK
24. To test the cursor, on the main menu, click Cursor -> Test
25. Draw various shapes
282
On the main menu of Image Editor, click File -> New Cursor (.cur)
If necessary, press Ctrl + I a few times to zoom in
2.
3.
In the drawing area, position your mouse on the top right corner 3 pixels from the
right border and one pixel from top
4.
Click and drag down and left to draw a diagonal line to stop at 3 pixels from the left
border and 1 pixel from the bottom border:
5.
To start the curved line, count the pixels on the line from top. Then click and drag
the 5th pixel to the left as if you were drawing a square as follows:
6.
To smooth the curved line, click the top-left pixel that is at the intersection of both
lines. Drag down and right:
283
7.
With the Curve tool still selected, draw the same diagonal line you drew earlier.
8.
Drag the 5th pixel from top of the line to right and down as if you were drawing a
square:
9.
Click the pixel at the intersection of both lines then drag left and up to draw a curved
line:
10. While the Curve tool is selected, draw one more diagonal line similar to the previous
ones
11. To make the line curved, click in the middle of the diagonal line and slightly drag
left and up:
284
12. As you may realize, we are drawing a leaf or feather. Therefore, add a few black
pixels to decorate the graphic. Using the Fill tool, add a white background to the
cursor:
13. To specify the position of the pointer, on the main menu, click Cursor -> Set Hot
Spot
14. Set the Horizontal value to 3 and the Vertical value to 30. Click OK
15. On the main menu, click Cursor -> Test...
16. Draw a few lines to test the cursor and click Close
17. Save the file as Feather
18. Back in Image Editor, make sure the window that has the previously designed cursor
has focus
Press Ctrl + A to select the cursor. Press Ctrl + C to copy the graphic
19. On the main menu, click File -> New -> Icon File (.ico)
20. On the Icon Properties dialog box, click the 32 x 32 (Standard Icon) radio button. In
the Colors section, click the 16 Color radio button and click OK
21. Press Ctrl + V to paste the graphic
22. On the Tools Palette, click Fill
23. On the Color Palette, right-click the button with a green color and red S
24. In the drawing area, right-click outside of the graphic to make background
transparent
Copyright 2003 FunctionX, Inc.
285
31. Select the silver color (2nd column, 2nd row) and create a checkered area on the left
side of the middle line
32. Save the icon as Feather but do not close its child window
286
Start WordPad
2.
Using the Formatting toolbar, change the Font to Wingdings and change the Font
Size to 32
3.
Type 7
This would produce the picture of a keyboard
4.
5.
Press Ctrl + C to copy the symbol (you can now close WordPad if you want)
6.
You should still have Microsoft Paint. Otherwise launch it (Start -> (All) Programs > Accessories -> Paint)
In Paint, click File -> New. If you are asked whether you want to save a file, click
No
Press Ctrl + V to paste the selection
7.
287
8.
Press Ctrl + C to copy the selection (you can now close Paint if you want)
9.
In Image Editor, to create a new icon, on the main menu, click File -> New -> Icon
File (.ico)
13. In WordPad, select the displaying character. Change the font size to 14. Copy and
paste the character in a new document in Windows Paint
288
289
C++ Builder in combination with Image Editor make the process of using a resource file
easy. You have two main alternatives.
If you plan to use just bitmaps, icons, and cursors, in Image Editor, create a Resource
File (.res) and add the desired bitmaps, icons, and resources. Once you have created
the res file, you can include it in your application by clicking Project -> Add To
Project from the main menu of C++ Builder. When you execute your project, C++
Builder would recognize it as a compiled file and you do not have to worry about
anything else
Sometimes you will need to create a non-compiled file. In this case, in Image Editor,
you can create a Component Resource File (*dcr) file and add the desired files to it.
Once you have a dcr file, in C++ Builder, add it to your project (Project -> Add To
Project). When you execute the project, C++ Builder would take care of compiling it
and produce a res file, then use that res file where needed in your project.
In the main menu of Image Editor, click File -> New... -> Resource File (.res)
2.
3.
While the new cursor is still selected, on the main menu, click Resource -> Rename.
Type PointMe and press Enter
4.
In the resource window, click Contents to select it. Then right-click it and New ->
Cursor
5.
Right-click the new cursor and click Rename. Type Scripter and press Enter
6.
On the main menu, click Window and click the line that has Push.cur
7.
To select the cursor, press Ctrl + A. to copy the selection, press Ctrl + C
8.
9.
10. On the Tools Palette, click any button to dismiss the blinking line.
11. On the main menu, click Window and click the line that has Feather.cur
12. Press Ctrl + A then press Ctrl + C
13. On the main menu, click Window -> Untitled1.res
14. Double-click SCRIPTER and press Ctrl + V. Click any button in the Tools Palette
15. On the main menu, click Window -> Untitled1.res
16. On the main menu, click File -> Save
17. Locate and display the Applications Resources folder in which our current
application is located. In the File Name box, click Untitled1 to select it.
18. Type Exercise and press Enter
19. Go to C++ Builder.
20. To include the necessary resource, on the main menu, click Project -> Add To
Project...
21. Using the bottom combo box, change the Files Of Types to Compiled Resource
(*.res)
22. In the list, click Exercise.res
290
23. In the header file of the main form, Main.h, on top of the class, declare two constant
integers as follows:
//--------------------------------------------------------------------------#ifndef MainH
#define MainH
//--------------------------------------------------------------------------#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
const int PushAway = 1;
const int WriteItDown = 2;
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published: // IDE-managed Components
private:
// User declarations
public:
// User declarations
__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------------------------#endif
28. Press F12 to display the Code Editor and click the Main.cpp tab
29. In the constructor of the form, initialize the cursors as follows:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Screen->Cursors[PushAway] = LoadCursor(HInstance, "POINTME");
291
30. Press F12 to display the form. Double-click in the middle of the form to access its
OnCreate event
31. Implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Panel1->Cursor = TCursor(PushAway);
Memo1->Cursor = TCursor(WriteItDown);
}
//---------------------------------------------------------------------------
292
A bitmap can also be used for aesthetic purposes to decorate a dialog box. Thats how it
is used on some of the installation wizard boxes such as the graphic on the left section of
the WordPerfect 2002 installer:
293
To create a bitmap, on the main menu of Image Editor, you would click File -> New ->
Bitmap File (.bmp). A dialog box would come up. This allows you to specify the possible
dimensions of the picture. You can specify the dimensions of your choice according to
the intended eventual use of the bitmap.
You can also get a bitmap by changing an existing picture. To modify an existing picture,
you can use any graphics software that is fit. Windows Paint that ships with the operating
system is a good cheap alternative to commerical applications. Jasc Paint Shop Pro is also
an excellent product suitable to do almost anything with a bitmap. To manipulate a
bitmap with such applications, you should first import it. On the main menu of Windows
Paint, you can click File -> Open Change the Files of Type to the kind of file you want
to open, or set it to All Files. Locate the desired file and click Open. Alternatively, you
can find a way to copy the graphic and paste it into Windows Paint.
After designing your bitmap, you must save it. A bitmap is a Windows file that has an
extension of .bmp
To launch Image Editor, on the taskbar, click Start -> (All) Programs -> Borland
C++ Builder -> Image Editor
2.
On the main menu of Image Editor, click File -> New -> Bitmap(.bmp)
3.
On the Bitmap Properties dialog box, set the both the Width and Height values to 32
4.
On the Colors section, click the VGA (16 colors) radio button
5.
Click OK
6.
Click Ctrl + I a few times until the height of the drawing area is the same as the
height of the child window
7.
Using the tools on the Toolbox and the colors on the Colors Palette, design the
bitmap as follows:
295
8.
Save it as Diamond1
296
Of course an alternative is to open the picture in another application such as Paint, select
then copy it to the clipboard, and then paste it in Image Editor.
One of the differences between both applications is that when creating a new bitmap in
Image Editor, you must specify its size. After doing this, if you paste an image that is
wider or taller than the allocated rectangle, part of the picture would not appear. You
would then have to resize the rectangle and paste again. In Paint, if you attempt to paste
an image that is wider and/or taller than the primarily allocated rectangle, you would be
asked whether you want the rectangle to be resized to accommodate the picture.
Based on this review, if you are creating a bitmap that would be displayed on top of
Windows controls that need small pictures (bitmap buttons, list view, tree view, etc) use
Image Editor to prepare them. If you are preparing an advanced picture to use in your
application, you should use either Paint or a more advanced graphic editor. C++ Builder
does not care where or how you created a bitmap as long as it is in the right format (either
bmp, jpeg, or jpg extension).
297
TBitmap. To do this, you must precede the name of the class with the Graphics
namespace. Here is an example:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Graphics::TBitmap *LaCourt = new Graphics::TBitmap;
}
//---------------------------------------------------------------------------
Like all other dynamic objects, after using the bitmap, you should delete it. If you declare
it locally, you can also delete it in the same event or method. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
Graphics::TBitmap *BmpMercedes = new Graphics::TBitmap;
try {
// Treat the bitmap file here
}
__finally
{
// Time to delete the pointer
delete BmpMercedes;
}
}
//---------------------------------------------------------------------------
If you declare it globally, make sure you delete it when the application is destroyed. After
declaring the variable, you must initialize it appropriately before actually using it.
The X and Y values specify the corner from where to start drawing. Normally, it will be
the top-left coordinates of the picture. The Graphic parameter is the graphic file to draw
on the canvas. This would be done as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
Graphics::TBitmap *BmpMercedes = new Graphics::TBitmap;
try {
Canvas->Draw(10, 10, BmpMercedes);
}
__finally
{
delete BmpMercedes;
298
}
}
//---------------------------------------------------------------------------
This method is particularly easy to use as long as you know the location of the bitmap
file. If the picture is in the same directory as the current project, you can simply type its
name and its extension as a string and pass it as argument. An example would be:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
Graphics::TBitmap *BmpMercedes = new Graphics::TBitmap;
try {
BmpMercedes->LoadFromFile("Mercedes2.bmp");
Canvas->Draw(10, 10, BmpMercedes);
}
__finally
{
delete BmpMercedes;
}
}
//---------------------------------------------------------------------------
If the file is not in the same directory as the project, you may have to specify its complete
path. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
Graphics::TBitmap *BmpMercedes = new Graphics::TBitmap;
try {
BmpMercedes->LoadFromFile("C:\\Programs\\Mercedes1.bmp");
Canvas->Draw(10, 10, BmpMercedes);
}
__finally
{
delete BmpMercedes;
}
}
//---------------------------------------------------------------------------
If the file, its path, and its extension are correct, the file can be used:
299
If the file does not exist when you try accessing it, in other words, if the file or the path
you specified is not valid (the file and the path are not checked at compilation time, they
are checked when the application is asked to retrieve the bitmap), you would receive an
error:
As you can see, the exception thrown is of type EFOpenError (which stands for
Exception-File-Open-Error), meaning the information given about opening the file is
incorrect somewhere. You can display our own message as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
Graphics::TBitmap *BmpMercedes = new Graphics::TBitmap;
try {
try {
BmpMercedes->LoadFromFile("Mercedes.bmp");
}
catch(EFOpenError *Error)
{
ShowMessage(Error->Message +
".\nThe file path, its name, or its extension
"may be invalid or they don't exist.");
}
}
__finally
{
delete BmpMercedes;
}
}
//---------------------------------------------------------------------------
300
After creating the header file, you can create a resource file, which is simply a text file
with an extension of .rc and, in the file, each resource can be specified using the name of
the constant created in the header file. A bitmap resource can be created as follows:
#include "resource.h"
DEUXFILLES BITMAP "Filles.bmp"
After creating the resource file, you must import it into your project. This is done by click
Project -> Add to Project from the main menu, selecting the rc file and clicking Open.
After adding the resource file, you should compile it to produce a .res file. This makes
your file ready.
Once a bitmap in a resource file is ready, to use it in your application, you can call the
LoadFromResourceName() method. Its syntax is:
void __fastcall LoadFromResourceName(unsigned Instance, const AnsiString ResName);
The easiest way to do this is to create the resource file in the same directory as the project
that is using it. This because the LoadFromResourceName() method requires the
instance of the executable file that contains the resource file. If it is located in the same
folder, you can simply pass the instance of the current project as the Instance argument.
The second parameter, ResName, is the name of the bitmap to be loaded. Normally, it
should be the identifier of the bitmap as defined in the header file.
Here is an example:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Exercise.h"
#include "resource.h"
//---------------------------------------------------------------------------
301
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Graphics::TBitmap *BmpFilles = new Graphics::TBitmap;
try {
try {
BmpFilles->LoadFromResourceName((int)HInstance, "DEUXFILLES");
Canvas->Draw(0, 0, BmpFilles);
}
catch(EResNotFound *Error)
{
ShowMessage(Error->Message +
".\nThe resource file was not found or its "
"name is incorrect.");
}
catch(...)
{
ShowMessage("The picture cannot be displayed...");
}
}
__finally
{
delete BmpFilles;
}
}
//---------------------------------------------------------------------------
302
2.
3.
4.
5.
From the resources that accompany this book, copy the food1.bmp file and paste it
in the folder of the current project
6.
On the main menu of C++ Builder, click File -> New -> Other
7.
In the New Items dialog box, click Header File and click OK
8.
9.
Save the file as resource.h and make sure you include the extension. Also, make
sure you save it in the folder of the current project
10. On the Standard toolbar of C++ Builder, click the New button
Copyright 2003 FunctionX, Inc.
303
11. In the New Items dialog box, double-click the Text icon
12. In the empty file, type:
#include "resource.h"
FOODITEM BITMAP "food1.bmp"
13. To save the file, on the Standard toolbar, click the Save button
14. Type ExoRes.rc to make sure the file is saved with the rc extension instead of txt:
304
13. When the compilation is complete, on the Compiling dialog box, click OK
14. On the Object Inspector, click the Events tab and access the OnPaint event of the
form
15. Implement it as follows:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Exercise.h"
#include "resource.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Graphics::TBitmap *BmpFood = new Graphics::TBitmap;
try {
try {
BmpFood->LoadFromResourceID((int)HInstance, FOODITEM);
Canvas->Draw(20, 10, BmpFood);
}
catch(EResNotFound *Problem)
{
ShowMessage(Problem->Message +
".\nThe resource file was not found "
"or its name is incorrect.");
}
catch(...)
{
ShowMessage("The picture cannot be displayed...");
}
}
__finally
{
delete BmpFood;
}
305
}
//---------------------------------------------------------------------------
306
you can display it on the form. You can also make a duplicate copy of it and store it in
another variable. This can be done using the Assign() method. Its syntax is:
virtual void __fastcall Assign(Classes::TPersistent* Source);
The Source parameter is the variable that holds the bitmap you want to copy and will be
assigned to the variable that is making the call.
From the resources that accompany this book, copy doughnut1.bmp and
doughnut2.bmp. Then paste them in the folder of this project
2.
3.
4.
5.
To display the new pictures, change the OnPaint event of the form as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
Graphics::TBitmap *BmpFood1 = new Graphics::TBitmap;
Graphics::TBitmap *BmpFood2 = new Graphics::TBitmap;
int width;
try {
try {
BmpFood1->LoadFromResourceID((int)HInstance, DOUGHNUT1);
Canvas->Draw(20, 10, BmpFood1);
width = BmpFood1->Width + 40;
BmpFood2->LoadFromResourceID((int)HInstance, DOUGHNUT2);
BmpFood2->Transparent = True;
Canvas->Draw(width, 10, BmpFood2);
}
catch(EResNotFound *Problem)
{
ShowMessage(Problem->Message +
".\nThere was a problem completing this assignment");
}
}
__finally
{
delete BmpFood1;
delete BmpFood2;
}
}
307
//---------------------------------------------------------------------------
6.
7.
8.
Save All
308
1.
2.
3.
4.
Using Image Editor, create a 38 x38 pixels bitmap and design it as follows:
5.
6.
7.
8.
Save the icon are PatBrush in the folder of the current project
9.
Using the Project -> Options menu and the Load Icon button from the Application
tab, select the new icon
10. Using the Events tab of the Object Inspector, access the OnPaint event of the form
and implement it as follows:
//---------------------------------------------------------------------------
309
310
The nWidth and nHeight parameters specify the dimensions of the bitmap, in pixels. The
cPlanes parameter holds the number of color planes used by the device. The cBitsPerPel
parameter is an array of color values.
This CreateBitmap() function returns a handle to the BITMAP structure. You can
assign that value to the Handle member variable of the TBitmap class.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
HBITMAP HBmp;
WORD wBits[] = { 0x00, 0x22, 0x44, 0x88, 0x00, 0x22, 0x44, 0x88,
0x22, 0x44, 0x88, 0x00, 0x22, 0x44, 0x88, 0x00,
0x44, 0x88, 0x00, 0x22, 0x44, 0x88, 0x00, 0x22,
0x88, 0x00, 0x22, 0x44, 0x88, 0x00, 0x22, 0x44 };
HBmp = CreateBitmap(32, 32, 1, 1, wBits);
HBRUSH hBrush = CreatePatternBrush(HBmp);
Canvas->Brush->Handle = hBrush;
Canvas->Rectangle(20, 20, 400, 400);
}
//---------------------------------------------------------------------------
311
create the actual pictures that would make up the list. Although Borland Image Editor
that ships with C++ Builder on one hand and the Windows Paint that installs with the
operating system on the other hand provide good and simple means of creating the
pictures, you can use any application that can create and manipulate pictures. Jasc Paint
Shop Pro is a good example.
There are two types of lists of pictures you can prepare for an image list. You can create
individual pictures that will each be added to the list, one at a time. All pictures should
(must) have the same dimensions (same width and same height). Here are examples of
such bitmaps:
The second technique consists of creating a single, long picture that contains each of the
needed pictures. In this case, you would add this single picture to the image list at once.
The picture should (must) provide a series of pictures uniformly dimensioned. Here is an
example of a combination of the above pictures:
bitmaps that will be used by another control, you can use a friendly dialog box that
from
allows you to create the list. To do this, you would click the ImageList button
the Win32 tab of the Component Palette and add it to your form or other container. To
make up the list, you can double-click the icon on the form. This would open the
ImageList Editor dialog box where you can easily locate and select the necessary bitmaps
or icons. In future lessons, we will use it.
If you are planning to use images that are larger than 32x32 pixels, you should create the
control programmatically. To do this, declare a pointer to TImageList. Here is an
example:
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published:
// IDE-managed Components
private:
TImageList *ImgList;
// User declarations
public:
// User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
Before implementing it, use the new operator on its constructor to indicate its owner.
This could be done as follows:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ImgList = new TImageList(this);
}
//---------------------------------------------------------------------------
If you plan to use more than one image list, you can then declare either various
TImageList variables (or an array of TImageList variables). An example would be:
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published:
// IDE-managed Components
private:
TImageList *SingleImage;
TImageList *MultiImages;
// User declarations
public:
// User declarations
__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------------------------//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
SingleImage = new TImageList(this);
MultiImages = new TImageList(this);
}
//---------------------------------------------------------------------------
313
Like other graphics controls, an image list has dimensions. The width of the image list is
the width of each one of its pictures. Remember that all pictures must have the same
width. The width is set or controlled by the Width property. This would be specified as
follows:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
SingleImage = new TImageList(this);
Graphics::TBitmap *SingleBitmap = new Graphics::TBitmap;
SingleImage->Width = 225;
MultiImages = new TImageList(this);
Graphics::TBitmap *MultiBitmaps[4];
MultiBitmaps[0] = new Graphics::TBitmap;
MultiImages->Width = MultiBitmaps[0]->Width;
MultiBitmaps[1] = new Graphics::TBitmap;
MultiImages->Width = MultiBitmaps[1]->Width;
}
//---------------------------------------------------------------------------
The height of an image list is the normal height of each of its pictures. It is set or
controlled by the Height property.
There are two kinds of bitmaps or icons that can be used on an image list: masked or
nonmasked. A nonmasked image list is made of pictures that each is represented as its
own entity. Here is an example:
A masked image list is made of pictures so that each is either doubled or uses two sets of
colors. Each picture can be provided in two versions and both versions would represent
the same illustration. The first picture is in color and the second would be monochrome.
Here is an example:
To indicate that an image list is masked, set the Masked property to true.
After specifying that the image list will use the masked attribute, use the ImageType
property to indicate whether the picture is doubled or will use two sets of colors. The
possible values of this property derive from the TImageType enumerator whose
members are:
Value
itImage
itMask
314
Description
The picture is single
The picture uses a mask
After specifying that the picture of an image list is masked, when drawing a picture from
an image list, you can control what color would serve as its background. This is set or
retrieved using the BkColor property. If the Masked property is set to true, the BkColor
color would be used to replace the masked sections of the picture.
The first parameter, Image, is the bitmap you are adding to the list. If the bitmap is
masked, specify the bitmap used as the mask for the second argument. If you had set the
Masked property to false, the Mask argument would be ignored. This means that you can
pass it as NULL.
If the image list is made of a single bitmap, you can simply add it normally. If the list will
be created from various separate bitmaps, make sure you add each. Here are examples:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
MultiImages = new TImageList(this);
MultiImages->Masked = False;
Graphics::TBitmap *MultiBitmaps[4];
MultiBitmaps[0] = new Graphics::TBitmap;
MultiBitmaps[0]->LoadFromFile("Picture1.bmp");
MultiImages->Width = MultiBitmaps[0]->Width;
MultiImages->Height = MultiBitmaps[0]->Height;
MultiImages->Add(MultiBitmaps[0], NULL);
...
MultiImages->Height = MultiBitmaps[0]->Height;
}
//---------------------------------------------------------------------------
If you had set the Masked property to true but the bitmap is not doubled, instead of using
a color as the mask, call the AddMasked() method. Its syntax is:
int __fastcall AddMasked(Graphics::TBitmap* Image, Graphics::TColor MaskColor);
The Image parameter is the bitmap to add to the list. Once again, the second argument
will depend on whether the Masked property is true. If it is, you can pass a MaskColor
color to be used to mask the bitmap.
Here is an example:
315
To retrieve the bitmap stored at a specific position in the list, call the GetBitmap()
method. Its syntax is:
void __fastcall GetBitmap(int Index, Graphics::TBitmap* Image);
Before calling this method, you should declare a pointer to TBitmap and pass it as the
second argument. The Index value indicates the index of the bitmap in the list. If the
bitmap exists, it is returned as the Image parameter. Here are examples (and here is the
complete source file):
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Main.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Graphics::TBitmap *SingleBitmap = new Graphics::TBitmap;
SingleBitmap->LoadFromFile("Picture5.bmp");
SingleImage = new TImageList(this);
SingleImage->Width = 225;
SingleImage->Height = 175;
SingleImage->Masked = True;
SingleImage->ImageType = itImage;
SingleImage->BkColor = clBlack;
SingleImage->AddMasked(SingleBitmap, clBlack);
MultiImages = new TImageList(this);
MultiImages->Masked = False;
Graphics::TBitmap *MultiBitmaps[4];
316
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Image1Click(Sender);
Image2Click(Sender);
}
//--------------------------------------------------------------------------void __fastcall TForm1::Image2Click(TObject *Sender)
{
static int ImgCounter = 0;
Graphics::TBitmap *Bmp = new Graphics::TBitmap;
if( ImgCounter < MultiImages->Count )
{
MultiImages->GetBitmap(ImgCounter++, Bmp);
Image2->Picture->Bitmap = Bmp;
317
}
else
ImgCounter = 0;
}
//---------------------------------------------------------------------------
To add an icon to an image list, call the AddIcon() method. Its syntax is:
int __fastcall AddIcon(Graphics::TIcon* Image);
The Index value is the index of the picture to be removed. Instead of removing a picture,
you can just replace it with another picture. This is done using the
TImageList::Replace() method whose syntaxes are:
void __fastcall Replace(int Index, Graphics::TBitmap* Image, Graphics::TBitmap* Mask);
318
its pictures on a form. To do this, you would call the TImageList::Draw() method. It
comes in two syntaxes as follows:
void __fastcall Draw(Graphics::TCanvas* Canvas, int X, int Y, int Index,
bool Enabled = true);
void __fastcall Draw(Graphics::TCanvas* Canvas, int X, int Y, int Index,
TDrawingStyle ADrawingStyle, TImageType AImageType,
bool Enabled = true);
The Draw() method is used to draw one of the images on a device context. The Canvas
parameter specifies the device on which the bitmap or icon will be drawn.
The X and the Y values are the point location where the drawing would start. That will be
the top-left corner of the displaying bitmap or icon.
The Index parameter specifies the index of the picture to be drawn, from the list of
images. The list is zero-based, meaning the first image has an index of 0, the second is 1,
etc.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Image2Click(TObject *Sender)
{
static int ImgCounter = 0;
Graphics::TBitmap *Bmp = new Graphics::TBitmap;
if( ImgCounter < MultiImages->Count )
{
MultiImages->GetBitmap(ImgCounter, Bmp);
// Image2->Picture->Bitmap = Bmp;
MultiImages->Draw(this->Canvas, Image2->Left,
Image2->Top, ImgCounter);
ImgCounter++;
}
else
ImgCounter = 0;
}
//---------------------------------------------------------------------------
319
PART IV
Windows Controls
Finally arrived, this is the section that performs a thorough study of Windows controls
featured in the Visual Component Library (VCL).
320
Owner and Parent: a window is referred to as a parent when there are, or there can
be, other windows that depend on it. The Standard toolbar of C++ Builder is an
example of a parent window. It is equipped with buttons and acts as their parent.
When a parent is created, it "gives life" to other windows that can depend on it. The
VCL provides various types of parent controls. When a parent is destroyed, it also
destroys its children.
An owner is a control that owns another control. The owner must be a parent type.
An owner "carries", "holds", or hosts the controls that it owns. When an owner is
created, made active, or made visible, it gives existence and visibility to its controls.
When an owner gets hidden, it also hides its controls. If an owner moves, it moves
with its controls. The controls keep their positions and dimensions inside the owner.
Child: A window is referred to as child when its existence and its visibility depend
on another window called its parent or owner. All of the Windows controls you will
use in your applications are child controls and they must be parented or owned by
another control.
A child window can be a parent of another control. For example, the Standard
toolbar of C++ Builder is the parent of the buttons on it. If you close or hide the
toolbar, its children disappear. At the same time, the toolbar is a child of the
application's frame. If you close the application, the toolbar disappears, along with its
own children. In this example, the toolbar is a child of the frame but is a parent to its
buttons.
321
After specifying the owner, you must also specify the parent of the control. The parent is
responsible for destroying the child control when it (the parent) is destroyed. To specify
the parent of a control, call its Parent property and assign it the name of the parent.
Normally, the name of the parent is the same as the owner. This would be done as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TSomeControl * ctlShowMe = new TSomeControl (Mom);
ctlShowMe->Parent
= Mom;
}
//---------------------------------------------------------------------------
If you declare and completely create a control in an event or a method, the control would
be available only in that member function and cannot be accessed outside. To make such
a control available to other events or controls owned by the same parent, you should
declare it in the private section of its parent. Here is an example:
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published:
// IDE-managed Components
private:
TSomeControl * ctlShowMe; // User declarations
public:
// User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
After declaring the control, you can sepecify its parent and owner in the constructor of
the form as follows:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ctlShowMe = new TSomeControl(Mom);
ctlShowMe->Parent = Mom;
}
322
//---------------------------------------------------------------------------
Besides the constructor, in your class, add the properties, methods, and/or events as you
see fit. When implementing the constructor, specify the owner as the base class. You can
also use it to globally set a value for a property of the base class. Here is an example:
//--------------------------------------------------------------------------__fastcall TPlatform::TPlatform(TComponent *Owner)
: TPanel(Owner)
{
Color = clBlue;
}
//---------------------------------------------------------------------------
Once the control is ready, you can dynamically create it like any other VCL control.
If you want to use a fresh class based on a Win32 control, you can create your own class
and either derive it from an existing Win32 object or overloaded the Win32 class that
would be used as base (for example, you can overload the HWND handle).
HWND CreateWindowEx(
DWORD dwExStyle,
323
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
Once you have the return value of a control, you can call that value any way you see fit in
your program.
When creating a VCL application, you will mostly use the properties, methods, and
events defined by that library. Because the VCL implements Windows controls through a
class called TWinControl, it makes sure it provides you with an HWND handle that
allows you to directly access the properties or messages of the control as defined in the
Win32 library. The handle to a Windows control is (simply) called Handle.
named Edit2. This causes the default names to be incremental. Since a program usually
consists of many controls, it is usually a good idea to rename your controls and give them
meaningful and friendlier names. The name should help you identify what the button is
used for. To change the name of a control, on the Object Inspector, click the word Name
and type a valid C++ name (following the rules of C++ identifiers).
If you are using the Win32s CreateWindow() or CreateWindowEx() function, you can
either use one of the existing names of controls or you are create a control that a custom
name. The Win32 library provides already defined names of classes you can use to create
a control. The Standard controls use the following class names:
Class Name
STATIC
EDIT
RichEdit
RICHEDIT_CLASS
LISTBOX
COMBOBOX
SCROLLBAR
BUTTON
MDICLIENT
Description
A static control can be used to display text, a drawing, or a
picture
As its name suggests, an edit control is used to display text to the
user, to request text from the user, or both
Like an edit box, a rich edit control displays text, requests it, or
does both. Besides all the capabilities of the edit control, this
control can display formatted text with characters or words that
use different colors or weight. The paragraphs can also be
individually aligned. The RichEdit class name is used to create a
rich edit control using the features of Release 1.0 of its class
This control is used for the same reasons as the RichEdit class
name except that it provides a few more text and paragraph
formatting features based on Release 2.0
A list box is a control that displays items, such as text, arranged
so each item, displays on its own line
A combo box is a combination of an edit control and a list box.
It holds a list of items so the current selection displays in the edit
box part of the control
A scroll bar is a rectangular object equipped with a bar
terminated by an arrow at each end. It is used to navigate left
and right or up and down on a document
A button is an object that the user clicks to initiate an action
This class name is used to create a child window for an MDI
application
Microsoft Windows added a few more class names for objects referred to as Common
controls. Fortunately, whether you want to use standard or common controls, the VCL
provide friendlier implementations of all of them with even more Windows control than
the Win32 provides.
To use one of the Win32 classes, pass its name as string to the lpszClassName argument
of the CreateWindow() or the CreateWindowEx() functions. Here is an example that
would start an edit box:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
CreateWindow("EDIT", );
}
//---------------------------------------------------------------------------
325
Whether you are using the VCL or the Win32 libraries, although you can change the
name of a control at runtime, refrain from doing it.
As we will see when studying messages and events, you can also change the text of a
control using the SendMessage() function with the message set as WM_SETTEXT.
If you are using the Win32 approach, the text of a text-based control is passed as the
lpWindowName argument of the CreateWindow() or the CreateWindowEx() functions:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
CreateWindow("Button", "Submit", );
}
//---------------------------------------------------------------------------
326
mouse on a control. A hint can also be made of two parts: the text that displays as a tool
tip on the control and a little longer text on the status bar.
Every visual control that displays when a form is running has a property called Hint. This
property can hold text that would display as the control's tool tip. To display a hint on a
control, the first thing you should do is to set the ShowHint property of the control to
true. The ShowHint property controls whether its control would display a hint or not.
Once the ShowHint property has a true value, you can specify the necessary text on the
Hint property.
Here is an example:
As seen on this picture, the background color of hint appears yellow. If you want a
different color, assign a TColor value of your choice to the global
TApplication::HintColor variable. When doing this, remember to select a color that
would still make the text readable. Here is an example:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Application->HintColor = clAqua;
}
//---------------------------------------------------------------------------
The hint displays its word or sentence using a default global font set on the operating
system. If you want to choose the font that should be used when displaying the hints, call
the TScreen::HintFont property and define the font of your choice. Like a
TApplication object, a TScreen variable is already available when you create a form.
Here is an example:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
327
}
//---------------------------------------------------------------------------
The Hint can also hold two strings: one that would display both as a tool tip and one that
would display on the status bar. You can create text on the Hint property using the
following syntax:
The Hint | Status Bar Text
This string is made of two actual strings separated by a bar. The string on the left side of
the bar is the text that would display on the control when the mouse rests on it for a few
seconds. The bar and the second string are optional. If you omit the bar and the right
string, the unique string would be used as the hint and the status bar text.
The ShowHint property of each control is used to manage hints for that control only. If
you use this approach, you would have to set the ShowHint of each control, whose hint
you want to provide, to true. If you plan to use hints on many, most, or all controls of a
form, the parent form provides a property. If you set the ShowHint property of the form
to true, the ShowHint property of all controls on the form would be automatically set to
true. In that case, you would not have to set the ShowHint property for each control. In
fact, if you plan to use hints for various controls of your form, set the ShowHint of the
form to true, unless you have a good reason why you do not need to.
The hints of a VCL application are globally controlled by the Hint member variable of
the TApplication class. Whenever you create an application, a variable called
Application is declared and can take care of such assignments that pertain to the
application as a whole. To actually show hints on your controls, you must define a global
function that is passed a TObject argument. The name of the function is not important. In
the body of the function, assign the Hint member of the global Application variable to
the desired panel of your status bar. After defining this function, in the OnCreate event
of the form, assign the name of the previously defined function to the OnHint member
variable of the global Application variable.
programmatically creating a VCL control, to specify that it is a child, we saw that you
must pass the name of its owner to the controls pointer and you must assign the name of
its parent to the Parent property.
If you are creating the control using the CreateWindow() or the CreateWindowEx()
function, to specify that the control is a child, add the WS_CHILD flag to the dwStyle
argument. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
CreateWindow("Button", "Submit", WS_CHILD, );
}
//---------------------------------------------------------------------------
If you have a good reason to do so, you can change the parent of a child control at run
time. To do that, simply assign the name of a different control to the object whose parent
you want to change. The designated parent must be a type of control that can be a parent
since some controls can act as a parent and some cannot. Alternatively, to change the
parent of a control, you can call the SetParent() function. Its syntax is:
HWND SetParent(HWND hWndChild, HWND hWndNewParent);
The hWndChild argument must be a handle to the control whose parent you want to
change. The hWndNewParent argument must be the handle of the new parent.
If the control has already been created and you want to know who its parent is, you can
assign its Parent property to a TWinControl variable. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TWinControl *Poppy = Button2->Parent;
if( Poppy == this )
ShowMessage("The form is your daddy");
}
//---------------------------------------------------------------------------
Alternatively, to find out what object acts as a controls parent, call the Win32 APIs
GetParent() function. Its syntax is:
HWND GetParent(HWND hWnd);
The hWnd argument must be a handle to the control whose parent you want to find out.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
HWND Poppy = GetParent(Button2->Handle);
if( Poppy == this->Handle )
ShowMessage("The form is your daddy");
}
//---------------------------------------------------------------------------
329
While the Visible property can be read or changed, Showing is a read-only property. This
means that you can assign a true or false value to Visible but you can only check the state
of Showing; you cannot change it. The following code will not compile:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
// This will not work because you cannot change Showing
if( Panel1->Showing )
Panel1->Showing = False;
}
//---------------------------------------------------------------------------
A useful feature of the C++ language comes in toggling the value of a Boolean variable.
This can be applied to the appearance and disappearance of a control. You can use this
to reverse the state of a controls Boolean property. This will come very handy when
using menus and toolbars, etc (imagine how easy it is to hide or display a control or a
menu item with one line of code, no pointer, no dynamic creation or declaration of any
class). The following example toggles the appearance of a form by checking whether
the control (in this case the second form) is displaying or not. If the control (the second
form) is visible, it gets hidden and vice-versa:
//--------------------------------------------------------------------------void __fastcall TForm1::ToggleControlAppearance()
{
330
Form2->Visible = !Form2->Visible;
}
//---------------------------------------------------------------------------
To find out whether a control is enabled or not, check its Enabled property state.
By default, a newly created control using the CreateWindow() or the
CreateWindowEx() functions is enabled. If you want to disable it, add the
WS_DISABLED style to its dwStyle argument.
331
13.2.10
Controls Location
The controls added to a parent window are confined to the area of the body offered by
that window. After adding it to a window, the control is positioned in the body of the
parent using a Cartesian coordinate system whose origin is located on the top-left corner
of the parent window. The horizontal measurements move from the origin to the right.
The vertical measurements move from the origin to the bottom.
The distance from the controls left border to the parents left border is called the Left
property. The distance from the controls top border to the parents top border is called
the Top property. The Left and Top values are known as the controls location. This can
be illustrated as follows:
332
13.2.11
Controls Dimensions
The distance from the left border to the right border of a control is referred to as its
Width property. In the same way, the distance from the top to the bottom borders of a
control is its Height value. This can be illustrated as follows:
333
If the value you give to a property is not allowed, the program would throw an error.
If you are creating the control using the CreateWindow() or the CreateWindowEx()
functions, specify the value for the distance from the left border of the parent window to
the left border of the control as the nWidth argument. In the same way, pass the desired
value for the distance from the top border of the parent to the top border of the control as
the nHeight argument. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
334
}
//---------------------------------------------------------------------------
If you specify negative values for the left and top distances, either the left or the top
borders, respectively, will be hidden.
13.2.12
The first member, x, represents the horizontal distance of the point from the top-left
corner of the object that owns the point. The y member represents the vertical
measurement of the point with regards to the top-left corner of the object that owns the
point.
Besides the Win32's POINT structure, the VCL provides a class that performs the same
operations with a more appropriate conception of a C++ class. It is called TPoint. While
Win32 is written in C and most of its objects are pure structures in the C sense, the VCL's
TPoint is a true and complete C++ class. It is defined as follows:
struct TPoint
{
TPoint() {}
TPoint(int _x, int _y) : x(_x), y(_y) {}
TPoint(POINT& pt)
335
{
x = pt.x;
y = pt.y;
}
operator POINT() const
{
POINT pt;
pt.x = x;
pt.y = y;
return pt;
}
int x;
int y;
};
As you can see, you can define or retrieve the position of a TPoint variable using its x
and y values. Because it has a constructor, you can also declare it as a C++ variable.
To represent the size of an object, the Win32 library provides the SIZE structure defined
as follows:
typedef struct tagSIZE {
LONG cx;
LONG cy;
} SIZE, *PSIZE;
The cx member variable is usually used as the distance from the left to the right borders
of a control. The cy member variable usually represents the distance from the top to the
bottom borders of the control.
Besides the Win32s SIZE, you can also use the VCLs TSize structure defined as
follows:
struct tagSIZE
{
long cx;
long cy;
} typedef tagSIZE TSize;
The member variables of the TSize structure represent the width as cx and the height as
cy of an object.
As seen so far, a control is a rectangular object that can be located, and whose dimensions
are visible, on the screen. A rectangle is a geometric figure that has four sides (for this
reason, it is called a quadrilateral figure). It is known for its horizontal and its vertical
measures. The horizontal measure of a rectangle is referred to as its length and sometimes
its width. The vertical measure is known as its height. In fact, on a Cartesian coordinate, a
rectangle is represented by its four corners (x1, y1, x2, y2) as illustrated on the left figure
below:
336
337
338
}
bool operator !=(const TRect& rc) const
{ return !(rc==*this); }
__property LONG Left = { read=left, write=left };
__property LONG Top
= { read=top, write=top };
__property LONG Right = { read=right, write=right };
__property LONG Bottom = { read=bottom, write=bottom };
};
339
If you do not know or cannot get the right and the bottom measures of the rectangle but
know its width and its height, you can still use them to declare and initialize a rectangle.
The TRect class provides one more alternative you can use to declare a rectangle.
Suppose you have the coordinates of the top-left and the bottom-right borders of the
rectangle, you can declare and initialize it. The constructor you would use is:
TRect(TPoint TopLeft, TPoint BottomRight);
Alternatively, to get the location and dimension of a control, you can call the
GetWindowRect() function. Its syntax is:
BOOL GetWindowRect(HWND hWnd, LPRECT lpRect);
This function returns a rectangle that holds the location and the dimensions of the control
as, lpRect.
340
If you are using an existing class such as one of those that control the objects from
the Component Palette, you can call any of its public methods. The requirements of
such a method depend on the class being used
If none of the existing methods can perform your desired task, you can add a method
to a class. There are two main options available to you:
o
If you are using a class that is being derived, this will always be the case of
designed forms, you can add a method to it by declaring it in the header file and
implementing it in the source file
If you are using a control from the Component Palette but find out that, by
design, the control lacks a method you need, you can create your own class
derived from that control. In the header of the new class, declare the method and
implement it in the source class. Then, in the header class of the form, declare a
pointer to your new class.
The methods of a class are available only at run time. Except for the constructor, to
access a method, first make sure you have the variable or pointer. Then use either the
period operator for a variable or the arrow operator for a pointer to access the desired
method.
The most fundamental and the most common method of a class is its constructor. As all
Windows controls of the VCL are based each on a particular class, each one of them has
at least one constructor. Dynamically creating a control consists of declaring a pointer to
its class using the new operator. If the control is a visual object, you must specify its
parent, as we will see. This is done by calling its main constructor. If the class does not
represent a visual object, call its default constructor.
The second most popular method is the destructor. Based on the rules of RAD
programming, you will never need to call the destructor of a control or a class, even if
you dynamically create the variable. If you need to destroy an object that was
programmatically created, you will use the delete operator.
If the control is visible, nothing would happen. If it were hidden, then it would be
revealed. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Panel1->Show();
}
//---------------------------------------------------------------------------
The Show() method internally sets the Visible property to true. Alternatively, to show a
window, you can call the ShowWindow() function. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
341
ShowWindow(Panel1->Handle, SW_NORMAL);
}
//---------------------------------------------------------------------------
On the other hand, we saw that, to hide a control, you can set its Visible property to false.
In the same way, to hide a control, you call can the TControl::Hide() method. Its syntax
is:
void __fastcall Hide();
Keep in mind that hiding a control does not close or destroy it.
13.3.3 Focus
The focus is a visual aspect that indicates that a control is ready to receive input from the
user. Various controls have different ways of expressing that they have received focus.
Buttons controls indicate that they have focus
by drawing a dotted rectangle around their
caption. Here are examples:
To give focus to a control, the user can click it or press a key. To programmatically give
focus to a control, call the TWinControl::SetFocus() method. Its syntax is:
void __fastcall SetFocus();
342
The first piece of information must state WHO, that is, what control, sent the
message
The second argument must specify the name of the message as each Windows
message is recognized by a name, which is simply a positive but constant numeric
value
The third and the fourth arguments carry information that depends on the message
being sent
Here is an example:
//--------------------------------------------------------------------------LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg,
WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;
343
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
//---------------------------------------------------------------------------
Once again, to make programming a fast process, the VCL provides its own message
structure called TMessage and defined as follows:
struct TMessage
{
Cardinal Msg;
union
{
struct
{
};
struct
{
};
Word WParamLo;
Word WParamHi;
Word LParamLo;
Word LParamHi;
Word ResultLo;
Word ResultHi;
int WParam;
int LParam;
int Result;
};
};
344
345
To process messages that do not require any additional argument, the VCL creates such
an event with the TNotifyEvent type. Such an event is declared as a pointer to function
in the classes.hpp library as follows:
typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);
When an event has been initiated, you would be transported to the Code Editor and the
cursor would be positioned in the body of the event, ready to receive your instructions.
To customize an event, the compiler divides its structure in three sections:
//--------------------------------------------------------------------------void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
// Write code associated with the event here
}
//---------------------------------------------------------------------------
The coding of an event starts with its return value. Most or all events in C++ Builder
return void. All events use the __fastcall convention.
346
The return type is followed by the name of the parent class from where the event would
be fired. This is mainly the class that controls the form.
After the name of the class, the compiler rightly uses the class member access operator
(::) to call the event, following a C++ rule.
The name of an event is made of a combination of the control that owns or fired the
event and the name of the event.
Each event has at least one argument, the TObject *Sender. Some events use additional
arguments that we will review when coding such events.
Used for
F2
F4
F6
F8
F10
F12
Prt Scrn (Depends on keyboard)
Tab
Caps Lock
Ctrl
Escape
Space Bar
Home
Delete
Page Down
Right Arrow Key
Left Arrow Key
Right Windows Key
347
VK_NUMLOCK
VK_NUMPAD0
VK_NUMPAD2
VK_NUMPAD4
VK_NUMPAD6
VK_NUMPAD8
VK_DIVIDE
VK_SUBTRACT
VK_SEPARATOR
Num Lock
0
2
4
6
8
/
-
VK_NUMPAD1
VK_NUMPAD3
VK_NUMPAD5
VK_NUMPAD7
VK_NUMPAD9
VK_MULTIPLY
VK_ADD
VK_DECIMAL
1
3
5
7
9
*
+
.
There are actually more keys than that but the above are the most frequently used.
The VCL implements keyboard events using two function pointers, TKeyEvent and TKeyPress
that depend on the message.
2.
3.
The Key argument specifies the key that was pressed. The OnKeyDown() event can be
sent by any key. Alphabetic keys are represented by their character. For example, if the
user presses p, the Key argument would be represented as p. If the user presses a key,
the Key argument would have the value of ;.
The Shift argument specifies whether the Shift, the Ctrl, or the Alt keys were pressed
along with the Key key. It is a Pascal Set whose enumerator has the following members:
Value
ssShift
ssAlt
ssCtrl
Description
One of the Shift keys was pressed
One of the Alt keys was pressed
One of the Ctrl keys was pressed
If the user presses two keys as an uppercase letter, such R, the Key argument would have
a value of r. The Shift argument would be used to indicate that the Shift key was down
when the letter r was pressed.
348
1.
To experiment with the WMKeyDown message, on the Object Inspector, click the
Events tab
2.
Double-click the right field of OnKeyDown and implement the event as follows:
Copyright 2003 FunctionX, Inc.
3.
The Key argument specifies the key that was pressed. Like the OnKeyDown() event, the
OnKeyUp event processes any key. Alphabetic keys are represented by their character.
The Shift argument indicates whether the Shift, the Ctrl, or the Alt key participates in a
key combination such as Shift + $
349
To experiment with the WMKeyPress message, on the Events tab of the Object
Inspector, double-click the right field of OnKeyPress
2.
Delete the code in the OnKeyDown event and implement the OnKeyPress event as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key)
{
switch( Key )
{
case VK_RETURN:
ShowMessage("You pressed Enter");
break;
case VK_F1:
ShowMessage("Help is not available at the moment");
break;
case VK_DELETE:
ShowMessage("Can't Delete This");
break;
case 'k':
ShowMessage("Lowercase k was pressed");
break;
case 'K':
ShowMessage("Uppercase K was pressed");
break;
case '$':
ShowMessage("Show me the money");
break;
}
}
//---------------------------------------------------------------------------
350
3.
Test the application and press the Delete or the F1 keys. Notice that nothing happens
4.
Press k
5.
6.
The Button argument specifies what button was clicked. The buttons of the mouse are
identified by the TMouseButton enumerator whose members are:
Value
mbLeft
mbRight
mbMiddle
Description
The left mouse button was clicked
The right mouse button was clicked
The middle mouse button was clicked
The Shift argument indicates whether mouse button and/or a keyboard key was/were
pressed and held down when the Button was clicked. It can have one of the following
values:
Value
ssShift
ssAlt
ssCtrl
ssLeft
ssRight
ssMiddle
ssDouble
Description
One of the Shift keys was pressed
One of the Alt keys was pressed
One of the Ctrl keys was pressed
The left mouse button was held down
The right mouse button was held down
The middle mouse button was held down
The Button was double-clicked
351
The X and the Y argument represent the TPoint(X, Y) point where the mouse was
clicked.
To experiment with the mouse down effect, on the Events tab of the Object
Inspector, double-click the right side of the OnMouseDown field
2.
3.
4.
The Shift argument is the same as the other mouse messages. The X and Y values
identify the location of the mouse at the time this event fires.
352
The Win32 library provides more messages than the VCL implements. Normally, you
should first check if a message you want to process is already available for your control.
If it is not, you can create your own message and its event. Once again you have various
options. If the message belongs to, or must be processed by, a form, you can create it in
the header file of the form. Otherwise, you can create a new control by simply deriving a
class from an existing control.
The hWnd argument is the object or control that is sending the message.
The Msg argument is the message to be sent.
The wParam and the lParam values depend on the message that is being sent.
The advantage of using the SendMessage() function is that, when sending this message,
it would target the procedure that can perform the task and this function would return
only after its message has been processed. Because this function can sometimes
universally be used, that is by almost any control or object, the application cannot predict
the type of message that SendMessage() is carrying. Therefore, (the probable
disadvantage is that) you must know the (name or identity of the) message you are
sending and you must provide accurate accompanying items.
Here is an example that changes the caption of a form using the WM_SETTEXT
message:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
const char *Msg = "This message was sent";
SendMessage(Handle, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)Msg);
}
//---------------------------------------------------------------------------
353
In order to use this method, you must override it in your own class. Here is an example:
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published:
// IDE-managed Components
private: // User declarations
public:
// User declarations
__fastcall TForm1(TComponent* Owner);
virtual void __fastcall WndProc(TMessage &Msg);
};
//---------------------------------------------------------------------------
The Message argument is any message you want to process. As done for a Win32
application, the Message value is typically passed to a switch condition and each desired
message is treated in a case statement. After processing the messages and before closing
the member function, you should (must) call the parent class to process the messages that
were not treated. Here is an example that treats the WM_MOVE message and prevents
the user from moving the form (by preventing the mouse from capturing the title bar):
//--------------------------------------------------------------------------void __fastcall TForm1::WndProc(TMessage &Message)
{
switch(Message.Msg)
{
case WM_MOVE:
ReleaseCapture();
break;
}
TForm::WndProc(Msg);
}
//---------------------------------------------------------------------------
354
After creating the message, if it is intended for the form to treat, the message is ready and
its event would fire appropriately. If the event is for a particular control, you must let the
compiler know that you have a window procedure that would process a particular
message or the messages of a certain control. To do this, you can assign WndProc to the
controls WindowProc member variable. This could be done in the constructor or the
OnCreate() event of the form as follows:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
this->WindowProc = WndProc;
}
//---------------------------------------------------------------------------
355
356
This function simply returns a handle to the desktop window. This handle can give some
information about the desktop such as its size, etc.
Because GetDesktopWindow() is just a function, it only produces a handle to the
desktop. If you want to perform other processing on the desktop, you should use the
TScreen class members or you can use some of the Win32 library functions such as
SystemParametersInfo().
357
position your control on the form, in which case the form would act as the parent, or you
can first select one of the containers, place it on a form, and then add other controls to it.
The above illustration shows a form positioned on a monitor. The desktop acts as the
parent window of the form. In the same way, if you place a control on a container, the
control is located with regards to its parent, not based on the screen. As seen previously,
in such case, the origin of the coordinate system is located on the top-left corner of the
parent window. The distance from the left border of the parent window to the left border
of the control is the controls Left property. The distance from the top border of the
358
parent window to the top border of the control is the controls Top property. Here is an
example in which a memo control as the child is positioned inside a panel that acts as its
parent:
359
The client area is a rectangle primarily used for its location and dimensions. To get the
values of the shape that compose the client area, you have various options and
considerations. For example, to get the area that represents the desktop, which would let
you know how much real estate is available for your application, you can call the
TScreen::DesktopRect member variable. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TRect Recto = Screen->DesktopRect;
Caption = "Width: " + IntToStr(Recto.Width()) +
" Height: " + IntToStr(Recto.Height());
}
//---------------------------------------------------------------------------
361
Rect Recto;
HWND hWndDesktop = GetDesktopWindow();
GetWindowRect(hWndDesktop, &Recto);
Caption = "Width: " + IntToStr(Recto.Width()) +
" Height: " + IntToStr(Recto.Height());
}
//---------------------------------------------------------------------------
It is important to know where the origin of a control is located. During control design, we
saw that, when a container is selected and you double-click a visual control on the
Component Palette, the new control would be added to the container. On the other hand,
if you have a container that is positioned on the form but the container, or any specific
container, is not selected, if you double-click a control on the Component Palette, the
control would be added to the form even though the new control may be positioned on a
container. In this case, the form would become the parent of the new control. Therefore,
in order to do anything related to the location and/or dimensions of a control, you must
know the coordinate of the origin used as the basis for its location and dimensions.
Because only a parent can host some control, it holds an origin and makes it available to
its children. To get the origin of a container, call the TControl::ClientOrigin property.
To get the location and dimensions of any window that serves as parent, you can call the
TControl::ClientRect property. A control hosted by a container can be displayed only
inside its parent, if the controls left or top measures are negative, its left or top borders
respectively would be hidden under the left or top sides of the parent. If the dimensions
of the child control span beyond the dimensions of the parent window, the controls right
or bottom border will be hidden. We will see that some controls cannot allow their child
or children to expand the client area and some other control can display scroll bars so the
hidden parts can be navigated to.
The ClientRect property mainly provides only the width and the height of a client area
since the left and the top measures are set to 0 each as the origin (0, 0) is the base.
Alternatively, you can get the width of a control using the TControl::ClientWidth and
you can get the height of the control using the TControl::ClientHeight properties.
362
363
In case of a (regular) form, the Align property controls how the form is positioned with
respect to the monitor's screen. When it comes to a form, the Align property should be
used only if you have a good reason. For example, to maximize a form at startup, you can
set its Align property to alClient, which you can also accomplish using the WindowSate
property. The default value of the Align property is alNone, which means the appearance
of the form is left to other properties. We will explore the Align property when we get to
the controls but remember that it is also available to the form.
364
365
366
On the left side of the form's title bar, there is a small picture called an icon. By default,
C++ Builder uses an icon shipped with the compiler. If you want to use your own icon,
you can specify it using the Icon property. To do this, on the Object Inspector, you can
either double-click the (None) value of the Icon field or click the ellipsis button of the
field. This would call the Picture Editor dialog box:
367
You can also indirectly control the caption. For example, after the user has typed his or
her name in an edit box, you can display it in the forms caption as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::ChangingFormCaption()
{
Caption = Edit1->Text;
}
//---------------------------------------------------------------------------
369
370
biSystemMenu = false
Notice that there is no system
button
biSystemMenu = true
biMinimize = false
biMaximize = true
Notice that the Minimize
button is disabled
biSystemMenu = true
biMinimize = true
biMaximize = false
Notice that the Maximize
button is disabled
biSystemMenu = true
biMinimize = true
biMaximize = true
Both the Minimize and
Maximize buttons are enabled
371
biSystemMenu = true
biMinimize = false
biMaximize = false
Notice that only the system
Close button is available
The biHelp property works only if the form is a dialog box. It does not bear any role on
the title bar of a regular form
You can also programmatically control the system buttons. Since the BorderIcons
property is a set, you must use the overloaded extractor operators. To remove or set a
property to false, use the >> operator. To include or set a property to true, use the <<
operator. For example, to set the biSystemMenu property to false, you can use code such
as:
biSystemMenu = false
is equivalent to:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
BorderIcons = TBorderIcons() >> biSystemMenu;
}
//---------------------------------------------------------------------------
biSystemMenu = true
biMinimize = false
biMaximize = true
is equivalent to:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
BorderIcons = TBorderIcons() << biSystemMenu >> biMinimize << biMaximize;
}
//---------------------------------------------------------------------------
biSystemMenu = true
biMinimize = true
biMaximize = false
is equivalent to:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
372
biSystemMenu = true
biMinimize = false
biMaximize = false
is equivalent to:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
BorderIcons = TBorderIcons() << biSystemMenu >> biMinimize >> biMaximize;
}
//---------------------------------------------------------------------------
If you do not explicitly define the values of the BorderIcons, by default, the form would
be equipped with all system buttons.
373
374
If you want to check the state of a window before taking action, simply use a conditional
statement to compare its WindowState property with the wsNormal, the wsMaximized,
or the wsMinimized values. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Panel1Click(TObject *Sender)
375
}
//---------------------------------------------------------------------------
Overall, you will hardly be concerned with the dimensions of the client area, unless you
want to draw or render an image. If you do not have a particular reason, let C++ Builder
take care of the client area.
15.1.10
Forms Transparency
An object is referred to as transparent when you can see through it. If you are working
under Windows 2000 or later running on a Pentium or equivalent, you can make your
form transparent. To create a transparent form, set the AlphaBlend Boolean property to
true from its default false value. Once this property is set to true, you can use the
AlphaBlendValue property to set the degree of transparency. The value must be a BYTE
integer between 0 and 255. At 0, you would not see the form at all. The only presence of
the form would be on the taskbar. At 255, the form would appear as if the property were
not applied.
376
Although this method can be used to close any form of an application, if it is called by
the main form, it also closes the application.
2.
3.
4.
5.
6.
377
378
To manage this setting, the windows are organized in a 3-dimensional coordinate system
and they are incrementally positioned on the Z coordinate, which defines the (0, 0, 0)
origin on the screen (on the top-left corner of your monitor) with Z coordinate coming
from the screen towards you.
In order to use a form other than the one that is active, it must be activated. To do this,
the OnActivate() event must be fired. OnActivate() is a TNotifyEvent event.
If there is more than one form or application on the screen, only one can be in front of the
others and be able to receive input from the others. If a form is not active and you want to
bring it to the top, you must activate it, which sends the OnActivate() event. When a
form is being activated, the one that was on top would become deactivated. The form that
was on top, when losing focus, would fire the OnDeactivate() event.
On the Events tab of the Object Inspector, double-click the right field of OnActivate
and implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormActivate(TObject *Sender)
{
ShowMessage("The form is now activated");
}
//---------------------------------------------------------------------------
2.
3.
Display the starting code for the OnDeactivate event and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDeactivate(TObject *Sender)
{
ShowMessage("Our form is deactivated");
}
//---------------------------------------------------------------------------
4.
5.
6.
7.
379
From the Events tab of the Object Inspector, display the starting code of the OnPaint
event and implement it:
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
ShowMessage("Window Painting: A hobby or a habit?");
}
//---------------------------------------------------------------------------
2.
15.3.5
Window Sizing
When using an application, one of the actions a user can perform on a form is to change
its size, provided the form allows it. Also, some time-to-time, if possible, the user can
minimize, maximize, or restore a window. Whenever any of these actions occur, the
operating system must keep track of the location and size of a window. For example, if a
previously minimized or maximized window is being restored, the operating system must
remember where the form was previously positioned and what its dimensions were.
When the size of a form has been changed, it fires the OnResize() event, which is a
TNotifyEvent type.
On the Events tab of the Object Inspector, double-click the right field of OnResize
and implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormResize(TObject *Sender)
{
ShowMessage("Changing size or changing sides - Who knows?");
}
//---------------------------------------------------------------------------
2.
When the form is being closed, you can use the Action argument to specify how you
want the closing to be performed. This argument is a value of the TCloseAction
enumerator whose members are:
Value
380
Description
Copyright 2003 FunctionX, Inc.
caNone
caHide
caFree
caMinimize
On the Events tab of the Object Inspector, double-click the right field of OnClose
and implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
ShowMessage("Time to close");
}
//---------------------------------------------------------------------------
2.
On the main menu, you can click File -> New -> Form
On the main menu, you can also click File -> New -> Other... and, in the New Items
dialog box, you can double-click Form
On the View toolbar, you can click the New Form button
Any of these techniques will add a new form to your application. If your application is
using various forms and you want to display a particular one at design time, on the main
menu, you can click View -> Forms, which would open the View Form dialog box. From
there, you can select the desired form and click OK.
If you visually add two (or more) forms to your application, you may need to link them,
allow one to call the other. Since each form is created in its own unit, to let one form
know about the existence of another, you must include its unit. A form or unit that wants
to communicate with another is called a client of the form. For example, if Unit2 wants to
use information stored in, or controlled by, Unit1, Unit2 needs to include Unit1 in its list
of header files; and Unit2 becomes a client of Unit.
381
There are two simple ways you can include a units header in another file. Imagine you
have created Form1 stored in Unit1 and Form2 stored in Unit2. If you want to have
access to Form2 from Form1, using C++, on top of the source file of Unit1, include
Unit2s header file. C++ Builder provides another technique. After making sure that
either Form1 displays on the screen or a Unit1 tab is displaying, on the main menu, you
can click File -> Include Unit Hdr From the Use Unit dialog box, you would click the
name of the unit you want to include to the current unit and click OK. Bcb will
automatically add the right and selected header to the client.
2.
On the Object Inspector, click Caption and type Rapid Application Development
3.
4.
To add another form to your application, on the View toolbar, click the New Form
button
5.
6.
As the new form is still selected, double-click in its middle to initiate its OnCreate
event.
7.
8.
To display the main form, on the main menu, click View -> Forms
9.
10. To include the header of the other form, on the main menu, click File -> Include Unit
Hdr
11. From the list, Unit2 should be selected already, otherwise click it
Click OK.
12. On the Object Inspector, click the Events tab and double-click the event field of the
OnDblClick field
382
22. We will call the floating window from the second form. On the View toolbar, click
the View Unit button
23. On the View Unit dialog, click Unit2
Copyright 2003 FunctionX, Inc.
383
24. Click OK
25. Press F12 to display the second form.
26. Press Alt + F11 to call the Use Unit dialog. Double-click Unit3.
27. On the Object Inspector, double-click the OnClick event.
28. Implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmSecond::FormClick(TObject *Sender)
{
frmFloater->Visible = !frmFloater->Visible;
}
//---------------------------------------------------------------------------
384
42. Close the second form. Notice that the floating window has been closed.
43. Close the main form
The problem with a duplicate is that, unless you go through a big deal of code writing,
every time you do anything on the form, the duplicate produces the same action.
Therefore, the second option consists of creating a fresh form. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
TForm* Former = new TForm(this);
}
//---------------------------------------------------------------------------
The biggest problem with a locally created form is that it is available only in the event or
the function in which it is created. If you try to access it outside, you would receive an
error. If you want a form to be available to more than one event or function, you must
create it in a header file of an existing form. If you want the form to be available outside
of the parent form that owns the header file, you must declare it in the public section.
Otherwise, you should declare it in the private section:
//--------------------------------------------------------------------------#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------------------------#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published: // IDE-managed Components
private: // User declarations
385
After declaring the form, you can use the constructor of the host form to initialize the
dynamic form. This is done by using the new operator and initializing the TForm class
with the this pointer. Here is an example:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Comet = new TForm(this);
}
//---------------------------------------------------------------------------
After creating and using a form, you should make sure the memory allocated to it is
regained. The Borland C++ Builder compiler can take care of cleaning your dynamic
controls when the application exists. Otherwise, you can delete it manually:
delete MyForm;
Furthermore, to avoid any memory leak, you can reclaim the dynamically allocated
memory by assigning NULL to the deleted object:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
386
}
//---------------------------------------------------------------------------
We have seen that you can initialize it in the constructor of the form that owns your
object:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Comet = new TForm(Form1);
}
//---------------------------------------------------------------------------
Once the dynamic object has been initialized you can display it any time you choose. For
example, you can do this when the user double-clicks in the primary form:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
Comet->Show();
}
//---------------------------------------------------------------------------
Over all, the C++ Builder compiler takes care of deleting the dynamic object when the
form that owns it is closed. If you want to make sure that your dynamic object is
destroyed, you can use the delete operator as seen above. In reality, since you can use
your judgment to decide when the object would be displayed, when the form that owns it
is closed, before deleting the dynamic object, you can first find out if the object was
really created. This can be performed with a simple if statement.
The best place to destroy a dynamically created object that a form owns is when the form
is destroyed. When a form is being closed, it fires the OnDestroy event. This is the
favorite place to perform any housecleaning necessary and related to the form. Based on
this, you can destroy the above Fake object as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDestroy(TObject *Sender)
{
387
if( Fake )
{
delete Fake;
Fake = NULL;
}
}
//---------------------------------------------------------------------------
As opposed to an MDI, a Single Document Interface (SDI) allows only one document at
a time in the application.
388
Each form that is child of the main form in an MDI can be a fully functional form and
most, if not all, child forms are of the same kind. There are two main ways a child
document of an MDI displays. To provide information about its state, a child form is
equipped with a title bar. The title bar of a child form displays an icon, the name of its
document, and its own system buttons. Since it can be confined only within the parent
form, it can be minimized, maximized, or restored within the parent form. When a child
form is not maximized, it clearly displays within the parent form. If it gets closed and
there is no other child form, the parent would appear empty. If there are various child
forms, they share the size of the client area of the parent form. If one of the child forms is
maximized, it occupies the whole client area of the main main form and it displays its
name on the title bar of the main form.
389
Visually creating an MDI is so simple that the only thing necessary is to add another form
and set its FormStyle property to fsMDIChild. If you run such an application, it would
provide all the basic functionality required of an MDI:
The first challenge of creating an MDI consists of performing the various assignments
that, on one hand, allow a parent and a child to communicate, and on the other hand,
allow the children to communicate.
C++ Builder ships with a wizard that allows you to quickly create an MDI-based
application. Unfortunately, this wizard creates only a text-based application. It does not
give you the option to specify the type of application you want.
390
It cannot be minimized, maximized, or restored. A dialog box does not have any
other system button but Close.
It is usually modal, in which case the user is not allowed to continue any other
operation on the same application until the dialog box is dismissed.
It provides a way for the user to close or dismiss the dialog. Most dialog boxes
have an OK and a Cancel buttons, although this depends on the application
developer. When the dialog has the OK and the Cancel buttons, the OK button is
configured to behave as if the user had pressed Enter. In that case, whatever the
user had done would be acknowledged and transferred to the hosting dialog box,
window, or application. Pressing Esc applies the same behavior as if the user
had clicked Cancel.
You should set a forms BorderStyle property to bsDialog. Setting this property
automatically removes the system Minimize and Maximize buttons and preserves
only the system Close button
. This fulfills the first suggested design of a dialog
box. If you insist on having other system buttons, you can add them using the
BorderIcons property.
The second action you should take is to provide a way for the user to close the dialog
box. A dialog box should have at least one button labeled OK. This button allows the
user to acknowledge the message of the dialog box and close it by clicking the
button. If the user press Enter, the dialog box should also be closed as if the OK
button was clicked. To fulfill this second requirement, from the Standard tab of the
Component Palette, you can click the button
Often the user will be presented with various options on a dialog box and may be asked
to make a decision on the available controls. Most of the time, if you are creating such a
dialog box, besides the OK button, it should also have a Cancel button. The OK button
should be the default so that if the user presses Enter, the dialog box would be closed as if
the user had clicked OK. Clicking OK or pressing Enter would indicate that, if the user
had made changes on the controls of the dialog box, those changes would be
acknowledged and kept when the dialog box is closed and usually the changed values of
the control would be transferred to another dialog box or form.
The Cancel button is used to allow the user to dismiss whatever changes would have been
made on the controls of the dialog box. The dialog box should also be configured so that
if the user presses Esc, the dialog box would be closed as if the user had clicked Cancel.
To fulfill these rules for the OK and the Cancel buttons, the Default property of the OK
button should be set to true and the Cancel property of the Cancel button should be set to
true.
391
Besides the OK and the Cancel buttons, a dialog box can be created with additional
buttons such as Finish or Help, etc. It depends on its role and the decision is made by the
application developer.
After creating a dialog used as an addition to an existing form or an existing dialog box,
to call it as modal, use the ShowModal() method.
392
Since the modeless dialog box does not display its button on the task bar, the user should
know that the dialog box is opened. To make the presence of a modeless dialog box
obvious to the user, it typically displays on top of its host application until the user closes
it.
Just like the Modal dialog box, to create a modeless dialog box, once you have added a
form to your application, to call it as a modeless dialog box, simply call the Show()
method. The only thing you need to take care of is the fact that the dialog box can
disappear behind the form that called it.
The fundamental difference between the ShowModal() and the Show() methods is that
the first displays a modal dialog box, which makes sure that the called dialog box cannot
go in the background of the main application. By contrast, the Show() method only calls
the dialog box every time it is requested. For this reason, it is your responsibility to make
sure that the modeless dialog box always remains on top of the application. This is easily
taken care of by setting the FormStyle property of the form to fsStayOnTop.
There are two main ways a normal modeless dialog box can be dismissed:
If the user has finished using it, he can close it and recall it at will
When the form or application that owns the modeless dialog box is closed, the form
or application closes the modeless dialog if it is opened; this means that you do not
need to find out whether a modeless dialog box is still opened when the application
is being destroyed: either the user or the application itself will take care of cleaning it
393
property set to true and the ModalResult property set to mrOk. The Cancel buttons have
their Cancel property set to true and their ModalResult set to mrCancel. You can
reposition the controls and add new ones to the form as you wish. You can add one of the
template dialog boxes to your application. Alternatively, if you want to base your
application on one of these templates, you can remove the default form from your
application.
394
1.
2.
On the main menu, click File -> New -> OtherOn the New Items dialog box, click
the Dialogs tab. Click Standard Dialog (Vertical):
3.
Click OK.
4.
Press F12 to access the Code Editor. Click Unit1.cpp to display its code.
5.
Once Unit1.cpp is displaying, right-click in the Code Editor and click Close Page.
You will be asked whether you want to Save the changes of the Unit1 unit:
6.
Click No
7.
To test the dialog box, on the main menu, click Run -> Run
8.
2.
3.
4.
5.
Save All
You must create a physical frame. This can be done from the main menu
where you would click File -> New -> Frame. You can also click File -> New ->
Other Then, in the New Items dialog box, you would select Frame. Any of
these actions would position an empty rectangular object on the screen. In the
same way, you can create additional frames as needed. Once a frame is
395
available, you can position and design controls on it as you would proceed for a
form. There is no restriction on the types of controls you can place on a frame.
2.
Once the frame exists, to embed it onto a form, from the Standard tab of the
Component Palette, you can click the Frame button and click the form. As soon
as you click the form, you would be asked, from a dialog box, to specify what
frame would be placed where you clicked.
After creating and embedding a frame, you can change its controls in either the form or
the frame. Anything you do in one, such as adding, removing, or resizing controls, would
be automatically updated on the other.
When a frame has focus at design time, you can change its controls as you see fit. From
the form on which a frame is embedded, to programmatically access a control placed on
that frame, do so indirectly through the frame. For example, the following code would
change to blue the background color of an edit control named Edit2 that is placed on a
frame named Frame21 and created in Unit2:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma link "Unit2"
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
Frame21->Edit2->Color = clBlue;
}
//---------------------------------------------------------------------------
396
1.
2.
3.
Control
Label
Edit
Label
Edit
Label
Edit
Button
Label
Edit
Label
Edit
Caption or Text
Principal
0
Interest Rate
0
Months
0
C&alculate
Interest Earned
0
Amount Earned
0
Name
lblPrincipal
edtPrincipal
lblInterestRate
edtInterestRate
lblMonths
edtMonths
btnCalculate
lblInterestEarned
edtInterestEarned
lblAmountEarned
edtAmountEarned
4.
5.
Control
Label
Edit
Label
Edit
Button
Caption or Text
Marked Price
0
Tax Rate
0
C&alculate
Name
lblMarkedPrice
edtMarkedPrice
lblTaxRate
edtTaxRate
btnCalculate
397
Label
Edit
Label
Edit
Tax Amount
0
Net Price
0
lblTaxAmount
edtTaxAmount
lblNetPrice
edtNetPrice
6.
To select the main form, on the View toolbar, click the View Form button. In the list,
double-click frmMain
7.
While the form is displaying, in the Standard tab of the Component Palette, click the
Frames button
8.
9.
10. Click OK
11. Once again, on the Standard tab of the Component Palette, click the Frames button
and click an unoccupied area on the right section of the form
12. In the Select Frame To Insert dialog box, double-click fraDeptStore
13. Adjust the positions of the frames as you see fit, using the same techniques we
reviewed for moving controls
398
399
To use a control, click it from the Component Palette and place it on the data module.
Here is an example with various controls:
To access a control placed on a data module, do so indirectly through the data module as
a control. For example, the following code accesses the ColorDialog1 object from a data
module named DataModule2 when the user double-clicks the form. If the user clicks OK
after changing the color of the dialog box, the new color would be used to paint the form:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
if( DataModule2->ColorDialog1->Execute() )
Color = DataModule2->ColorDialog1->Color;
}
//---------------------------------------------------------------------------
400
401
A panel can be used as a button, in which case the user would click it to initiate an action.
A panel can also simply display a message to the user. In any case, if you want to display
a word or a group of words, you can use the Caption property to show it.
A property that is highly used on panels (and forms) is the Color. If you change the
Color property, the new color would cover the face of the panel.
402
1.
2.
3.
From the Standard page of the Component Palette, double-click the Panel control
.
4.
Set the Align property of the new Panel to alLeft. Set the BevelOuter property to
bvLowered. Also set the DockSite property to true
5.
6.
While Panel1 is still selected, from the Standard page, double-click the Panel control
. Set the Align property to alClient. Set the DragKind to dkDock and the
DragMode to dmAutomatic. Optionally set the BevelOuter to bvNone and its
Caption to Window Floater
7.
While the Panel2 control is still selected, from the Standard tab of the Component
Palette, double-click the Memo control
8.
To test it, press F9. At this time, the Memo1 control is dockable but there is a
problem. When the application starts, the Memo does not appear the way we want it,
even though we can drag it away and bring it back. Close the form.
9.
Double-click an empty area on the form to access the form's OnCreate event.
Implement it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TRect Recto(Left, Top, Left + Panel1->Width, Top + ClientHeight);
Panel2->ManualFloat(Recto);
Panel2->ManualDock(Panel1, Panel1, alClient);
}
//---------------------------------------------------------------------------
403
12. Remember that, at this time, if the user closes the docking or floating window, it
disappears completely. To recover from that, you can provide a menu or a button that
would easily toggle the appearance or disappearance of the docking window.
From the Standard tab of the Component Palette, click Panel and click an
unoccupied area on the form
13. On the Object Inspector, click Caption and type Toggle
14. On the form, double-click Toggle to access its OnClick event and implemented it as
follows (remember that, in our example, Panel2 is the real docking window while
Panel1 is just the host of the floating window):
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Panel2->Visible = !Panel2->Visible;
}
//---------------------------------------------------------------------------
404
In most other cases, a property page appears in a group with other pages. It functions like
a group of pieces of paper placed on top of each other. Each piece is represented by a tab
that allows the user to identify them:
To use a property page, the user clicks the header, called a tab, of the desired page. This
brings that page up and sends the others in the background:
If the user needs access to another page, he can click the desired tab, which would bring
that page in front and send the previously selected to the back.
The pages are grouped and hosted by an object called a property sheet. Like a form, the
pages of a property sheet are simply used to carry or hold other controls. The major idea
of using a property sheet is its ability to have many controls available in a smaller
container.
405
To create or add a property page, you can click New Page; you can do this
continuously until you have added all desired pages.
If you had added a page by mistake or you do not want a particular page anymore,
you can remove it. To do this, first click the page's tab. Then right-click in the middle
of the PageControl control and click Delete Page.
Many of the effects you will need on pages have to be set on the PageControl and not on
individual pages. This means that, to manage the characteristics of pages, you will change
the properties of the parent PageControl control. At any time, whether working on the
PageControl control or on one of its property pages, you should first know what object
you are working on by selecting it.
To select the PageControl control itself, you have two main options:
Any time you want to select the PageControl, click an unoccupied area on the right
side of the most right tab
If you want the property sheet to occupy the whole form or to occupy a section of it, you
can specify this using the Align property. A PageControl by itself can be used as a
control and you can place the necessary controls on it, but this is usually not the reason
you would need a PageControl control.
If you want the property pages to have bitmaps on their tabs, you should first add a series
of images using an ImageList control and then assign that control to the Images property
of the PageControl object.
406
If you have many property pages and the width of the PageControl cannot show all tabs
at the same time, the control would add two navigation arrow buttons to its top right
corner to let the user know that there are more property pages:
By default, the navigation buttons would come up because the control uses a property
that controls their availability. If you do not want the navigation arrows, you can set the
MultiLine property of the PageControl control to true. This would create cascading tabs
and the user can simply select the desired property page from its tab:
As you are adding pages to a PageControl control, the tabs of the pages are positioned on
the top border of the PageControl area. You can reposition them to the left, the right, or
the bottom borders of the control. The placement of the tab is set using the TabPosition
property of the PageControl. The possible values of this property are tpTop, tpLeft,
tpRight, and tpBottom.
TabPosition: tpLeft
TabPosition: tpTop
407
TabPosition: tpBottom
TabPosition: tpRight
If you want to create a discrete property sheet and do not want to display the traditional
tabs, you can replace the tabs with buttons. This option works only if the TabPosition
property is set to tbTop, that is, only if the tabs would have been positioned to the top
border of the control. To display buttons instead of tabs, use the Style property. Its values
are tsTabs, tsButtons, and tsFlatButtons. The button options produce the following
effects:
Style: tsButtons
Style: tsFlatButtons
I
I
f
y
o
u
s
e
l
e
c
t
408
If you attempt to set the Style property to a buttons value when the tabs are not
positioned on top, you would receive an error message:
After adding the PageControl control to your form and after adding one or more property
pages, the property pages are created where the PageControl control is positioned and its
dimensions are used by the eventual property pages. This means that, if you want a
smaller or larger property sheet, you must modify the dimensions of the PageControl
control and not those of the property pages, even though each property page has a
location (Left and Top properties) and dimensions Height and Width properties).
Right-click the PageControl control on the form and click Next Page or Previous
Page. The pages are considered items of a rotating array. If the second page out of
three is displaying and you click Next Page, the third page would be selected. If you
are on the third page out of three and you click Next Page, the first page would be
selected.
Clicking Previous Page would have the reverse effect of Next Page
On the form, you can click its tab. This would bring the page to the front and send
the other(s), if any, to the back
With the PageControl itself having focus on the form, on the Object Inspector, you
can select the desired page using the ActivePage property
While the PageControl control is selected, on the Object Inspector, type the array
index of the page in the TabIndex property. The pages are store in a zero-based
array with the first having an index of 0 and the second an index of 1, etc. If you do
not want any tab selected, set this property to a negative integer. If you type a value
higher than the total number of pages - 1, the previous page selected, if any, would
be selected again.
Like all other controls, the names of property pages are cumulative. As you add them, the
first would be named TabSheet1, the second would be TabSheet2, etc. If you plan to
programmatically refer to the property pages, you should give them more explicit names.
As done with any other control, to set the name of a property page, after selecting it, on
the Object Inspector, change the value of the Name property.
If you have added pages but do not like their sequence, especially after adding the desired
controls, you can move the page and change their sequence. The property pages are
stored in a zero-based array. Each page has an index represented by the PageIndex
Copyright 2003 FunctionX, Inc.
409
property. The first page has a PageIndex value of 0, the second, if available, has a value
of 1, etc. To move the property page, after selecting it, on the Object Inspector, change its
PageIndex value. The value must be an unsigned integer less than the total number of
pages.
Probably the first obvious characteristic of a property page is the word or string that
identifies it to the user. That is, the string that displays on the tab of a property page. This
is known as its title or caption. By default, the captions are set cumulatively as the pages
are added. Usually you will not use these titles because they are meaningless. To display
a custom title for a property page, first select it and, on the Object Inspector, change the
value of the Caption property. You can also change the title of a property page
programmatically, for example, in response to an intermediary action. To change the title
of a page, assign a string to its Caption property. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
pgeSecurity->Caption = "Security Issues";
}
//---------------------------------------------------------------------------
If you had associated an ImageList control with the PageControl object and you want to
use images on the title of a property page, specify the desired image using the
ImageIndex property. You can use the images on some or all property pages. If you do
not want an image on a certain property page, set its ImageIndex value to -1.
If for some reason you do not want to show a property page but do not want to delete it,
you can hide it by setting its TabVisible property to false.
2.
3.
4.
5.
From the Standard tab of the Component Palette, add a button with the following
properties: Caption = OK, Default = true, Left = 280, Name = btnOK, and Top = 344
6.
Add another button with the following properties: Cancel = true, Caption = Cancel,
Left = 364, Name = btnCancel, and Top = 344
7.
8.
To save the project, on the Standard toolbar, click the Save All button
9.
10. Save the unit as Main and save the project as Geometry
11. On the Component Palette, click the Win32 tab. Click PageControl and click on the
form.
12. While the new PageControl object is still selected, on the Object Inspector, set its
properties as follows: Height = 328, Left = 8, Name = shtGeometry, Top = 8, Width
= 432
13. Right-click in the middle of the PageControl on the form and click New Page
14. Add two more property pages
410
15. Click TabSheet1 then click in the body of TabSheet1 to select the first property page.
16. Make sure the top combo box of the Object Inspector displays TabSheet1. Click
Caption and type Quadrilateral
17. Click Name and type pgeQuadrilateral
18. On the form, click TabSheet2 then click the large area under TabSheet2. Type
pgeCircular and change its Caption to Circular
19. Click TabSheet3 and click the wide area under TabSheet3. Change its caption to 3Dimensional and its name to pge3Dimensional
20. Click the empty area on the right-side of the 3-Dimensional tab to select the
PageControl control. On the Object Inspector, set the ActivePage to
pgeQuadrilateral.
21. From the Additional tab of the Component Palette, click Image and click in the body
of the Quadrilateral property page.
22. While the Image1 control is still selected, on the Object Inspector, double-click the
right area of Picture and, using the Load button, select the Quadrilateral image from
the Images folder
23. Set its properties as follows: Height=297, Left=0, Top=0, Width=161
24. In the same way, add an Image control to the Circular property page and set its
Picture to Circular. Set the following properties: Height=297, Left=0, Top=0,
Width=161
25. Add an Image control to the 3-Dimensional property page and set its Picture to
Dimension3. Set the following properties: Height=297, Left=0, Top=0, Width=161
26. By selecting the Label and Edit controls from the Standard tab of the Component
Palette, design each page as follows:
411
27. From the Quadrilateral to the 3-Dimensional property pages, from left to right to
down on each page, set the names of the Edit and Button controls as follows:
edtSquareSide, btnSquareCalculate, edtSquarePerimeter, edtSquareArea,
edtRectLength, edtRectHeight, btnRectCalculate, edtRectPerimeter, edtRectArea,
edtCircleRadius, btnCircleCalculate, edtCircleCircumference, edtCircleArea,
edtEllipseRadius1, edtEllipseRadius2, btnEllipseCalculate, edtEllipseCircumference,
edtEllipseArea, edtCubeSide, btnCubeCalculate, edtCubeArea, edtCubeVolume,
edtBoxBase, edtBoxHeight, edtBoxWidth, btnBoxCalculate, edtBoxArea,
edtBoxVolume
28. Save your project
412
A wizard is a series of dialog pages that display in sequence, one after another. A wizard
is usually used to guide a user through a process of performing a task.
413
414
415
The Style property is a TBevelStyle enumerator that specifies whether the bevel will be
lowered or raised with regard to the surface of its host container. Its values are
bsLowered (the default) and bsRaised. The above bevels were created with the
BevelStyle set to bsLowered. Here is the effect when the BevelStyle is set to bsRaised:
416
1.
417
2.
3.
Click the + sign on the HorzScrollBar and VertScrollBar fields and set each of
their Visible properties to false
4.
On the Component Palette, click the Additional tab and double-click the Bevel
control
5.
6.
On the Component Palette, click the Standard tab. Double-click the Panel control
7.
8.
Double-click an unoccupied area on the form to initiate the forms OnCreate event.
Implement it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Height = 228;
Bevel1->Visible = False;
}
//---------------------------------------------------------------------------
9.
Press F12 to display the form. Double-click the Details panel to initiate its OnClick
event. Implement it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Panel1Click(TObject *Sender)
{
if( Height == 228 )
{
Height = 456;
Bevel1->Visible = True;
Button1->Caption = "&No Details";
}
else if( Height == 456 )
{
Height = 228;
Bevel1->Visible = False;
Button1->Caption = "&Details >>";
418
}
}
//---------------------------------------------------------------------------
10. To test the application, on the Debug toolbar, click the Run button.
If you had created the control in the header file, you can use a function or event to
initialize the control.
419
you click the container that will hold the Image control, a primary dashed rectangle is
drawn as the placeholder of the picture:
The borders of the control display as dash lines to indicate that they will not display when
the picture comes up. Like any other visual control, you can resize the Image object using
the handles on its borders and/or corners.
The most important piece of information the control needs is probably a picture. This can
be specified using the Picture property of the Object Inspector.
After specifying the picture for the Image object, you may find out that your image is
smaller or bigger than the rectangle drawn on the form when you added the Image
control. You have many options to solve this. If you want to use the whole actual size of
the picture, you can manually resize the border of the Image control to accommodate the
picture. Since the border of the Image control will not appear when the picture displays
on the form at run time, you can enlarge the size of the Image control to be greater than
the picture itself:
If you want to keep the size of the Image, you can instead resize the picture itself to fit in
the Image controls rectangle. This is taken care of by the Stretch Boolean property. The
default value of the Stretch property is false, which means that you decide how to deal
with the difference between the size of the Image control and the actual size of the
picture. Using the Object Inspector, if you set the Stretch property to true, the picture
would be automatically resized to use the whole size of the Image control. With Stretch
420
set to true, even if you manually resize the picture or the Image by dragging the border,
the picture and the Image would always have the same size.
If you set the Stretch propertys value to true, and if you resize the image by dragging
one of the borders, the picture may become distorted. For example, the picture of a slim
person may make him or her appear larger (or fat):
If you want to keep the ratio between the length and the height of the picture balanced
during resizing, you can use the Proportional Boolean property. Its default value is false,
in which case the picture can be resized "as is". To respect the ratio of the dimensions, set
the Proportional property to true:
After adding a picture to an Image control, if the picture is smaller than the size of the
Image control, the picture would be positioned in the top-left corner of the control. The
positioning of the picture inside an Image is controlled by the Center property. Its default
value is false, which means the top-left corner of the picture coincides with the top-left
corner of the Image rectangular border. If the picture you are using is smaller than the
size of the Image control but want to keep the size of the Image control, which could be
Copyright 2003 FunctionX, Inc.
421
valid during design, you can position the picture in the horizontal center and vertical
middle of the borders of the Image. This is done by setting the Center Boolean property
to true.
422
from the Standard tab of the Component Palette. Therefore, to add a button
423
question or request a follow-up action from the user. Whenever a dialog box allows the
user to dismiss it without continuing the action, you should provide a button with a
Cancel caption.
The easiest way to change the caption on a control, on the Object Inspector, is to click the
word Caption and type the desired text.
After adding a button to a form (by design or with code), you can change its caption with
code by calling the TControl::Caption property. For example, you can change the
caption of a button as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::ChangeButtonCaption()
{
Button1->Caption = "Let Me Go!";
}
//---------------------------------------------------------------------------
424
Value
mrNone
mrOk
0
idOK
mrCancel
idCancel
mrAbort
mrRetry
mrIgnore
mrYes
mrNo
mrAll
mrNoToAll
mrYesToAll
idAbort
idRetry
idIgnore
idYes
idNo
mrNo + 1
mrAll + 1
mrNoToAll + 1
Description
No special role
The user clicked OK or pressed Enter (assuming the
button has the Default property set to true) to close
The used clicked Cancel or pressed Esc (assuming
the button has the Cancel property set to true) to
close
The user clicked Abort to close
The user clicked Retry to close
The user clicked Ignore to close
The user clicked Yes to close
The user clicked No to close
The user clicked All to close
The user clicked No To All to close
The user clicked Yes To All to close
If the form that is hosting the button is not the first form or dialog (in other words, if the
form is accessed by a call from another form), you can use the ModalResult property to
conveniently associate an action. By default, the ModalResul is set to mrNone.
The ModalResult property is an integer that represents a button that the user has clicked
on a dependent dialog box. To use a button as a valid integral ModalResult value, set its
ModalResult property. When coding the result of the user clicking one of the buttons,
call the TForm::ShowModal() method (once again, the ShowModal() method actually
belongs to the TCustomForm class) and assign it the corresponding value of the
TModalResult integer.
The following example uses two forms. The first form has a button used to call the
second. The second form has buttons with different ModalResult values. After the user
clicks the button on the first form, the second would display, the program simply finds
out what button was clicked, and the programmer can act accordingly:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
425
}
//---------------------------------------------------------------------------
1.
2.
From the Component Palette, on the Standard tab, click the Button control
3.
Click on the form. Notice that a button has been drawn on the form.
4.
To move the button, click and hold your mouse on it. Then drag to the desired
location, for example, move it to the upper left section of the form.
5.
On the Standard tab of the Component Palette, double-click the Button control.
6.
On the form, move the second button, Button2, and position it under Button1
7.
8.
9.
Notice that its caption has changed. Also notice the Caption field in the Object
Inspector.
10. In the Object Inspector, click the Name field and type btnClose
11. On the form, click Button2 to select it.
12. Type btnQuit
13. Notice that this time, the name has changed because the Name field had focus.
14. On the Object Inspector, click the Caption field and type &Quit
15. To make this button dismiss its form when the Esc key is pressed, double-click the
false value of the Cancel field to change it to true.
16. Click anywhere on the form and change its dimensions to Height = 152 and Width
= 315.
17. To test the form, press F9.
18. After viewing the form, close it.
426
On the form, double-click the Close button. Notice that the Code Editor opens.
2.
3.
Click the arrow of the combo box on the top section of the Object Inspector and
select btnQuit
4.
5.
6.
7.
8.
To test the form, on the Debug toolbar, click the Run button.
9.
Click the Close button. Notice that this action closes the form.
13. Execute the program to test it. Click the OK button to close the form.
14. Press F12 to display the form.
15. Double-click the Quit button to get to the Code Editor. Notice that you are taken
back to the Code Editor. Edit the PostQuitMessage() function as follows:
PostQuitMessage(WM_DESTROY);
16. To test the form press F9. After viewing the form, close it.
17. To add another form, on the View toolbar, click the New Form button.
427
18. While the new form is still displaying, on the Object Inspector, click the Properties
tab and set the following properties for the second form:
BorderStyle = bsDialog
Caption = Mission and Assignment
Height = 260
Width = 395
19. Press and hold Shift. On the Component Palette, click the Button control. Release the
Shift key.
20. Click on the form three times to add three buttons
21. On the Component Palette, click the arrow button
selection.
22. Arrange the three buttons on the form to make them visible.
23. Set the properties of the first button as follows:
Caption = OK
Default = true
ModalResult = mrOk
24. Set the properties of the second button as follows
Cancel = true
Caption = Cancel
ModalResult = mrCancel
25. Set the properties of the 3rd button as follows:
Caption = Abort
ModalResult = mrAbort
428
29. Click on the form to add the button and move it to under the Quit button. Change its
caption to Form &2
30. Double-click the Form 2 button and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
Form2->ShowModal();
}
//---------------------------------------------------------------------------
31. Add another button to the form and enlarge it to cover most of the unoccupied area
on the form. Set the name of the new button to btnMessage and its caption to
Message
32. While the new button is still selected, on the Object Inspector, click the field on the
right side of the Font property (it displays (TFont)).
33. Set the characteristics to
Font = Times New Roman
Font Style = Bold
Size = 14
34. Click OK
35. Double-click the new large button and implement its OnClick event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnMessageClick(TObject *Sender)
{
if( Form2->ShowModal() == mrOk )
btnMessage->Caption = "I came back OK";
else if( Form2->ShowModal() == mrCancel )
btnMessage->Caption = "I was dismissed!!!";
else if( Form2->ShowModal() == mrAbort )
btnMessage->Caption = "Mission Impossible";
else
btnMessage->Caption = "What's Happening?";
}
//---------------------------------------------------------------------------
429
42. From the Standard tab of the Component Palette, double-click Button.
43. On the Object Inspector, click Caption and type &Juventus
44. Click Name and type btnTeam
45. Click Left and type 16. Click Top and type 16
46. On the Component Palette, double-click Button
47. On the Object Inspector, click the + of the Anchors properties.
48. Set its characteristics as follows:
akLeft = false
akTop = false
akRight = true
akBottom = true.
49. Cchange the following other properties:
Caption = &Sidney
Left = 288
Name = btnCity
Top = 216
50. To test the project, press F9
51. To resize the form, position the cursor on the lower-right corner then drag down and
right. Notice that the Sidney button is anchored to the bottom and right borders of the
form
52. Close the form
53. On the Component Palette, double-click Button
430
63. To test the form, on the View toolbar, click the Run button.
64. Click Juventus. Notice that the Close button is disabled.
65. Click the Close button on the form. Notice that it is not working
66. To close the form, click its Windows Close button
67. To toggle enabling and disabling the Close button, change the OnClick event of the
btnTeam button with:
btnClose->Enabled = !btnClose->Enabled;
431
72. Make sure you close the form using the Close button in the middle of the form.
73. Press F12 to display the form and click an unoccupied area of the form.
74. Click the Events tab.
75. Double-click the box on the right side of the OnResize field.
76. In the implementation of the event, press Tab and type:
//--------------------------------------------------------------------------void __fastcall TForm1::FormResize(TObject *Sender)
{
btnAnimal->Left = Width - 92;
btnClose->Top = (ClientHeight - btnClose->Height) / 2;
btnClose->Left = (ClientWidth - btnClose->Width) / 2;
}
//---------------------------------------------------------------------------
77. Press F12 to display the form. To test the form, press F9.
78. Resize the form by enlarging or shrinking it. Notice how the turtle and the Close
buttons are anchored to the borders of the form.
which they are used. This works only if the form or dialog box is added to an existing
form. The possible values of the Kind property and their effect on the application are:
Value
bkCustom
bkOK
Picture
See Below
ModalResult
mrNone
mrOk
bkCancel
mrCancel
bkAbort
mrAbort
bkRetry
mrRetry
bkIgnore
mrIgnore
bkYes
mrYes
bkNo
mrNo
bkHelp
Observation
No special role
This value should be used if either it is the only
button on a dialog box or the user will need to
acknowledge a message after performing some
action on the form. In this case the user should
either click this button or press Enter.
After this Kind value is set, the Default property is
automatically set to true, which gives the button a
thick border.
After this value is set, the ModalResult of the
button becomes mrOk
This value should be used if the user will need the
opportunity to dismiss whatever action would have
been performed on the form. The button that uses
this Kind is usually accompanied by another button
labelled OK
After this Kind value is set, the Cancel property is
automatically set to true and the ModalResult of
the button becomes mrCancel
Used to display Abort and return the mrAbort
value
Used to display Retry and return the mrRetry value
Used to display Ignore and return the mrIgnore
value
This value should be used the user will be presented
with a Yes/No question. The button should not be
used with another button labelled OK on the same
container.
After this Kind value is set, the Default property is
automatically set to true and the button gets
equipped with a thick border.
Used to display No and return the mrAbort value
Used to display Help
bkClose
mrNoToAll
bkAll
mrYesToAll
If none of the default bitmaps is satisfying to you, you can replace it with a bitmap of
your choice. If you do not like the caption, you can simply click the Caption field in the
Object Inspector and type a string of your choice.
The bkCustom value of the Kind property allows you to set both the bitmap and the
caption without using the pre-selections. In either case, the bitmap of the button is
specified using the Glyph property of the Object Inspector. To change it, click the Glyph
field and then click its ellipsis button to display the Picture Editor. This allows you to
load, select, and open a bitmap of your choice. You can either design your own bitmap or
Copyright 2003 FunctionX, Inc.
433
use those that get installed with C++ Builder. By default, they are located in the
C:\Program Files\Common Files\Borland Shared\Images\Buttons.
To position itself, a bitmap uses a margin distance between its location and the buttons
borders. This is controlled by the Margin property.
On a bitmap button, you can use only the bitmap, only the caption, or both the bitmap and
the caption. If you decide to use both, you have the opportunity to specify how they
would appear next to each other. The alignment of the bitmap with regards to the caption
is controlled by the Layout property, which is based on the TButtonLayout enumerator.
Its possible values are:
Value
Appearance
blGlyphLeft
blGlyphRight
blGlyphTop
blGlyphBottom
If you decide to use both a bitmap and a caption, the distance between them is controlled
by the Spacing property whose default value is 4 pixels. You can change it to a
reasonable value if you need to.
The bitmap used should/must have at least a 16x16 pixels size. C++ Builder allows you
to use a bitmap made of more than one glyph. In this case, the bitmap must be created
like a single picture image list with a height of 16 pixels at most. If it contains two
bitmaps, it should have a size of 32x16. The picture used for a bitmap button should not
have more than 3 bitmaps. Otherwise, the fourth and beyond glyphs would be ignored.
After adding the bitmaps, C++ Builder would find the number of glyphs that the bitmap
contains using the physical size of the picture based on the fact that each bitmap has a
width of 16 pixels. The number of glyphs is set using the NumGlyphs property and it is
automatically set by C++ Builder. Based on this, if you add a 16x16 pixels bitmap, its
NumGlyphs value would be set to 1. A 32x16 bitmap would have a NumGlyphs value
set to 2. A 48x16 bitmap would have the NumGlyph property set to 3. If you think that
the number set by C++ Builder is not right, you can change it on the Object Inspector.
Here is how the number of glyphs influences the appearance of the bitmap button:
434
If the bitmap contains 1 glyph, the single bitmap would always be used except when
the button is disabled. The operating system is in charge of painting the face of the
button when it is disabled
If the bitmap contains 2 glyphs, the first glyph would be used both when the button is
clicked and not clicked. The second glyph would be used when the button is
disabled.
If the bitmap contains 3 glyphs, the first would be used when the button displays
normally. The second glyph would display while the button is down when it has been
clicked. The third would display when the button is disabled.
2.
3.
4.
5.
Design it as follows:
6.
7.
8.
9.
While the form is displaying, on the Object Inspector, Set the following properties:
Caption = Bitmap Buttons
435
Name = frmMain
BorderStyle: bsDialog
Height: 210
Width: 144
ShowHint = true
10. From the Additional tab of the Component Palette, click the BitBtn control
and click on the form
11. On the Object Inspector, click Kind and click the arrow of its combo box. Select
bkClose
12. Add another BitBtn control
13. While the new bitmap button is still selected, on the Object Inspector, click Glyph
and click its ellipsis button
14. On the Picture Editor dialog box, click the Load button. In the Load Picture dialog
box, click Library
15. Whikle the new button is still selected, on the Object Inspector, click Caption and
type Library
16. Set its Hint to Consult the Library and heck that the NumGlyphs property has a
value of 3
under the second one. While the new bitmap
17. Add another BitBtn control
button is still selected, on the Object Inspector, click Glyph and click its ellipsis
button . Using the Load button of the Picture Editor dialog box and the Load
Picture dialog box, select the Attached bitmap
18. On the Object Inspector, check that the NumGlyphs property has a value of 3 and
change the buttons properties as follows:
Caption: Attach
Enabled: false
Name: btnAttachment
Hint: Send With Attachment
19. Add one BitBtn control
under the previous one. Set its Glyph to C:\Program
Files\Common Files\Borland Shared\Images\Buttons\bulbon.bmp
436
437
using either a speed or a radio buttons, you should pay attention to the container you are
using as it can play a critical role to the behavior of the button.
A newly added speed button appears with a 3D border, like the regular command button.
You can make it appear flat with a mouse over effect. This appearance is controlled by
the Flat property. Its default value is false. If you set it to true, its border would
disappear. When the mouse passes over it, its borders would be raised and they would
disappear when the mouse is no longer over it. Because its borders disappear with a true
value, when you set this property like that, make sure you provide a way for the user to
know that the button is present. This can be done using a caption, a bitmap, or both on the
face of the button.
By default, when a speed button is clicked, it performs its Click action and remains as it
was before. As mentioned already, a speed button can be configured to keep its clicked or
not clicked state. This means that, after a speed button has been clicked, it can stay down
even after the mouse gets set to another control. To provide this down appearance,
change the value of the GroupIndex property. The default value of the GroupIndex
property is 0, which means that it would appear down only when clicked and while the
mouse is pressing it. If you change it to a value other than 0 but with a value used by no
other speed button on the same container, after the the button has been clicked, it would
stay clicked until the user clicks it again.
Alternatively, you can create a series of speed buttons that behave in what is described as
mutually exclusive. In this case, when one button has been clicked, it stays down and
the other buttons stay up. If another button of the same group gets clicked, it becomes
down; the previous down button and all the other buttons, if more than one, would stay
up. To provide this mutual-exclusive functionality, set the same value of GroupIndex to
all button that should be part of the group.
The value of the GroupIndex property can be specified in decimal or hexadecimal
format. At design time, if you set it to a hexadecimal format, C++ Builder would convert
it to its decimal value.
After changing the GroupIndex value of a button or a group of buttons, you can give a
primary down state to the button or to one of the buttons of the group or keep it or them
to its, or their, default appearance. The state of the button can be set using the Down
property. At design time and at run time, you can set its value to true or false. At run
time, you can check the value of the Down property to find out whether the button is
down or not.
If you have created a group of speed buttons and gave them the same GroupIndex value,
as mentioned already, they behave in a mutual exclusive scenario. At any given time, one
button in the group would always be down while the others are up. If the user clicks a
button that is already down, nothing would happen with regards to their appearance. This
is the default behavior of the group. If you want the buttons to be up all of them in
response to an action of your choice, use the AllowUp Boolean property. Here is what
this characteristic provides:
438
If the user clicks a button that is not down in the group, it would become down
and the other button(s) would become up
If the user clicks a button that is already down in the group, nothing would
change with regards to their appearances
If the user clicks a button that is not down in the group, it would become down
and the other button(s) would become up
If the user clicks a button that is already down in the group, the button would
become up like the other button(s) and no button would be down
By default, a newly added speed button has a Height of 22 pixel and a Width of 23
pixels. These dimensions are appropriate to display a 16x16 pixels bitmap. A speed
button is usually used to display a bitmap but you can use it to show a bitmap and a
caption. After adding the button to a container, if you want it to display only a caption,
you can type it in the Caption property of the Object Inspector. If you prefer only a
bitmap, use the Glyph property to select and specify the bitmap. If you want both a
caption and a bitmap, you should resize the button enough to accommodate them.
If you plan to use a bitmap on the speed button, its Glyph, its Spacing, its NumGlyphs,
its Margin, and its Layout properties function similar to the same properties of the
bitmap button. The Glyph property of the speed button provides a small addition. Like
the bitmap button, the speed button can use a bitmap made of one or more glyphs. Unlike
the bitmap button, the speed button can use up to four glyphs and here is how they work:
If the bitmap contains 1 glyph, the bitmap should have a size of 16x16 pixels (width
x height). This single bitmap would always be used except when the button is
disabled. The operating system is in charge of displaying or painting the face of the
button when it is disabled.
If the bitmap contains 2 glyphs, the bitmap should have a size of 32x16 pixels. The
first glyph would be used both when the button is clicked or down and when the
button is not clicked or is not down. The second glyph would be used when the
button is disabled.
If the bitmap contains 3 glyphs, it should have a size of 48x16 pixels. The first would
be used when the button displays normally. The second glyph would display when
the button is disabled. The third glyph would display while the button is down when
it has been clicked.
If the bitmap contains 4 glyphs, it should have a size of 64x16 pixels. This style is
valid only if the button is a member of a group (like radio buttons). The first would
be used when the button displays normally. The second glyph would display when
the button is disabled. The third glyph would display while the button is down when
it has been clicked, exactly like the button with a NumGlyphs value equal to 3 but
with the following exception. If the button is a member of a group, the third glyph
would display while the mouse is still pushing it; that is, while the button is down
but held by the mouse. The fourth glyph is used only if the button is part of a group
and when it is down after the mouse has been released
439
Most other property sheets are equipped with buttons like a regular dialog box. As such, a
property sheet is usually equipped with the OK and Cancel buttons. Sometimes it is
additionally equipped with an Apply button. And sometimes it has a Help button.
Most of the time, the buttons are positioned on the bottom section of the property sheet.
440
Generally, the button labeled OK, if present, should have the Default property set to true.
The Cancel button should have the Cancel property set to true. By convention, the OK
button, if available should be either the top most or the left most. If the Apply button is
used, it should be the right most, which should have the Cancel button between both.
Like any regular dialog box, the property page(s) of a property sheet is(are) equipped
with Windows controls whose values the user may be asked to modify. When a property
sheet is equipped with buttons, there are rules you should follow when implementing
their behavior, to be in accordance with other Windows applications:
After using the property sheet, if the user wants to validate the changes that were
made on the property pages, he can click OK. The OK button closes the property
sheet and acknowledges whatever the user would have done on the controls the
page(s) is(are) hosting. If the user did not make any changes, clicking OK should not
make any difference, unless you configured some controls to automatically change
their values when the user opens the property sheet
When the property sheet comes up, if (many property sheets do not have an Apply
button, as you can check on most dialog boxes of C++ Builder) it is equipped with it,
the Apply button should be disabled, indicating to the user that, so far, there has not
been any change made on any of the control(s) of the property page(s)
After the property sheet is opened, once the user changes any value of any of the
controls, the Apply button should be enabled, indicating to the user that the property
sheet is now dirty (in computer programming, a document is referred to as dirty
when it is not the way it was when it was opened). Here is an example:
441
While the user is making changes, if she wants to validate or check them without
closing the property sheet, she can click the Apply button. This means that, clicking
the Apply button (if available) would acknowledge the changes of values of the
controls, just like the OK button. The difference is that the property sheet would not
be closed. After clicking it, the Apply button should become disabled again,
indicating that, at this time, the controls are clean
442
The user can continue making changes and clicking the Apply button if necessary
If the property sheet is equipped with an OK and a Cancel button without an Apply
button, if the user makes changes on some or all of the controls but does not want to
validate them, he can click Cancel to dismiss the dialog box. In this case, any
change(s) made on the control(s) would be discarded.
If the property sheet is equipped with an OK, a Cancel, and an Apply buttons, if the
user makes changes and clicks Apply, we saw that the changes would be
acknowledged and validated. This would then disable the Apply button, again. If the
user makes changes and clicks Cancel, only the new changes would be discarded;
any change made before the Apply button was clicked would be validated and sent to
the object that called the property sheet.
C++ Builder provides a fast means of creating a propertysheet and equips it with the OK,
the Cancel, and the Help buttons. To use it, open the New Items dialog box and click the
Forms property page. Then double-click Tabbed Pages.
443
2.
From the Standard page of the Component Palette, double-click the Panel control.
3.
Set the following properties for the panel (leave the other properties intact):
BevelOuter: bvNone
Caption: empty
Height: 265
Left: 0
Name: pnlFirstPage
Top: 0
Width: 430
4.
From the Component Palette, click the Edit control and click anywhere on the panel.
This is a simple control that will serve as an indication when we are in the first page.
5.
6.
7.
Click an empty area on the form to deselect the panel and select the form.
8.
9.
With the new pasted panel still selected, change its properties as follows:
Left: 0
Name: pnlSecondPage
Top: 0
10. From the Component Palette, click the ScrollBox control and click on the panel.
11. Click an empty area on the form to select the form.
12. From the main menu, click Edit -> Paste.
13. For the newly pasted panel, change the properties as follows:
Left: 0
Name: pnlThirdPage
Top: 0
444
14. On the Component Palette, click the GroupBox control and click on the current
panel.
15. On the Component Palette, click the Additional tab.
16. Click the Bevel control
If the button displays at all times, that is, regardless of the page that is displaying, the
presence of a Finish button means the user can click it to close the dialog box and
end the task(s) performed. Here is an example of such a wizard:
445
In most other wizards, when the user gets to the last page, the Next button would be
changed to Finish. This allows the user to click Finish to close the dialog box. Here
is an example:
Clicking Finish closes the wizard and sends the results or values of the controls on the
pages to whatever is the purpose of the wizard.
446
2.
3.
4.
Double-click an empty area on the form to access the form's OnCreate() event
5.
6.
7.
8.
Implement it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnBackClick(TObject *Sender)
{
if( pnlSecondPage->Visible == True )
{
pnlFirstPage->Visible = True;
447
pnlSecondPage->Visible = False;
pnlThirdPage->Visible = False;
btnBack->Enabled
= False;
btnNext->Enabled
= True;
}
else if( pnlThirdPage->Visible == True )
{
pnlFirstPage->Visible = False;
pnlSecondPage->Visible = True;
pnlThirdPage->Visible = False;
btnBack->Enabled
= True;
btnNext->Enabled
= True;
btnFinish->Enabled
= True;
}
}
//---------------------------------------------------------------------------
9.
10. Double-click the Next button and implement its OnClick() event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnNextClick(TObject *Sender)
{
// If you are in the first page
if( pnlFirstPage->Visible == True )
{
pnlFirstPage->Visible = False;
pnlSecondPage->Visible = True;
pnlThirdPage->Visible = False;
btnBack->Enabled
= True;
btnNext->Enabled
= True;
btnFinish->Enabled
= True;
}
else if( pnlSecondPage->Visible == True )
{
pnlFirstPage->Visible = False;
pnlSecondPage->Visible = False;
pnlThirdPage->Visible = True;
btnBack->Enabled
= True;
btnNext->Enabled
= False;
btnFinish->Enabled
= False;
}
else// if( pnlThirdPage->Visible == True )
{
btnNext->Enabled = True;
btnBack->Enabled = True;
btnFinish->Enabled = True;
}
}
//---------------------------------------------------------------------------
448
pnlThirdPage->Visible = True;
btnBack->Enabled
= True;
btnNext->Enabled
= False;
}
//---------------------------------------------------------------------------
449
450
On the C++ Builder's IDE, the categories of menus are File, Edit, Search, View, etc. To
use a menu, the user first clicks one of the words that displays on top. Upon clicking, the
menu expands and displays a list of items that belong to that category. Here is an
example where the View menu of WordPerfect was clicked therefore got expanded:
451
There is no strict rule on how a menu is organized. There are only suggestions. For
example, actions that are related to file processing, such as creating a new file, opening
an existing file, saving a file, printing the open file, or closing the file usually stay under
a category called File. In the same way, actions related to viewing documents can be
listed under a View menu.
452
453
2.
On the Component Palette, click the Standard property sheet. Click the MainMenu
button
3.
4.
As the first item is selected, on the Object Inspector, click Caption, type &Staff and
press Enter.
5.
On the Menu Designer window, click Staff and click the empty box under Staff.
6.
7.
On the Object Inspector, click ShortCut. Click the arrow of the ShortCut property
and select Ctrl + N
8.
On the Menu Designer window, click the item under New Hire.
9.
18. When the Time Sheet item is highlighted, release the mouse.
19. To start a new menu category, click the item on the right side of Staff
454
20. On the Object Inspector, click Caption, type &Books and press Enter.
21. Click Books and click the empty item under it.
22. Type &New and press Enter
23. Type Show All &Titles and press Enter.
24. To create a submenu, click New and press Ctrl and the right arrow key.
25. Click the new empty item on the right side of New.
26. Type &Title and press Enter
27. Type &Author and press Enter
28. Type &Category and press Enter
29. Clicking each item and using the Object Inspector, specify the shortcuts as follows:
455
2.
3.
456
On the Standard tab of the Component Palette, click the PopupMenu button
and click on the form.
2.
3.
On the Menu Designer window, as the first item is select, on the Object Inspector,
click Caption, type &Font... and press Enter
4.
Click the empty box under Font to select it. Type &Options and press Enter.
5.
Click the empty box under Intermediate. Type &Properties and press Enter.
6.
Click the empty box under Properties and, on the Object Inspector, click Caption,
type - and press Enter.
7.
8.
457
9.
On the Object Inspector, click the Events tab and click the OnClick field.
10. Click the arrow of the right field and select Exit1Click.
11. Close the Menu Designer window
12. Click an empty area on the form to select the form
13. On the Object Inspector, click the Properties tab and click PopupMenu.
14. Click the arrow of the PopupMenu property and select PopupMenu1
15. Test the form. Right-click in the middle of the form and click Finish.
19.2 Toolbars
19.2.1 Introduction
A toolbar is a Windows control that allows the user the perform some actions on a form
by clicking a button instead of using a menu. What a toolbar provides is a convenient
group of buttons that simplifies the user's job by bringing the most accessible actions as
buttons so that, instead of performing various steps to access a menu, a button on a
toolbar can bring such common actions closer to the user:
Toolbars usually display under the main menu. They can be equipped with buttons but
sometimes their buttons or some of their buttons have a caption. Toolbars can also be
equipped with other types of controls.
There are various ways you can create a toolbar. The simplest and the most common
means of getting a toolbar is by using the TToolBar VCL class. To create a toolbar, on
the Win32 tab of the Component Palette, click ToolBar
and click on the form. By
default, a toolbar is positioned on the top section of the form because it takes the alTop
value of the Align property.
Like a form, a toolbar is only a container and does not provide much role by itself. To
make a toolbar efficient, you should equip it with the necessary controls. The most
common control used on a toolbar is a button. To add a button to a toolbar, right-click the
toolbar and click New Button. The new button would appear empty. A button on a
toolbar is a rectangular object with a picture on top. The picture on the button should
suggest what the button is used for but this is not always possible. Providing a picture is
more important. The easiest way to provide pictures for your toolbar is by creating an
image list and associating it with the toolbar. If a toolbar is given an image list, whenever
you right-click the toolbar and click Add Button, the image corresponding to the index of
the list would display on the new button. You can keep adding buttons in this fashion.
A separator is a line that separates buttons (or controls) as groups of objects on a toolbar.
To create a separator, you can right-click the toolbar and click New Separator.
As a toolbar can be equipped with various types of controls, there are various types of
buttons you can place on a toolbar. The types of buttons can be configured using the
Style property on the Object Inspector.
458
To create a new popup menu, on the Standard tab of the Component Palette, doubleclick PopupMenu
2.
Using the Object Inspector, change the name of the new popup menu to
mnuNewPopup
3.
Double-click the mnuNewPopup icon. Create a menu with the &Staff, &Customer,
&Book, and &Author
4.
5.
To create a new popup menu, on the Standard tab of the Component Palette, doubleclick PopupMenu
6.
7.
8.
Set the Checked property of each to true then close the Menu Designer window
9.
To create a new toolbar, on the Component Palette, click the Win32 tab.
459
21. While the new button on the toolbar is still selected, on the Object Inspector, click
Style and set its value to tbsDropDown
22. Still on the Object Inspector, click DropDownMenu and set its value to
mnuNewPopup
23. Click an empty area on the toolbar to select it.
24. On the Object Inspector, set its PopupMenu to mnuViewPopup
2.
460
3.
On the form, double-click the mnuViewPopup icon to open the Menu Designer
window
4.
Click Standard and, using the Object Inspector, set the OnClick event of Standard to
Toolbar1Click
Copyright 2003 FunctionX, Inc.
5.
Test the form. Right-click the toolbar and click Standard. On the main menu of the
form, click View -> Standard
6.
461
2.
3.
4.
While the new control is still selected, on the Object Inspector, click Name and type
DefaultStatusBar and press Enter
5.
On the form, click View -> Status Bar and implement its Click event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::StatusBar1Click(TObject *Sender)
{
DefaultStatusBar->Visible = !DefaultStatusBar->Visible;
StatusBar1->Checked
= !StatusBar1->Checked;
}
//---------------------------------------------------------------------------
6.
462
On the form, click the newly added status bar. On the Object Inspector, set its
SimplePanel property to true.
Copyright 2003 FunctionX, Inc.
7.
8.
On the Menu Designer window, click Status Bar. On the Object Inspector, click the
Event tab and, in the OnClick event, select StatusBar1Click
9.
10. On the Object Inspector, set the value of the ShowHint property to true.
11. On the form, click an empty area on the toolbar and, on the Object Inspector, set the
ShowHint property to true.
12. Press F12 to display the Code Editor
If the Class Explorer is not displaying, on the main menu, click View ->
ClassExplorer
In the Class Explorer, expand everything.
13. In the Class Explorer, right-click TForm1 and click NewMethod...
14. In the Method Name edit box, type ShowToolTips
15. In the Arguments edit box, type TObject* Sender
16. Set the Function Result to void
17. Click the Private radio button and the __fastcall check box
463
Hint
New Hire
Records
Search
Time Sheet
Exit
Toolbar
Status Bar
Show All Titles
Title
Author
Category
window and click Add. You can also press Insert. Either of these actions performed for
the first time would create a formal panel for the status bar. At this time, instead of using
the SimpleText property of the status bar, you can refer to the Panels collection of the
TStatusBar class.
If you want to use more than one section on a status bar, you must divide the status bar in
as many sections as necessary. To do this, you can use the Add New button or Insert key
to create each subsequent section. If you create a section by mistake or do not need it
anymore, select it in the Panels window and either click the Delete Selected button or
press Delete. You can also right-click an undesired item and click Delete on the popup
menu.
The panels are added incrementally as you insert them. If you create more than one panel
but do not like their sequence, you can rearrange them using the arrow buttons on the
toolbar of the Panels window. You can also right-click an item and use the popup menu
to move an item.
A status bar equipped with different sections is a collection or array of panels. Each panel
belongs to an array of Items. Based on this array, each panel can be configured
independently of the other(s). In fact, the main thing that unites the panels is their
belonging to the same parent object: the status bar control. To access a particular panel of
the status bar, you can refer to it using its index in the Items array. The first panel has an
Items index of 0, the second has an index of 1, etc. For example, imagine you have a
status bar with four panels. To access the 3rd panel, you can refer to it using StatusBar>Panels->Items[2]. You can specify the individual characteristics of each panel.
After creating or adding a new panel, it assumes a default length of 50 pixels. The length
of a panel is specified using the Width property. The Width of panels is directly related
to the panel and can be dependent on the other panels.
The most common item displayed on a panel is a string. For a status bar panel, the string
to display is defined by the Text property.
If you plan to use something else than a string to display on a panel, you can specify this
using the Style property. The default value of a panel's Style is psText which indicates
that the panel is used to display text. Instead of text, you can draw on the panel after
setting the Style value to psOwnerDraw.
By default, the text on a panel aligns to the left as a normal text of a paragraph does. The
alignment of text is controlled by the Alignment property that provides three
possibilities: taLeftJustify, taCenter, taRightJustify.
By default, the text of a panel in sunken into the panel. This appearance of text is
controlled by the Bevel property. Its default value is pbLowered. If you want the panel's
surface to be raised, set it Bevel property to pbRaised. If you do not want any of these
characteristics, sunk or raised, set its Bevel property to pbNone.
465
application. You saw earlier that to use menus and toolbars for different assignments, the
actions had to sometimes be performed individually on the controls that shared the same
functionality. An action list does not solve all problems but reduces the steps performed
to find a common solution to menu items and toolbar buttons combinations.
2.
To save it, on the Standard toolbar, click the Save All button
3.
Create a new folder called Notice1 and display it in the Save In combo box
4.
5.
6.
7.
8.
9.
10. Click Add. Locate the resources that accompany this book. From the Bitmaps folder,
click New and click Open
11. In the same way, add the Save bitmap to the list of images
12. Click OK
466
1.
and click on
2.
While ActionList1 is still selected, on the Object Inspector, click Images and select
ImageList1
3.
4.
On the toolbar of the Action List Editor, click the New Action button
5.
While the new Action1 item is still selected, on the Object Inspector, click Name
and type FileNew
6.
7.
8.
9.
Click Shortcut, then click the arrow of its combo box and select Ctrl + N
467
468
469
13. From the Standard tab of the Component Palette, click MainMenu
the form
and click
14. While the MainMenu1 icon is still selected, on the Object Inspector, set its Images
property to ImageList1
15. On the form, double-click MainMenu1 to open the Menu Editor
16. Right-click the selected box on the Menu Editor and click Insert From Template
17. On the Insert Template dialog box, double-click File Menu
18. Click the empty box on the right side of File to select it. Display the Insert Template
dialog box again and double-click Edit Menu
19. Delete Repeat, Paste Special, Go To, the separator under Go To, Links, and Object
20. On the Menu Editor, click File and click New to select it
21. On the Object Inspector, click the Action field and select FileNew from its combo
box
22. In the same way, selecting the menu items and using the Action field from the Object
Inspector, associate the menu items to their corresponding actions as follows:
Menu Item
Open
Save
Save As
Print Setup
Exit
Undo
Cut
Copy
Paste
Action
FileOpen1
FileSave
FileSaveAs1
PrintSetup1
FileExit1
EditUndo1
EditCut1
EditCopy1
EditPaste1
23. In the Edit menu, click the separator under Paste and press Insert. Set its Action to
EditDelete1
24. Close the Menu Editor
25. Save All
26. From the Win32 tab of the Component Palette, click ToolBar
empty area on the form
and click an
470
36. Press F12 to get back to the form. Double-click in the middle of the form and
implement its OnCreate event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
Application->OnHint = ShowHints;
}
//---------------------------------------------------------------------------
This means that an event created from this function will require two arguments. The first
argument, Action, is in fact a pointer to the TBasicAction class. The TBasicAction class
carries information about the action that needs to be carried. Information can be used to
specify whether the action must be executed anew or only updated, whether there was a
change to take into consideration or the change that occurred must be dismissed.
The Handled argument specifies how the action will be carried, whether it will execute or
simply update the action.
When an action needs to be carried, the object that initiates the action fires an
OnExecute() event, which is a TActionEvent type. If the action needs to be updated, the
OnUpdate() event isfired, which also is a TActionEvent type. When a change occurres
in the action list, an OnChange() event is fired. This is a TNotifyEvent type.
Copyright 2003 FunctionX, Inc.
471
472
473
By default, the caption of a label is positioned starting on the left side of its allocated
rectangle. Alternatively, you can position it to the center or the right side inside of its
rectangle. This positioning is controlled by the Alignment property which is based on the
TAlignment enumerator. It can have the following values:
taLeftJustify
taCenter
taRightJustify
Because the caption of a label is confined to a rectangle, you can increase the height of
that rectangle and align text to the top, the middle or the bottom side of that allocated
rectangle. The vertical alignment of a label is controlled by the Layout property which is
based on the TTextLayout enumerator and defined as follows:
enum TTextLayout { tlTop, tlCenter, tlBottom };
The effects of the Alignment and the Layout properties are as follows:
Alignment
Layout
taLeftJustify
tlTop
Alignment
Layout
taLeftJustify
tlCenter
Alignment
Layout
taLeftJustify
tlBottom
Alignment
Layout
taCenter
tlTop
Alignment
Layout
taCenter
tlCenter
Alignment
Layout
taCenter
tlBottom
Alignment
Layout
taRightJustify
tlTop
Alignment
Layout
Alignment
Layout
taRightJustify
tlBottom
taRightJustify
tlCenter
If you have allocated a rectangular area wider and taller than the actual string of the label,
you can display it on more than one line. This ability is controlled by the WordWrap
Boolean property. Its default value is false, which makes the label display on a single
line. When the value of the WordWrap property is set to true, the text of the label can
span multiple lines.
If you want to create a shadow effect on a label, you can place one label on top of
another. Using the Transparent property, which is a Boolean value, you should make the
top label transparent.
Other fancy characteristics you can apply to a label include its font and color. The Label
control is also equipped with a Canvas property. This allows you to display a bitmap or
474
to perform any needed drawing in the allocated rectangle of the label. Here is an
example:
Start Borland C++ Builder if you did not yet. To start a new program, on the main
menu, click File -> New -> Application
2.
To save the current project, on the Standard toolbar, click Save All
3.
4.
Type Employment Application and press Enter twice to display the new folder in
the Save In combo box
5.
To rename the current unit, in the File Name box, replace the name with Main and
press Enter
6.
to create a folder
475
7.
From the Standard tab of the Component Palette, click the Label control
8.
9.
As the new label is still selected, on the Object Inspector, click Caption and
type Date &Hired
10. To move the new label, click and drag it to the left section of the form. To position it
precisely, using the Object Inspector, set the labels properties as follows: Left = 16,
Top = 96
11. To add another label, on the Standard tab, double-click the Label control
12. On the Object Inspector, click the Caption field and type &First Name:
13. Set its Left property to 16 and its Top to 120
14. Add the following other labels:
476
23. Click OK
24. On the Object Inspector, click the Name field and type lblMainTitle
25. Double-click the box on the right side of the Transparent field. Instead of false, it
should now display true.
26. On the form, click the Employment Application label to select it.
27. On the main menu, click Edit -> Copy
28. Click an unoccupied area on the form to make sure nothing is selected.
29. On the main menu, click Edit -> Paste.
30. As the new label is still selected, click the + on the Font field.
31. Under the Font field, click Color to reveal its combo box and select clGray
32. Click the on the Font field
33. Click the Name field and type lblTitleShadow
34. Click the arrow on the top section of the Object Inspector and select lblMainTitle
35. Set following properties
Left: 16
Top: 8
36. Click the arrow on the top section of the Object Inspector and select lblTitleShadow
37. Set its properties as follows:
Left: 19
Top: 11
38. Right-click on the group of those big labels. One of them will be selected. On the
context menu, click Send To Front. If the blue label does not come in front, rightclick again and click Bring To Back until the blue label is on top of the white label:
477
40. While the new label is still selected, click the Caption field and type
Tenley Associates
41. Click the Cursor field
42. Click the arrow of the cursor field and field select crHandPoint
43. Click the + on the Font field to expand it and set its properties as follows:
Color: clRed
Name: Garamond
Size: 10
44. Click the + button on the Style field to expand it and set its fsBold field to true
45. Double-click the field on the right side of fsUnderline to change it to true.
46. Click the on the Font field to collapse
47. Click the Left field and type 416
48. Click the Top field and type 8
49. Add the following other labels:
Name
Caption
Font
lblCompAdr
lblCompCity
lblCompPhone
MS Serif
MS Serif
MS Serif
Size
Left
Top
7
7
7
414
420
440
24
34
46
478
479
ShellExecute(NULL,
NULL,
"C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe",
NULL,
NULL,
SW_SHOWNORMAL);
}
//---------------------------------------------------------------------------
The label inherits two valuable events from its parent the TCustomLabel class. Although
the label cannot receive focus, when the mouse is positioned over it, it fires the
OnMouseEnter() event, which is a TNotifyType. When the mouse is taken away from
the top of the label, it fires the OnMouseLeave() event, which also is a TNotifyEvent
type.
On the form, double-click the (red) Tenley Associates label to access its OnClick
event
2.
Implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmEmployment::Label3Click(TObject *Sender)
{
ShellExecute(NULL,
"open", "http://www.tenleyassociatesinc.com",
NULL,
NULL,
SW_MAXIMIZE);
}
//---------------------------------------------------------------------------
3.
While the Tenley Associates label still has focus, on the Events tab of the Object
Inspector, double-click the right side of the OnMouseEnter field and implement its
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmEmployment::Label3MouseEnter(TObject *Sender)
{
Label3->Font->Color = clBlue;
Label3->Font->Style = TFontStyles() << fsUnderline;
}
//---------------------------------------------------------------------------
4.
In the same way, access the OnMouseLeave event of the Tenley Associates label
and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmEmployment::Label3MouseLeave(TObject *Sender)
{
Label3->Font->Color = clBlack;
Label3->Font->Style = TFontStyles() >> fsUnderline;
}
//---------------------------------------------------------------------------
5.
480
6.
sbsSingle
sbsSunken
Probably the biggest difference for us is that the StaticText, which is a descendent of
TWinControls has a Handle property. This allows you to access the properties of the
Win32s STATIC class and apply them to the VCLs StaticText control. For example,
you can draw an etched border around the static text control as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
LONG GWL = GetWindowLong(StaticText1->Handle, GWL_STYLE);
GWL |= SS_ETCHEDFRAME;
SetWindowLong(StaticText1->Handle, GWL_STYLE, GWL);
481
}
//---------------------------------------------------------------------------
In the same way, you can use the rectangular area of the StaticText control to draw,
display an icon or a bitmap, or perform any other complicated task allowed by the
Win32s STATIC class.
This example demonstrates that through one or another control, the VCL provides most ,
if not any, of the functionality you need from Windows controls. For example, instead of
writing so many lines of code to get the above look, you could have used either a panel, a
group box, a radio group, or a bevel controls, to get the above frame.
482
By default, a newly created edit box is used to both display and receive text from the
user. If you want to user to read text without being able to change it, set the ReadOnly
Boolean property to true. Its default value is false.
As mentioned already, an edit box should be accompanied by a label that indicates what
it is used for. To support this relationship, the Label control provides various properties.
An accelerator character is a symbol of the label that provides easy access to its edit box.
On the label, such a character is underlined. An example would be First Name. The idea
is that, if the user presses Alt key in combination with the labels underlined characters,
the edit box it accompanies would receive focus.
To create an accelerator key, choose one of the labels characters and precede it with an
ampersand character when setting its caption. An example would be &First Name. If you
want a label to display the accelerator character instead of a plain ampersand, set the
labels ShowAccelChar property to true. If you set it to true but need to display an
ampersand, type two & characters where the ampersand would be shown.
The ShowAccelChar property of a label is only used to indicate that the label will
display an accelerator character and the & symbol typed on the label creates that
accelerator character. To indicate which edit box would receive focus when the
accelerator character of the label is invoked; the label control provides the FocusControl
property. To use this property, select the label on the container. Then, on the Object
Inspector, click the FocusControl field to display its combo box. From there, you can
select one of the existing controls. The control, such as an edit box, must be able to
receive focus.
The CharCase property of an edit box control allows the content of an Edit control to
apply a character case of your choice. The CharCase property is controlled by the
TEditCharCase enumerator defined as follows:
enum TEditCharCase { ecNormal, ecUpperCase, ecLowerCase };
483
performs is to select text contained in the control. The user must have the option to select
only part of the text or its whole content. To do this, the user can click and hold the
mouse on one end of the selection and drag to the desired end. This allows for partial
selection. The area where the user start dragging during selection is represented by the
SelStart property. When the user ends the selections, the length of the text selected is
represented by the SelLength property. The portion of text selected is represented by the
SelText property, which is an AnsiString type. These properties allow you either to
programmatically select text or to get information about text that the user would have
selected in the edit box.
2.
3.
To reposition the control, click and drag the Edit control to the right side of the Date
Hired label
4.
As the Edit control is still selected, on the Object Inspector, click the Name field and
type edtDateHired
5.
Click the Text field and press Delete to empty its content.
6.
Using the same process, add new labels and edit boxes as follows. The name of an
edit box starts with edt follows by the caption of the label omitting the special
characters:
Set the FocusControl property of each label to Edit control on its right.
8.
9.
484
The contents of an edit box can be empty or contain some text. If you want it empty, use
the Clear() method. This would delete the whole contents of the control:
//--------------------------------------------------------------------------void __fastcall TForm1::btnClearClick(TObject *Sender)
{
Edit1->Clear();
}
//---------------------------------------------------------------------------
If the user or another control or action had selected text on the edit control, you can delete
the selection using the ClearSelection() method. This method first finds if some text is
selected. If so, the selected text would be discarded. If nothing is selected, the method
would not do anything. To delete a selected text, you can write:
//--------------------------------------------------------------------------void __fastcall TForm1::btnClearSelectionClick(TObject *Sender)
{
Edit1->ClearSelection();
}
//---------------------------------------------------------------------------
485
The OnExit event occurs when the control loses focus. In the case of an edit box, this
could happen if the control has focus and the user presses Tab; the edit box would lose
focus.
486
If you are familiar with the masking properties of this control, you can type a value
using the appropriate symbols. Otherwise you should use an appropriate dialog box
to guide you. You have two alternatives. You can double-click the empty area of the
EditMask field or you can click the ellipsis button. This would call the Input Mask
Editor dialog box
If none of the samples matches your desired format and you know the symbols used,
you can type your own. You can also check masks created for foreign languages to
see if one of them would have the mask you need. To do this, click the Masks
button. This would call the Open Mask File dialog box:
487
Another alternative is to create your own list of masks. To do this, follow the format used
to create a mask. This is:
Name | Sample | Mask
This line is made of three sections. The first and the second, then the second and the third
are separated by a beam. To see a sample file, using Notepad, locate the C:\Program
Files\Borland\Cbuilder6\Bin folder. After changing the Files of Type to All files, click
one of the files with .dem extensions:
Files\Borland\Cbuilder5\Bin folder. Change the Files of Types to All files. Click the file
you created and click Open.
You can also set a mask programmatically using the symbols appropriate for the masks.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Mask for an 8 character file name + 3-character extension
// The first character is automatically converted to uppercase
// After the first character, the user can enter an alphabetical
// character or a digit to complete the 8 characters.
// The other 7 characters and the extensions are converted to lowercase
edtFileName->EditMask = ">L<aaaaaaa.<LLL;1;_";
}
//---------------------------------------------------------------------------
As a text-based control, the content of the MaskEdit control is represented by the Text
property, which is an AnsiString object. Following the EditMask you had set in the Input
Mask Editor editor, you can use a default text that would display when the control opens.
To set this text, on the Object inspector, click Text and type a value that abides by the
rules of the EditText field. At design time, C++ Builder will assist you and make sure
that the value you type is appropriate. At runtime also, the user will have to follow the
rules of the mask.
When a mask is configured for a MaskEdit control, the compiler reinforces the rule to
make sure the user would follow number and types of characters allowed in the edit box.
If you add a MaskEdit control but do not apply a mask to it, you can limit the number of
characters that the user can enter in the box. The maximum number of characters allowed
is set using the MaxLength property. This property has any effect only if no mask is
applied to the control. At design time, type an integer value for the property. At runtime,
assign an appropriate value to the control.
The IsMasked Boolean property can be used to check whether a MaskEdit control has
been configured with a mask already.
2.
Click the edit box on the right side of the Date Hired label and press Delete. Also
delete the edit boxes on the right sides of the MI, the Home Phone, the Employee #,
the ZIP, the Work Phone, and the Ext labels
3.
On the Component Palette, click the Additional tab. Click the MaskEdit button
.
4.
Click on the right side of the Date Hired label on the form.
5.
6.
Click EditMask field to display the ellipsis button . Click the ellipsis button. On
the Input Mask Editor dialog, click Date. In the Input Mask edit box, change the two
zeros to four zeros:
489
Click OK
8.
As the MaskEdit control still has focus, on the Object Inspector, click the Text field
and press Delete. Click its ellipsis button. Set the text to 12/05/1996:
Click OK.
10. On the Component Palette, click the MaskEdit control and click on the right side of
the Home Phone label. On the Object Inspector, click the Text field and delete the
content of the field. Click the EditMask field and click the ellipsis button. Click
Phone and click OK.
11. On the form, click the new masked edit box of the Home Phone box. On the main
menu, click Edit -> Copy. On the main menu again, click Edit -> Paste. Move the
new paste edit box to the right side of the Work Phone label.
12. Add another MaskEdit control to the right side of the Employee # label. Click the
EditMask and type 00-000;1;_
13. Add a MaskEdit control to the right side of the MI label. Set its EditMask to >L and
delete the content of the Text field.
14. Add a MaskEdit control on the right side of the ZIP label. Set its Text field to 00000
and set its EditMask to 99999
15. Add a MaskEdit control to the right side of the Ext label. Delete its Text field and set
its EditMask to #####;1;_
490
491
}
//---------------------------------------------------------------------------
A MaskEdit object created like this is just a classic Edit control. If you want to make a
true MaskEdit object, set its properties as needed, namely an EditMask and possibly a
Text properties. This time, not only will you have to work in a non-visual setting but you
should also make sure that the EditMask and the Text properties are synchronized.
Sometimes, this would involve trial-and-error.
The IP Address control is aware of the basic rules of IP address formatting. To proceed
with the control, the user can click the first field and start typing. Each section allows
only digits. By default, the control is configured to allow only numbers that range from 0
to 255 but you the programmer can change this allowed range. It does not allow a
negative character. If the user enters a value higher than the allowed, the number is reset.
Each field can allow only three digits. Once a valid value has been entered in a field, the
focus shifts to the next section to the right.
//--------------------------------------------------------------------------#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------------------------#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published:
// IDE-managed Components
void __fastcall FormCreate(TObject *Sender);
private:
HWND hWndIPAddress;
// User declarations
public:
// User declarations
__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------------------------#endif
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
hWndIPAddress = CreateWindowEx(0,
WC_IPADDRESS,
NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
90, 14, 120, 20,
Handle,
NULL,
HInstance,
NULL);
}
//---------------------------------------------------------------------------
493
control, the wParam and the lParam parameters are not used and must be passed with 0
values. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
BOOL IsThereAnAddress = SendMessage(hWndIPAddress, IPM_ISBLANK, 0, 0);
if( IsThereAnAddress == True )
ShowMessage("There is no IP address");
}
//---------------------------------------------------------------------------
On the other hand, if the control already contains an IP address, whether it is complete or
not, before performing such operations as changing its values or else, you may want to
completely delete the current IP address. This is done by sending an
IPM_CLEARADDRESS message to the control using the SendMessage() function. The
wParam and the lParam parameters are not used and will be passed as 0. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
SendMessage(hWndIPAddress, IPM_CLEARADDRESS, 0, 0);
}
//---------------------------------------------------------------------------
By default, when the IP Address control is created, it is blank: its field do not contain
values. Whether the control is empty or not, if you want to fill it up, you must first create
an address. This is done by calling the MAKEIPADDRESS macro. Its syntax is:
LPARAM MAKEIPADDRESS(BYTE b0, BYTE b1, BYTE b2, BYTE b3);
This macro simply requires 4 byte values as arguments. Each value should range from 0
to 255. Here is an example:
MAKEIPADDRESS(212, 56, 198, 92);
The MAKEIPADDRESS macro is only used to create an address. If you want to pass
that address to an IP Address control, you can send it an IPM_SETADDRESS message
using the SendMessage() function. The wParam argument is not used and must be
passed as 0. The address to be set is passed as the lParam argument, which results from a
call to the MAKEIPADDRESS macro. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
LPARAM lpAdr = MAKEIPADDRESS(212, 56, 198, 92);
hWndIPAddress = CreateWindowEx(0,
WC_IPADDRESS,
NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
90, 14, 120, 20,
Handle,
NULL,
HInstance,
494
NULL);
SendMessage(hWndIPAddress, IPM_SETADDRESS, 0, lpAdr);
}
//---------------------------------------------------------------------------
If the control already contains an address and you want to retrieve it, send it an
IPM_GETADDRESS message using the SendMessage() function. The syntax you
would use is:
lResult = SendMessage((HWND) hWndControl,
(UINT) IPM_GETADDRESS,
0,
(LPARAM) lParam);
This version of the SendMessage() function returns the number of non-empty fields of
the address. Each one of the four fields of an IP address has a particular value. The values
of these fields can be identified using the following macros:
BYTE
BYTE
BYTE
BYTE
FIRST_IPADDRESS(LPARAM lParam);
SECOND_IPADDRESS(LPARAM lParam);
THIRD_IPADDRESS(LPARAM lParam);
FOURTH_IPADDRESS(LPARAM lParam);
Each one of these macros retrieves the value stored in their respective fields. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
DWORD CurAddress;
LRESULT SM = SendMessage(hWndIPAddress, IPM_GETADDRESS, 0,
(LPARAM)(LPDWORD)&CurAddress);
BYTE
BYTE
BYTE
BYTE
IPPart1 = FIRST_IPADDRESS((LPARAM)CurAddress);
IPPart2 = SECOND_IPADDRESS((LPARAM)CurAddress);
IPPart3 = THIRD_IPADDRESS((LPARAM)CurAddress);
IPPart4 = FOURTH_IPADDRESS((LPARAM)CurAddress);
}
//---------------------------------------------------------------------------
495
496
Handle,
NULL,
HInstance,
NULL);
SendMessage(hWndIPAddress, IPM_SETRANGE, 2, wRangeForField3);
}
//---------------------------------------------------------------------------
Normally, you will usually need to control the range of values for a field in response to
the users entry in the provious field. This assignment is not handled by the control
because it lets the programmer take this role and responsibility.
497
498
2.
3.
4.
Change the Caption of the form to Notice and change its Name to frmMain
5.
to the
From the Win32 tab of the Component Palette, add an ImageList control
form and fill it with the following bitmaps: New, Open, Save, Exit, Undo, Cut, Copy,
and Paste from the resources that accompany this book. Close the ImageList Editor
using the OK button
6.
7.
Right-click the Menu Designer window and click Insert From Template. Doubleclick Edit Menu. Delete the following menu items: Repeat, Paste Special, Go To,
the separator, Link, and Object
8.
Click the Edit menu item to select it. Right-click an empty area in the Menu Editor
window again and click Insert From Template. Double-click File Menu
9.
In the Menu Designer, use your intuition to set the BitmapIndex value of each menu
item that can use one and close the Menu Editor:
499
10. From the Win32 tab of the Component Palette, click the Toolbar button
and
click in the middle of the form. On the Object Inspector, set its properties as follows:
Height: 40
Name: tbrStandard
ShowCaptions: true
Images: ImageList1
11. Right-click the toolbar and click New Button. Set its MenuItem property to New1.
Continue right-clicking and changing the MenuItem properties to produce the
following toolbar:
500
you want to prevent the user from altering the text in the memo, set the ReadOnly
property to false. You can also do this programmatically.
When a memo box opens, the compiler registers the content of the control. If the user has
the ability to change the text in the control and if the user changes it, the compiler flags
the control as Modified. This allows you to take actions. You can acknowledge this by
programmatically setting the Modified property to true. If another control or some other
action alters the contents of the memo, you can make sure that this property reflects the
change. You can change this programmatically as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Memo1KeyPress(TObject *Sender, char &Key)
{
Memo1->Modified = True;
}
//---------------------------------------------------------------------------
Although the user can enter any number of characters into a memo box, you can set a
maximum number that would prevent the user from going over this number of characters.
At design time, you can set the maximum number of characters in the MaxLength field.
Programmatically, you can change it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Memo1KeyPress(TObject *Sender, char &Key)
{
Memo1->MaxLength = 24;
}
//---------------------------------------------------------------------------
If the control will be used to enter text, the user can press Enter at the end of a line to
move to the next line. This ability is controlled by the Boolean WantReturns property.
By default, this property is set to true, which means the user can press Enter when typing
text. If you do not want to validate the Enter key in the Memo control, set this property to
false. To set it programmatically, assign the desired value to the WantReturns property:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Memo1->WantReturns = False;
}
//---------------------------------------------------------------------------
When the WantReturns property is set to false, if the user presses Enter while the memo
has focus, the Enter action would be transferred to the form as the parent. The form in
turn can find out what to do. For example, you may have configured the form to perform
a particular action when the Enter key is pressed. The typical example is by setting a
buttons Default property to true. In this case, pressing Enter from the Memo control
would cause the action associated with the button. Even if the WantReturns of a memo
is set to false, the user can still press Ctrl + Enter to move to the next line.
The user is accustomed to pressing Tab to insert tab characters in the text. By default,
when the user presses Tab when interacting with your application, the focus moves from
one control to the next, following the TabOrder values of the form. Even when using a
memo to perform text editing, if the user presses Tab, the focus would switch to another
control or to the form. If you want a memo to receive focus when the user presses the Tab
key, set the WantTabs property from false (the default), to true.
Copyright 2003 FunctionX, Inc.
501
When entering text in a Memo control, the characters start on the left side of the memo
and are subsequently added on the right side. The ability to align text is controlled by the
Alignment property. For a Memo control, the alignment is configured using the
TAlignment enumerator:
enum TAlignment { taLeftJustify, taRightJustify, taCenter };
On the form, click the Memo1 control to select it. On the Object Inspector, set its
Align property to alClient
2.
Double-click the box to the right side of the Lines to display the String List Editor
3.
4.
5.
Make sure the WantTabs property is set to true and set the WantTabs property to
true
502
Notes->Top = 20;
Notes->Height = 120;
Notes->Width = 320;
}
//---------------------------------------------------------------------------
The other operations the user can perform on a memo can be controlled by methods such
as Clear() or SelectAll() that we reviewed for the edit control and they function the same.
The TMemo class natively supports regular operations performed on a text control such
as undoing an action, cutting or copying text from the control, pasting text from another
control. The methods used to accomplish these assignments are Undo(),
CutToClipboard(), CopyToClipboard(), PasteFromClipboard().
Right-click anywhere on the form and click Tab Border. In the Edit Tab Order dialog
box, move mmoNotice to the top of the list unless it is set already and click OK
2.
On the main menu of the form, click Edit -> Undo and implement the OnClick event
as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::Undo1Click(TObject *Sender)
{
mmoNotice->Undo();
}
//---------------------------------------------------------------------------
3.
On the main menu of the form, click Edit -> Cut and implement the OnClick event
as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::Cut1Click(TObject *Sender)
{
mmoNotice->CutToClipboard();
}
//---------------------------------------------------------------------------
4.
On the main menu of the form, click Edit -> Copy and implement the OnClick event
as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::Copy1Click(TObject *Sender)
{
mmoNotice->CopyToClipboard();
}
//---------------------------------------------------------------------------
5.
On the main menu of the form, click Edit -> Paste and implement the OnClick event
as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::Paste1Click(TObject *Sender)
{
mmoNotice->PasteFromClipboard();
}
//---------------------------------------------------------------------------
6.
503
7.
2.
To save it, on the Standard toolbar, click the Save All button
3.
Create a new folder called Editor1 and display it in the Save In combo box
4.
5.
Change the Caption of the form to Editor Untitled and change its Name to
frmMain
6.
7.
504
{
__published: // IDE-managed Components
private:
AnsiString CurrentFileName;
// User declarations
public:
// User declarations
__fastcall TfrmMain(TComponent* Owner);
};
//--------------------------------------------------------------------------extern PACKAGE TfrmMain *frmMain;
//--------------------------------------------------------------------------#endif
8.
9.
Display the form and double-click its body to access its OnCreate() event
10. Because we will use the features of the rich edit control stored in a DLL, implement
the event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
int iRichDLL = (int)LoadLibrary("RICHED20.DLL");
if( !iRichDLL ) // If you could not load RICHED20.DLL
{
ShowMessage("Could not load the RICHED20.DLL");
Application->Terminate();
return;
}
}
//---------------------------------------------------------------------------
and, on
505
13. Using the Add button, add the following bitmaps: New, Save, Redo, NumBullet,
Mail, FirstIndent, IndentLeft, IndentRight, and Paragraph
14. Click OK
15. From the Standard tab of the Component Palette, double-click ActionList
and,
while the new ActionList1 is still selected, on the Object Inspector, set its Images
property to ImageList1
16. On the form, double-click ActionList1 to create a list of actions
17. In the ActionList window, right-click an empty area and click New Action. Set its
properties as follows:
Caption: &New
Category: File
Hint: New|Create a new document
ImageIndex: 0
Name: FileNew
Shortcut: Ctrl+N
18. In the same way, create a new action and set its properties as follows:
Caption: &Save
Category: File
Hint: Save|Save the current document
ImageIndex: 1
Name: FileSave
Shortcut: Ctrl+S
19. Once again, create a new action and set its properties as follows:
Caption: &Redo
Category: Edit
Hint: Redo|Redo the previous action
ImageIndex: 2
Name: EditRedo
Shortcut: Ctrl+Y
20. Add Another action and set its properties as follows:
Caption: &Numbering
Category: Format
Hint: Numbering|Format a numeric list
ImageIndex: 3
Name: FormatNbr
21. Add one more action and set its properties as follows:
Caption: &Numbering
Category: Format
Hint: Numbering|Format a numeric list
ImageIndex: 3
Name: FormatNbr
22. Add one more action and set its properties as follows:
Caption: &Standard
Category: View
Checked: true
Hint: Standard|Toggle the Standard toolbar
Name: ViewStandard
23. Add one more action and set its properties as follows:
Caption: &Formatting
Category: View
506
Checked: true
Hint: Formatting|Toggle the Formatting toolbar
Name: ViewFormatting
24. Add one more action and set its properties as follows:
Caption: Status &Bar
Category: View
Checked: true
Hint: Status Bar|Toggle the Status Bar
Name: ViewStatusBar
25. Right-click any frame in the Action List Editor and click New Standard Action.
From the Standard Action Classes dialog box, under the Internet node, double-click
TSendMail
26. In the left frame of the ActionList Editor, click Internet and, in the right frame, click
SendMail1. On the Object Inspector, set ImageIndex to 4
27. Right-click an empty area in the ActionList Editor and click New Standard Action.
Once in the Standard Action Classes dialog box, click Edit. Press and hold Shift.
Then click TRichEditAlignCenter and release Shift:
28. Click OK
29. Right-click in the ActionList Editor again and click New Standard Action. Click
TFileOpen. Press and hold Ctrl. Then click TFileSaveAs, TFilePrintSetup, TFileExit,
TSearchFind, TSearchFindNext, TSearchReplace, TSearchFindFirst, TColorSelect,
TFontEdit, and TPrintDlg. Release Ctrl and click OK
30. In the ActionList Editor window, on the left frame, click File. In the right frame,
click FileSaveAs1.
31. In the Object Inspector, click the + button of Dialog to expand it.
32. Click DefaultExt and type rtf
507
35. Click OK
36. Click Title, type Save File As and press Enter
37. Click the - button of Dialog to collapse it and click the Events property page
38. Double-click the empty box on the right side of OnAccept() and implement the
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FileSaveAs1Accept(TObject *Sender)
{
// Save the file as we review in the lesson on strings
rchEditor->Lines->SaveToFile(FileSaveAs1->Dialog->FileName);
// Change the name of the file
CurrentFileName = FileSaveAs1->Dialog->FileName;
// Retrieve the name of the file to display on the title bar
AnsiString FName = ExtractFileName(CurrentFileName);
Caption = "Editor - " + FName;
}
//---------------------------------------------------------------------------
508
509
//---------------------------------------------------------------------------
45. Press F12 to display the form. Click the form to select it
46. To handle the closing of the application, click the Events tab. Then, double-click the
empty field of the OnClose event and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormClose(TObject *Sender, TCloseAction &Action)
{
// Is the document dirty?
if( rchEditor->Modified )
{
// Since the document is dirty, find out if the user wants to save it
int Response = Application->MessageBox(
"The document has changed. Do you want to save it?"
"\nClick\n"
"Yes:\tTo save the document and close the application.\n"
"No:\tNot to save the document but close the application.\n"
"Cancel:\tNot to do anything",
"Editor - Saving a File",
MB_YESNOCANCEL | MB_ICONQUESTION);
// If the user wants to save it
if( Response == IDYES )
{
// Behave as if the user had clicked File -> Save
FileSave1Execute(Sender);
// Free the action
Action = caFree;
}
// If the user doesn't want to save the document
else if( Response == IDNO )
Action = caFree; // Simply free the action
else
Action = caNone; // The user cancelled the action: do nothing
}
else // There is no action to take
Action = caFree;
}
//---------------------------------------------------------------------------
47. Display the ActionList Editor. Make sure that File is selected on the left frame
48. To handle the creation of a new document, click the FileNew. In the Object
Inspector, click the Events tab if necessary. Double-click the empty box on the right
side of OnExecute and implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FileNewExecute(TObject *Sender)
{
// When the user clicks the New button to start a new document,
// find out if the document is dirty
if( rchEditor->Modified )
{
// Since the document is dirty, find out if the user wants to save it
int Response = Application->MessageBox(
"The document has changed. Do you want to save it?"
"\nClick\n"
"Yes:\tTo save the document and create a new one.\n"
"No:\tNot to save the document but create a new one.\n"
510
"Cancel:\tNot to do anything",
"Editor - Saving a File",
MB_YESNOCANCEL | MB_ICONQUESTION);
// If the user wants to save the document
if( Response == IDYES )
{
// Behave as if the Save action was initiated
FileSave1Execute(Sender);
// After saving the document, delete the whole document
rchEditor->Clear();
// Reset the current file name to garbage
CurrentFileName = "<Not Allowed>";
// Show on the title bar that the file is clean
Caption = "Editor - Untitled";
// Reset the Modified flag of the Rich Edit control
rchEditor->Modified = False;
}
// If the user doesn't want to save the document
else if( Response == IDNO )
{
// Delete the whole contents of the rich edit text
rchEditor->Clear();
// Fill the file name with garbage
CurrentFileName = "<Not Allowed>";
// Show on the title bar that the document is clean
Caption = "Editor - Untitled";
// Reset the Modified flag
rchEditor->Modified = False;
}
// The user clicked Cancel: Don't do nothing
else
Action = caNone;
}
else
{
rchEditor->Clear();
CurrentFileName = "<Not Allowed>";
Caption = "Editor - Untitled";
rchEditor->Modified = False;
}
}
//---------------------------------------------------------------------------
49. In the ActionList Editor, in the right frame, click FileOpen1. Move FileOpen1 to be
just under FileNew1 in the Actions list
50. In the Object Inspector, click the Properties tab and expand the Dialog property if
necessary. Click DefaultExt and type rtf
51. Click Filter and click its ellipsis button
follows:
511
52. Click OK
53. Click Title, type Open an Existing Document and press Enter.
54. Click the - button of Dialog to collapse it and click the Events tab
55. Double-click the event field of OnAccept and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FileOpen1Accept(TObject *Sender)
{
// Before opening a new document, find out if the current one is dirty
if ( rchEditor->Modified )
{
// Since the document is dirty, prompt the user to save
int Response = Application->MessageBox(
"The document has changed. Do you want to save it?"
"\nClick\n"
"Yes:\tTo save the document and open the new one.\n"
"No:\tNot to save the document but open the new one.\n"
"Cancel:\tNot to do anything",
"Editor - Saving a File",
MB_YESNOCANCEL | MB_ICONQUESTION);
// If the user wants to save the current document
if( Response == IDYES )
{
// Behave as if the Save action was initiated
FileSaveExecute(Sender);
rchEditor->Lines->LoadFromFile(FileOpen1->Dialog->FileName);
CurrentFileName = FileOpen1->Dialog->FileName;
AnsiString FName = ExtractFileName(CurrentFileName);
Caption = "Editor - " + FName;
rchEditor->Modified = False;
}
// If the user doesn't want to save the document
else if( Response == IDNO )
{
// Open the new document after letting the user select it
rchEditor->Lines->LoadFromFile(FileOpen1->Dialog->FileName);
// Change the file name of our archives
CurrentFileName = FileOpen1->Dialog->FileName;
// Get the name of the file that the user selected
512
}
}
// Apparently the current document is not dirty
else
{
rchEditor->Lines->LoadFromFile(FileOpen1->Dialog->FileName);
CurrentFileName = FileOpen1->Dialog->FileName;
AnsiString FName = ExtractFileName(CurrentFileName);
Caption = "Editor - " + FName;
rchEditor->Modified = False;
}
}
//---------------------------------------------------------------------------
56. Close the Action Editor window. Save All and display the form
57. From the Standard tab of the Component Palette, click the MainMenu
and click the form.
button
58. On the Object Inspector, click the Properties tab and set the Images of the
MainMenu1 item to ImageList1
59. On the form, double-click MainMenu1.
60. Right-click an empty area in the Menu Designer and click Insert From Template.
In the Insert Template dialog box, double-click File Menu
61. Click the last item under Exit and, on the Object Inspector, set its Action property to
SendMail1
62. Set the caption of the empty box under SendMail to - to add a separator. Move the
Send Mail menu item and its separator above the Exit menu item
63. Set the Actions to the menu items as follows: New = FileNew, Open = FileOpen1,
Save = FileSave, Save As = FileSaveAs1, Print = PrintDlg1, Print Setup =
FilePrintSetup1, Exit = FileExit1
513
514
and click
and
79. On the Object Inspector, set the Images property of the toolbar to ImageList1. Set its
Flat property to true and set its Height to 24. Set the Name to tbrStandard
80. Right-click the toolbar, position the mouse on Edit and click Copy.
81. Right-click an empty area on the form, position your mouse on Edit and click Paste.
515
82. While the new toolbar is still selected, on the Object Inspector, change its Name to
tbrFormatting
83. Right-click the top toolbar and click New Button. Set its Action property to FileNew.
Add other buttons and set their Action values to FileOpen1, FileSave, a separator,
EditCut1, EditCopy1, EditPaste1, a separator, EditUndo1, EditRedo, a separator, and
PrintDlg1:
88. From the Win32 tab of the Component Palette, click StatusBar
empty area on the form
and click an
516
98. Press F12 to get back to the form. Double-click in an unoccupied area of the form
and change its OnCreate event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
int iRichDLL = (int)LoadLibrary("RICHED20.DLL");
if( !iRichDLL ) // If you could not load RICHED20.DLL
{
ShowMessage("Could not load the RICHED20.DLL");
Application->Terminate();
return;
}
Application->OnHint = ShowHints;
517
}
//---------------------------------------------------------------------------
101. In the right frame of the ActionList Editor, double-click ViewFormatting and
implement its OnExecute event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::ViewFormattingExecute(TObject *Sender)
{
tbrFormatting->Visible = !tbrFormatting->Visible;
ViewFormatting->Checked = !ViewFormatting->Checked;
}
//---------------------------------------------------------------------------
102. In the right frame of the ActionList Editor, double-click ViewStatusBar and
implement its OnExecute event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::ViewStatusBarExecute(TObject *Sender)
{
StatusBar->Visible = !StatusBar->Visible;
StatusBar1->Checked = !StatusBar1->Checked;
}
//---------------------------------------------------------------------------
518
519
assign the desired value to the Alignment property. In the following examples, the
alignment of the selected paragraph is set in response to the user clicking one of the
buttons:
//--------------------------------------------------------------------------void __fastcall TForm1::btnAlignLeftClick(TObject *Sender)
{
rchEditor->Paragraph->Alignment = taLeftJustify;
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnCenterClick(TObject *Sender)
{
rchEditor->Paragraph->Alignment = taCenter;
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnAlignRightClick(TObject *Sender)
{
rchEditor->Paragraph->Alignment = taRightJustify;
}
//---------------------------------------------------------------------------
Indentation consists of pushing a paragraph from one of the margins of the document.
The TParaAttributes class allows you to indent the first line of a paragraph using the
FirstIndent property. It is an integer value that sets or controls the amount of indentation
of the first line. The LeftIndent property is an integer number that controls how much a
paragraph is indented from the left margin of the document. The RightIndent value
controls a similar indentation from the right margin.
To create an unordered list of items in your document, you can use the
TParaAttributes::Numbering property. This property controls the assignment of a
bulleted list. You have two options, If the paragraph is a regular one and you want to
create a list, assign the nsBullet value to the Numbering property. If the paragraph is
already a list but you want to convert it into a regular paragraph, assign the nsNone value
to the property. Here is an example that applies when the user clicks a button called
BulletList:
//--------------------------------------------------------------------------void __fastcall TForm1::BulletList1Click(TObject *Sender)
{
if( rchEditor->Paragraph->Numbering == nsBullet )
rchEditor->Paragraph->Numbering = nsNone;
else
rchEditor->Paragraph->Numbering = nsBullet;
}
//---------------------------------------------------------------------------
One of the main characteristics of a rich text is the ability to set or control individual
characteristics of sections of its text. The rich characteristics of text are controlled by the
TTextAttributes class. This property allows you to change the font, its color, size,
and/or style. To manipulate the text attributes, the text must be selected first. This means
that the change applies only if there is a formal selection. For example, you can set or
change the Bold style of the selected text when the user clicks a button called btnBold as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnBoldClick(TObject *Sender)
520
if( btnBold->Down )
rchEditor->SelAttributes->Style = TFontStyles() << fsBold;
else
rchEditor->SelAttributes->Style = TFontStyles() >> fsBold;
}
//---------------------------------------------------------------------------
Besides the proper characteristics of a rich text, the TRichEdit also shares or inherits the
characteristics of a memo or an edit control.
Borland C++ Builder simplifies the configuring of a RichEdit control through the use of
an ActionList control. The ActionList has many attributes already configured to
seamlessly function with a RichEdit present on a form.
2.
On the Object Inspector, change its Align property to alClient. Set HideSelection to
false. Double-click the right field to Lines, delete the text and click OK. Set the
PopupMenu property to PopupMenu1. Set the WantTabs property to true.
3.
In the Object Inspector, click the Events tab. Display the ActionList Editor. In the
left frame, click Dialog. In the right frame, click FontEdit1. In the Object Inspector,
double-click on the right field to BeforeExecute
4.
5.
Go back to the ActionList Editor. On the Events tab of the FontEdit1, double-click
the right field of OnAccept and implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FontEdit1Accept(TObject *Sender)
{
// Do the inverse of the BeforeExecute event
// If the user clicks OK, get the characteristics of the font
// Apply them to the selected text of the Rich Edit control
rchEditor->SelAttributes->Name = FontEdit1->Dialog->Font->Name;
rchEditor->SelAttributes->Style = FontEdit1->Dialog->Font->Style;
rchEditor->SelAttributes->Size = FontEdit1->Dialog->Font->Size;
rchEditor->SelAttributes->Color = FontEdit1->Dialog->Font->Color;
}
//---------------------------------------------------------------------------
6.
Press F12 to access the form. On the form, double-click MainMenu1. Click the box
on the right side of View. In the Object Inspector, click the Properties tab and click
Caption. Type F&ormat and press Enter.
521
7.
On the Menu Designer, click Format. Under the Format menu, click the empty box
and, on the Object Inspector, set its Action to FontEdit1
8.
9.
On the bottom toolbar, double-click the FirstIndent button and implement its
OnClick event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnFirstIndentClick(TObject *Sender)
{
rchEditor->Paragraph->FirstIndent += 10;
}
//---------------------------------------------------------------------------
10. On the bottom toolbar, double-click the IndentLeft button and implement its OnClick
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnIndentLeftClick(TObject *Sender)
{
rchEditor->Paragraph->LeftIndent += 10;
}
//---------------------------------------------------------------------------
11. On the bottom toolbar, double-click the IndentRight button and implement its
OnClick event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnIndentRightClick(TObject *Sender)
{
rchEditor->Paragraph->RightIndent += 10;
}
//---------------------------------------------------------------------------
522
Display the ActionList Editor and, on the left frame, click Dialog. On the right
frame, click PrintDlg. On the Object Inspector, click the Events tab and double-click
the event side of the OnAccept field
2.
Implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::PrintDlg1Accept(TObject *Sender)
{
rchEditor->Print(CurrentFileName);
}
//---------------------------------------------------------------------------
3.
Display the ActionList Editor. In the left frame, click Edit. In the right frame,
double-click EditRedo and implement its OnExecute() event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::EditRedoExecute(TObject *Sender)
{
SendMessage(rchEditor->Handle, EM_REDO, 0, 0);
}
//---------------------------------------------------------------------------
4.
On the main menu, click File -> New -> Other In the New Items dialog box, click
the Dialogs tab. Click Standard Dialog (Vertical) and click OK
5.
On the Object Inspector, change its Name to dlgFont and change its Caption to Font
6.
Save it as Font
7.
523
BYTE bReserved1;
} CHARFORMAT2;
To use this structure, declare a variable of it and use its cbSize member variable to
specify its size. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
CHARFORMAT2 cfm2;
cfm2.cbSize = sizeof(CHARFORMAT2);
}
//---------------------------------------------------------------------------
Once the compiler is aware of the size of the structure, use the dwMask member variable
to specify the type of formatting you want to perform. Formatting examples include all
caps, bold, italic, subscript, etc.
After selecting the type of formatting that will applied, initialize the dwEffects member
variable to the corresponding format. (Microsoft has highly improved the documentation
on the RichEdit control libraries so much that, to save space on the book, we will not
repeat that documentation here. Instead, we will provide examples).
Display the main form. Double-click ImageList1. From the resources that
accompany this book, add the AllCaps,
2.
3.
Right-click it again and click New Button. Set its ImageIndex to 29 (AllCaps).
Change its Name to btnAllCaps. Double-click it again and implement its OnClick
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnAllCapsClick(TObject *Sender)
{
Richedit::CHARFORMAT2 cfm2;
cfm2.cbSize = sizeof(cfm2);
cfm2.dwMask = CFM_ALLCAPS;
cfm2.dwEffects = CFE_ALLCAPS;
SendMessage(RichEdit1->Handle, EM_SETCHARFORMAT,
static_cast<WPARAM>(SCF_SELECTION),
reinterpret_cast<LPARAM>(&cfm2));
}
//---------------------------------------------------------------------------
4.
524
To use it, declare a PARAFORMAT2 variable and use the cbSize member variable to
specify the size of the structure. After this, use the dwMask to specify the type of
formatting you want to perform.
Make sure the Editor project you created is still opened. Display the main form and
double-click MainMenu1
2.
In the Menu Designer, click Format and click the first empty box under the Format
menu. On the Object Inspector, click Caption and type &Paragraph...
3.
Set the ImageIndex of the Paragraph menu item to 8 and close the Menu Designer
4.
To use one of the dialog templates, on the main menu of C++ Builder, click File ->
New -> Other... On the New Items dialog box, click Dialogs and double-click
Standard Dialog (Horizontal)
5.
While the new dialog box is still selected, on the Object Inspector, change its
Caption value to Paragraph and change its Name property to dlgParagraph
6.
Save it as Paragraph
7.
From the Standard tab of the Component Palette, add three labels widht captions as
&Left:, &Right:, and &First Line:
8.
Add an edit box on the right side of each of the last three labels. Change their names
to edtLeft, edtRight, edtFirstLine. Position and resize the controls as you see fit:
525
Right-click anywhere on the dialog box and click Tab Order. Arrange the controls
sequence in the following order: edtLeft, edtRight, edtFirstLine, OKBtn, and
CancelBtn:
526
edtRight->Text
= frmMain->rchEditor->Paragraph->RightIndent;
edtFirstLine->Text = frmMain->rchEditor->Paragraph->FirstIndent;
}
//---------------------------------------------------------------------
18. Display the ActionList Editor. In the left frame, click Format. In the right frame,
double-click FormatNbr, and implement its OnExecute() event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::ToolButton26Click(TObject *Sender)
{
PARAFORMAT2 pfm2;
pfm2.cbSize = sizeof(pfm2);
pfm2.dwMask = PFM_NUMBERING;
pfm2.wNumbering = 3;
SendMessage(RichEdit1->Handle, EM_SETPARAFORMAT,
0, reinterpret_cast<LPARAM>(&pfm2));
}
//---------------------------------------------------------------------------
527
you can specify a natural number as the Top property. The TFindDialog object provides
options to control the availability of the check boxes and radio buttons of the dialog box.
Display the ActionList Editor. In the left frame, click Search. In the right frame,
click SearchFind1
2.
On the Object Inspector, in the Properties tab, expand Dialog and expand Options.
Set the frFindNext property to true
3.
click Events. Under the expanded Dialog, double-click the right empty field to
OnFind and implement its event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::SearchFind1FindDialogFind(TObject *Sender)
{
int MatchPos, StartPos, EndPos;
if( rchEditor->SelLength )
StartPos = rchEditor->SelStart + rchEditor->SelLength;
else
StartPos = 0;
EndPos = rchEditor->Text.Length() - StartPos;
MatchPos = rchEditor->FindText(SearchFind1->Dialog->FindText,
StartPos,
EndPos,
TSearchTypes() << stMatchCase);
if( MatchPos != -1 )
{
rchEditor->SelStart = MatchPos;
rchEditor->SelLength = SearchFind1->Dialog->FindText.Length();
}
}
//---------------------------------------------------------------------------
6.
529
To replace a word or an expression, the user first displays the Replace dialog. It is
equipped with two text boxes. In the Find What text box, the user would type the word or
the expression that should be searched in the whole text. In the Replace With edit box,
the user can type another word or an expression that would replace a possible match of
the Find What string. The user can proceed as if she were using the Find dialog box and
click the Find Next button to find a match in the document. If a match is found, the user
can click Replace to replace the matched word or expression. If the Replace edit box is
empty, the match would be deleted. On the other hand, if the Replace With edit box
contains a string, upon clicking Replace, the matched text would be replaced by the
Replace With string. After the text has been found or replaced, the dialog box would
attempt to find the next match. If the user wants to replace all occurrences of the Find
What string, she can click Replace All.
At any time, the user can click Cancel to dismiss the dialog box or continue working in
the background text without necessarily closing the dialog because it is modeless.
2.
In the Properties tab of the Object Inspector, under the expanded Options, set the
frFindNext to true
3.
Click the Events tab and double-click the empty field on the right side of OnFind
4.
530
if( rchEditor->SelLength )
StartPos = rchEditor->SelStart + rchEditor->SelLength;
else
StartPos = 0;
EndPos = rchEditor->Text.Length() - StartPos;
MatchPos = rchEditor->FindText(SearchReplace1->Dialog->FindText,
StartPos,
EndPos,
TSearchTypes() << stMatchCase);
if( MatchPos != -1 )
{
rchEditor->SetFocus();
rchEditor->SelStart = MatchPos;
rchEditor->SelLength = SearchReplace1->Dialog->FindText.Length();
}
}
//---------------------------------------------------------------------------
5.
On the Object Inspector, in the Events tab, double-click the box on the right side of
OnReplace and implement its event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::SearchReplace1ReplaceDialogReplace(TObject *Sender)
{
// Find out if the Find What text is selected in the rich edit control
if( rchEditor->SelText == SearchReplace1->Dialog->FindText )
{
// Since a match was found, get ready to replace it with the content
// of the Replace With edit box
// First find out if the user clicked the Replace button
if( SearchReplace1->Dialog->Options.Contains(frReplace) )
{
// Since the user clicked Replace, replace only the selection
rchEditor->SelText = SearchReplace1->Dialog->ReplaceText;
// Perform a new search
SearchReplace1ReplaceDialogFind(Sender);
}
// Find out if the user clicked Replace All instead
else if( SearchReplace1->Dialog->Options.Contains(frReplaceAll) )
{
// Since the user clicked Replace All, replace all occurrences
// of the Find What edit box with the Replace With edit box
do {
// Find an occurrence and replace it
rchEditor->SelText = SearchReplace1->Dialog->ReplaceText;
// Find another occurrence before repeating
SearchReplace1ReplaceDialogFind(Sender);
} while( !rchEditor->SelText.IsEmpty() );
// Let the user know that all occurrences have been replaced
ShowMessage("No more text to replace");
}
}
else // If no text is selected or none was matched, let the user know
ShowMessage("No text to replace");
}
531
//---------------------------------------------------------------------------
6.
532
2.
3.
4.
Change the Caption of the form to Color Previewer and change its name to
frmMain
5.
6.
533
7.
8.
9.
In C++ Builder, access the project options (Project -> Options) dialog box
10. Change the Title to Color Previewer and set the icon to the above
11. Click OK
534
12. From the Standard tab of the Component Palette, click Panel
panel in the top section of the form. Delete its caption
The UpDown control appears with two arrow buttonss pointing up and down,
respectively. This feature is controlled by the Orientation property. An alternative is to
point the arrows to left and right. To do this, use the Orientation property to set the
arrows to your liking.
Probably the most important piece of information you would need from an updown
control is the value it is holding at a particular time. As mentioned already, the updown
control navigates from a minimum to a maximum values. The values of the control are
Copyright 2003 FunctionX, Inc.
535
short integer numbers. These numbers range from a minimum controlled by the Min
property to a maximum value controlled by the Max property. By default, a freshly added
UpDown control on a from has its Min and Max values set to 0 and 100 respectively.
You can set the minimum value of the control to 32768 and the maximum to 32767.
These values are set using the Min and Max fields of the Object Inspector. You can
change them programmatically as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
UpDown1->Min = -224;
UpDown1->Max = -1;
}
//---------------------------------------------------------------------------
If you use numbers in the thousands, the control that accompanies the UpDown (such as
the edit control) will display the values using the comma to separate the thousands. This
is because the UpDown control is configured, by default, to separate the thousands. If
you do not want this feature, change the value of the Thousands property from true to
false.
When using the UpDown button, the user clicks one of the arrows of the control to
increase or decrease the value. By default, the value increases or decreases by 1. If you
want the value to augment by more than 1, set an integer value using the Increment
property. To set the Increment value programmatically, you can use code as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
UpDown1->Max = 125;
UpDown1->Min = 10;
UpDown1->Increment = 5;
}
//---------------------------------------------------------------------------
When an UpDown control is accessed, the value it holds can be set by its Position. You
can use this property to specify what value the control would use at startup. It should be
an integer between the Min and the Max values. You can also set it programmatically as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
UpDown1->Max = 125;
UpDown1->Min = 10;
UpDown1->Increment = 5;
UpDown1->Position = 55;
}
//---------------------------------------------------------------------------
The Position property also allows you to find out the value of the UpDown control at any
time. After setting the Increment value, when the user clicks the arrow buttons, the value
would increase accordingly. When the maximum value is reached, the control would use
the Wrap property to find out what to do:
536
If the Wrap Boolean property is set to false (the default), the increment would stop
at the Max value even if Max is not divisible by the Increment value. The same
goes for the Min value
If the Wrap property is set to true and if the user increases the value of the control,
the incrementing would stop to the last value divisible by the Increment value but
less than the Max. The same would apply when decrementing the value of the
control.
As mentioned already, an updown control does not visually display its value. Therefore,
you can add a text-based or other control to it. This accompanying object is specified
using the Associate property. The associated control would display the current value of
the UpDown control. To associate a control to the UpDown control, first create or add the
desired control to your application. Then, at design time on the Object Inspector, you can
click the Associate field to display its combo box. Click the arrow and select the desired
control. You can also associate a control programmatically using code such as this:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
UpDown1->Associate = Edit1;
}
//---------------------------------------------------------------------------
The UpDown control usually has its associated control on the left side. This is controlled
by the AlignButton property. Alternatively, you can ask it to have the accompanying
control on its right side by setting the AlignButton property to udRight. At design time,
both controls still display as they are designed. If you change the AlignButton value, the
control would apply the value only at runtime.
One of the nicest features that make the UpDown button easy to use is that the user can
change its values by pressing the up and down arrow keys of the keyboard. This ability is
by default set to true from the Boolean ArrowKeys property. If you want to prevent the
user from using the keyboard to increase or decrease the value of the UpDown control,
set the ArrowKeys property to false.
From the Standard tab of the Component Palette, click the Edit control
click under the panel on the form
2.
Change the name of the new edit control to edtRed and set the content of the Text
field to 192
3.
4.
5.
6.
7.
Make sure the Increment field display 1. Change the Max value to 255 and accept
the Min value as 0
8.
and
537
9.
On the form, select both the edit and the updown controls. Press Ctrl + C to top.
Then press Ctrl + V to paste
538
Counter->Associate = Displayer;
}
//---------------------------------------------------------------------------
On the form, double-click the most left updown control to access its OnClick event
2.
3.
4.
539
pnlPreview->Color = CurrentColor;
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::updGreenClick(TObject *Sender, TUDBtnType Button)
{
TColor CurrentColor = TColor(RGB(updRed->Position,
updGreen->Position,
updBlue->Position));
pnlPreview->Color = CurrentColor;
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::updBlueClick(TObject *Sender, TUDBtnType Button)
{
TColor CurrentColor = TColor(RGB(updRed->Position,
updGreen->Position,
updBlue->Position));
pnlPreview->Color = CurrentColor;
}
//---------------------------------------------------------------------------
5.
540
The application we are about to develop is for a CD publishing small business. This
company manufactures compact discs for self-promoting musicians and small business
that want to sell their own CDs. When taking an order of a new CD, the company
charges:
2.
3.
4.
5.
6.
541
Control
Bevel
Label
Edit
Label
Edit
Label
Edit
Button
7.
Caption or Text
Number of Items:
0
Unit Price
20.00
Total Price:
0.00
Kind = bkClose
Name
edtQuantity
EdtUnitPrice
EdtTotal
Save all
542
1.
2.
Click CspinButton
the form
3.
4.
Display the header file of the form. In the private section of the header file, declare a
Value variable of type int. Also, declare a method named EvaluatePrice() of type
void that uses __fastcall:
private:
int Value;
void __fastcall EvaluatePrice();
5.
// User declarations
In the forms source file, initialize the value to 0 and implement the new method as
follows:
//--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
Value = 0;
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::EvaluatePrice()
{
int Quantity;
double UnitPrice, TotalPrice;
Quantity = edtQuantity->Text.ToInt();
if( Quantity < 20 )
UnitPrice = 20;
else if( Quantity < 50 )
UnitPrice = 15;
else if( Quantity < 100 )
UnitPrice = 12;
else if( Quantity < 500 )
UnitPrice = 8;
else
UnitPrice = 5;
TotalPrice = Quantity * UnitPrice;
edtUnitPrice->Text = edtUnitPrice->Text.FormatFloat("#,##0.00", UnitPrice);
edtTotal->Text = edtTotal->Text.FormatFloat("#,##0.00", TotalPrice);
}
//---------------------------------------------------------------------------
6.
On the form, click the SpinButton control to select it. On the Object Inspector, click
the Events tab
7.
Double-click the empty field on the right side of OnUpClick and implement the
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::spnQuantityUpClick(TObject *Sender)
{
edtQuantity->Text = Value++;
EvaluatePrice();
}
//---------------------------------------------------------------------------
8.
On the Object Inspector, double-click the field of the OnDownClick event and
implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::spnQuantityDownClick(TObject *Sender)
{
if( Value > 0 )
edtQuantity->Text = Value--;
543
else
edtQuantity->Text = 0;
EvaluatePrice();
}
//---------------------------------------------------------------------------
9.
To test the form, on the main menu, click Run -> Run
position of the SpinEdit control, use the Value property. You can control it at design time
or at runtime. If you set the Value property to a value that is less than the MinValue, the
value would be automatically set to the minimum value. If you try to set it to a value
greater than the MaxValue in the Object Inspector, the Value would be reset to the
MaxValue.
You must include the CSPIN.h header file to the form or unit that would be using the
Spin Edit control.
545
546
1.
2.
3.
4.
Open Image Editor and design 32 x 32 pixels icon and its 16 x 16 associated icon as
follows:
5.
6.
Using the Project Options dialog box, set the Title to Car Inventory and set the icon
as the above
Copyright 2003 FunctionX, Inc.
7.
Change the name of the form to frmMain and change its caption to Car Inventory
8.
9.
From the resources that accompany this book, copy the following files from the
Pictures folder to the folder of the current project: Civic, Elantra, Escort, Focus,
GdMarquis, E350, Navigator, Sentra, and Rio
10. In the header file of the form, create a structure named TCarInventory and declare
its array variable named Car that contains 10 items. Also declare:
//--------------------------------------------------------------------------struct TCarInventory
{
AnsiString Make;
AnsiString Model;
unsigned int CarYear;
unsigned int Doors;
Graphics::TBitmap *CarPicture;
};
//--------------------------------------------------------------------------class TfrmMain : public TForm
{
__published: // IDE-managed Components
private:
TCarInventory Car[10]; // User declarations
public:
// User declarations
__fastcall TfrmMain(TComponent* Owner);
};
//--------------------------------------------------------------------------extern PACKAGE TfrmMain *frmMain;
//--------------------------------------------------------------------------#endif
547
}
//---------------------------------------------------------------------------
548
After placing a TrackBar control on a form or other container, by default, its assumes a
horizontal position. The position of the trackbar is controlled by Orientation property
implemented through the TTrackBarOrientation enumerator:
enum TTrackBarOrientation { trHorizontal, trVertical };
To change the direction of the control, on the Object Inspector, set the Orientation
property to the desired value. For example, to make it vertical, change the field from
trHorizontal to trVertical. To change this property at runtime, assign the desired value
to the property. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TrackBar1->Orientation = trVertical;
}
//---------------------------------------------------------------------------
The Min property controls the minimum positional value of the control while the Max
value controls the opposite. The user is allowed to slide only between these two values.
These two properties are set in Object Inspector using their respective fields. By default,
the minimum value is set to 0 and the maximum is 10. As integers, the lowest minimum
allowed is INT_MIN which is 2147483647. The maximum allowed value is
INT_MAX which is 2147483647. To change the minimum or maximum values
programmatically, assign the desired value to the appropriate property. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TrackBar1->Min = -1205;
TrackBar1->Max = -540;
}
//---------------------------------------------------------------------------
Always make sure that the minimum value is lower than the maximum. This safe
measure will allow you to better control the current value of the control. At design time,
if you try inversing the values, C++ Builder would reset them. For example, if the Min
field is 12 and you try setting it to 48 when the Max field is 25, the Min field would be
reset to its original value 12. At runtime, if you try setting wrong values as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TrackBar1->Min = 55;
TrackBar1->Max = 12;
}
549
//---------------------------------------------------------------------------
The minimum would be set to the previous minimum value the property had and the new
maximum value would be kept. If you do not know for sure which value would be greater
due to an intermediary action of the user, you can write a conditional statement that
would control the minimum and the maximum values.
When using the trackbar, the user can slide the thumb in the desired direction, thus
changing the value of the control. While it is moving, you can control the incrementing of
the thumb. By default, the thumb advances or regresses by 1 unit each time it is scrolled.
This unit is controlled by the Frequency property.
The thumbs visual display appears as a small standing pentagon with two straight
borders. Its size is set using the ThumbLength property; the smaller the value, the
narrower the thumb. The visual appearance of the thumb is controlled by the
SliderVisible property whose Boolean value is by default set to true. Therefore, if you
wish to hide the thumb, set its SliderVisible property to false.
A trackbar is also equipped with small bars | that serve as indicators of the position
occupied by the slider. These bars are called ticks. By default, the tick marks are
positioned on the same side the slider is pointing. This conditional position of the ticks is
controlled by the value of TickMarks property set from the TTickMark enumerator:
enum TTickMark { tmBottomRight, tmTopLeft, tmBoth };
By default, when you add a new TrackBar control to a form, it is horizontally oriented,
the slider points down, the tick marks are positioned under the sliding field. In this
setting, the TickMarks property is set to tmBottomRight. To place the tick marks above
the sliding field, change the value of the TickMarks property to tmTopLeft; this also
has the effect of reversing the direction of the slider. As a third option, you can have the
tick marks on both sides of the slider. To get this, set the TickMarks property to
tmBoth. With this value, the thumb becomes a small rectangle (changing from its
pentagon shape).
The sliding field of a track bar is a rectangle with a background. It stays white even as the
user slides the thumb to change the controls value.
550
1.
On the Win32 tab of the Component Palette, click the TrackBar button
click on the lower-left section of the form
2.
and
3.
Save all
2.
Implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::TrackBar1Change(TObject *Sender)
{
int CurPos = TrackBar1->Position - 1;
edtMake->Text = Car[CurPos].Make;
edtModel->Text = Car[CurPos].Model;
edtYear->Text = IntToStr(Car[CurPos].CarYear);
edtDoors->Text = IntToStr(Car[CurPos].Doors);
Image1->Picture->Bitmap = Car[CurPos].CarPicture;
}
//---------------------------------------------------------------------------
3.
On the form, double-click an unoccupied area to access its OnCreate event and
implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
edtMake->Text = Car[0].Make;
edtModel->Text = Car[0].Model;
edtYear->Text = IntToStr(Car[0].CarYear);
edtDoors->Text = IntToStr(Car[0].Doors);
551
Image1->Picture->Bitmap = Car[0].CarPicture;
}
//---------------------------------------------------------------------------
4.
552
5.
6.
Save All
2.
3.
4.
5.
6.
7.
From the Statndard tab of the Component Palette, double-click the Panel control
8.
9.
While the panel is still selected on the form, from the Additional tab of the
Component Palette, double-click the Shape control
= clGray
553
554
The Timer control has two properties that are particularly important for its functionality.
A timer is an object used to count lapses of time and send a message when it has finished
counting. The amount of time allocated for counting is called an interval. The Interval is
probably the most important characteristic of the Timer control because it measures and
controls the total time needed to perform a complete count. The Interval is measured in
milliseconds. Like any counter, the lower the value, the faster the count will finish, and
the higher the value, the longer the count (if you ask one kid to count from 1 to 10 and
you ask another to count from 1 to 20 and shout when finished, the first kid would finish
first and would shout first). The amount of interval you specify will depend on what you
are trying to do.
One of the uses you can make of a Timer control is to decide when it should start
counting. In some applications, you may want the control to work full-time while in some
other applications, you may want the control to work only in response to an intermediate
event. The ability to stop and start a Timer control is set using the Enabled Boolean
property. When, or as soon as, this property is set to true, the control starts counting. If,
when, or as soon as, the Enabled property is set to false, the control stops and resets its
counter to 0.
2.
3.
Although the dialog box will be equipped with the system Close button, we should
provide our own mean of closing the application.
4.
5.
Press and hold Shift, then click the other two circles
6.
On the Object Inspector, double-click the empty area on the right side of
OnMouseDown
7.
8.
9.
555
shpYellow->Brush->Color = clSilver;
shpGreen->Brush->Color = clGreen;
}
// But if the color is green
else if( shpGreen->Brush->Color == clGreen )
{
// Change the color to yellow
Timer1->Interval
= 2000;
shpRed->Brush->Color = clSilver;
shpYellow->Brush->Color = clYellow;
shpGreen->Brush->Color = clSilver;
}
// Otherwise, if the color is yellow
else // if(shpYellow->Brush->Color == clYellow
{
// Change the color to red
Timer1->Interval
= 5000;
shpRed->Brush->Color = clRed;
shpYellow->Brush->Color = clSilver;
shpGreen->Brush->Color = clSilver;
}
}
//---------------------------------------------------------------------------
your computer. Just like the VCL Timer control, what you do with the result of this
function is up to you and it can be used in various circumstances. For example, computer
games and simulations make great use of this function.
After retrieving the value that this function provides, you can display it in a text-based
control. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnElapsedClick(TObject *Sender)
{
unsigned long Elapsed = GetTickCount();
edtElapsed->Text = IntToStr(Elapsed);
}
//---------------------------------------------------------------------------
2.
3.
4.
Design the form as follows: set its BorderStyle to bsDialog. Change its name to
frmMain and set its Caption to Counting Computer Ticks
5.
6.
7.
8.
9.
Press F12 to access the Code Editor. In the private section of the form, declare an
unsigned integer as follows:
private:
unsigned int TimeTheComputerStarted; // User declarations
public:
// User declarations
__fastcall TfrmMain(TComponent* Owner);
};
//---------------------------------------------------------------------------
557
10. On the form, double-click the Timer1 icon to access its OnTimer event and
implement the source file as follows:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmMain *frmMain;
//--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
TimeTheComputerStarted = GetTickCount();
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::Timer1Timer(TObject *Sender)
{
unsigned long CurrentTickValue = GetTickCount();
unsigned int Difference = CurrentTickValue - TimeTheComputerStarted;
edtComputerTime->Text = IntToStr(CurrentTickValue);
edtApplicationTime->Text = IntToStr(Difference);
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::BitBtn1Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
558
559
560
1.
2.
3.
4.
Open Image Editor. Design a 32 x 32 icon and its associated 16 x 16 icon as follows:
5.
Save it as BMon
6.
Open the Project Options dialog. In the Application property page, set the title to
Body Monitor Simulation
7.
8.
9.
Save All
The default value of the Orientation property is pbHorizontal. This is equivalent to not
specifying an orientation when programmatically creating the control using either the
VCL or the Win32 libraries. If you want the progress bar to appear vertical, at design
time, set the Orientation value to pbVertical. If you are creating the progress bar using
the Win32 library, OR the PBS_VERTICAL style. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
CreateWindowEx(0, PROGRESS_CLASS, NULL,
WS_CHILD | WS_VISIBLE | PBS_VERTICAL,
20, 20, 18, 170,
Handle, NULL, HInstance, NULL);
}
//---------------------------------------------------------------------------
561
false, making the small rectangles separate. If you set this property to true, the bar would
appear continuous. If creating the control using the CreateWindow() or
CreateWindowEx() Win32 function, you can OR the PBS_SMOOTH style.
To display its small rectangles or the smooth bar, the progress bar uses a preset color,
which is usually blue. If you prefer to use a different color, call the SendMessage()
function with the PBM_SETBARCOLOR message. The syntax you would is:
SendMessage(HWND hWnd,
PBM_SETBARCOLOR,
wParam = 0,
lParam = (LPARAM)(COLORREF)clrBar;
As you can see from this syntax, the wParam argument is not used and must be passed as
0. The desired color for the bar is specified using the lParam argument. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
SendMessage(ProgressBar1->Handle, PBM_SETBARCOLOR, 0, clRed);
}
//---------------------------------------------------------------------------
To show its effect, the progress bar draws its small rectangles on a bar. These small
shapes are from a starting position to an end. This means that the progress bar uses a
range of values. This range is controlled by the Min and the Max properties whose
default values are 0 and 100 respectively. At design time, you can set them using the
limits of an unsigned short integer, that is, from 0 to 65,535. In Win32, the range of
values of a progress bar is set using the PBM_SETRANGE message using the following
syntax:
SendMessage(HWND hWnd, PBM_SETRANGE,
wParam = 0, lParam = MAKELPARAM(nMinRange, nMaxRange);
Alternative, you can send the PBM_SETRANGE32 message to set the range of the
progress bar. This time, the syntax used would be:
SendMessage(HWND hWnd, PBM_SETRANGE32,
wParam = (WPARAM)(int) iLowLim,
lParam = (LPARAM)(int) iHighLim);
For a horizontal progress bar, the small rectangles are drawn from left to right. For a
vertical progress bar, the small rectangles are drawn from bottom to top. At one particular
time, the most top or the most right rectangle of a progress bar is referred to as its
position. At design time, to set a specific position for the control, change the value of the
Position property whose default is 0. The position must always be between the Min and
Max values. If you set it to a value lower than the Min, the Object Inspector would reset
it to Min. In the same way, if it is set to a value higher than Max, it would be reset to the
Max value. At run time, you can assign the desired value to the Position property. Once
again, avoid specifying a value that is out of range.
Because a progress bar is usually meant to indicate the progress of an activity, when
drawing its small rectangles, it increases its current position in order to draw the next
rectangle, except if the control is reset. The number of units that the control must increase
562
value is controlled by the Step property. By default, it is set to 1. Otherwise, you can set
it to a different value of your choice.
On the Win32 tab of the Component Palette, click the ProgressBar button
click on the form
2.
3.
In the same way, add other progress bars and design the form as follows:
Control
Label
Label
Label
Label
Label
Copyright 2003 FunctionX, Inc.
Name
lblBlood
lblHeart
lblKidney
lblBrain
lblLLung
Caption
000
000
000
000
000
and
Additional Proeprties
563
Label
Label
Label
Label
Label
lblRLung
lblPancreas
lblLiver
lblBladder
lblStomach
ProgressBar
pgrBlood
ProgressBar
pgrHeart
ProgressBar
pgrKidney
ProgressBar
pgrBrain
ProgressBar
pgrLLung
ProgressBar
pgrRLung
ProgressBar
pgrPancreas
ProgressBar
pgrLiver
ProgressBar
pgrBladder
ProgressBar
pgrStomach
Shape
shpBlood
Shape
shpHeart
Shape
shpKidney
Shape
shpBrain
Shape
shpLLung
Shape
shpRLung
Shape
shpPancreas
Shape
shpLiver
Shape
shpBladder
Shape
shpStomach
000
000
000
000
000
Max: 650
Position: 288
Max: 240
Position: 204
Max: 450
Position: 120
Max: 1000
Position: 760
Max: 750
Position: 428
Max: 750
Position: 320
Max: 800
Position: 224
Max: 1200
Position: 240
Max: 550
Position: 350
Max: 1250
Position: 650
Brush: Color: clGray
Shape: stCircle
Hint: Start Blood Monitoring
Brush: Color: clGray
Shape: stCircle
Hint: Start Heart Monitoring
Brush: Color: clGray
Shape: stCircle
Hint: Start Kidney Monitoring
Brush: Color: clGray
Shape: stCircle
Hint: Start Brain Monitoring
Brush: Color: clGray
Shape: stCircle
Hint: Start Left Lung
Brush: Color: clGray
Shape: stCircle
Hint: Start Right Lung
Brush: Color: clGray
Shape: stCircle
Hint: Start Pancreas Monitoring
Brush: Color: clGray
Shape: stCircle
Hint: Start Liver Monitoring
Brush: Color: clGray
Shape: stCircle
Brush: Color: clGray
Shape: stCircle
Hint: Start Stomach Monitoring
Bevel
564
Label
Label
Label
Label
Label
Label
Label
Label
Label
Label
Label
Panel
4.
Add 10 timers
Control
Timer
Timer
Timer
Timer
Timer
Timer
Timer
Timer
Timer
Timer
Copyright 2003 FunctionX, Inc.
pnlClose
Blood
Heart
Kidney
Brain
Lungs
Left
Right
Pancreas
Liver
Bladder
Stomach
Close
Name
tmrBlood
tmrHeart
tmrKidney
tmrBrain
tmrLLung
tmrRLung
tmrPancreas
tmrLiver
tmrBladder
tmrStomach
Enabled
False
False
False
False
False
False
False
False
False
False
Interval
650
240
450
1000
750
750
800
1200
550
1250
565
5.
Double-click an unoccupied area on the form and implement its OnCreate() event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
SendMessage(pgrBlood->Handle, PBM_SETBARCOLOR, 0, clRed);
SendMessage(pgrHeart->Handle, PBM_SETBARCOLOR, 0, clGreen);
SendMessage(pgrKidney->Handle, PBM_SETBARCOLOR, 0, clYellow);
SendMessage(pgrBrain->Handle, PBM_SETBARCOLOR, 0, clGray);
SendMessage(pgrLLung->Handle, PBM_SETBARCOLOR, 0, clFuchsia);
SendMessage(pgrRLung->Handle, PBM_SETBARCOLOR, 0, clFuchsia);
SendMessage(pgrPancreas->Handle, PBM_SETBARCOLOR, 0, clBlue);
SendMessage(pgrLiver->Handle, PBM_SETBARCOLOR, 0, clAqua);
SendMessage(pgrBladder->Handle, PBM_SETBARCOLOR, 0, clLime);
SendMessage(pgrStomach->Handle, PBM_SETBARCOLOR, 0, clNavy);
}
//---------------------------------------------------------------------------
6.
Save all
If you want to increase the progress bars position by a value other than Step, you can
call the StepBy() method. Its syntax is:
void __fastcall StepBy(int Delta);
On the form, double-click the tmrBlood timer to access its OnTimer event and
implement it as follows:
//--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
Randomize();
566
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrBloodTimer(TObject *Sender)
{
int BloodLevel = random(650);
pgrBlood->Position = BloodLevel;
if( BloodLevel > 480 )
shpBlood->Brush->Color = clRed;
else
shpBlood->Brush->Color = clGreen;
lblBlood->Caption = lblBlood->Caption.sprintf("%d.%d",
BloodLevel/100, random(50));
}
//---------------------------------------------------------------------------
2.
Again, on the form, double-click the tmrHeart timer to access its OnTimer event
event and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrHeartTimer(TObject *Sender)
{
int HeartLevel = random(240);
pgrHeart->Position = HeartLevel;
if( HeartLevel > 180 )
shpHeart->Brush->Color = clRed;
else
shpHeart->Brush->Color = clGreen;
lblHeart->Caption = lblHeart->Caption.sprintf("%d\260", HeartLevel);
}
//---------------------------------------------------------------------------
3.
In the same way, initiate the OnTimer event of the tmrKidney timer event and
implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrKidneyTimer(TObject *Sender)
{
int KidneyLevel = random(450);
pgrKidney->Position = KidneyLevel;
if( KidneyLevel > 400 )
shpKidney->Brush->Color = clRed;
else
shpKidney->Brush->Color = clGreen;
lblKidney->Caption = lblKidney->Caption.sprintf("%d\045", KidneyLevel);
}
//---------------------------------------------------------------------------
4.
Initiate the OnTimer event of the tmrBrain timer event and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrBrainTimer(TObject *Sender)
{
567
5.
Initiate the OnTimer event of the tmrLLung timer event and implement it as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrLLungTimer(TObject *Sender)
{
int LLungLevel = random(750);
pgrLLung->Position = LLungLevel;
if( LLungLevel > 600 )
shpLLung->Brush->Color = clRed;
else
shpLLung->Brush->Color = clGreen;
lblLLung->Caption = lblLLung->Caption.sprintf("%d.%d\"",
LLungLevel, 2 + random(5));
}
//---------------------------------------------------------------------------
6.
Initiate the OnTimer event of the tmrRLung timer event and implement it as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrRLungTimer(TObject *Sender)
{
int RLungLevel = random(750);
pgrRLung->Position = RLungLevel;
if( RLungLevel > 500 )
shpRLung->Brush->Color = clRed;
else
shpRLung->Brush->Color = clGreen;
lblRLung->Caption = lblRLung->Caption.sprintf("%d.%d\"",
RLungLevel, 2 + random(5));
}
//---------------------------------------------------------------------------
7.
Initiate the OnTimer event of the tmrPancreas timer event and implement it as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrPancreasTimer(TObject *Sender)
{
int PancreasLevel = random(800);
568
pgrPancreas->Position = PancreasLevel;
if( PancreasLevel > 600 )
shpPancreas->Brush->Color = clRed;
else
shpPancreas->Brush->Color = clGreen;
lblPancreas->Caption = lblPancreas->Caption.sprintf("\273%d",
PancreasLevel);
}
//---------------------------------------------------------------------------
8.
Initiate the OnTimer event of the tmrLiver timer event and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrLiverTimer(TObject *Sender)
{
int LiverLevel = random(1200);
pgrLiver->Position = LiverLevel;
if( LiverLevel > 1100 )
shpLiver->Brush->Color = clRed;
else
shpLiver->Brush->Color = clGreen;
lblLiver->Caption = lblLiver->Caption.sprintf("%d\264", LiverLevel);
}
//---------------------------------------------------------------------------
9.
Initiate the OnTimer event of the tmrBladder timer event and implement it as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrBladderTimer(TObject *Sender)
{
int BladderLevel = random(550);
pgrBladder->Position = BladderLevel;
if( BladderLevel > 450 )
shpBladder->Brush->Color = clRed;
else
shpBladder->Brush->Color = clGreen;
lblBladder->Caption = lblBladder->Caption.sprintf("\247%d\252",
BladderLevel);
}
//---------------------------------------------------------------------------
10. Initiate the OnTimer event of the tmrStomach timer event and implement it as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::tmrStomachTimer(TObject *Sender)
{
int StomachLevel = random(1250);
pgrStomach->Position = StomachLevel;
if( StomachLevel > 1100 )
569
shpStomach->Brush->Color = clRed;
else
shpStomach->Brush->Color = clGreen;
lblStomach->Caption = lblStomach->Caption.sprintf("%d\274",
StomachLevel);
}
//---------------------------------------------------------------------------
11. On the form, click the shape control above the Blood label. In the Object Inspector,
click the Events tab. Double-click the right field to OnMouseDown and implement
it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::shpBloodMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( tmrBlood->Enabled )
{
tmrBlood->Enabled = False;
shpBlood->Brush->Color = clGray;
shpBlood->Hint = "Start Blood Monitoring";
}
else
{
tmrBlood->Enabled = True;
shpBlood->Hint = "Stop Blood Monitoring";
}
}
//---------------------------------------------------------------------------
12. In the same way, initiate the OnMouseDown event of the shape above the Heart
label and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::shpHeartMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( tmrHeart->Enabled )
{
tmrHeart->Enabled = False;
shpHeart->Brush->Color = clGray;
shpHeart->Hint = "Start Heart Monitoring";
}
else
{
tmrHeart->Enabled = True;
shpHeart->Hint = "Stop Heart Monitoring";
}
}
//---------------------------------------------------------------------------
13. Also, initiate the OnMouseDown event of the shape above the Kidney label and
implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::shpKidneyMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
570
if( tmrKidney->Enabled )
{
tmrKidney->Enabled = False;
shpKidney->Brush->Color = clGray;
shpKidney->Hint = "Start Kidney Monitoring";
}
else
{
tmrKidney->Enabled = True;
shpKidney->Hint = "Stop Kidney Monitoring";
}
}
//---------------------------------------------------------------------------
14. Initiate the OnMouseDown event of the shape above the Brain label and implement
it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::shpBrainMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( tmrBrain->Enabled )
{
tmrBrain->Enabled = False;
shpBrain->Brush->Color = clGray;
shpBrain->Hint = "Start Brain Monitoring";
}
else
{
tmrBrain->Enabled = True;
shpBrain->Hint = "Stop Brain Monitoring";
}
}
//---------------------------------------------------------------------------
15. Initiate the OnMouseDown event of the shape above the Left label and implement it
as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::shpLLungMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( tmrLLung->Enabled )
{
tmrLLung->Enabled = False;
shpLLung->Brush->Color = clGray;
shpLLung->Hint = "Start Left Lung Monitoring";
}
else
{
tmrLLung->Enabled = True;
shpLLung->Hint = "Stop Left Lung Monitoring";
}
}
//---------------------------------------------------------------------------
16. Initiate the OnMouseDown event of the shape above the Right label and implement
it as follows:
//---------------------------------------------------------------------------
571
17. Initiate the OnMouseDown event of the shape above the Pancreas label and
implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::shpPancreasMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( tmrPancreas->Enabled )
{
tmrPancreas->Enabled = False;
shpPancreas->Brush->Color = clGray;
shpPancreas->Hint = "Start Pancreas Monitoring";
}
else
{
tmrPancreas->Enabled = True;
shpPancreas->Hint = "Stop Pancreas Monitoring";
}
}
//---------------------------------------------------------------------------
18. Initiate the OnMouseDown event of the shape above the Liver label and implement
it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::shpLiverMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( tmrLiver->Enabled )
{
tmrLiver->Enabled = False;
shpLiver->Brush->Color = clGray;
shpLiver->Hint = "Start Liver Monitoring";
}
else
{
tmrLiver->Enabled = True;
shpLiver->Hint = "Stop Liver Monitoring";
}
}
//---------------------------------------------------------------------------
19. Initiate the OnMouseDown event of the shape above the Bladder label and
implement it as follows:
572
20. Initiate the OnMouseDown event of the shape above the Stomach label and
implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::shpStomachMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( tmrStomach->Enabled )
{
tmrStomach->Enabled = False;
shpStomach->Brush->Color = clGray;
shpStomach->Hint = "Start Stomach Monitoring";
}
else
{
tmrStomach->Enabled = True;
shpStomach->Hint = "Stop Stomach Monitoring";
}
}
//---------------------------------------------------------------------------
21. Double-click the (bottom) panel to access its OnClick event and implement it as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::Panel1Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
573
574
575
576
Comments
No scroll bar will be displayed
This is the default value
A horizontal scroll bar will display at
the bottom of the control or document
A vertical scroll bar will display on the
right side of the control or document
A horizontal scroll bar will display at
the bottom of the control and a vertical
scroll bar will display on the right side
of the control or document
Open the Editor1 application you created in previous lessons. If you wo not have it,
open the Editor1 project from the resources that accompany this book.
2.
Display the main form and click the rchEditor control. On the Object Inspector, set
the ScrollBars to ssVertical
3.
On the form, double-click the MainMenu1 icon on the form. On the Menu Designer,
click the View menu item and add a separator on the first empty box. Then add a
menu item named mnuWordWrap and whose Caption is &Word Wrap then set its
Checked property to true. Close the Menu Designer
4.
On the main menu of the form, click View -> Word Wrap and implement its event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::mnuWordWrapClick(TObject *Sender)
{
rchEditor->WordWrap = !rchEditor->WordWrap;
mnuWordWrap->Checked = !mnuWordWrap->Checked;
if( rchEditor->WordWrap )
rchEditor->ScrollBars = ssVertical;
else
rchEditor->ScrollBars = ssBoth;
}
//---------------------------------------------------------------------------
5.
577
2.
3.
4.
5.
Open Image Editor and create a new icon. Design the 32 x 32 and the 16 x 16 sizes
as follows:
32 x 32
578
16 x 16
6.
7.
From the Project menu, access the project options. From the Application tab, seth the
Title to Body Tag Formatter. set the Icon to the above
8.
Control
Name
Label
Bevel
Panel
pnlPreview
BitBtn
GroupBox
Label
Edit
Label
Edit
Label
Edit
GroupBox
Label
Edit
Label
Edit
Label
Edit
Label
Edit
9.
edtHexaRed
edtHexaGreen
edtHexaBlue
edtNumRed
edtNumGreen
edtNumBlue
Caption or
Text
Preview
Other Properties
Shape: bsBottomLine
Color: clWhite
Hint: Current Color
Kind: bkClose
Hexadecimal
Red
FF
Green
FF
Blue
FF
Numeric
Red
255
Green
255
Blue
255
Color
edtBody
Save All
579
To set this property programmatically, assign the desired value. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
ScrollBar1->Kind = sbVertical;
}
//---------------------------------------------------------------------------
When using a scroll bar, the user can navigate from one end of the control to the other
end. These are the controls minimum and maximum values. For a horizontal scrollbar,
the minimum is the far left position that the bar can assume. For a vertical scrollbar, this
would be most bottom position. The maximum would be the opposite. These two values
are controlled by the Min and Max properties. By default, a newly added scrollbar allows
scrolling from 0 to 100. To change these values at design time, type an integer number
for each field in the Object Inspector. The lowest number the Min property can have is
2147483648 and the highest number for Max would be 2147483647.
The primary technique the user applies to a scrollbar is to click one of the arrows at the
ends of the control. As the bar slides inside of the control, it assumes an integer position
from Min to Max. At design time, you can use the Position property to set the position
that the scrollbar would assume when the form opens. If you set the Position to a value
less than the Min, the Object Inspector would restore it to the Min. If you set a Position
greater than the Max, the Object Inspector would assign it the Max value. To
programmatically set the position of the bar, assign the desired value, which must be
between Min and Max, to the Position property. At run time, when the user scrolls the
control, you can find the position of the thumb by getting the value of the Position
property. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1->Caption = ScrollBar1->Position;
Label2->Caption = ScrollBar2->Position;
}
//---------------------------------------------------------------------------
580
The bar inside the scroll region has a size relative to the Min and Max values. By default,
it is a square of the approximate size of the arrow buttons. This size of the bar is
controlled by the PageSize property. Approximately, this represents the percentage of the
scrolling range (the difference between the Max and Min). You can change this value at
design time in the Object Inspector, by an integer value between Min and Max. To
change it programmatically, assign the desired integer to the PageSize property. Here is
an example:
581
From the Standard tab of the Component Palette, click the ScrollBar control
and, on the form, click on the right side of the panel
2.
3.
4.
Press Ctrl + C to copy the control and press Ctrl + V to paste it on the form
5.
Move the new scroll bar to the right of the previous one. Change its Name to
scrGreen
6.
Click on the form and press Ctrl + V. Move the new scroll bar to the right of the
others. Change its Name to scrBlue
7.
Add one label on top of each ScrollBar control. Set their captions to R, G, and B,
respectively:
Save All
Also specify the parent of the control. This is usually the form but can also be any
container that is hosting the scroll bar. This dynamic creation can be inside of an event or
a function. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TScrollBar * Scroller = new TScrollBar(Form1);
Scroller->Parent = Form1;
}
//---------------------------------------------------------------------------
After declaring the variable, you can set its properties as desired. Another method of the
TScrollBar class is the SetParams(). It allows you to set the Position, Min, and Max
values using one function. Here is an example of using it;
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TScrollBar * Scroller = new TScrollBar(Form1);
Scroller->Parent = Form1;
Scroller->Left = 24;
Scroller->Width = 228;
Scroller->Top = 35;
Scroller->Kind = sbVertical;
Scroller->Max = 1500;
Scroller->Position = 780;
Scroller->SmallChange = 5;
Scroller->LargeChange = 150;
Scroller->PageSize = 250;
Scroller->SetParams(780, 0, 1500);
}
//---------------------------------------------------------------------------
Every time the user performs one of these actions, the position of the bar changes unless
it is already at one of the extremes. When the position of the bar has changed, a message
is sent to the operating system that the bar has changed its position. Using the OnChange
event of the scrollbar, you can tell the compiler what to do. Fundamentally you can find
out the new position and display it on a label. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::ScrollBar1Change(TObject *Sender)
{
Label1->Caption = ScrollBar1->Position;
}
//---------------------------------------------------------------------------
583
The OnScroll event occurs when the user scrolls the bar. This event is appropriate if or
when you want to capture the specific action that caused the scrolling action. These
actions relate to the TScrollCode enumerator:
enum TScrollCode {scLineUp, scLineDown, scPageUp, scPageDown,
scPosition, scTrack, scTop, scBottom, scEndScroll};
On the form, double-click the left ScrollBar control and implement its OnChange
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::scrRedChange(TObject *Sender)
{
// While the user is scrolling the Red scroll bar
// get the integer value or position of the scroll bar
edtNumRed->Text = 255 - scrRed->Position;
// Get the Position value of the Red scroll bar.
// Format it to a HEX value
// Display the result in the Red Edit control of the RGB section
edtHexaRed->Text = IntToHex(255 - scrRed->Position, 2);
// Get the current Position of each scroll bar
// Combine these values to create an RGB color
// Preview the resulting color in the Panel
pnlPreview->Color = TColor(RGB(255 - scrRed->Position,
255 - scrGreen->Position,
255 - scrBlue->Position));
// Get the current Position of each scroll bar
// Convert this Position to a HEX value
// Using these HEX values, create an RGB (Web) color
// Display the resulting RGB color in the Color edit box,
// preceded by a # sign
edtBody->Text = "#" + IntToHex(255 - scrRed->Position, 2)
+ IntToHex(255 - scrGreen->Position, 2)
+ IntToHex(255 - scrBlue->Position, 2);
}
//---------------------------------------------------------------------------
2.
On the form, double-click the middle ScrollBar control and implement its
OnChange event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::scrGreenChange(TObject *Sender)
{
edtNumGreen->Text = 255 - scrGreen->Position;
edtHexaGreen->Text = IntToHex(255 - scrGreen->Position, 2);
584
3.
On the form, double-click the right ScrollBar control and implement its OnChange
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::scrBlueChange(TObject *Sender)
{
edtBlue->Text = 255 - scrBlue->Position;
edtHexaBlue->Text = IntToHex(255 - scrBlue->Position, 2);
pnlPreview->Color = TColor(RGB(255 - scrRed->Position,
255 - scrGreen->Position,
255 - scrBlue->Position));
edtBody->Text = "#" + IntToHex(255 - scrRed->Position, 2)
+ IntToHex(255 - scrGreen->Position, 2)
+ IntToHex(255 - scrBlue->Position, 2);
}
//---------------------------------------------------------------------------
4.
5.
After using the form, close it and save the project. Press F12 to display the form
6.
Click the Red edit box in the Numeric GroupBox. On the Object Inspector, click the
Events tab. Double-click the OnKeyPress event and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::edtNumRedKeyPress(TObject *Sender, char &Key)
{
// Allow only digits as entries
if( (Key != '0') &&
(Key != '1') &&
(Key != '2') &&
(Key != '3') &&
(Key != '4') &&
(Key != '5') &&
(Key != '6') &&
(Key != '7') &&
(Key != '8') &&
(Key != '9') &&
(Key != VK_DELETE) &&
(Key != VK_BACK) )
Key = '\0';
scrRed->Position = 255 - edtNumRed->Text.ToInt();
scrRedChange(Sender);
}
//---------------------------------------------------------------------------
585
7.
In the Events tab of the Component Palette, double-click the OnChange event and
implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::edtNumRedChange(TObject *Sender)
{
// If the user tries to have this box empty,
// set its value to 0
if( edtNumRed->Text.IsEmpty() )
edtNumRed->Text = IntToStr(0);
else if( edtNumRed->Text.ToInt() > 255 ) // No value > 255 allowed
edtNumRed->Text = 255;
scrRed->Position = 255 - edtNumRed->Text.ToInt();
scrRedChange(Sender);
}
//---------------------------------------------------------------------------
8.
Implement the OnKeyPress event of the Green edit control from the same GroupBox
control as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::edtNumGreenKeyPress(TObject *Sender, char &Key)
{
if( (Key != '0') &&
(Key != '1') &&
(Key != '2') &&
(Key != '3') &&
(Key != '4') &&
(Key != '5') &&
(Key != '6') &&
(Key != '7') &&
(Key != '8') &&
(Key != '9') &&
(Key != VK_DELETE) &&
(Key != VK_BACK) )
Key = '\0';
scrGreen->Position = 255 - edtNumGreen->Text.ToInt();
scrGreenChange(Sender);
}
//---------------------------------------------------------------------------
9.
Implement the OnChange event of the Green edit box from the same GroupBox
control as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::edtNumGreenChange(TObject *Sender)
{
if( edtNumGreen->Text.IsEmpty() )
edtNumGreen->Text = IntToStr(0);
else if( edtNumGreen->Text.ToInt() > 255 )
edtNumGreen->Text = 255;
scrGreen->Position = 255 - edtNumGreen->Text.ToInt();
scrGreenChange(Sender);
}
//---------------------------------------------------------------------------
10. Implement the OnKeyPress event of the Blue edit box from the same GroupBox as
follows:
586
11. Implement the OnChange event of the Blue edit box from the same GroupBox
control as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::edtBlueChange(TObject *Sender)
{
if( edtBlue->Text.IsEmpty() )
edtBlue->Text = IntToStr(0);
else if( edtBlue->Text.ToInt() > 255 )
edtBlue->Text = 255;
scrBlue->Position = 255 - edtBlue->Text.ToInt();
scrBlueChange(Sender);
}
//---------------------------------------------------------------------------
587
588
589
//---------------------------------------------------------------------------
The second of the most important properties of a radio button is its state: whether it is
selected or not. When the user clicks a radio button, it becomes exclusively selected. This
is seen by the dot inside its rounded box . When a radio button is selected, it is said to
be checked. By default, a newly created radio button is not checked. You can select a
radio button using the Checked property. This same property allows you to decide what
button would be selected when the form opens. As a Boolean property, to set the
Checked state of a radio button, set this value to true. At runtime, to set a particular radio
button as checked, assign a true value to its Checked property:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
rdoGender2->Checked = True;
}
//---------------------------------------------------------------------------
Once one button has been checked in a group, even if there was no selection in the
beginning, one radio button is always selected in the group. The user cannot deselect all
radio buttons. Only the developer can do this at runtime. By setting the Checked property
of a radio button to false, you can remove its selection. Otherwise, you can assign a false
value to the Checked property of each radio button.
When designing your radio buttons, to manage the space, you can distribute them on
more than one column. If you want to use various columns on a group of radio buttons
created using a GroupBox or a panel controls, you can visually position each radio button
on the container. Programmatically, you can also change the Left and Top values of each
control as desired.
By default, a radio button is configured so that the label would be positioned on the right
side of the rounded box. This is controlled by the Alignment property. If you want the
label on the left side, set the radio buttons Alignment property accordingly. The possible
values are: taRightJustify and taLeftJustify.
2.
To save it, on the Standard toolbar, click the Save All button.
3.
Click the Create New Folder button. Type Operations and press Enter twice
4.
5.
6.
On the Standard tab of the Component Palette, click the Panel control
7.
On the form, click and drag from the top left section to the middle center section.
8.
On the Object Inspector, click Caption and press Delete to delete the caption of the
panel.
9.
590
24. While the new and last panel is selected, click the Align field. Click its arrow and
select alBottom
591
On the form double-click the Addition radio button to access its OnClick event
2.
Implement it as follows:
3.
Click the arrow on the top section of the Object Inspector and select rdoSubtraction
4.
5.
6.
Implement the function just like the Click event of the addition but change the
operation as follows:
Result = Num1 - Num2;
7.
592
8.
9.
Implement the Click event like the previous two but change the operation as follows:
Result = Num1 * Num2;
10. In the same way, access the OnClick event for the rdoDivision control and
implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmOperations::rdoDivisionClick(TObject *Sender)
{
double Num1;
double Num2;
double Result;
if( edtNumber1->Text == "" )
Num1 = 0;
else
Num1 = StrToFloat(edtNumber1->Text);
if( edtNumber2->Text == "" )
{
MessageBox(NULL, "The Number 2 Edit Box should not be empty",
"Algebraic Operations", MB_OK);
edtNumber2->SetFocus();
}
else if( edtNumber2->Text == 0 )
{
MessageBox(0, "Cannot divide by zero",
"Algebraic Operations", MB_OK);
edtNumber2->SetFocus();
}
else
{
Num2 = StrToFloat(edtNumber2->Text);
Result = Num1 / Num2;
edtResult->Text = Result;
}
}
//---------------------------------------------------------------------------
593
24.2.1 Introduction
There are two main ways you can create a group of radio buttons. We saw above that you
can place them either on a group box or on a panel. Alternatively, the VCL provides a
control specially made to create radio buttons: the RadioGroup control. To use it, first
select it from the Standard tab of the Component Palette and position it on a form or other
container.
A RadioGroup control is a special control used to create a group of radio buttons.
Open the BodyTag1 application you created previously. If you do not have it, open
the BodyTag1 project from the exercises that accompany this book. Display the
form
2.
On the form, delete the label marked Color. Change the Text property of the bottom
Edit to
3.
Enlarge the form and select all controls (Ctrl + A). Then move all controls to the
right side as follows:
4.
Save All
594
Items property. You can then type a label for each radio button on its own line. Here is an
example:
Like the other radio buttons, one created using the RadioGroup control can also be
selected. Since these radio buttons are stored in a string list, you can set which radio
button is selected by changing the integer value of the ItemIndex property. Since no
radio button is selected by default, its value is 1. The items in the string are counted
starting at 0, then 1, and so on. For example, to set the second radio button as checked,
set the ItemIndex property of the RadioGroup control to 1. This property can be changed
Copyright 2003 FunctionX, Inc.
595
only after the list is created. If you create the list programmatically, you can also decide
which radio button would be selected when the list shows up. This is done by assigning a
short integer value to the ItemIndex property. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
grpEmplStatus->Items->Add("Part-Time");
grpEmplStatus->Items->Add("Full-Time");
grpEmplStatus->Items->Add("Contractor");
grpEmplStatus->Items->Add("Consultant");
grpEmplStatus->ItemIndex = 2;
}
//---------------------------------------------------------------------------
This value should less than the total number of radio buttons. For example, if the
RadioGroup control contains 4 strings, the ItemIndex value should be less than 4; in this
case the value 0, 1, 2, or 3 would select a radio button, a 1 value would remove the dot
from any radio button.
To distribute the radio buttons on different columns, you can use the Columns property
on the Object Inspector. You can also do it at runtime:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
grpEmplStatus->Items->Add("Part-Time");
grpEmplStatus->Items->Add("Full-Time");
grpEmplStatus->Items->Add("Contractor");
grpEmplStatus->Items->Add("Consultant");
grpEmplStatus->ItemIndex = 2;
grpEmplStatus->Columns = 2;
}
//---------------------------------------------------------------------------
596
1.
From the Standard tab of the Component Palette, click the RadioGroup control
and click the empty area on the left side of the form
2.
3.
Type Background and press Enter. Complete the list with Text, Link, Active Link,
and Visited Link
4.
Click OK
5.
While the RadioGroup control is still selected, on the Object Inspector, change its
properties as follows:
Caption = Body Attributes
ItemIndex = 0
Name = grpBodyAttributes
TabOrder = 0
6.
Add an Edit control to the right side of each item of the RadioGroup control. From
top to bottom, Name the Edit controls edtBackground, edtText, edtLink, edtALink,
and edtVLink respectively
7.
Set their Text value to the following respective values: #FFFFFF, #000000,
#0000FF, #008000, and #FF0000. Resize and move the controls as necessary:
597
8.
and
From the Standard tab of the Component Palette, click the Memo control
click below the Body Attributes RadioGroup control. Using the Lines property,
delete its contents. Change its other properties as follows:
Color = clWhite
Name = mmoPreview
ReadOnly = true
9.
10. Add another Edit control inside the Memo with the following properties:
Font -> Color = clBlue
Name = edtPreviewLink
ReadOnly = true
Text = Sample text as link
11. Add another Edit control inside the Memo with the following properties
Font -> Color = clGreen
Name = edtPreviewALink
ReadOnly = true
Text = Active link that is being visited
12. Add another Edit control inside the Memo with the following properties:
Font -> Color = clRed
Name = edtPreviewVLink
ReadOnly = true
598
13. Set the BorderStyle property of all edit controls inside of the memo to bsNone
14. Add a BitBtn control using the Copy bitmap as Glyph with a Caption of Cop&y and
change its Name to btnCopy
15. Move and position the controls as you see fit
16. Double-click the Copy button and implement its OnClick event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnCopyClick(TObject *Sender)
{
edtBody->SelectAll();
edtBody->CopyToClipboard();
}
//---------------------------------------------------------------------------
17. We will need some global variables that can hold the hexadecimal values of colors.
Therefore, in the private section of the TfrmMain class, declare the following
variables:
AnsiString HexBG, HexText, HexLink, HexALink, HexVLink;
18. Whenever the user decides to use a scroll bar, the Preview panel, the Body Attribute
group box, and all of the Edit controls are concerned. Therefore, instead of
performing the same operation in the OnChange event of each control, we will use a
central function that can take care of these. Then we can simply call this function
whenever the position of a scroll bar has changed
In the Class Explorer, right-click TForm1 and click New Method... In the Method
Name, type ApplyColor
19. Set the Function Result as void and click the __fastcall check box. Click OK and
implement the function as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::ApplyColor()
599
600
case 2:
edtLink->Text = "#" +
IntToHex(255 - scrRed->Position, 2) +
IntToHex(255 - scrGreen->Position, 2) +
IntToHex(255 - scrBlue->Position, 2);
edtPreviewLink->Font->Color = TColor( RGB(Red, Green, Blue) );
HexLink = edtLink->Text;
break;
case 3:
edtALink->Text = "#" +
IntToHex(255 - scrRed->Position, 2) +
IntToHex(255 - scrGreen->Position, 2) +
IntToHex(255 - scrBlue->Position, 2);
edtPreviewALink->Font->Color = TColor( RGB(Red, Green, Blue) );
HexALink = edtALink->Text;
break;
case 4:
edtVLink->Text = "#" +
IntToHex(255 - scrRed->Position, 2) +
IntToHex(255 - scrGreen->Position, 2) +
IntToHex(255 - scrBlue->Position, 2);
edtPreviewVLink->Font->Color = TColor( RGB(Red, Green, Blue) );
HexVLink = edtVLink->Text;
break;
}
// Update the contents of the bottom Edit control
edtBody->Text = "<body bgcolor=\"" +
HexBG +
"\" text=\"" +
HexText +
"\" link=\"" +
HexLink +
"\" alink=\"" +
HexALink +
"\" vlink=\"" +
HexVLink +
"\">";
}
//---------------------------------------------------------------------------
20. Change the contents of the OnChange event for each ScrollBar control as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::scrRedChange(TObject *Sender)
{
ApplyColor();
}
//--------------------------------------------------------------------------void __fastcall TForm1::scrGreenChange(TObject *Sender)
{
ApplyColor();
}
//--------------------------------------------------------------------------void __fastcall TForm1::scrBlueChange(TObject *Sender)
{
ApplyColor();
601
}
//---------------------------------------------------------------------------
25. Now, we need to call this function whenever a radio button is clicked
On the form, double-click the Body Attributes group box and implement its OnClick
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::grpBodyAttributesClick(TObject *Sender)
{
// If the user clicks a button from the RadioGroup control
// find out what button the user clicked
602
// set color of the panel to that of the radio button that was clicked
TColor BGColor = mmoPreview->Color;
mmoPreview->Color
= BGColor;
edtPreviewText->Color = BGColor;
edtPreviewLink->Color = BGColor;
edtPreviewALink->Color = BGColor;
edtPreviewVLink->Color = BGColor;
switch(grpBodyAttributes->ItemIndex)
{
case 0:
ClickOption(mmoPreview->Color, edtBackground->Text);
HexBG = edtBackground->Text;
break;
case 1:
ClickOption(edtPreviewText->Font->Color, edtText->Text);
HexText = edtText->Text;
break;
case 2:
ClickOption(edtPreviewLink->Font->Color, edtLink->Text);
HexLink = edtLink->Text;
break;
case 3:
ClickOption(edtPreviewALink->Font->Color, edtALink->Text);
HexALink = edtALink->Text;
break;
case 4:
ClickOption(edtPreviewVLink->Font->Color, edtVLink->Text);
HexVLink = edtVLink->Text;
break;
}
}
//---------------------------------------------------------------------------
603
If you are planning to use the control in more than one location, declare a TRadioGroup
object in the private or public sections of the form or unit that would use it:
private: // User declarations
TRadioGroup *grpMaritalStatus;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
To create the list that represents the radio buttons, use the TStrings::Items property of
the RadioGroup control. You can do this in the function where you create the control
locally:
//--------------------------------------------------------------------------void __fastcall TForm1::btnCreateGroupClick(TObject *Sender)
{
TRadioGroup* Group = new TRadioGroup(Form1);
Group->Parent = Form1;
Group->Caption = "Membership";
Group->Items->Add("Senior");
Group->Items->Add("Adult");
Group->Items->Add("Tean");
Group->Items->Add("Child");
Group->Columns = 2;
Group->Left = 8;
Group->Top = 20;
}
//---------------------------------------------------------------------------
If the RadioGroup was created globally, use the appropriate function or event to initialize
it:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender)
{
604
605
since these two are true containers for other controls, you will not be able to move all
controls as one object during design.
2.
3.
4.
5.
6.
7.
8.
In the project options (Project -> Options), access the Application tab. Set the Title
to Fast Food Corner and sect the icon to the above FastFood icon
9.
Control
Group Box
Radio Button
Radio Button
Name
Dont Care
rdoBun
rdoRoll
Caption
Bread
B&un
&Roll
Group Box
Radio Button
Dont Care
rdoBeefPatty
Meat
Bee&f Patty
Radio Button
rdoGrilledChicken
Radio Button
Group Box
rdoChickedBreast
Dont Care
&Grilled
Chicken
C&hicken Breast
Ingredients
Other Properties
Alignment: taLeftJustify
Alignment: taLeftJustify
Checked: true
Alignment: taLeftJustify
Checked: true
Alignment: taLeftJustify
Alignment: taLeftJustify
607
//---------------------------------------------------------------------------
When a check box is clicked, its Checked property has a value of true. Otherwise, the
value is false. Since the Checked property is a Boolean value, you can toggle its state
based on an intermediary action from the program, the user, or the computer:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
CheckBox1->Checked = !CheckBox1->Checked;
}
//---------------------------------------------------------------------------
Another important property of a check box is its title, which is controlled by the Caption
property. This can easily be set at design or runtime.
The position of the caption is controlled by the Alignment property. By default, the
control's caption is aligned to the right side of the check box:
To change the caption alignment of a check box, use the Alignment property of the
Object Inspector. The values are taRightJustify for the right alignment and the
taLeftJustify.
Fundamentally, a check box can have only one of two states: checked or unchecked.
When a check box Checked property is dependent of other controls or actions,
sometimes, you cannot categorically set it to Checked or not Checked. Imagine that, in a
certain company, for an employee to qualify for stock options, she must be have a fulltime status and must have been in the company for at least two years. If an (important)
employee fulfills one of these requirements but not the other requirements, you can gray
out the Stock Options check box. since the control would not be completely checked, you
can show it as half checked:
608
This property is controlled by the AllowGrayed property. In the Object Inspector, change
the (Boolean) AllowGrayed property of the desired check box to true or false (the
default). Programmatically, to set this property, assign it a true value (because otherwise
the false value is set by default).
At design time or when the user is interacting with your application, you can control the
display of a check box using one of three states. Unlike being checked or unchecked, like
the AllowGrayed property, you can use an intermediary state that would help you and/or
the user know that the controls condition cannot be definitely decided. This is set using
the State property. This property, based on the TCheckBoxState enumerator, allows you
to set a check box as unchecked (the default) by assigning the cbUnchecked value. To
definitely check it, set this property to cbChecked. As a 3rd alternative, you can assign it
a cbGrayed value.
2.
Save it as Ingredients
3.
4.
From the Standard tab of the Component Palette, click the CheckBox button
and click inside the existing bevel on the form. Change its proeprties as follows:
Alignment: taLeftJustify
Caption: &Lettuce
Checked: true
609
Name: chkLettuce
5.
Add another check box under the first one and set its properties as follows:
Alignment: taLeftJustify
Caption: &Onion
Name: chkOnion
6.
Add another check box under the previous one and set its properties as follows:
Alignment: taLeftJustify
Caption: &Tomato
Checked: true
Name: chkTomato
7.
Add another check box under the previous one and set its properties as follows:
Alignment: taLeftJustify
Caption: &Pickles
Name: chkPickles
8.
Display the main form (View -> Forms -> frmMain -> OK)
9.
In the top section inside the Ingredients group box, add a check box
610
Control
CheckBox
Name
chkSweetener
Caption or Text
&Sweetener
CheckBox
CheckBox
GroupBox
BitBtn
RadioButton
chkCheese
chkBacon
Dont Care
btnIngredients
rdoMayonnaise
Ch&eese
B&acon
Options
&Ingredients
&Mayonnaise
RadioButton
RadioButton
rdoKetchup
chkMustard
&Ketchup
Mus&tard
Other Property
Alignment: taLeftJustify
Checked: true
Alignment: taLeftJustify
Alignment: taLeftJustify
Glyph: Ingredients
Alignment: taLeftJustify
Checked: true
Alignment: taLeftJustify
Alignment: taLeftJustify
Bevel
BitBtn
Label
Edit
Shape: bsFrame
Kind: bkClose
edtTotalPrice
Total Price:
$2.35
12. On the main form, double-click the Ingredients button and implement its OnClick()
event as follows:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Exercise.h"
#include "Ingredients.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmMain *frmMain;
//--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnIngredientsClick(TObject *Sender)
{
dlgIngredients->ShowModal();
}
//---------------------------------------------------------------------------
If the check box will be hosted by a container other than the form, specify this as the
parent:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TCheckBox* Checker = new TCheckBox(Form1);
611
Checker->Parent = GroupBox1;
}
//---------------------------------------------------------------------------
If you do not have or cannot get a container at design time, you can also dynamically
create one that would host the dynamic check boxes. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TGroupBox* Group = new TGroupBox(Form1);
Group->Parent = Form1;
Group->Left = 16;
Group->Top = 32;
Group->Caption = "Preferred Sports";
TCheckBox* chkFootball = new TCheckBox(Form1);
chkFootball->Parent = Group;
chkFootball->Left = 16;
chkFootball->Top = 16;
chkFootball->Alignment = taRightJustify;
chkFootball->Caption = "Football";
chkFootball->Checked = True;
TCheckBox* chkHandball = new TCheckBox(Form1);
chkHandball->Parent = Group;
chkHandball->Left = 16;
chkHandball->Top = 36;
chkHandball->Caption = "Handball";
}
//---------------------------------------------------------------------------
If you want to know the alignment applied on a check box, call the
GetControlsAlignment() method. Its syntax is:
TAlignment __fastcall GetControlsAlignment(void);
Most of the other methods a check box uses derive from its ancestors the TControl and
the TWinControl classes.
612
If the user completely removes the check mark on the Sweetener check box, this
suggests that the customer does not want this item on the sandwich. Consequently,
the radio buttons in the Options group should be disabled. When the user clicks a
check box, whether the control was already checked or not, the OnClick() event
fires. Therefore, in this case, the first thing you should do it to check the state of the
3.
To keep track of the users selection of ingredients, we will use four global variables
that each represents a check box from the Ingredients dialog.
In the header file of the main form, declare four private Boolean variables as follows:
private:
Boolean bLettuce, bOnion, bTomato, bPickles;
4.
In the constructor of the main form, initialize the variables with a false value each:
//--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
bLettuce = False;
bOnion = False;
bTomato = False;
bPickles = False;
}
//---------------------------------------------------------------------------
5.
When the user clicks the Regulars check box, we will update the global variables
appropriately.
On the form, double-click the Regulars check box and implement its OnClick()
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::chkRegularsClick(TObject *Sender)
{
// If the Regulars check box is completely unchecked
if( chkRegulars->State == cbUnchecked )
{
// Set the global Boolean variables to false each
bLettuce = False;
bOnion = False;
bTomato = False;
bPickles = False;
613
}
// If the Regulars check box is completely checked
else if( chkRegulars->State == cbChecked )
{
// Set the global Boolean variables to true each
bLettuce = True;
bOnion = True;
bTomato = True;
bPickles = True;
}
// Otherwise, refer to the state of the check boxes
// from the Ingredients dialog box. Whatever they are
else
{
bLettuce = dlgIngredients->chkLettuce->Checked;
bOnion = dlgIngredients->chkOnion->Checked;
bTomato = dlgIngredients->chkTomato->Checked;
bPickles = dlgIngredients->chkPickles->Checked;
}
}
//---------------------------------------------------------------------------
6.
When the Regulars check box is not checked at all, no basic ingredient is selected.
When this control is checked, all ingredients will be added to the sandwich. If at least
one ingredient is selected and at least one ingredient is not selected, the Regulars
check box should appear grayed.
When the user clicks the Ingredients button, we will display the Ingredients Selection
dialog box and allow the user to select the ingredients. If the user clicks OK to
dismiss the dialog box, we will aply the above scenario.
On the form, double-click the Ingredients button and change its OnClick event as
follws:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnIngredientsClick(TObject *Sender)
{
// Before displaying the dialog box, synchronize its
// check boxes with the global variables
dlgIngredients->chkLettuce->Checked = bLettuce;
dlgIngredients->chkOnion->Checked = bOnion;
dlgIngredients->chkTomato->Checked = bTomato;
dlgIngredients->chkPickles->Checked = bPickles;
// Call the Ingregients Selection dialog box for the user
// If the user clicked OK when closing the dialog box,
dlgIngredients->ShowModal();
if( dlgIngredients->ModalResult == mrOk )
{
// If the user clicks OK, update the global values of the check boxes
bLettuce = dlgIngredients->chkLettuce->Checked;
bOnion = dlgIngredients->chkOnion->Checked;
bTomato = dlgIngredients->chkTomato->Checked;
bPickles = dlgIngredients->chkPickles->Checked;
// if no check box is checked
if( (bLettuce == False) &&
(bOnion == False) &&
(bTomato == False) &&
(bPickles == False) )
614
chkRegulars->State = cbUnchecked;
// then uncheck this one
// if all check boxes are checked
else if( (bLettuce == True) &&
(bOnion == True) &&
(bTomato == True) &&
(bPickles == True) )
chkRegulars->State = cbChecked;
// then check this one
else // if at least one check box is checked and at one is unchecked
chkRegulars->State = cbGrayed; // then set this one as indeterminate
}
// If the user clicked Cancel, don't do nothing
}
//---------------------------------------------------------------------------
7.
615
}
//---------------------------------------------------------------------------
9.
To update the price and its display live whenever the user makes a new selection,
double-click the following controls: Beef Patty, Grilled Chicken, Chicken Breast,
Cheese, and Bacon
10. In the body of each, simply call the above EvaluatePrice() method:
//--------------------------------------------------------------------------void __fastcall TfrmMain::rdoBeefPattyClick(TObject *Sender)
{
EvaluatePrice();
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::rdoGrilledChickenClick(TObject *Sender)
{
EvaluatePrice();
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::rdoChickedBreastClick(TObject *Sender)
{
EvaluatePrice();
}
//--------------------------------------------------------------------------void __fastcall TfrmMain::chkCheeseClick(TObject *Sender)
{
EvaluatePrice();
}
//---------------------------------------------------------------------------
616
Name
Caption or Text
Processed By:
Other Properties
edtClerk
Order Date:
edtOrderDate
EditMask: !99/99/0000;1;_
617
618
619
Alternatively, to add an item or items to a list box, you can call the Win32 APIs
SendMessage() function with the LP_ADDSTRING as the message. The wParam
argument is not used and can be passed as 0. The string to add must be null-terminated
and must be cast to LPARAM. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
SendMessage(ListBox1->Handle, LB_ADDSTRING, 0, (LPARAM)("Edem Kodjo"));
}
//---------------------------------------------------------------------------
620
As mentioned already, the user mostly interacts with a list box by selecting an item. At
any time you can find out whether a particular item has been selected. This is done using
the TCustomListBox::Selected Boolean property.
When the Items of a list box appear in alphabetical order, the list is said to be sorted. By
default, the items of a list box are not sorted. To arrange the list to ascending order, set
the Sorted propertys value to true. As a Boolean data type, you can set the sorting feature
programmatically as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
ListBox1->Sorted = True;
}
//---------------------------------------------------------------------------
If you create an unsorted list, then at one time get it sorted (for example, you can give the
user the ability to sort the list, by clicking a button), the list would be sorted. If an item is
added to the sorted list, the compiler would automatically insert to the right position
following the order. If at another time you allow the user to unsort the list, the list
would keep its current order. If another item is added when the list is not sorted, the item
would be positioned at the end of the list. If you want the list to have its original state,
you would have to reset it through code.If you change the Color property of the ListBox
control, its whole background would appear with that color. If you want each item to
have its own color, you would have to change the style of the list and properly configure
it.
When you create a list of items, they appear in one range of columns. If the number of
items exceeds the height, a scrollbar would appear on the control. One alternative you can
use is to span the list on more than one column. This is set using the Columns property.
By default, the Columns value is set to 0, which means the items appear in one column.
If you position the control on a form whose DockSite property is set to true with the
control having a DragKind property dkDock and the DragMode property set to
dmAutomatic, the user will be able to move the control and position it anywhere inside
the form.
By default, the user can select only one item in the list. This is controlled by both the
ExtendedSelect and the MultiSelect properties. If you want the user to be able to select
more than one item, you should set the MultiSelect property to true since the
ExtendedSelect property would already be set to true. After the user has selected more
than one item, you can use the TCustomList::SelCount to find out the number of items
that the user would have selected.
The list boxes are designed in three types of style. The default is the lbStandard in
which case each item in the list is an AnsiString object. On the other hand, if you want
each item of the list to display a graphic or a color, you must set the style to an owner
draw. The lbOwnerDrawFixed allows you to set a desired height for each item of the
list. This height is controlled through the ItemHeight property. You can set a different
height for each item if you set the list style to lbOwnerDrawVariable.
The ItemIndex is a property that identifies which item is selected in a list box. This is a
property of highly particular interest. If you try performing an operation that requires that
an item be selected and no item is selected, the program would throw an error. The items
in a list box are counted from 0, then 1, etc. The first item, at position 0, can be identified
as ListBox1->ItemIndex = 0. If no item is selected in the list, the
Copyright 2003 FunctionX, Inc.
621
The items of a list box are AnsiString objects created from the TStrings class. This
allows you to use the properties of the TStrings class. We will study the TStrings class
in another lesson.
If the list box will be positioned on another type of container, such as a panel called
Panel1, you can create it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::CreateTheList()
{
TListBox* Liste = new TListBox(this);
Liste->Parent = Panel1;
}
//---------------------------------------------------------------------------
If you create the list box in a function, a method, or an event, the list will exist only inside
of that function: you cannot manipulate it from another function, method, or event. If you
want the control to be accessed by many functions declare a TListBox object in the
header file of the form where the control will be hosted.. Here is an example:
//--------------------------------------------------------------------------#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------------------------#include <Classes.hpp>
622
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published: // IDE-managed Components
private: // User declarations
TListBox *Listing;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------------------------#endif
After declaring the control, you should initialize it in the forms constructor. This allows
you to specify the container that will host the control:
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Listing = new TListBox(Form1);
Listing->Parent = Form1;
Listing->Left = 120;
Listing->Top = 80;
}
//---------------------------------------------------------------------------
When an application terminates, Borland C++ Builder takes care of destroying all of the
objects that are part of the application. If the objects were created at design time, they are
owned by the form. Since the form is owned by the application, the application would
destroy the form including its hosted controls. If a control was placed on another
container such as a panel, the panel is owned by the form and the form by the application.
The destruction would still be smooth when the application executes. If you dynamically
create a control, you must specify the owner of the control; otherwise the program would
not compile. Since you will have specified the owner or container of the control, when
the application exits, the compiler would destroy the form and its control, which would
include the control you dynamically created. Therefore, you do not have to worry about
the state of the dynamic controls when your application exits. This does not mean that
your application would never produce a memory leak, but it would be highly unlikely.
If you want to explicitly destroy your dynamically created object, the best place to
destroy a global object is to use the delete operator on the OnDestroy event of the form:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDestroy(TObject *Sender)
{
delete Listing;
Listing = NULL;
}
//---------------------------------------------------------------------------
If the list box was dynamically created inside of a function, you cannot access it outside
of that function. If it were declared in the header file, you can use any appropriate
Copyright 2003 FunctionX, Inc.
623
method, event, or function to fill the list or to manipulate the control. For example, you
can fill out the Listing control we declared earlier as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Listing->Items->Add("C++ Builder");
Listing->Items->Add("Delphi");
Listing->Items->Add("JBuilder");
Listing->Items->Add("Kylix");
}
//---------------------------------------------------------------------------
Since the items of a list box are derived from the TStrings class, you can use this class
methods to perform the desired operations on the control. The strings in a list box are
counted starting at 0, then 1, and so on.To add an item at the end of the list, you can write
code such as:
//--------------------------------------------------------------------------void __fastcall TForm1::btnAddLesothoClick(TObject *Sender)
{
lstCountries->Items->Add("Lesotho");
}
//--------------------------------------------------------------------------To insert an item in the 2nd position you can write code such as:
//--------------------------------------------------------------------------void __fastcall TForm1::btnAddSecondClick(TObject *Sender)
{
lstCountries->Items->Insert(1, "Mauritania");
}
//--------------------------------------------------------------------------To delete the 4th item of the list, you could write:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDelete4Click(TObject *Sender)
{
lstCountries->Items->Delete(3);
}
//---------------------------------------------------------------------------
If the list contains less than 4 items, the TStrings::Delete() method would ignore the
operation. If you have an Edit control called edtCountry, you can let the user add its
content to the list box when he double-clicks the edit box. The code could look as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Edit1DblClick(TObject *Sender)
{
lstCountries->Items->Add(edtCountry->Text);
}
//---------------------------------------------------------------------------
If the user is filling out a list box while typing additional items in an Edit control, the best
and fastest way to add a new item is when the user presses Enter after typing the item. To
implement this behavior, you should find out what key the user pressed. If the user
presses Enter when the edit box has focus, you should make sure the edit box contains a
string. The code could appear like this:
624
The TCustomListBox::Clear() method is used to clear the control of its whole content:
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
lstCountries->Clear();
}
//---------------------------------------------------------------------------
When performing your operations, sometimes you will want to find out if a particular
item is selected. You can do this using the ordinal position of the item. For example, to
find out if the 3rd item is selected, you can write:
//--------------------------------------------------------------------------void __fastcall TForm1::Button7Click(TObject *Sender)
{
if( lstCountries->ItemIndex == 2)
Panel1->Caption = "The third item is selected";
}
//---------------------------------------------------------------------------
If an item is selected, you can allow the user to perform an operation on it. If a string
(that is, any string) is not selected, most operations would fail and the program would
crash. Therefore, usually you should make sure an item is selected. An item is selected
when the value of the TCustomListBox::ItemIndex integer value is not negative.
Operations include displaying the selected item of the list box to an edit box:
Copyright 2003 FunctionX, Inc.
625
Many times during your design, you will allow users to add or delete items to or from a
list box. One way you will do this is to create a list of items originating from another list;
this allows you to control the items the user can select from a list before continuing with
the issue at hand. Borland C++ Builder ships with a dialog box completely configured to
handle this transaction.
To allow two list boxes to exchange data, you provide the appropriate buttons. If only
one list will be used as the source, provide one button that could allow the user to select
an item. It is also a good idea to allow the user to select and add all items at once. Also
important is the ability for the user to remove mistakenly selected items. Over all, these
kinds of list boxes handle their transactions through the use of four buttons: two are used
to exchange one item at a time from one list to another, two to exchange all items from
one list to another.
To start a new project, on the Standard toolbar, click the New button
2.
3.
To add Borland C++ Builders built-in list box dialog, on the Standard toolbar, click
the New button . In the Object Repository, click the Forms property page.
4.
5.
Click OK.
6.
To remove the starting form, press F12 to display the Code Editor.
7.
8.
9.
11. Use the existing transfer buttons to exchange items between both list boxes.
12. Close the form using its Windows Close button.
13. On the form, click the Source List label.
14. On the Object Inspector, click Caption and type Cities to locate on a map:
15. Click Destination List and type Cities Selected:
16. To remove the items in the list, click the left list to select it
17. On the Object Inspector, click the ellipsis button of the Items field
Delete all items in the String List Editor dialog box and click OK
18. To create our own list, double-click an unoccupied area of the form and implement
the OnCreate event as follows:
//--------------------------------------------------------------------------void __fastcall TDualListDlg::FormCreate(TObject *Sender)
{
SrcList->Items->Add("Shangai");
SrcList->Items->Add("Melbourne");
SrcList->Items->Add("Kenitra");
SrcList->Items->Add("London");
SrcList->Items->Add("Valencia");
SrcList->Items->Add("Rio de Oro");
SrcList->Items->Add("Santiago");
SrcList->Items->Add("Dublin");
SrcList->Items->Add("Bamako");
SrcList->Items->Add("New Delhi");
SrcList->Items->Add("Tokyo");
SrcList->Items->Add("Alexandria");
SrcList->Items->Add("Boston");
SrcList->Items->Add("Quebec");
SrcList->Items->Add("Dar-Es-Salam");
SrcList->Items->Add("Munich");
}
//---------------------------------------------------------------------------
627
IncludeBtnClick(Sender);
}
//---------------------------------------------------------------------------
34. To use one of the template dialog boxes, on the main menu of C++ Builder, click
File -> New -> Other...
35. In the New Items dialog box, click Dialogs. In the Dialogs property page, click
Standard Dialog (Vertical) and click OK
36. On the Object Inspector, change the Caption to Date and Time
37. Change the Name to dlgDateAndTime
38. To save the new dialog box, on the Standard toolbar of C++ Builder, click the Save
All button
39. Type DateAndTime to replace the name of the unit and press Enter
40. On the Standard tab of the Component Palette, click Label and click on the top
border of the Bevel on the dialog box. Change its Caption to Select a Format
41. On the Standard tab of the Component Palette, click ListBox and click inside the
Bevel on the dialog box
42. Change its Name to lstDateAndTime
43. Design the dialog box as the above picture
44. Click an unoccupied area on the dialog box to select it. On the Object Inspector,
click the Events tab
45. Double-click the empty box next to OnActivate and implement its event as follows:
628
629
}
//---------------------------------------------------------------------------
630
2.
3.
4.
631
Control
Form
GroupBox
RadioButton
RadioButton
RadioButton
5.
Name
Caption
Other Properties
BorderStyle: bsDialog
ShowHint: true
rdoSmall
rdoMedium
rdoLarge
Alignment: taLeftJustify
Alignment: taLeftJustify
Alignment: taLeftJustify
Save All
and click the form or container that would host the control.
Like the regular List Box control, the items of a Check List Box object are AnsiString
strings controlled by a TStrings list called Items. At design time, to create a list of items
and add it to the control, open the String List Editor dialog box, add the necessary
strings, and click OK. Of course, you can add the strings at run time, using the
Items::Add() method.
After creating the list, each item appears with a flat check box to its left. If you want a 3D check box, you can change the Boolean Flat property from its true default to a false
value:
Flat = true
632
Flat = false
When you create the list, the items are stored in the same order you entered them, if you
want, you can rearrange them alphabetically or numerically. This can be done by setting
the Boolean value of the Sorted property accordingly. As described for the list box, if
you set it to true, the items of the Check List Box would be sorted. If you set it back to
false, the items of the list box would not go back to the way they were but new items
would be added at the end of the list.
If you provide a longer list than the control's height can display, it would have a vertical
scroll bar. If just one or a few items are hidden by the scroll bar, you can heighten it if the
form provides more space. Alternatively, you can also create the list in various columns.
To do this, set the value of the Columns property to a number of your choice. Here is a
Check List Box with two columns:
Normally, the number of columns should not exceed 5 or this would indicate that you
may simply need more than one Check List Box control:
If at least one of the items is wider than the width of the control, you have various
alternatives. You can display a horizontal scroll bar by calling the Win32 API
SendMessage() function and specifying the LB_SETHORIZONTALEXTENT value
as the message to send:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
SendMessage(CheckListBox1->Handle, LB_SETHORIZONTALEXTENT, 140, 0);
}
//---------------------------------------------------------------------------
You can also resize the control to make it wider to accommodate the large item. After
creating the list, sorted or not, each item has a positional index that allows you to access
it. The array of this index can have a different name depending on why you want to
access it. For example, the items are stored in an array called Header and each item can
be accessed using the Header[Index] array. The Header index is used if you want an
item to be distinct from the others. To do this, at run time, access that item Header index
and set its Header Boolean property to true. To distinguish the header item from the
Copyright 2003 FunctionX, Inc.
633
others, it uses a different text color and it does not display a check box. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
CheckListBox1->Header[2] = True;
}
//---------------------------------------------------------------------------
In the same way, you can make more than one item appear as Header. The Header item
displays a color represented by the HeaderBackgroundColor property. The text of the
Header item displays in a color known as the HeaderColor property. You can set these
values to any valid color you want.
Also, the items are stored in an array called Checked. The first, top, item has an index of
0 and can be accessed with Checked[0]. The second item has an index of 1 and can be
accessed with Checked[0], etc. As stated already, to select an item, the user clicks its
check box. To find out if an item has been checked, access its Checked index and check
whether it is true or false. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::CheckListBox1Click(TObject *Sender)
{
if( CheckListBox1->Checked[1] == True )
ShowMessage("Welcome to Montgomery County!");
}
//---------------------------------------------------------------------------
To increase the options of the Check List Box, you can let the user "half-Select" an item
or indicate when an item is not completely selected and not completely deselected. Such
an item is referred to as grayed. To allow this, you must set the AllowGrayed Boolean
property to true. If a Check List Box control has this property on, to check the
appearance of a check mark or absence of it on an item, use the State property. The value
of this property is an enumeration type defined as follows:
enum TCheckBoxState {cbUnchecked, cbChecked, cbGrayed};
634
If for any reason you do not want the users to be able to select items, only to view the list,
you can set the Enabled property of the control to false. On the other hand, if you want to
disable only one or a few items, you can do that. The items of a Check List Box are
stored in an array called ItemEnabled[Index]. To enable or disable an item, call this
property and specify the index of the item you want to enable or disable. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
CheckListBox1->ItemEnabled[2] = False;
CheckListBox1->ItemEnabled[4] = False;
CheckListBox1->ItemEnabled[5] = False;
}
//---------------------------------------------------------------------------
On the Component Palette, click the Additional tab and click the CheckListBox
button
2.
3.
While the CheckListBox control is still selected, on the Object Inspector, click the
ellipsis button of the Items
4.
635
Figure 90: The String List Editor for a Check List Box
5.
Click OK
6.
7.
636
Name
Caption or Text
Other
Properties
Label
CheckListBox
GroupBox
RadioButton
RadioButton
RadioButton
RadioButton
Label
Edit
Label
Edit
Label
Edit
BitBtn
8.
Toppings Selection
rdoMixToppings
rdoHalfAndHalf
rdoThirdAnd2Thirds
rdoThirdEach
edtPizzaPrice
edtPriceToppings
edtTotalPrice
Topping Distribution
Mix Toppings
Half and Half
1/3 and 2/3
1/3 Each
Pizza Price
9.25
Price Toppings
0.00
Total Price
0.00
Kind: bkClose
Save All
On the form, click the Check List Box to select it and, on the Object Inspector, click
the Events tab
2.
Double-click the event side of the OnClickCheck field and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::lstToppingsClickCheck(TObject *Sender)
{
int Pepperoni, Sausage, GroundBeef, Olives,
Pineapple, ExtraCheese, Onions, GreenPepper;
int NumberOfToppings;
double PricePizza, PriceToppings, PriceTotal;
const double PriceEachTopping = 0.45;
// Get the price of the pizza based on the selected size
if( rdoSmall->Checked == True )
PricePizza = 8.65;
else if( rdoMedium->Checked == True )
PricePizza = 9.55;
else if( rdoLarge->Checked == True )
PricePizza = 10.75;
// Check each item.
// If a check box is on an item, the item counts as one
637
638
3.
On the form, double-click the Small, the Medium, and the Large radio buttons
4.
639
// Let the OnClickCheck of the Check List Box perform the calculations
lstToppingsClickCheck(Sender);
}
//---------------------------------------------------------------------------
5.
7.
Save All
640
By default, the items you add to the combo box will appear in the order they are supplied.
For example the TStrings::Add() method would add the new string at the end of the list.
If you want the list of items to be sorted, you can change the value of the Sorted property
in the Object Inspector from false (the default) to true. To sort a list programmatically,
you can write:
//--------------------------------------------------------------------------void __fastcall TForm1::btnSortTheListClick(TObject *Sender)
{
cbxColors->Sorted = True;
}
//---------------------------------------------------------------------------
You can un-sort the list by changing the value of the Sorted property. This property
works exactly like its equivalent in the TListBox control
There are three styles of combo boxes, although all allow the user to make only one
selection. These styles are controlled by the TComboBoxStyle enumerator and the Style
property of the Object Inspector. A combo box can be configured to allow the user to add
items to the list. In this case, if the user does not find the desired item in the list, he can
type a new value. To provide this ability, set the Style to csDropDown. If you set the
Style to csDropDownList, the user cannot enter a new item in the list but can still select
one from the control. A combo box with the csSimple Style permanently displays a
combination of an edit box and a list box. The user can scroll in the list box and select an
item; after the selection, the control would still display the list. The other two styles,
csOwnerDrawFixed and csOwnerDrawVariable, are typically used to display varying
objects such as pictures or colors.
641
If the combo box has a style other than csSimple, there is typically a fixed number of
items that display when the user clicks the controls arrow. You can control the number
of items that displays using the DropDownCount property. By default, this is set to 8. If
the list contains a number of items less than the DropdownCount integer value, all of the
items would display fine. If the list contains more than the DropDownCount number of
items, when the user clicks the arrow, a scroll box would appear. The control would
display DropDownCount number of items; to reveal more, the user would have to scroll
in the list.
The text that displays on a non-drawn combo box is an AnsiString object. If you want
the combo box to display a certain string, use the Text property. At design time, you can
set this only if the controls Style is csDropDown or csSimple. At run time, you can set
the text that would display in the combo box at startup using the ItemIndex property.
This property represents the ordinal integer item of the combo box. The items are counted
from 0, then 1, and so on. The ItemIndex of each item is set depending on the value to
the Sorted property. If the list is not sorted, the first item entered in the list has an
ItemIndex value of 0. If the list gets sorted, the first item in ascending order has an
ItemIndex property set at 0. You can therefore use this property to control what item to
display in the list:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
cbxColors->ItemIndex = 0;
}
//---------------------------------------------------------------------------
You can also use the ItemIndex property to find out what item is selected at a given time.
After creating the object, you can manipulate its properties the change the defaults. If you
create a local list, you will manipulate it in the same function or event:
//--------------------------------------------------------------------------void __fastcall TForm1::btnCreateComboBoxClick(TObject *Sender)
{
TComboBox *CarMake = new TComboBox(this);
CarMake->Parent = this;
CarMake->Left = 32;
CarMake->Top = 16;
642
CarMake->Items->Add("Ford");
CarMake->Items->Add("Renault");
CarMake->Items->Add("Fiat");
CarMake->Items->Add("Honda");
}
//---------------------------------------------------------------------------
The most regular operation a user performs on a combo box is to select an item from the
list. This happens when the user clicks the arrow to expand the list and then clicks one
item. Once the user clicks one of the items, the list disappears and the combo box
becomes a regular edit box, unless the style of the combo box is csSimple. Using this
event, you can find out what item the user would have selected and act accordingly. For
example, you can transfer the selected item to another control such as an edit box. Here is
an example:
//--------------------------------------------------------------------------void __fastcall TForm1::cbxMajorClick(TObject *Sender)
{
edtMajor->Text = cbxMajor->Items->Strings[cbxMajor->ItemIndex].c_str();
}
//---------------------------------------------------------------------------
The OnChange event occurs when the user changes the event that was displaying in the
edit box of the combo box. Also this event occurs also in response to the user making a
selection, it is more appropriate if you allow the user to edit an item of the list or if you
allow the user to add items to the list by typing directly in the edit box. Like the Edit
Copyright 2003 FunctionX, Inc.
643
control, the OnChange event occurs immediately as the user types anything in the edit
box portion of the combo box. The OnClick event cannot respond to these events. You
can use the OnChange event to deal with the user trying to modify the item on the edit
box. You can also use it to respond to other actions associated with the user making a
selection. For example, you can simply display the list the number of items in the list:
//--------------------------------------------------------------------------void __fastcall TForm1::cbxMajorChange(TObject *Sender)
{
edtCount->Text = IntToStr(cbxMajor->Items->Count);
}
//---------------------------------------------------------------------------
Open the Editor2 application from the resources that accompany this book
2.
Make sure the main form is displaying. From the Standard tab of the Component
Palette, click ComboBox
3.
4.
As the new combo box is still selected, on the Object Inspector, change the following
properties:
Text = Times New Roman
Name = cboFonts
Hint = Font|Changes the font of the selection
5.
Add another combo box on the right side of the Font combo box with following
properties:
Text = 10
Name = cboFontSize
Hint = Font Size|Changes the font size of the selection
Width = 50
644
6.
7.
8.
Complete the list with 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 32, 36, 48, 54, 72
9.
Click OK
10. Drag the Font combo box to the left of the toolbar
11. Also drag the Font Size combo box to the right side of the Font combo box
12. Right-click the Formatting toolbar and click New Separator. Move the new separator
to the right side of the Font Size combo box
13. Double-click the Font combo box and implement its OnChange event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::cboFontsChange(TObject *Sender)
{
rchEditor->SelAttributes->Name = cboFonts->Text;
rchEditor->SetFocus();
}
//---------------------------------------------------------------------------
14. On the form, double-click the Font Size combo box and implement its OnChange
event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::cboFontSizeChange(TObject *Sender)
{
rchEditor->SelAttributes->Size = StrToInt(cboFontSize->Text);
rchEditor->SetFocus();
}
15. While you are in the Code Editor, on the top combo box of the Object Inspector,
select frmMain and click the Events tab
16. Double-click FormCreate to access its event. At the end of the event, before the
closing bracket, fill the Font combo box with the computer's fonts as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
Application->OnHint = ShowHints;
int i;
for(i = 0; i < Screen->Fonts->Count; i++)
{
// Get a pointer of the menu we want to change
TMenuItem *FontItem;
FontItem = new TMenuItem(ListOfFonts);
645
// Get a font from the system and store it as a caption of the menu
FontItem->Caption = Screen->Fonts->Strings[i];
// Add the font to the menu item to create a list of fonts
ListOfFonts->Add(FontItem);
// Eventually, if the user selects a font here,
// we will call a function, called Applyfont, to apply that font
FontItem->OnClick = ApplyFont;
}
//---------------------------------------------------------------------------
17. We also need to make sure that when the user clicks anywhere in the text, both
combo boxes can display the font name and its size.
18. Click the RichEdit control (the big wide area on the main form). On the Object
Inspector, double-click the box on the right side of OnSelectionChange
19. Implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::rchEditorSelectionChange(TObject *Sender)
{
cboFonts->Text = rchEditor->SelAttributes->Name;
cboFontSize->Text = IntToStr(rchEditor->SelAttributes->Size);
}
//---------------------------------------------------------------------------
20. Finally, after the user has used the Font dialog box, if he clicks OK, since we took
care of giving focus to the RichEdit control already, we need to make sure the combo
boxes display the font and the font size that were selected
21. On the form, double-click ActionList1
22. On the left frame of the ActionList Editor, click Dialog. On the right frame, click
FontEdit1
23. In the Object Inspector, click the Event tab if necessary and double-click the right
section of OnAccept
24. Add the following two lines to the end of the event:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FontEdit1Accept(TObject *Sender)
{
// Do the inverse of the BeforeExecute event
// If the user clicks OK, get the characteristics of the font
// Apply them to the selected text of the Rich Edit control
rchEditor->SelAttributes->Name = FontEdit1->Dialog->Font->Name;
rchEditor->SelAttributes->Style = FontEdit1->Dialog->Font->Style;
rchEditor->SelAttributes->Size = FontEdit1->Dialog->Font->Size;
rchEditor->SelAttributes->Color = FontEdit1->Dialog->Font->Color;
cboFonts->Text = FontEdit1->Dialog->Font->Name;
cboFontSize->Text = StrToInt(FontEdit1->Dialog->Font->Size);
}
//---------------------------------------------------------------------------
25. Test your project. After using it, click the application
26. Save and close the project.
646
647
648
This convenient control is called Month Calendar. The title bar of the control displays
two buttons and two labels. The left button allows the user to select the previous month
by clicking the button. The left label displays the currently selected month. The right
label displays the year of the displayed date. The right button is used to get to the next
month.
The calendar can be configured to display more than one month. Here is an example that
displays two months:
If the control is displaying more than one month, the buttons would increment or
decrement by the previous or next month in the list. For example, if the control is
displaying April and May, if the user clicks the left button, the control would display
March and April. If the control is displaying April and May and the user clicks the right
Copyright 2003 FunctionX, Inc.
649
button, the control would display May and June. Also, to select any month of the current
year, the user can click the name of the month, which displays the list of months and then
allows the user to click the desired month:
To select a year, the user clicks the year number. This changes the year label into a spin
button:
To change the year, the user can click the up or down arrows of the spin button. As the
spin button is displaying, the user can also use the arrow keys of the keyboard to increase
or decrease the value.
Under the title bar, the short names of week days display, using the format set in Control
Panel. In US English, the first day is usually Sunday. The first day can be changed by the
programmer.
On the control, the currently selected date has a circle around. To select a date on the
control, the user clicks the desired date, which changes from the previous selection.
650
In the main area, the numeric days of the month display on a white background (this
color and any color on the control can be changed as we will see in the next section). To
select a date, the user clicks it in the list. By default, the calendar opens with today's day
circled with a hand-drawn-look-alike ellipse. Using the buttons of the title bar, the month
label, and/or the year, the user can change the date. If at one time the calendar is
displaying a date other than today, and if the user wants to return to today's date, he can
click the bottom label that displays Today (you as the programmer can hide the Today
label if you want).
Start Borland C++ Builder or a new project with its default form
2.
3.
4.
5.
6.
Form
BorderStyle: dsDialog
Caption: Employees Payroll
Name: frmMain
ShowHint: true
Panel
Height: 128
Width: 578
Label
Caption: Start Period:
Edit
Name: edtStartPeriod
SpeedButton
AllowAllUp: true
GroupIndex: 18
Name: btnStartPeriod
Glyph: C:\Programs Files\Common Files\Borland
Shared\Images\Buttons\calendar.bmp
Label
Caption: Ending Period
Edit
Name: edtEndPeriod
SpeedButton
AllowAllUp: true
GroupIndex: 20
Name: btnEndPeriod
651
Double-click an empty area on the form to access its OnCreate event and implement
it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
edtStartPeriod->Text = Date();
edtEndPeriod->Text = Date() + 13;
}
//---------------------------------------------------------------------------
8.
Save All
In the same way, you can increase the height to display many months.
To make it a highly visual object, a calendar uses different colors to represent the
background, week days, the background of the title bar, the text of the title bar, the text of
the days of the previous month, and the text of the days of the next month. These colors
are controlled by the CalColors property of the control. At design time, you can set these
colors to the values of your choice:
652
Of course, you can programmatically change these colors. Although any color is allowed
in any category, you should make sure that the calendar is still reasonably appealing and
usable.
Under the title bar, the short names of week days display, using the format set in Control
Panel. In US English, the first day is usually Sunday. If you want to start with a different
day, set the value using the FirstDayOfWeek property. Under the names of the week and
their line separator, the numeric days of the month are listed.
The MonthCalendar control is used to let the user know today's date in two ways. On the
calendar, today's date is circled by a hand-drawn ellipse. In the bottom section of the
calendar, today's date is also displayed as a sentence. If you want to display or hide the
bottom label, set the ShowToday Boolean property accordingly. For example, to hide it,
set this property to false.
At any time, a particular date is selected and has an ellipse with the same color as the
background of the title bar. By default, the selected date is today's date. On the Object
Inspector, it is represented by the Date property. When the user clicks the calendar, a date
is selected. To find out what date the user selected, you can access the
TMonthCalendar::Date value. The Date value of the MonthCalendar is a TDateTime
type. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::MonthCalendar1Click(TObject *Sender)
{
Label1->Caption = MonthCalendar1->Date;
}
//---------------------------------------------------------------------------
When the user clicks the MonthCalendar control, one date is selected. To control whether
the user can select one or more dates, set the value of the MultiSelect property
accordingly. For example, if you want the user to select a range of dates on the control,
set the MuiltiSelect property to true.
2.
On the Component Palette, click the Win32 tab. Double-click the MonthCalendar
button
653
3.
Click an empty area on the form again and, once more, from the Win32 tab of the
Component Palette, double-click the Calendar button
4.
5.
Change the Name of the left Calendar control to calStartPeriod and change the
Name of the right Calendar to calEndPeriod
6.
7.
8.
On the form, double-click the left SpeedButton and implement its OnClick event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnStartPeriodClick(TObject *Sender)
{
if( btnStartPeriod->Down == True )
calStartPeriod->Visible = True;
else
calStartPeriod->Visible = False;
}
//---------------------------------------------------------------------------
9.
On the form, double-click the right SpeedButton and implement its OnClick event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnEndPeriodClick(TObject *Sender)
{
if( btnEndPeriod->Down == True )
calEndPeriod->Visible = True;
else
calEndPeriod->Visible = False;
}
//---------------------------------------------------------------------------
654
TMonthCalendar and, using the new operator, specify a the container and the parent of
the control. Here is an example:
//--------------------------------------------------------------------------#include <vcl.h>
#include <ComCtrls.hpp>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TMonthCalendar *MCal = new TMonthCalendar(this);
MCal->Parent = this;
}
//---------------------------------------------------------------------------
After creating the control, all of the properties we have seen above can be accessed.
To accentuate the importance of one or more days of a month, you can call the
BoldDays() method. Its syntax is:
void __fastcall BoldDays(const unsigned * Days, const int Days_Size,
unsigned &MonthBoldInfo);
This method can be used to format some days in bold and it is mostly used in conjunction
with the OnGetMonthInfo() event. The only event the MonthCalendar control handles
on its own is the OnGetMonthInfo() event. Its syntax is:
void __fastcall OnGetMonthInfo(TObject *Sender, DWORD Month,
DWORD &MonthBoldInfo)
This event fires as soon as the month of the calendar has been changed. As a descendant
of TWinControl, the MonthCalendar control fires the same regular events of a Windows
control.
On the top section of the source file of the form, include the DateUtils header file
under the vcl header file:
#include <DateUtils.hpp>
2.
On the form, double-click the left Calendar and implement its OnClick event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::calStartPeriodClick(TObject *Sender)
{
655
}
//---------------------------------------------------------------------------
3.
On the form, double-click the right Calendar and implement its OnClick event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::calEndPeriodClick(TObject *Sender)
{
// Get the current date selected on the End Period Calendar
TDateTime DateSelected = calEndPeriod->Date;
// Find the numeric "name" of the weekday
Word DOTW = DayOfTheWeek(DateSelected);
// Get the date of the left calendar
TDateTime StartPeriod = calStartPeriod->Date;
// Find out if the user selected a Sunday date
if( DOTW == 7 )
{
// Since the user selected a date on Sunday,
// display it in the Ending Period edit box
if( DateSelected == StartPeriod + 13 )
edtEndPeriod->Text = calEndPeriod->Date;
// Make sure the date the user selected is the week following
// the previous
else
{
ShowMessage("Invalid date selection\n"
"The time sheet period spans 14 days");
}
}
// Make sure the user selects a Sunday date.
// Otherwise, dismiss it
else if( DOTW != 7 )
{
ShowMessage("The selected date is invalid\n"
"A time sheet period ends on Sunday");
}
}
//---------------------------------------------------------------------------
656
4.
5.
657
One of the advantages of the Date and Time Picker control is that it allows the user to
select a time or a date value instead of typing it. This tremendously reduces the likelihood
of mistakes.
To create a Date or Time Picker control, add a DateTimePicker control
or other container.
to a form
2.
3.
4.
Form
BorderStyle: dsDialog
Name: frmMain
Panel
Caption: None
5.
Save All
658
The Time Picker control is a spin button made of different sections: the hours value, the
minutes value, the optional seconds value, and the optional AM/PM string. To change the
time, the user clicks a section and uses either the mouse or the keyboard to increase or
decrease that particular value. To change another value, the user must first click it and
then use the spin button.
By default, the time displays using the HH:MM:SS AM/PM format. This means that the
time uses two digits for the hours from 00 to 11, two digits for the minutes from 00 to 59,
two digits for the seconds from 00 to 59 and the AM or PM for morning or afternoon. To
control how the time displays, set the desired value in the Format property. The possible
values are:
Format
Used For
hh
HH
Minute
mm
Minute
t
tt
AM/PM
AM/PM
Description
Used to display the hour with one digit if the value
is less than 10
Used to display the hour with a leading 0 if the
value is less than 10
Used to display the hour with one digit if the value
is less than 10
Used to display the hour with a leading 0 if the
value is less than 10
Used to display the minute with one digit if the
value is less than 10
Used to display the minute with a leading 0 if the
value is less than 10
Displays the letter A or P for the AM or PM section
Displays the letters AM or PM for the last section
You can set the format at design time using the Format field on the Object Inspector. To
set the format at run time, assign the desired format to the TDateTimePicker::Format
property.
By default, after adding the control to the form or container, it assumes the time of the
computer when the control was added. If you want to set a different time, apply a Format
combination to the Time property. In the same way, at any time you can retrieve the time
value on the control by accessing the Time property.
Sometimes the user may want to manually edit the time of the control such as typing the
hour, the minute, the second, or AM/PM. Fortunately, the Date Picker control is equipped
to natively allow or disallow some values. For example, the user cannot type anything
else than a digit for the hours, minutes, or second portions and he can type only a, A, p, or
P for the AM/PM section. This is the default scenario where you let this object help you
control the values that the user can type. If you want to allow the user to type the value of
the date or time of the control, set the ParseInput Boolean property to true.
659
If the control displays a combo box, if the user clicks the arrow on the Date control, a
calendar object similar to the MonthCalendar control we saw earlier displays:
This calendar displays to the bottom-left or the bottom-right side of the combo box. To
control this alignment, change the value of the CalAlignment property.
The displayed MonthCalendar object allows the user to select a date using the same
techniques we described for the MonthCalendar control. The MonthCalendar of the
DateTimePicker control displays using the same colors and other properties as we saw
with the MonthCalendar control. After the user has selected a date, the date value
displays in the Edit section of the combo box and the calendar disappears.
If the control displays a spin button, the object is divided in different sections that can
each be changed individually:
To change either the day, the month, or the year, the user must click the desired section
and use either the arrows of the button or the arrow keys on the keyboard to increase or
decrease the selected value.
If you want to control the range of dates the user can select, use the MinDate and the
MaxDate properties.
When you add the DateTimePicker control to your form or container, it displays the date
of the computer at the time the control was added. If you want the control to display a
different date, set the desired value in the Date field using the Object Inspector. At any
time, you can find out what value the Date Picker has by retrieving the value of the
TDateTimePicker::Date property.
By default, the date displays using either the Short Date or the Long Date formats of
Control Panel. This display is controlled by the DateFormat field in the Object
Inspector. If you want to customize the way the date is displayed, set the desired string
using the Format property. The possible formats are as follows:
660
Format
Used For
Days
dd
Days
ddd
dddd
M
Weekdays
Weekdays
Months
MM
Months
MMM
MMMM
yy
yyyy
Months
Months
Years
Years
Description
Displays the day as a number from 1 to 31
Displays the day as a number with a leading 0 if the number
is less than 10
Displays a weekday name with 3 letters as Mon, Tue, etc
Displays the complete name of a week day as Monday, etc
Displays the numeric month from 1 to 12
Displays the numeric month with a leading 0 if the number is
less than 10
Displays the short name of the month as Jan, Feb, Mar, etc
Displays the complete name of the month as January, etc
Displays two digits for the year as 00 for 2000 or 03 for 2003
Displays the numeric year with 4 digits
The user may want to edit the date value of the control, including typing the month, day,
year, the name of the month or the name of the weekday. The Date Picker object is
equipped to control the types of values that can be entered. For example, the user cannot
type the name of a month, only a number and the control would display the
corresponding name of the month. This is the default scenario where you let this object
help you control the values that the user can type. If you want to take matters into your
own hands, that is, if you want to allow the user to type the value of the date or time of
the control, set the ParseInput Boolean property to true. If you allow the user to
manually enter the date value, if the value is incorrect, when the control looses focus, the
compiler would make an attempt to convert the date entered into a valid and true date. If
the user enters an invalid value, the compiler would throw an EconvertError error:
This means that you should be reluctant to let the users type whatever they want. The less
they type, the less checking you need to do.
661
2.
3.
Change the Name of the right control to dteEndPeriod and set its CalAlignment
property to dtaRight
4.
Double-click an unoccupied area on the form and implement its OnCreate event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
dteStartPeriod->CalColors->BackColor = TColor(RGB(230, 245, 255));
dteStartPeriod->CalColors->MonthBackColor = TColor(RGB(212, 235, 255));
dteStartPeriod->CalColors->TextColor = clBlue;
dteStartPeriod->CalColors->TitleBackColor = TColor(RGB(0, 0, 160));
dteStartPeriod->CalColors->TitleTextColor = clWhite;
dteStartPeriod->CalColors->TrailingTextColor = TColor(RGB(190, 125, 255));
dteStartPeriod->Format = "ddd d MMM yyyy";
dteEndPeriod->CalColors->BackColor = TColor(RGB(255, 236, 218));
dteEndPeriod->CalColors->MonthBackColor = TColor(RGB(255, 235, 214));
dteEndPeriod->CalColors->TextColor = TColor(RGB(102, 52, 0));
dteEndPeriod->CalColors->TitleBackColor = TColor(RGB(176, 88, 0));
dteEndPeriod->CalColors->TitleTextColor = TColor(RGB(255, 238, 220));
dteEndPeriod->CalColors->TrailingTextColor = TColor(RGB(230, 115, 0));
dteEndPeriod->Format = "ddd d MMM yyyy";
}
//---------------------------------------------------------------------------
5.
6.
The UserString argument is the value that the user is typing. It could be a digit or a letter.
The control returns the DateAndTime as the value of the control. If, during your
checking, you find out that the user typed a wrong value, you can allow or disallow by
setting the value of the AllowChange argument accordingly. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::DateTimePicker1UserInput(TObject *Sender,
const AnsiString UserString, TDateTime &DateAndTime,
662
bool &AllowChange)
if( TDateTime(UserString) < Date() )
{
ShowMessage("The cleaning date you entered, " +
UserString + ", is not valid!");
AllowChange = False;
}
else
{
Edit1->Text = DateAndTime;
AllowChange = True;
}
}
//---------------------------------------------------------------------------
If the Kind property of the control is set to dtkDate and if the DateMode value is
dmComboBox, when the user clicks the arrow of the combo box to display the
MonthCalendar, the OnDropDown() event fires. On the other hand, if the user clicks the
arrow to retract the Calendar, an OnCloseUp() event fires. Both event are TNotifyEvent
type.
On the form, click the left Date and Time Picker control to select it
2.
On the Object Inspector, click the Events tab and scroll down completely if
necessary
3.
Double-click the event side of the OnCloseUp field and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::dteStartPeriodCloseUp(TObject *Sender)
{
// Add two weeks to the date selected by the user
dteEndPeriod->Date = dteStartPeriod->Date + 13;
}
//---------------------------------------------------------------------------
4.
Test the application by changing the date values of the left control and notice that
two weeks are always added to set the value of the right control
663
5.
The intersection of a column and a row is called a cell. The cell is the most important
entity and the most used aspect of a grid. It holds the actual values of the grid. The cells
of a grid can be used to display data or they can be used to receive data from the user.
This means that data of a grid is entered or stored in cells.
Because the role of a grid is unpredictable, the most top cell of each column can be used
to display a label. By nature, a column specifies a category of value. Therefore, the label
of a column signifies the category of values of that column:
To create a series of values for each category, you use a row of data. A row is also called
a record. To make a record explicit, the most left row can display a label. The easiest and
most basic label consists of a number. In this case, rows can be labeled from top to
bottom as 1, 2, 3, 4, etc.
664
In most cases, each cell is in fact an Edit control and its content is an AnsiString. This
means that a cell can contain a natural number, a floating-point variable, or a string.
Like many other controls, a grid is represented with a 3-D effect that raises its borders.
This effect is controlled by the BorderStyle property. If you do not want to display
borders on the control, set the BorderStyle property to bsNone:
A grid is made of vertical divisions called columns and horizontal divisions called rows.
Two of the most visual characteristics of a StringGrid control are its number of columns
and its number of rows. These two values are set using the ColCount and the RowCount
properties. The values are integer type and should be >= 0. If you set either property to a
negative value, it would be set to 1. If you do not want to display columns, set the
ColCount to 0. In the same way, if you do not want to display rows, set the RowCount
value to 0.
By default, if a grid contains more columns than its width can show, it would display a
vertical scroll bar. In the same way, if there are more rows than the control's height can
accommodate, it would be equipped with a horizontal scroll bar. The ability to display
scroll bars is controlled by the ScrollBars property. You can use it to display only the
vertical scroll bar (ssVertical), only the horizontal scroll bar (ssHorizontal), both scroll
bars (ssBoth), or no scroll bar (ssNone) at all.
Like most other list-based controls, a grid is used to display data and, in some
applications, you may want the users to enter or use values of the grid control. To guide
Copyright 2003 FunctionX, Inc.
665
the users with the values in the grid, you can display explicit text in the fixed columns
and fixed rows. The top and left cells are qualified as fixed and, by default, they are the
most top and the left cells respectively. Besides these ranges of cells, you can add a fixed
row of cells and a fixed column of rows.
If you want to display only one fixed row, it must be the most top range. This
characteristic is controlled by the FixedRows property and, by default, is set to 1. If you
want to display an additional range of fixed cells on top, change the value of FixedRows.
In the same way, the number of fixed columns on the left side of the object is controlled
by the FixedCols property. Setting either of these values to 0 would hide the fixed
column or row:
FixedCols=1; FixedRows=1
FixedCols=0; FixedRows=0
FixedCols=1; FixedRows=0
FixedCols=0; FixedRows=1
FixedCols=2; FixedRows=1
FixedCols=1; FixedRows=2
when displaying a different color than the cells in the middle-center section of the
control, are called fixed cells.
To guide the user with the values on the grid, the cells on top and those on the left display
the same color as the form, known in the Control Panel as the Button Color. The cells
that display usable and modifiable values have a background color known in Control
Panel as Window Color. To change the background color of cells that display values, use
the Color property of the Object Inspector. Here is a grid with the clSkyBlue color:
To change the colors of the fixed columns and rows, change the color value of the
FixedColor property. Here is a grid with the clNavy FixedColor:
}
//---------------------------------------------------------------------------
To distinguish cells, they are separated by vertical and horizontal lines known as grid
lines. By default, the grid lines have a width of 1 integer. To display a wider line, change
the value of the GridLineWidth. A reasonable value should be less than 10. If you do
not want to display grid lines, set the GridLineWidth to 0.
In order to access all of the cells that are part of a column, you should know the columns
index number. The most left column, which is sometimes the fixed column, unless the
FixedCols value is set to 0, has an index of 0. The second column from left has an index
of 1, etc. In the same way, rows are presented by an index. The most top row has an
index of 0; the second row from top has an index of 1, etc.
667
By default, all columns have a width of 64 pixels. At design or run time, you can control
this by changing the value of the DefaultColWidth property. If you want to control the
widths of individual columns, at run time, call the TStringGrid::ColWidths property
and specify the index of the column you need. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
StringGrid1->Color = TColor(RGB(255, 230, 204));
StringGrid1->FixedColor = TColor(RGB(255, 128, 0));
StringGrid1->ColWidths[2] = 48;
StringGrid1->ColWidths[3] = 22;
StringGrid1->ColWidths[4] = 96;
}
//---------------------------------------------------------------------------
By default, all rows have a height of 24 pixels. At design or run time, you can control this
by changing the value of the DefaultRowHeight property. If you want to control the
height of individual rows, at run time, call the TStringGrid::ColHeights property and
specify the index of the row you want access to. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
StringGrid1->Color = TColor(RGB(255, 230, 204));
StringGrid1->FixedColor = TColor(RGB(255, 128, 0));
StringGrid1->ColWidths[2] = 48;
StringGrid1->ColWidths[3] = 22;
StringGrid1->ColWidths[4] = 96;
StringGrid1->RowHeights[1] = 18;
StringGrid1->RowHeights[2] = 40;
StringGrid1->RowHeights[3] = 12;
}
//---------------------------------------------------------------------------
668
The columns of the grid are stored in a collection or array called Cols. By specifying the
index of a column, you can change the label of the column header. In the same way, the
rows are grouped in a collection called Rows. This allows you to change the text of a row
header based on its index. The cells of a grid are stored in a two-dimensional array called
Cells.
The TStringGrid class provides Options that allow you to customize the behavior of the
StringGrid control. For example, if you want to allow the user to move the position of a
column, set the goColMoving option to true. If you want users to be able to move rows,
set the goRowMoving option to true.
In order to display cells with their default control appearance, the DefaultDrawing
property must be set to true, which is the default. If you want to further customize the
appearance of cells, you may have to draw them at run time. In this case, you would set
the DefaultDrawing value to false.
2.
Label
Caption: First Week
Label
Caption: Second Week
StringGrid
Hint: Enter hours worked for each day in the appropriate cell
ColCount: 7
FixedCols: 0
Height: 78
Name: grdTimeSheet
RowCount: 3
Width: 458
Copyright 2003 FunctionX, Inc.
669
Options
GoEditing: true
Panel
BitBtn
Glyph C:\Program Files\Common Files\Borland
Shared\Images\Buttons\calculat.bmp
Caption: &Process It
Default: true
StringGrid
ColCount: 3
Height: 78
RowCount: 3
Width: 185
Label
Caption: Hourly Salary:
Edit
Name: edtHourlySalary
Width: 58
Label
Caption: Total Earnings:
Edit
Name: edtTotalEarnings
Width: 58
BitBtn
Kind: bkClose
GoTabs: true
Name: btnProcessIt
Name: grdEarnings
3.
Save All
4.
5.
=
=
=
=
=
=
=
"Monday";
"Tuesday";
"Wednesday";
"Thursday";
"Friday";
"Saturday";
"Sunday";
grdTimeSheet->RowHeights[0] = 18;
grdTimeSheet->Cells[0][1] = "0.00";
670
grdTimeSheet->Cells[1][1]
grdTimeSheet->Cells[2][1]
grdTimeSheet->Cells[3][1]
grdTimeSheet->Cells[4][1]
grdTimeSheet->Cells[5][1]
grdTimeSheet->Cells[6][1]
=
=
=
=
=
=
"0.00";
"0.00";
"0.00";
"0.00";
"0.00";
"0.00";
grdTimeSheet->RowHeights[1] = 18;
grdTimeSheet->Cells[0][2]
grdTimeSheet->Cells[1][2]
grdTimeSheet->Cells[2][2]
grdTimeSheet->Cells[3][2]
grdTimeSheet->Cells[4][2]
grdTimeSheet->Cells[5][2]
grdTimeSheet->Cells[6][2]
=
=
=
=
=
=
=
"0.00";
"0.00";
"0.00";
"0.00";
"0.00";
"0.00";
"0.00";
grdTimeSheet->RowHeights[2] = 18;
grdEarnings->Cells[0][1]
grdEarnings->Cells[0][2]
grdEarnings->Cells[1][0]
grdEarnings->Cells[2][0]
=
=
=
=
"Regular";
"Overtime";
"Hours";
"Amount";
}
//---------------------------------------------------------------------------
6.
7.
671
After creating the control, you can set its properties to the values of your choice. After
using the control, you can get rid of it using the delete operator, or you can trust its parent
to do it for you.
If at any time a cell is selected, you can get the rectangular dimension of that cell using
the CellRect() method. Its syntax is:
TRect __fastcall CellRect(int ACol, int ARow);
The arguments, ACol and ARow, represent the column index and the row index of the cell
that has focus. This method returns the TRect rectangle of the cell. You can call this
method when the user clicks a cell in the StringGrid control. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
StringGrid1->ColWidths[0] = 72;
StringGrid1->ColWidths[1] = 54;
StringGrid1->ColWidths[2] = 35;
StringGrid1->ColWidths[3] = 75;
StringGrid1->RowHeights[0]=
StringGrid1->RowHeights[1]=
StringGrid1->RowHeights[2]=
StringGrid1->RowHeights[3]=
15;
22;
10;
35;
}
//--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1Click(TObject *Sender)
{
TRect Recto = StringGrid1->CellRect(StringGrid1->Col, StringGrid1->Row);
int Area = Recto.Width() * Recto.Height();
Label1->Caption = Area;
}
//---------------------------------------------------------------------------
672
If the mouse is positioned or passing somewhere on or over the StringGrid control and
you want to know on what cell the mouse is, you can use the MouseToCell() method. Its
syntax is:
void __fastcall MouseToCell(int X, int Y, int &ACol, int &ARow);
In the same way, sometimes when the user clicks a cell, you may want to find out what
cell was clicked. To get this information, you can call the GridCoord() method. Its
syntax is:
struct TGridCoord
{
int X;
int Y;
};
673
This method also is usually used in a mouse event. It takes as arguments the mouse
position and returns a TPoint-like object, called TGridCoord. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
TGridCoord GC = StringGrid1->MouseCoord(X, Y);
Label1->Caption = "Col: " + AnsiString(GC.X) + " Row: " + AnsiString(GC.Y);
}
//---------------------------------------------------------------------------
The ACol and ARow arguments represent the cell that is about to be selected. The
CanSelect argument allows you to specify whether the user is allowed to select the
content of the cell. This event allows you to decide whether the cell can be selected or
not. The following event is used to let the user know that a certain cell cannot be
accessed:
//--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1SelectCell(TObject *Sender, int ACol,
int ARow, bool &CanSelect)
{
if( ACol == 2 && ARow == 4 )
{
ShowMessage("The content of this cell is not accessible.\n"
"Please select another cell!");
return;
}
}
//---------------------------------------------------------------------------
674
If the user has selected a cell and wants to edit its content, you can find out the content of
such a cell using the OnGetEdtText() event. This even fires as soon as the user has
selected text included in a cell but just before the user has had a chance to edit it. This
means that you can determine whether the user is allowed to change the contents of a
particular cell. When this even fires, it communicates the grid coordinates of the cell that
was clicked, allowing you to retrieve the content of that cell and do what you want. Here
is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1GetEditText(TObject *Sender, int ACol,
int ARow, AnsiString &Value)
{
AnsiString Content = StringGrid1->Cells[ACol][ARow];
Label1->Caption = Content;
}
//---------------------------------------------------------------------------
On the other hand, when the user has changed the content of a cell, the StringGrid control
fires an OnSetEditText() event. This is a good place to validate, accept, or reject the
changes that the user has performed. This event also provides you with the grid
coordinates of the cell whose contents the user has modified.
To better control what type of text the user is allowed to enter in a cell, in all cells of a
particular row, or in all cells of a particular column, you can use the OnGetEditMask()
event. Its syntax is:
void __fastcall OnGetEditMask(TObject *Sender, int ACol, int ARow, AnsiString &Value)
The ACol and the ARow parameters represent the grid indexes of the cell. The Value is a
string of the same type used for the EditMask property of the MaskEdit control. This
event is used to set the EditMask needed for a particular cell. The following event
restricts only US Social Security Numbers in all cells of the second column:
//--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1GetEditMask(TObject *Sender, int ACol,
int ARow, AnsiString &Value)
{
if( StringGrid1->Col == 2 )
Value = "000-00-0000";
}
//---------------------------------------------------------------------------
675
If you had allowed the user to move the columns, whenever a user has performed this
operation, the StringGrid control would fire the OnColumnMoved() event. Its syntax is:
void __fastcall TStringGrid(TObject* Sender, long FromIndex, long ToIndex);
This event is a good place to decide what to do, if there is anything to do, when the user
has moved a column. In the same way, if you had allowed the user to move rows, the
StringGrid control sends an OnRowMoved() event immediately after the user has moved
a row.
If you had let the compiler know that you would set the appearance of cells yourself,
which would have been communicated by setting the DefaultDrawing property to false,
you can use the OnDrawCell() event to perform this customization. The syntax of this
event is:
void __fastcall StringGridDrawCell(TObject *Sender, int ACol, int ARow,
TRect &Rect, TGridDrawState State)
The cell whose characteristics need to be set is Cell[ACol][ARow]. This means that you
can locate any cell in the grid and set its properties as you like and as possible. For
example, you can change the individual background color of a cell. The following code
changes the background color of Cell[3][2] to blue:
//--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol,
int ARow, TRect &Rect, TGridDrawState State)
{
if( ACol == 3 && ARow == 2 )
{
StringGrid1->Canvas->Brush->Color = clBlue;
StringGrid1->Canvas->FillRect(Rect);
}
}
//---------------------------------------------------------------------------
In the same way, you can change the text color of any cell of your choice independently
of the other cells.
The Rect parameter is the location and dimension of the cell whose characteristics you
want to change.
The State argument is a member of the TGridDrawState set which is defined as follows:
enum Grids__3 { gdSelected, gdFocused, gdFixed };
676
This set allows you to examine the state of a particular cell. Because this value is a set, a
particular cell can have more than one of these values. If a cell is selected, which by
default gives it a background color different than the others, then its State contains the
gdSelected value. If a cell has focus, which could mean that the user has just clicked it,
sometimes to edit, the cell has the gdFocused value. Note that a cell can be selected and
have focus, which means it would have both gdSelected and gdFocused. If a cell is a
fixed cell as we described previously, then the cell has the gdFixed value.
Here is an example of using the OnDrawCell() event to customize the appearance of a
StringGrid object:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
StringGrid1->DefaultDrawing = False;
}
//--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol,
int ARow, TRect &Rect, TGridDrawState State)
{
if( State.Contains(gdFixed) )
{
StringGrid1->Canvas->Brush->Color = static_cast<TColor>(RGB(255, 155, 0));
StringGrid1->Canvas->Font->Style = TFontStyles() << fsBold;
StringGrid1->Canvas->Font->Color = static_cast<TColor>(RGB(250, 245, 135));
StringGrid1->Canvas->Rectangle(Rect);
}
else if( State.Contains(gdSelected) )
{
StringGrid1->Canvas->Brush->Color = static_cast<TColor>(RGB(255, 205, 155));
StringGrid1->Canvas->Font->Style = TFontStyles() >> fsBold;
StringGrid1->Canvas->Font->Color = clNavy;
StringGrid1->Canvas->FillRect(Rect);
}
else
{
StringGrid1->Canvas->Brush->Color = clWhite;
StringGrid1->Canvas->Font->Color = clBlue;
StringGrid1->Canvas->FillRect(Rect);
}
StringGrid1->ColWidths[0]
StringGrid1->ColWidths[1]
StringGrid1->ColWidths[2]
StringGrid1->ColWidths[3]
=
=
=
=
15;
75;
75;
90;
677
StringGrid1->ColWidths[4] = 120;
StringGrid1->RowHeights[0]
StringGrid1->RowHeights[1]
StringGrid1->RowHeights[2]
StringGrid1->RowHeights[3]
StringGrid1->RowHeights[4]
=
=
=
=
=
16;
16;
16;
16;
16;
=
=
=
=
"First Name";
"Last Name";
"Phone Number";
"Email Address";
StringGrid1->Cells[1][1] = "Alex";
StringGrid1->Cells[2][1] = "Walters";
StringGrid1->Cells[3][1] = "(202) 133-7402";
StringGrid1->Cells[4][1] = "[email protected]";
StringGrid1->Cells[1][2] = "Bertrand";
StringGrid1->Cells[2][2] = "Kumar";
StringGrid1->Cells[4][2] = "[email protected]";
StringGrid1->Cells[3][3] = "Hermine";
}
//---------------------------------------------------------------------------
In the private section of the form, declare two variables and a method as follows:
private:
double HoursWeek1, HoursWeek2;
double __fastcall EvaluateTime(AnsiString StrTime); // User declarations
2.
In the top section of the source file of the form, include the StrUtils header file:
//--------------------------------------------------------------------------#include <vcl.h>
678
#include <DateUtils.hpp>
#include <StrUtils.hpp>
#pragma hdrstop
#include "Main.h"
//---------------------------------------------------------------------------
3.
4.
To use of the StringGrid events, on the form, click the top StringGrid control
5.
In the Object Inspector, click the Events tab and double-click the event side of
OnSetEditText
6.
Implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::grdTimeSheetSetEditText(TObject *Sender, int ACol,
int ARow, const AnsiString Value)
{
double Monday1 = EvaluateTime(grdTimeSheet->Cells[0][1]);
double Tuesday1 = EvaluateTime(grdTimeSheet->Cells[1][1]);
double Wednesday1 = EvaluateTime(grdTimeSheet->Cells[2][1]);
double Thursday1 = EvaluateTime(grdTimeSheet->Cells[3][1]);
double Friday1 = EvaluateTime(grdTimeSheet->Cells[4][1]);
double Saturday1 = EvaluateTime(grdTimeSheet->Cells[5][1]);
double Sunday1 = EvaluateTime(grdTimeSheet->Cells[6][1]);
HoursWeek1 = Monday1 + Tuesday1 + Wednesday1 +
Thursday1 + Friday1 + Saturday1 + Sunday1;
double Monday2 = EvaluateTime(grdTimeSheet->Cells[0][2]);
double Tuesday2 = EvaluateTime(grdTimeSheet->Cells[1][2]);
double Wednesday2 = EvaluateTime(grdTimeSheet->Cells[2][2]);
double Thursday2 = EvaluateTime(grdTimeSheet->Cells[3][2]);
double Friday2 = EvaluateTime(grdTimeSheet->Cells[4][2]);
double Saturday2 = EvaluateTime(grdTimeSheet->Cells[5][2]);
double Sunday2 = EvaluateTime(grdTimeSheet->Cells[6][2]);
HoursWeek2 = Monday2 + Tuesday2 + Wednesday2 +
Thursday2 + Friday2 + Saturday2 + Sunday2;
}
//---------------------------------------------------------------------------
7.
On the form, double-click the Process It button and implement its OnClick event as
follows:
//---------------------------------------------------------------------------
679
}
//---------------------------------------------------------------------------
8.
680
9.
This control can be used anywhere you need a flexible calendar other than the Windows
MonthCalendar control. You may be wondering why one more calendar, considering that
those we reviewed above seemed to be enough. The CCalendar control was configured to
only display the days of one month, on a grid. As such, it gives you a lot of customization
on the way the control appears. It gives you complete control on the way date-related
calculations are performed.
Behind the scenes, the CCalendar control is based on the TCustomGrid class. Therefore,
the control is made of a series of 7 columns and 6 rows, separated by grid lines whose
width is one pixel. As you would do with the StringGrid object, you can change the
Copyright 2003 FunctionX, Inc.
681
background color of the Ccalendar control using the Color property and you can control
the width of the grid lines with the GridLineWidth property.
Like most other controls, the CCalendar control is surrounded with a border. Unlike most
controls, this object uses the same BorderStyle as the Form but with different effects.
For example, the default bsSingle value gives it a sunken effect but all the other styles
(bsDialog, bsNone, bsSizeable, bsSizeToolWin, and bsToolWindow) remove the
borders on the left, the right, and the bottom sides of the control while keeping the names
of days raised.
The CCalendar control is meant to display one month starting with the first week of the
month. If you want the calendar to start on a different week, such as the second, third, or
fourth week, change the value of the StartOfWeek property.
The days of the month display in cells. When you add the control on a form, it assumes
and displays the current date, selecting the current month and year, highlighting the cell
that contains the current day, using the current date of the computer. If you do not want to
display the current date, you can set the UseCurrentDate Boolean value to false from its
true default. You can also specify what day of the month, what month, and what year
should be selected when the control appears. These characteristics are respectively
controlled by the Day, the Month, and the Year properties.
To change the day of the month, the user can click the desired day, which selects its cell
and gives it a different background color than the other cells. If you want a calendar to
only display a date and you would not like the user to be able to change the date, set the
ReadOnly property to true.
682
The Color Grid is a custom control used to display a series of 16 colors in columns and
rows. The colors appear in cells. The grid can be organized to display as 4 rows of 4
columns, 1 row of 16 columns, 16 rows of 1 column, 8 rows of 2 columns, or 2 rows of 8
columns:
To use this control, the user clicks a color in a cell. When using this control, the user can
take advantage of the left and the right mouse buttons because the selection of a color
depends on what button of the mouse was pressed. If the user clicks a cell with the left
mouse button, a color known as Foreground is selected. The right mouse button is used
to select a color known as Background.
There is no predictable reason why you would implement a Color Grid control on your
application except to let the user select colors. This means that it is up to you to decide
what this control is used for. Although you can create an object, add 16 buttons, and
configure their colors, this control highly simplifies your efforts.
After adding the control, it display 4 columns and 4 rows of colors. If you want to align
cells different, as seen on the above picture, change the value of the GridOrdering
property. The options are go16x1, go1x16, go2x8, go4x4 (the default), and go8x2. Their
enumerator is defined as:
enum TGridOrdering { go16x1, go8x2, go4x4, go2x8, go1x16 };
The Color Grid control is used to specify two colors. The colors are specified by the cells
of the grid. These cells are numbered from left to right, then from top to bottom. This
numbering allows you to refer to any cell when necessary.
When the control has just been added to a form, both the foreground and the background
colors are set to the first cell, 0, which is black. When using the control, the user would
Copyright 2003 FunctionX, Inc.
683
click a cell on the grid. If you do not want the users to be able to select or change a color,
set the Enabled property to false.
If the control is enabled and if the user clicks with the left mouse button, a color referred
to as Foreground is selected and its cell displays FG. If the user clicks with the right
mouse button, a color called Background is selected and its cell would display the GB
label. These two labels allow the user to know the current colors that are selected. If you
want to hide the indicating labels, use the ForegroundEnabled and the
BackgroundEnabled properties. Setting either to false would hide its corresponding
label.
After adding the control to a form or to another container such as a frame or a panel, you
can create the actual list using the Strings property. To edit the list, type a string under
the Key column and type another string under the Value column.
You can also edit the list in an event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Fill out the Value List Editor with a few strings
ValueListEditor1->Strings->Add("Maryland=MD");
ValueListEditor1->Strings->Add("Virginia=VA");
ValueListEditor1->Strings->Add("District of Columbia=DC");
ValueListEditor1->Strings->Add("West Virginia=WV");
ValueListEditor1->Strings->Add("Delaware=DE");
}
//---------------------------------------------------------------------------
684
To provide a combo box on a Value cell, first create a list, and then assign it to the
PickList property of the ItemProps member variable of the TValueListEditor class.
When doing this, you must specify on the ItemProps which value will use the combo
box. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Fill out the Value List Editor with a few strings
ValueListEditor1->Strings->Add("Maryland=MD");
ValueListEditor1->Strings->Add("Virginia=VA");
ValueListEditor1->Strings->Add("District of Columbia=DC");
ValueListEditor1->Strings->Add("West Virginia=WV");
ValueListEditor1->Strings->Add("Delaware=DE");
// Before creating a combo box, create a list of items
TStringList *ListOfStates = new TStringList;
ListOfStates->Add("SD");
ListOfStates->Add("ND");
ListOfStates->Add("NV");
ListOfStates->Add("VA");
ListOfStates->Add("WV");
ListOfStates->Add("WA");
ListOfStates->Add("SC");
ListOfStates->Add("NC");
ListOfStates->Add("PA");
ListOfStates->Add("IL");
ListOfStates->Add("LA");
ListOfStates->Add("TX");
ListOfStates->Add("MD");
ListOfStates->Add("DC");
ListOfStates->Add("TN");
ListOfStates->Add("MI");
// Put a combo box in the second item
ValueListEditor1->ItemProps[1]->PickList = ListOfStates;
}
//---------------------------------------------------------------------------
685
686
Values->Add("TN");
Values->Add("MI");
}
//---------------------------------------------------------------------------
In the same way, you can display a list that depends on the string of the Key cell. This
allows you to display a combo box only if the Key cell is not empty. This can be done as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::ValueListEditor1GetPickList(TObject *Sender,
const AnsiString KeyName, TStrings *Values)
{
if( KeyName == "CAF" )
{
Values->Add("Cameroon");
Values->Add("Senegal");
Values->Add("Lybia");
Values->Add("Ghana");
Values->Add("Tunisia");
}
else if( KeyName == "CONCACAF" )
{
Values->Add("USA");
Values->Add("Costa Rica");
Values->Add("Canada");
Values->Add("Colombia");
Values->Add("Nicaragua");
Values->Add("Jamaica");
}
else if( KeyName == "UEFA" )
{
Values->Add("Portugal");
Values->Add("Spain");
Values->Add("Italy");
Values->Add("Holland");
}
else
{
Values->Add("New Zealand");
Values->Add("Greece");
Values->Add("Gabon");
}
}
//---------------------------------------------------------------------------
687
ValueListEditor1->Strings->Add("District of Columbia=DC");
ValueListEditor1->Strings->Add("West Virginia=WV");
ValueListEditor1->Strings->Add("Delaware=DE");
// Put an ellipsis button in the 4th item
ValueListEditor1->ItemProps[3]->EditStyle = esEllipsis;
}
//---------------------------------------------------------------------------
After adding a button to a Value cell, it is your responsibility to specify what would
happen when the user clicks it. For example, imagine you want the user to select a file
and provide its path as the value of a cell, you can implement this behavior in the
TValueListEditor::OnEditButtonClick() event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::ValueListEditor1EditButtonClick(TObject *Sender)
{
if( OpenDialog1->Execute() )
ValueListEditor1->Values["West Virginia"] = "Result=" +
OpenDialog1->FileName;
}
//---------------------------------------------------------------------------
688
A TreeView is not limited to a one-to-one correspondence. Not only can an item have
more than one dependency, but also a child can make the tree stop at any category.
Categories of a TreeView are organized by levels. The most used trees have one parent
called the root; then under the root start the dependents.
Here is an example representing the world and some countries:
The children of a parent are recognized by their belonging to the same level but can have
different behaviors; for example, while one child might have another child (or other
children), an item on the same level does not necessarily abide by a set rule. Everything
usually depends on the tree designer.
689
2.
3.
4.
Click the Create New Folder button, type Countries1 and press Enter twice to
display the new folder in the Save In combo box.
5.
Click Save twice to save the unit and the project names.
6.
7.
From the Standard tab of the Component Palette, double-click the Panel control
.
8.
9.
To type the caption, click the Caption field, press Delete, press Space, and type
Countries
again.
12. Set its Align property to alBottom, its BevelOuter to bvLowered, delete the value
of its Caption, and set its Height to 20
(expanded) buttons. These last two details will be important because at times you will
need to know whether a node is selected or not, whether a node has a child or not. The
first issue is to know how to create a TreeView and how to assign a child or children to a
node.To create a tree view, use the TreeView control
from the Win32 tab of the
Component Palette. After placing the control on the form, create its children using the
Items property. To include pictures on your tree, use an ImageList control.
Click in the middle of the form. On the Win32 tab of the Component Palette doubleclick the TreeView control
2.
While the new TreeView1 control is still selected, on the Object Inspector, set its
Align property to alLeft and its Width to 140
3.
4.
On the TreeView Items Editor dialog box, click the New Item button
5.
6.
Type Universe
7.
8.
9.
691
25. After using the form, close it and save the project
26. To use some images, make sure the Component Palette displays the Win32 tab. Click
the ImageList control.
27. Click anywhere on the form.
28. On the form, double-click the ImageList button
29. On the Form1->ImageList1 ImageList dialog box, click the Add button.
30. Locate the folder where the exercises are located and display the Bitmaps folder.
31. Click World and click Open
32. Repeat the same process to add Band, Dans, Category, Insert, and Records
692
Image Index
Selected Index
World
America / Africa/ Europe / Asia
Chile / Canada / Thailand / Afghanistan
0
2
4
1
3
5
42. Click OK
43. To test the TreeView, press F9
44. Click the various + to expand and click items to select them. Notice the change of
graphics
693
45. After using the form, close it and save the application
694
1.
2.
3.
4.
On the Win32 tab of the Component Palette double-click the TreeView control
Copyright 2003 FunctionX, Inc.
5.
While the new TreeView control is still selected, set its Align property to alLeft and
its width to 175
6.
To create the treeview, double-click an unoccupied area of the form to access the
forms OnCreate event.Implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
// The root node
TreeView1->Items->Add(NULL, "World");
// First child and its children
TreeView1->Items->AddChild(TreeView1->Items->Item[0], "Africa");
TreeView1->Items->AddChild(TreeView1->Items->Item[1], "Botswana");
TreeView1->Items->AddChild(TreeView1->Items->Item[1], "Benin");
// Second child and its children
TreeView1->Items->AddChild(TreeView1->Items->Item[0],
TreeView1->Items->AddChild(TreeView1->Items->Item[4],
TreeView1->Items->AddChild(TreeView1->Items->Item[4],
TreeView1->Items->AddChild(TreeView1->Items->Item[4],
"Europe");
"Belgium");
"Denmark");
"Poland");
}
//---------------------------------------------------------------------------
7.
8.
9.
10. To implement a more professional treeview creation, change the listing of the
OnCreate event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TTreeNode *World, *Continent;
// The root node
TreeView1->Items->Add(NULL, "World");
World = TreeView1->Items->Item[0];
// First child of the root
TreeView1->Items->AddChild(World, "Africa");
Continent = TreeView1->Items->Item[1];
// Children of first node
TreeView1->Items->AddChild(Continent, "Botswana");
TreeView1->Items->AddChild(Continent, "Benin");
// Second child of the root
TreeView1->Items->AddChild(World, "Europe");
695
Continent = TreeView1->Items->Item[4];
// Children of second node
TreeView1->Items->AddChild(Continent, "Belgium");
TreeView1->Items->AddChild(Continent, "Denmark");
TreeView1->Items->AddChild(Continent, "Poland");
// Third child of the root
TreeView1->Items->AddChild(World, "America");
Continent = TreeView1->Items->Item[8];
// Children of the third node
TreeView1->Items->AddChild(Continent, "Panama");
TreeView1->Items->AddChild(Continent, "Colombia");
// Fourth child of the root
TreeView1->Items->AddChild(World, "Asia");
Continent = TreeView1->Items->Item[11];
TreeView1->Items->AddChild(Continent, "Asia");
TreeView1->Items->AddChild(Continent, "Bangladesh");
}
//---------------------------------------------------------------------------
696
1.
To add some pictures to the treeview, from the Win32 tab, double-click the
ImageList to add it to the form
2.
3.
Click Add.
4.
From the Icons folder, include the following icons: Options, World, Target, Dans,
Botswana, Belgium, Denmark, and Columbia.
5.
Click OK
Copyright 2003 FunctionX, Inc.
6.
7.
8.
To assign the images to the appropriate nodes, at the end of the OnCreate even, just
before the closing curly bracket, type the following:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TTreeNode *World, *Continent;
...
TreeView1->Items->Item[2]->ImageIndex = 4;
TreeView1->Items->Item[5]->ImageIndex = 5;
TreeView1->Items->Item[6]->ImageIndex = 6;
TreeView1->Items->Item[10]->ImageIndex = 7;
}
//---------------------------------------------------------------------------
9.
697
2.
3.
4.
5.
6.
To create our listview, on the Component Palette, click the Win32 tab.
7.
8.
9.
10. While the listview control is still selected on the form, on the Object Inspector, click
the Items field and click its ellipsis button
11. On the ListView Items Editor dialog, click New Item
12. Type Yemen and press Enter.
13. Type Botswana and press Enter
14. Type Belgium and press Enter
15. Type Colombia and press Enter
16. Type Denmark and press Enter
17. Type Benin
21. On the form, click the listview. On the Object Inspector, click the Columns field and
click its ellipsis button.
22. From the Editing ListView1->Columns window, click Add New Item.
23. While the 0 TListColumn line is still selected, type Countries to change the
Caption of the column header.
24. Click the Width field and type 80
25. Click Add New Item, type Area and change its width to 100
26. Click Add New Item, type Population and change its width to 100
27. Click Add New Item, type Budget and change its width to 50
28. Click Add New Item, type Capital and change its width to 60
699
700
Image Index
0
Botswana
Belgium
Colombia
Danemark
Benin
New SubItem
527,970
17,479,206
17B
Sanaa
600,370
1,576,470
1.6B
Gaborone
30,510
10,241,506
116.5B
Brussels
1,138,910
39,685,655
22B
Bogota
43,094
5,336,394
59.7B
Copenhagen
112,620
6,395,919
299M
Cotonou
701
ListView1->ViewStyle = vsReport;
}
//---------------------------------------------------------------------------
702
1.
2.
3.
4.
From the Win32 tab of the Component Palette, double-click the ListView control
5.
Change the Height of the ListView1 control to 200 and its Width to 350
6.
Double-click an empty area on the form and implement the OnCreate event of the
form as form
7.
At the end of the existing content of the event but before the closing bracket, type:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TListColumn *ListCol;
TListItem *ListIt;
ListCol = ListView1->Columns->Add();
ListCol->Caption = "Country";
ListCol->Width = 95;
ListCol = ListView1->Columns->Add();
ListCol->Caption = "Area (Km2)";
ListCol->Width = 70;
ListCol->Alignment = taRightJustify;
ListCol = ListView1->Columns->Add();
ListCol->Caption = "Population (M)";
ListCol->Width = 90;
ListCol->Alignment = taRightJustify;
ListCol = ListView1->Columns->Add();
ListCol->Caption = "Budget";
ListCol->Width = 50;
ListCol->Alignment = taRightJustify;
ListCol = ListView1->Columns->Add();
ListCol->Caption = "Capital";
ListCol->Width = 85;
ListCol = ListView1->Columns->Add();
ListCol->Caption = "@";
ListCol->Width = 30;
ListIt = ListView1->Items->Add();
ListIt->Caption = "Belgium";
ListIt->SubItems->Add("30,510");
ListIt->SubItems->Add("10,241,506");
ListIt->SubItems->Add("116.5B");
ListIt->SubItems->Add("Gaborone");
ListIt->SubItems->Add("BC");
ListIt = ListView1->Items->Add();
ListIt->Caption = "Colombia";
ListIt->SubItems->Add("1,138,910");
ListIt->SubItems->Add("39,685,655");
ListIt->SubItems->Add("22B");
ListIt->SubItems->Add("Bogota");
ListIt->SubItems->Add("CO");
ListIt = ListView1->Items->Add();
ListIt->Caption = "Botswana";
ListIt->SubItems->Add("600,370");
ListIt->SubItems->Add("1,576,470");
ListIt->SubItems->Add("1.6B");
ListIt->SubItems->Add("Gaborone");
703
ListIt->SubItems->Add("BC");
ListIt = ListView1->Items->Add();
ListIt->Caption = "Danemark";
ListIt->SubItems->Add("43,094");
ListIt->SubItems->Add("5,336,394");
ListIt->SubItems->Add("59.7B");
ListIt->SubItems->Add("Copenhagen");
ListIt->SubItems->Add("DA");
ListIt = ListView1->Items->Add();
ListIt->Caption = "Bangladesh";
ListIt->SubItems->Add("144,000");
ListIt->SubItems->Add("129,194,224");
ListIt->SubItems->Add("4.3B");
ListIt->SubItems->Add("Dhaka");
ListIt->SubItems->Add("BG");
ListIt = ListView1->Items->Add();
ListIt->Caption = "Benin";
ListIt->SubItems->Add("112,620");
ListIt->SubItems->Add("6,395,919");
ListIt->SubItems->Add("299M");
ListIt->SubItems->Add("Cotonou");
ListIt->SubItems->Add("BN");
ListView1->ViewStyle = vsReport;
}
//---------------------------------------------------------------------------
8.
9.
10. Press F12 to display the form. To add images, from the Win32 tab, double-click the
ImageList control.
11. Double-click the ImageList icon on the form.
12. Click Add and add the following images: Map, Belgium, Colombia, Botswana,
Denmark, Bangladesh, and Benin
704
13. Click OK
14. On the form, click the ListView1 control and, on the Object Inspector, set its Images
value to ImageList1
15. Open the FormCreate event again.
16. After the TListItem line, add the following:
ListView1->LargeImages = ImageList1;
ListView1->SmallImages = ImageList1;
17. After the first ListIt->Caption line, add the following:
ListIt->Caption = "Belgium";
ListIt->ImageIndex = 0;
18. In the same way, assign an image to each item list:
ListIt->Caption = "Colombia";
ListIt->ImageIndex = 1;
ListIt->Caption = "Botswana";
ListIt->ImageIndex = 2;
ListIt->Caption = "Danemark";
ListIt->ImageIndex = 3;
ListIt->Caption = "Bangladesh";
ListIt->ImageIndex = 4;
ListIt->Caption = "Benin";
ListIt->ImageIndex = 5;
705
24. Click OK
25. On the form, click an unoccupied area of the form
26. From the Standard tab of the Component Palette, double-click the Panel control
27. Delete its caption and set its Align property to alClient
28. Make sure the latest panel is still selected.
29. From the Standard tab, double-click the Panel control again.
30. Set its Align property to alTop. Set its BevelOuter to bvLowered. Delete its caption
and set its Height to 38
31. On the form, click the biggest section of the form, it is occupied by the biggest panel
(this should be Panel3).
32. From the Win32 tab of the Component Palette, double-click the ListView control.
33. With the ListView still selected, change its Align property to alClient and its
ViewStyle to vsReport.
34. From the Win32 tab of the Component Palette, double-click ImageList.
35. On the form, click the ListView1 control. On the Object Inspector, set both the
LargeImages and SmallImages values to ImageList2
36. On the form, double-click ImageList2. Using the Add button and from the Icons
folder, add the following icons: Zambia, Guinea, Benin, Austria, Finland, Romania,
Monaco, Jamaica, Colombia, Yemen, SouthKorea, and Lebanon
37. Click OK
38. Press F12 to display the Code Editor. In the private section of the TForm1 class,
declare the following function:
void __fastcall DisplayColumns(TTreeNode *NodeSelected);
706
Col = ListView1->Columns->Add();
Col->Caption = "Name";
Col->Width = 100;
Col = ListView1->Columns->Add();
Col->Caption = "Area (sq km)";
Col->Width = 120;
Col->Alignment = taRightJustify;
Col = ListView1->Columns->Add();
Col->Caption = "Population";
Col->Width = 120;
Col->Alignment = taRightJustify;
}
else
{
Col = ListView1->Columns->Add();
Col->Caption = "Name";
Col->Width = 100;
Col = ListView1->Columns->Add();
Col->Caption = "Area (sq km)";
Col->Width = 100;
Col->Alignment = taRightJustify;
Col = ListView1->Columns->Add();
Col->Caption = "Population";
Col->Width = 100;
Col->Alignment = taRightJustify;
Col = ListView1->Columns->Add();
Col->Caption = "Capital";
Col->Width = 100;
Col = ListView1->Columns->Add();
Col->Caption = "Code";
Col->Width = 40;
Col->Alignment = taCenter;
}
}
//---------------------------------------------------------------------------
40. In the header file (Unit1.h), just on top of the class, declare the following twodimensional array:
//--------------------------------------------------------------------------#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------------------------#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
#include <ImgList.hpp>
const AnsiString CountryStats[12][10] = {
// Name
Area
Population Capital Internet Code
{ "Zambia",
"752,614", "9,959,037", "Lusaka", "zm"
},
{ "Guinea",
"245,857", "7,775,065", "Conakry", "gn"
},
707
{ "Benin",
"112,620", "6,787,625", "Cotonou", "bj"
},
{ "Austria",
"83,858", "8,169,929", "Vienna", "at"
},
{ "Finland",
"337,030", "5,183,545", "Helsinki", "fi"
},
{ "Romania",
"237,500", "22,317,730", "Bucharest", "ro"
},
{ "Monaco",
"1.95",
"31,987",
"Monaco", "mc"
},
{ "Jamaica",
"10,991", "2,680,029", "Kingston", "jm"
},
{ "Colombia", "1,138,910", "41,008,227", "Bogota", "co"
},
{ "Yemen",
"527,970", "18,701,257", "Sanaa",
"ye"
},
{ "South Korea", "98,480", "48.324 Mlns","Seoul",
"kr"
},
{ "Lebanon",
"10,400", "3,677,780", "Beirut", "lb"
} };
//--------------------------------------------------------------------------class TForm1 : public TForm
41. Press F12 to display the form. On the form, click the TreeView1 control and, on the
Object Inspector, click the Events tab
42. Double-click the event side of OnChange and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::TreeView1Change(TObject *Sender, TTreeNode *Node)
{
DisplayColumns(Node);
ListView1->Items->Clear();
TListItem *lstItem;
if( Node->Text == "World" )
{
lstItem = ListView1->Items->Add();
lstItem->Caption = "Africa";
lstItem = ListView1->Items->Add();
lstItem->Caption = "Europe";
lstItem = ListView1->Items->Add();
lstItem->Caption = "America";
lstItem = ListView1->Items->Add();
lstItem->Caption = "Asia";
}
else if( Node->Text == "Africa" )
{
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[0][0];
lstItem->SubItems->Add(CountryStats[0][1]);
lstItem->SubItems->Add(CountryStats[0][2]);
lstItem->SubItems->Add(CountryStats[0][3]);
lstItem->SubItems->Add(CountryStats[0][4]);
lstItem->ImageIndex = 1;
//
//
//
//
Area
Population
Capital
Internet Code
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[1][0];
lstItem->SubItems->Add(CountryStats[1][1]);
lstItem->SubItems->Add(CountryStats[1][2]);
lstItem->SubItems->Add(CountryStats[1][3]);
lstItem->SubItems->Add(CountryStats[1][4]);
lstItem->ImageIndex = 2;
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[2][0];
lstItem->SubItems->Add(CountryStats[2][1]);
lstItem->SubItems->Add(CountryStats[2][2]);
lstItem->SubItems->Add(CountryStats[2][3]);
708
lstItem->SubItems->Add(CountryStats[2][4]);
lstItem->ImageIndex = 3;
}
else if( Node->Text == "Europe" )
{
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[3][0];
lstItem->SubItems->Add(CountryStats[3][1]);
lstItem->SubItems->Add(CountryStats[3][2]);
lstItem->SubItems->Add(CountryStats[3][3]);
lstItem->SubItems->Add(CountryStats[3][4]);
lstItem->ImageIndex = 4;
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[4][0];
lstItem->SubItems->Add(CountryStats[4][1]);
lstItem->SubItems->Add(CountryStats[4][2]);
lstItem->SubItems->Add(CountryStats[4][3]);
lstItem->SubItems->Add(CountryStats[4][4]);
lstItem->ImageIndex = 5;
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[5][0];
lstItem->SubItems->Add(CountryStats[5][1]);
lstItem->SubItems->Add(CountryStats[5][2]);
lstItem->SubItems->Add(CountryStats[5][3]);
lstItem->SubItems->Add(CountryStats[5][4]);
lstItem->ImageIndex = 6;
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[6][0];
lstItem->SubItems->Add(CountryStats[6][1]);
lstItem->SubItems->Add(CountryStats[6][2]);
lstItem->SubItems->Add(CountryStats[6][3]);
lstItem->SubItems->Add(CountryStats[6][4]);
lstItem->ImageIndex = 7;
}
else if( Node->Text == "America" )
{
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[7][0];
lstItem->SubItems->Add(CountryStats[7][1]);
lstItem->SubItems->Add(CountryStats[7][2]);
lstItem->SubItems->Add(CountryStats[7][3]);
lstItem->SubItems->Add(CountryStats[7][4]);
lstItem->ImageIndex = 8;
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[8][0];
lstItem->SubItems->Add(CountryStats[8][1]);
lstItem->SubItems->Add(CountryStats[8][2]);
lstItem->SubItems->Add(CountryStats[8][3]);
lstItem->SubItems->Add(CountryStats[8][4]);
lstItem->ImageIndex = 9;
}
else if( Node->Text == "Asia" )
{
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[9][0];
709
lstItem->SubItems->Add(CountryStats[9][1]);
lstItem->SubItems->Add(CountryStats[9][2]);
lstItem->SubItems->Add(CountryStats[9][3]);
lstItem->SubItems->Add(CountryStats[9][4]);
lstItem->ImageIndex = 10;
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[10][0];
lstItem->SubItems->Add(CountryStats[10][1]);
lstItem->SubItems->Add(CountryStats[10][2]);
lstItem->SubItems->Add(CountryStats[10][3]);
lstItem->SubItems->Add(CountryStats[10][4]);
lstItem->ImageIndex = 11;
lstItem = ListView1->Items->Add();
lstItem->Caption = CountryStats[11][0];
lstItem->SubItems->Add(CountryStats[11][1]);
lstItem->SubItems->Add(CountryStats[11][2]);
lstItem->SubItems->Add(CountryStats[11][3]);
lstItem->SubItems->Add(CountryStats[11][4]);
lstItem->ImageIndex = 12;
}
}
//---------------------------------------------------------------------------
710
Display the form. Click the TreeView control on the left side of the form to select it.
Make sure its Align property is set to alLeft
Copyright 2003 FunctionX, Inc.
2.
From the Additional tab of the Component Palette, click the Splitter control
3.
Click the treeview on the left side of the form. Notice that the splitter aligns
vertically with the treeview
4.
5.
Drag the splitting line in the middle of the form to resize the sections
6.
711
712
2.
To save the project, on the Standard toolbar, click the Save All button
3.
Locate the folder that contains your exercises and display it in the Save In combo
box.
4.
Click the Create New Folder button. Type Address Book1 and press Enter twice
5.
6.
7.
Change the Caption of the form to Address Book - Contacts List and change its
Name to frmMain
As you can see from this definition, TList does not care what type of list you want to
create as long as you can define it as an entity, an object. The second issue to keep in
mind is that, because TList does not care about the kind of list you want to create and
does not know in advance the number of objects in your list, it conveniently keeps the
objects of your list in an array. This means that, as you create your list, TList adds your
objects in an expanding list.
Copyright 2003 FunctionX, Inc.
713
To initiate a VCL list, the first thing you must do is to declare an instance of a TList. If
the list will be used as a local object, you can declare it in a function or event. If the list
will be accessed by more than one event or function, you should declare its instance in
the class that will host the list. Because TList is a VCL object, it must be declared as a
pointer:
TList * ListOfObjects;
In the constructor of the class that will host the list, initialize the pointer by letting the
compiler know which class the list instance belongs to. This is done using the new
operator. After using the class, you should (must) delete it and recover the memory it was
using. This is done using the delete operator. If the list was created globally in a form's
class, you can delete it in the OnDestroy event of the form.
Press F12 to display the Code Editor. In the public section of the TForm1 class,
declare a pointer to TList as follows:
2.
Click the Main.cpp tab to access the source file. In the constructor of the TForm1
class, initialize the TList pointer as follows:
//--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
ListOfContacts = new TList;
}
//---------------------------------------------------------------------------
714
3.
On the Object Inspector, click the Events tab. Double-click the event of OnDestroy
and delete the list as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormDestroy(TObject *Sender)
{
delete ListOfContacts;
ListOfContacts = NULL;
}
//---------------------------------------------------------------------------
4.
To create such an object, you have two options, you can create a fully functional C++
class:
class TCountry
{
private:
AnsiString CountryName;
Double Area;
Extended Population;
AnsiString Capital;
AnsiString InternetCode;
public:
__fastcall TCountry(AnsiString cn="", AnsiString a="",
Extended p=0, AnsiString c="",
AnsiString ic="");
virtual __fastcall ~TCountry();
void __fastcall setCountryName(const AnsiString CName);
AnsiString __fastcall getCountryName() const;
void __fastcall setArea(const Double A);
Double __fastcall getArea() const;
void __fastcall setPopulation(const Extended P);
Extended __fastcall getPopulation() const;
void __fastcall setCapital(const AnsiString C);
AnsiString __fastcall getCapital() const;
void __fastcall setInternetCode(const AnsiString IC);
AnsiString __fastcall getInternetCode() const;
715
Because your object will only be used as a "frame", it is a traditional shortcut to create it
as a simple structure, listing only the members you will need. Such an object can be
created as follows:
struct TCountry
{
AnsiString CountryName;
Double Area;
Extended Population;
AnsiString Capital;
AnsiString InternetCode;
__fastcall TCountry(AnsiString cn="", AnsiString a="",
Extended p=0, AnsiString c="",
AnsiString ic="");
virtual __fastcall ~TCountry();
TCountry& __fastcall operator=(const TCountry &Ctry);
};
This option provides you with three possibilities to create an instance of a country:
you can use a constructor to initialize a complete object or you can use the
constructor to initialize only the members whose value you know
You can use the overloaded assignment operator to make a copy of an instance and
assign it to another instance of the object when necessary.
2.
In the New property sheet of the New Items dialog box, click Unit and click OK
3.
4.
716
__fastcall TContact();
// Contact with only the first and last names
__fastcall TContact(AnsiString FN, AnsiString LN);
// Complete contact
__fastcall TContact(AnsiString FN, AnsiString LN, AnsiString Adr,
AnsiString CT, AnsiString St, AnsiString ZIP,
AnsiString Ctry, AnsiString Email, AnsiString HP);
// Copy constructor
__fastcall TContact(const TContact &Cont);
// Destructor
virtual __fastcall ~TContact();
// This provides the ability to copy a contact
TContact& operator=(const TContact& Cont);
};
//--------------------------------------------------------------------------#endif
5.
In the source file of Contacts.cpp, implement the constructors and the operator
function as follows:
//--------------------------------------------------------------------------#pragma hdrstop
#include "Contacts.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
__fastcall TContact::TContact()
: FirstName("John"), LastName("Doe"),
Address("123 Main Street"), City("Great City"),
State("Big State"), ZIPCode("01234"), Country("USA"),
EmailAddress("[email protected]"), HomePhone("(123) 456-7890")
{
}
//--------------------------------------------------------------------------__fastcall TContact::TContact(AnsiString FN, AnsiString LN)
: FirstName(FN), LastName(LN),
Address("<Null>"), City("<Null>"),
State("<Null>"), ZIPCode("<Null>"), Country("<Null>"),
EmailAddress("<Null>"), HomePhone("<Null>")
{
// <Null> means Undefined
}
//--------------------------------------------------------------------------__fastcall TContact::TContact(AnsiString FN, AnsiString LN, AnsiString Adr,
AnsiString CT, AnsiString St, AnsiString ZIP,
AnsiString Ctry, AnsiString Email, AnsiString HP)
: FirstName(FN), LastName(LN),
Address(Adr), City(CT),
State(St), ZIPCode(ZIP), Country(Ctry),
EmailAddress(Email), HomePhone(HP)
{
}
//--------------------------------------------------------------------------__fastcall TContact::TContact(const TContact &Cont)
: FirstName(Cont.FirstName), LastName(Cont.LastName),
Address(Cont.Address), City(Cont.City),
State(Cont.State), ZIPCode(Cont.ZIPCode), Country(Cont.Country),
EmailAddress(Cont.EmailAddress), HomePhone(Cont.HomePhone)
{
}
//---------------------------------------------------------------------------
717
__fastcall TContact::~TContact()
{
}
//--------------------------------------------------------------------------TContact& TContact::operator=(const TContact& Cont)
{
FirstName = Cont.FirstName;
LastName
= Cont.LastName;
Address
= Cont.Address;
City
= Cont.City;
State
= Cont.State;
ZIPCode
= Cont.ZIPCode;
Country
= Cont.Country;
EmailAddress = Cont.EmailAddress;
HomePhone = Cont.HomePhone;
return *this;
}
//---------------------------------------------------------------------------
6.
The Item to add must be a recognizable and defined object. TList has an internal
mechanism of counting the objects that are added to its list. This count is stored in the
Count member variable. Therefore, you can call it anytime to find out how many items
exist in a list. When using the Add() method, if the Item parameter is the first item to be
added to the list, it would receive an index of 0 and the Count would be 1. When you add
an object, the Count is incremented. The newly added object receives an index of Count1. This means that there are two main actions the Add() method performs. First it adds an
object at the end of the list because the additions of items are incremental. Second, it
returns the index of the newly added object. Knowing this index, you can access a
specific object in the list array.
As you are adding objects to the list, the TList class takes care of incrementing the
number of objects in the list. This is taken care of by the Capacity member variable. In
718
fact, the Capacity serves two purposes. If you want to specify the number of items in a
list, for example imagine you want to create a list of ten countries, you can specify this
using the Capacity variable. In the same way, if you want to find out how many items
the list can include, you can call the Capacity member variable.
2.
To create a list, in the constructor of the TfrmMain class, type the following
instances of TContact and add them to the list as follows:
__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
ListOfContacts = new TList;
Contact = new TContact;
Contact->FirstName = "Hermine";
Contact->LastName = "Toussaint";
Contact->Address = "4088 Patient Rd";
Contact->City
= "Silver Spring";
Contact->State
= "MD";
Contact->ZIPCode = "20904";
Contact->Country = "USA";
Contact->EmailAddress = "[email protected]";
Contact->HomePhone = "(301) 805-5008";
ListOfContacts->Add(Contact);
Contact = new TContact("Bertrand", "Yamaguchi", "7215 16th Street #D14",
"Washington", "DC", "20002", "USA",
"[email protected]", "(202) 661-5000");
719
ListOfContacts->Add(Contact);
Contact = new TContact;
Contact->FirstName = "Lester";
Contact->LastName = "Aarons";
Contact->Address = "10882 Washington Ave";
Contact->City
= "Arlington";
Contact->State
= "VA";
Contact->ZIPCode = "22231";
Contact->Country = "USA";
Contact->EmailAddress = "[email protected]";
Contact->HomePhone = "(703) 790-4000";
ListOfContacts->Add(Contact);
Contact = new TContact;
Contact->FirstName = "Charlotte";
Contact->LastName = "Singh";
Contact->Address = "442 Southampton Dr Ste402";
Contact->City
= "Rockville";
Contact->State
= "MD";
Contact->ZIPCode = "20852";
Contact->Country = "USA";
Contact->EmailAddress = "[email protected]";
Contact->HomePhone = "(301) 667-1437";
ListOfContacts->Add(Contact);
}
//---------------------------------------------------------------------------
3.
As you can see, the TList::Items member variable takes an index as an integer and
returns the object that is stored at that index. You can also use the Items member variable
in a for loop to scan the list.
To get to the very first item in the list, you have two options. You can call the Items
variable with an index of 0 as Items[0]. Alternatively, you can call the TList::First()
member variable whose syntax is:
void * __fastcall First(void);
In the same way, to access the last item of the list, you can either call
Items[TList::Count-1] or use the TList::Last() method. Its syntax is:
void * __fastcall Last(void);
On the other hand, if you know the item you want access to, you can call the
TList::IndexOf() method. Its syntax is:
int __fastcall IndexOf(void * Item);
720
To use the IndexOf() method, you must provide the item that you want to locate. If the
item exists, this member function returns the index of the item.
2.
3.
4.
Set the ReadOnly property value to true for all Edit controls except for the bottom
one (the one on the right side of the Record label, leave its ReadOnly value to false).
5.
6.
7.
8.
To keep track of the record that is displaying at all times, we will need a global
variable. Therefore, in the private section of the form, declare a variable as:
721
int CurrentRecord;
9.
In the constructor of the form, just after the opening bracket, initialize the
CurrentRecord variable to 0:
//--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
CurrentRecord = 0;
ListOfContacts = new TList;
...
10. Every time we change the displaying record on the form, we will need to update the
bottom buttons. For example, when we are on the first record, the user should not be
able to use the First and the Previous buttons since this could cause the compiler to
throw an error. In the same way, when the last record is displaying, the user would
not need the Next and the Last buttons. When displaying each record, we can just
update this information. Professionally, it would be nice to have a function that
manages these buttons every time a record changes. Object oriented programming
means we should divide jobs.
11. In the private section of the form, declare the following method:
void __fastcall UpdateButtons();
13. Also, every time the user clicks a button to navigate the records, the values for that
record will need to display. We can handle this update for ever event of the
navigation buttons. It appears more professional to have a function that can take care
of this so that, whenever a new record needs to be displayed, the function or event
that needs this update can just call the function that displays the values for the
722
record. The function that makes the call must specify which record needs to be
displayed.
In the private section of the TForm1 class, declare a function as follows:
void __fastcall DisplayRecord(const TContact * Contact);
15. On the form, double-click the First button and implement its OnClick event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnFirstClick(TObject *Sender)
{
// Set the CurrentRecord variable to the first record
CurrentRecord = ListOfContacts->IndexOf( ListOfContacts->First() ) + 1;
// Retrieve the first record and assign it to a TContact object
Contact = reinterpret_cast<TContact *>(ListOfContacts->First());
DisplayRecord(Contact);
// Display the current index in the bottom Edit control
edtRecordNumber->Text = IntToStr(CurrentRecord);
// Call the UpdateButtons() method to decide what to do with the buttons
UpdateButtons();
}
//---------------------------------------------------------------------------
16. On the form, double-click the Previous button and implement its OnClick event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnPreviousClick(TObject *Sender)
{
// Decrease the CurrentRecord variable
CurrentRecord--;
// Get the current record and assign it to a TContact instance
Contact = reinterpret_cast<TContact *>(ListOfContacts->Items[CurrentRecord1]);
DisplayRecord(Contact);
edtRecordNumber->Text = IntToStr(CurrentRecord);
UpdateButtons();
}
//---------------------------------------------------------------------------
723
17. On the form, double-click the Next button and implement its OnClick event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnNextClick(TObject *Sender)
{
Contact = reinterpret_cast<TContact *>(ListOfContacts->Items[CurrentRecord]);
DisplayRecord(Contact);
edtRecordNumber->Text = IntToStr(CurrentRecord+1);
// Decrease the record numbering
CurrentRecord++;
UpdateButtons();
}
//---------------------------------------------------------------------------
18. On the form, double-click the Last button and implement its OnClick event as
follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnLastClick(TObject *Sender)
{
CurrentRecord = ListOfContacts->Count;
Contact = reinterpret_cast<TContact *>(ListOfContacts->Last());
DisplayRecord(Contact);
edtRecordNumber->Text = IntToStr(ListOfContacts->Count);
UpdateButtons();
}
//---------------------------------------------------------------------------
19. When the form opens, we need to make sure that the first record displays. All we
have to do is to call the OnClick event of the Next button. While we are at it, we will
display the number of records on the second label of the bottom panel.
Find an unoccupied area on the form and double-click it to access the OnCreate
event of the form
20. Implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
// When the form opens, act as if the Next button was clicked
btnNextClick(Sender);
// Display the total number of records on the navigation bar
lblCount->Caption = ListOfContacts->Count;
}
//---------------------------------------------------------------------------
21. If the user types a number in the Record Number edit box and press Enter, we will
allow the corresponding record to display. The real problem we need to solve is to
avoid invalid values.
On the top combo box of the Object Inspector, select edtRecordNumber and click the
Events tab.
22. Double-click the event of OnKeyDown and implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::edtRecordNumberKeyDown(TObject *Sender,
WORD &Key, TShiftState Shift)
{
// If the user press Enter while in the Edit control
if( Key == VK_RETURN )
724
725
24. After using the form, close it and save your project
2.
3.
4.
726
5.
6.
From the Bitmaps folder, and using the Add button, select the Exit bitmap
7.
8.
While the MainMenu1 icon is still selected, set its Images property to ImageList1
9.
Double-click MainMenu1
and. on
10. While the first menu item is still selected, on the Object Inspector, click Caption,
type &File and press Enter
11. Set the Caption of the item under File to E&xit and set its ImageIndex value to 0
12. Close the Menu Designer
13. On the main menu of the form, click File -> Exit and implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmListView::Exit1Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
14. From the Win32 tab of the Component Palette, click Toolbar
unoccupied area on the form
and click an
under
Alignment
taLeftJustify
taLeftJustify
taLeftJustify
taLeftJustify
Width
80
24
80
160
727
Home Phone
taCenter
100
728
AnsiString CT="USA",
AnsiString St="ZZ", AnsiString ZIP="01234",
AnsiString Ctry="USA",
AnsiString Email="[email protected]",
AnsiString HP="(123) 456-7890");
__fastcall TContact(const TContact &Cont);
virtual __fastcall ~TContact();
TContact& operator=(const TContact& Cont);
};
//--------------------------------------------------------------------------#endif
729
return *this;
}
//---------------------------------------------------------------------------
25. In the header file of Main.h, declare a TContacts and a TList instances as follows:
//--------------------------------------------------------------------------#ifndef MainH
#define MainH
//--------------------------------------------------------------------------#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ImgList.hpp>
#include <Menus.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
#include <ToolWin.hpp>
#include "Contacts.h"
//--------------------------------------------------------------------------class TfrmMain : public TForm
{
__published: // IDE-managed Components
TImageList *ImageList1;
TMainMenu *MainMenu1;
TMenuItem *File1;
TMenuItem *Exit1;
TToolBar *ToolBar1;
TListView *lvwContacts;
TPanel *Panel1;
void __fastcall Exit1Click(TObject *Sender);
private:
TContact *Contact;
TList *ListOfContacts;
// User declarations
public:
// User declarations
__fastcall TfrmMain(TComponent* Owner);
};
//--------------------------------------------------------------------------extern PACKAGE TfrmMain *frmMain;
//--------------------------------------------------------------------------#endif
26. In the constructor of the TfrmMain class in the Main.cpp file, initialize the
ListOfContacts variable as follows:
//--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
ListOfContacts = new TList;
}
//---------------------------------------------------------------------------
27. In the OnDestroy event of the the frmMain form, destroy the ListOfContacts
variable as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::FormDestroy(TObject *Sender)
{
730
delete ListOfContacts;
ListOfContacts = NULL;
}
//---------------------------------------------------------------------------
2.
3.
731
4.
5.
The Name to give to each Edit control is displayed on the above form. The middle
Edit control in the top group is named edtMI
6.
7.
8.
Display the other form (View -> Forms, frmMain) and double-click ImageList1
9.
732
733
lstItem->SubItems->Add(reinterpret_cast<TContact *>(ListOfContacts->Items[i])->MI);
lstItem->SubItems->Add(reinterpret_cast<TContact *>(ListOfContacts->Items[i])->LastName);
lstItem->SubItems->Add(reinterpret_cast<TContact *>(ListOfContacts->Items[i])->EmailAddress);
lstItem->SubItems->Add(reinterpret_cast<TContact *>(ListOfContacts->Items[i])->HomePhone);
}
}
//---------------------------------------------------------------------------
18. To allow the user to create a new contact, on the main menu of the form, click File > New Record and implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::mnuNewRecordClick(TObject *Sender)
{
// Make sure that all edit boxes
// from the Record Details dialog boxes are empty
dlgRecord->edtFirstName->Text = "";
dlgRecord->edtMI->Text = "";
dlgRecord->edtLastName->Text = "";
dlgRecord->edtAddress->Text = "";
dlgRecord->edtCity->Text = "";
dlgRecord->edtState->Text = "";
dlgRecord->edtZIPCode->Text = "";
dlgRecord->edtCountry->Text = "USA";
dlgRecord->edtEmailAddress->Text = "";
dlgRecord->edtHomePhone->Text = "";
// Display the Record dialog to the user
dlgRecord->ShowModal();
// Find out if the user clicked OK
if( dlgRecord->ModalResult == mrOk )
{
// Create a new record
Contact = new TContact;
Contact->FirstName = dlgRecord->edtFirstName->Text;
Contact->MI = dlgRecord->edtMI->Text;
Contact->LastName = dlgRecord->edtLastName->Text;
Contact->Address = dlgRecord->edtAddress->Text;
Contact->City = dlgRecord->edtCity->Text;
Contact->State = dlgRecord->edtState->Text;
Contact->ZIPCode = dlgRecord->edtZIPCode->Text;
Contact->Country = dlgRecord->edtCountry->Text;
Contact->EmailAddress = dlgRecord->edtEmailAddress->Text;
Contact->HomePhone = dlgRecord->edtHomePhone->Text;
// Add the new record to the list
ListOfContacts->Add(Contact);
}
// Display a condensed list of contacts
ShowContacts();
}
//---------------------------------------------------------------------------
19. On the form, right-click the toolbar and click New Button. Change the ImageIndex
of the new button to 1.
20. Change its Hint to New Record
734
21. Click the Events tab of the Object Inspector and on its OnClick right field, select
mnuNewRecordClick
22. Test the application. Click File -> New Record...
735
23. After using the application, close the form and save the project
24. To allow the user to view more details about a record, on the form, click the
ListView1 control.
25. On the Object Inspector, click the Events tab. Double-click the empty box on the
right side of OnDblClick and implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::lvwContactsDblClick(TObject *Sender)
{
// Suppose the user double-clicks an item, get its index
int ItemSelected = lvwContacts->ItemIndex;
// If an item was double-clicked, display its record
if( ItemSelected >= 0 )
{
Contact = reinterpret_cast<TContact *>(ListOfContacts>Items[ItemSelected]);
dlgRecord->edtFirstName->Text = Contact->FirstName;
dlgRecord->edtMI->Text = Contact->MI;
dlgRecord->edtLastName->Text = Contact->LastName;
dlgRecord->edtAddress->Text = Contact->Address;
dlgRecord->edtCity->Text = Contact->City;
dlgRecord->edtState->Text = Contact->State;
dlgRecord->edtZIPCode->Text = Contact->ZIPCode;
dlgRecord->edtCountry->Text = Contact->Country;
dlgRecord->edtEmailAddress->Text = Contact->EmailAddress;
dlgRecord->edtHomePhone->Text = Contact->HomePhone;
dlgRecord->ShowModal();
// After viewing the record and upon closing the dialog box
// find out if the user clicked OK
if( dlgRecord->ModalResult == mrOk )
{
// Since the user clicked OK
// find out if any value of the record was changed
if( (dlgRecord->edtFirstName->Text != Contact->FirstName) ||
(dlgRecord->edtMI->Text != Contact->MI) ||
(dlgRecord->edtLastName->Text != Contact->LastName) ||
736
(dlgRecord->edtAddress->Text != Contact->Address) ||
(dlgRecord->edtCity->Text != Contact->City) ||
(dlgRecord->edtState->Text != Contact->State) ||
(dlgRecord->edtZIPCode->Text != Contact->ZIPCode) ||
(dlgRecord->edtCountry->Text != Contact->Country) ||
(dlgRecord->edtEmailAddress->Text != Contact->EmailAddress) ||
(dlgRecord->edtHomePhone->Text != Contact->HomePhone) )
// Since a record has changed, ask a question to the user
int Response =
Application->MessageBox("The record has changed\n"
"Do you want to save it?",
"Address Book",
MB_YESNO | MB_ICONQUESTION);
// If the user wants to update the change on the record
if( Response == IDYES )
{
// Replace each value of the selected record
// with the new value
Contact->FirstName = dlgRecord->edtFirstName->Text;
Contact->MI = dlgRecord->edtMI->Text;
Contact->LastName = dlgRecord->edtLastName->Text;
Contact->Address = dlgRecord->edtAddress->Text;
Contact->City = dlgRecord->edtCity->Text;
Contact->State = dlgRecord->edtState->Text;
Contact->ZIPCode = dlgRecord->edtZIPCode->Text;
Contact->Country = dlgRecord->edtCountry->Text;
Contact->EmailAddress = dlgRecord->edtEmailAddress->Text;
Contact->HomePhone = dlgRecord->edtHomePhone->Text;
// Redisplay the contacts
ShowContacts();
}
}
}
//---------------------------------------------------------------------------
26. On the form, double-click ImageList1 and, using the Add button, add the Details
bitmap
27. On the form, right-click the toolbar and click New Button. Make sure the
ImageIndex of the new button is set to 2 and change its Name to btnRecordDetails
28. Click the Events tab. In its OnClick right box, select lvwContactsDblClick
29. Test the application
30. After using it, close the form and save the project
737
The first argument of this member function, Index, specifies the index that the new item
will occupy after being added. Because the list of items is zero-based, to add an item to
the first position, specify Index as 0. In the same way, to add an item to the 3rd position,
specify Index as 2. The Item argument is the item that you want to insert.
There are two main ways you would use the Insert() method. If you know the exact
position where you want to insert an object, then supply the known index. By contrast,
you can use Insert() to insert a new item before or after one of your choice. To do this,
you must first retrieve the index of the item that will succeed the one you want to add.
This index would be used as a the Index argument.
On the form, double-click ImageList1 and, using the Add button, add the Insert
bitmap
2.
3.
Make sure that the new bitmap is assigned to the new button and change its Name to
btnInsertRecord
4.
5.
Double-click the new button and implement its OnClick event as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnInsertRecordClick(TObject *Sender)
{
// In order to insert a record, we need to find out if a record is selected
int ItemSelected = lvwContacts->ItemIndex;
// If a record is selected
if( ItemSelected >= 0 )
{
// Empty all edit boxes of the Record Details dialog box
dlgRecord->edtFirstName->Text = "";
dlgRecord->edtMI->Text = "";
dlgRecord->edtLastName->Text = "";
dlgRecord->edtAddress->Text = "";
dlgRecord->edtCity->Text = "";
dlgRecord->edtState->Text = "";
dlgRecord->edtZIPCode->Text = "";
dlgRecord->edtCountry->Text = "USA";
dlgRecord->edtEmailAddress->Text = "";
dlgRecord->edtHomePhone->Text = "";
// Display the Record dialog to the user
dlgRecord->ShowModal();
// Find out if the user clicked OK
if( dlgRecord->ModalResult == mrOk )
{
// Create a new record
Contact = new TContact;
Contact->FirstName = dlgRecord->edtFirstName->Text;
Contact->MI = dlgRecord->edtMI->Text;
Contact->LastName = dlgRecord->edtLastName->Text;
Contact->Address = dlgRecord->edtAddress->Text;
738
Contact->City = dlgRecord->edtCity->Text;
Contact->State = dlgRecord->edtState->Text;
Contact->ZIPCode = dlgRecord->edtZIPCode->Text;
Contact->Country = dlgRecord->edtCountry->Text;
Contact->EmailAddress = dlgRecord->edtEmailAddress->Text;
Contact->HomePhone = dlgRecord->edtHomePhone->Text;
}
// Display an update list of contacts in the ListView
ShowContacts();
}
//---------------------------------------------------------------------------
6.
If a record is selected and the user presses Insert, we can perform the same task
7.
On the form, click the ListView control and, on the Object Inspector, click the
Events tab
8.
9.
10. After using it, close the form and save the project
To process your request, this method needs the index of the item you want to delete. You
can get this index using the TList::Items[] member variable.
Besides the Delete() method, the TList class provides a method used to delete a record if
you know the value of the record instead of its position. The method is TList::Remove()
and its syntax is:
int __fastcall Remove(void *Item);
Instead of the index, the Remove() method needs the item itself. If you supply an item
that exists in the list, Remove() would delete it. If the operation is successful, Remove()
returns the index the item had.
739
There are two significant differences between the Delete() and the Remove() methods:
Because Delete() takes the index of the item you want to remove, if the index is
valid, the operation would be successful and stop. In other words, Delete() only
makes sure that the index corresponds to a valid item number in the list: Delete()
does not look for it; it only checks that the index is true and then deletes it
Remove() looks for the item you supply to it. If the item exists, Remove() deletes it.
If more than one item matches the Item parameter you supply, Remove() deletes the
first item that matches Item.
On the form, double-click ImageList1 and, using the Add button, add the Delete
bitmap
2.
Right-click the toolbar and click New Button. Make sure the Delete bitmap is
assigned to the new button and change its Name to btnDeleteRecord
3.
4.
5.
6.
On the form, click the ListView control and, on the Object Inspector, click the
Events tab
7.
Double-click the event on the right side of OnKeyDown and change it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmMain::lvwContactsKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if( Key == VK_INSERT )
btnInsertRecordClick(Sender);
if( Key == VK_DELETE )
740
btnDeleteRecordClick(Sender);
}
//---------------------------------------------------------------------------
28.1.10
8.
9.
After using it, close the form and save the project
List Clearance
Clearing a list consists of deleting all of its records in one step. This operation can be
taken care of by the TList::Clear() method. Its syntax is:
void __fastcall Clear();
As you can see, there is no particular information this function needs. If the list is empty,
the method would not do anything. If the list contains records, then all of them would be
deleted. The only thing you might do is to warn the user especially if the records cannot
be recovered.
On the form, double-click ImageList1 and, using the Add button, add the Clear
bitmap
2.
On the form, right-click the toolbar and click New Button. Make sure the Clear
bitmap is assigned to the new button. Change its Name to btnClear and its Hint to
Delete All Records
3.
4.
5.
After using it, close the form and save the project
741
28.2.2
742
ListOfNumbers->Push(Std);
Std = reinterpret_cast<TContractor *>(ListOfNumbers->Peek());
edtFirstName2->Text = Std->FirstName;
edtLastName2->Text = Std->LastName;
Std->FirstName = "Colette";
Std->LastName = "Arnolds";
ListOfNumbers->Push(Std);
Std = reinterpret_cast<TContractor *>(ListOfNumbers->Peek());
edtFirstName3->Text = Std->FirstName;
edtLastName3->Text = Std->LastName;
Std->FirstName = "Harry";
Std->LastName = "Pons";
ListOfNumbers->Push(Std);
Std = reinterpret_cast<TContractor *>(ListOfNumbers->Peek());
edtFirstName4->Text = Std->FirstName;
edtLastName4->Text = Std->LastName;
edtCounter->Text = ListOfNumbers->Count();
}
//---------------------------------------------------------------------------
There are many other classes the VCL provides for lists.
743
Appendix
Appendices
Computers: An Overview
Creating a Program
744
Appendix
Declaring a Date
To declare a date value, use one of the TDateTime constructors. The simplest way is to
use the default constructor and provide a valid C++ name. Here is an example:
TDateTime Mine;
If not assigned a value, this variable is initialized to 12/30/1899 at midnight. If you know
the exact date that you want to initialize, you have two alternatives. You can initialize the
variable using a string. Here is an example:
TDateTime Mine("12/05/1990");
You can also provide the integer values of the year, the mont, and the day respectively.
Here is an example:
TDateTime Mine(1990, 11, 26);
To initialize a date, you can also provide an integer that represents the number of days
passed since 1899 to the specified date. Here is and example:
TDateTime Mine = 33895;
745
Appendix
If the string contains an invalid date, the conversion would fail and the program would
throw an error. If the conversion is successful, the function would return a valid date.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm2::btnConvertClick(TObject *Sender)
{
TDateTime Value = StrToDate(edtSource->Text);
edtTarget->Text = Value;
}
//---------------------------------------------------------------------------
746
Appendix
}
//---------------------------------------------------------------------------
If you have a date value that needs to be converted to a string, you can use the
DateToStr() function. Its syntax is:
AnsiString __fastcall DateToStr(System::TDateTime Date);
747
Appendix
This function takes one argument, which is the date value that needs to be converted. If
the argument is not a valid date, the conversion would fail and the program would throw
an error (in simple but practical terms, the program would crash). If the conversion is
successful, the function returns a string.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnConvertClick(TObject *Sender)
{
AnsiString Current = DateToStr(Date());
edtTarget->Text = Current;
}
//---------------------------------------------------------------------------
Alternatively, you can use the TDateTime::DateString() function to convert a valid date
to a string. The syntax of the DateString() method is:
AnsiString __fastcall DateString() const;
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime Today = Date();
AnsiString Value = Today.DateString();
Edit1->Text = Value;
}
//---------------------------------------------------------------------------
748
Appendix
}
//---------------------------------------------------------------------------
To convert a TDateTime value to double using the double overloaded operator, declare a
double variable and assign the TDateTime variable to it by calling the operator double()
function. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
TDateTime Diff = End - Start;
double d = Diff.operator double();
edtDifference->Text = d;
}
//---------------------------------------------------------------------------
749
Appendix
From the Date, Time, Language, and Regional Options window, click either Change the
Format of Numbers, Dates, and Times or Regional and Language Options. From the
Regional or Language Options, click the Regional Options property page and click the
Customize button:
750
Appendix
The computer uses two main categories of date display. These categories are based on the
language used by your computer. For example, most user computers that reside in the
United States use a standard known as US English. This commands how the date displays
in the continental US. Each category uses specific characters to represent its value. The m
or M is used for a month, the d or D is used for a day, and the y or Y is used for the year.
Meaning
The month displays as a single digit if the
numeric month is less than 10.
The month displays a leading 0 if the
numeric month is less than 10.
The day displays as a single digit if the day
of the month is less than 10.
The day displays a leading 0 if the day of
the month is less than 10.
The year displays with two digits like 88 for
1988
The year displays with 4 digits such as 1988
and not 88
751
Appendix
752
Appendix
edtM->Text = DateValue;
ShortDateFormat = "MM";
edtMM->Text = DateValue;
ShortDateFormat = "y";
edty->Text = DateValue;
ShortDateFormat = "yy";
edtyy->Text = DateValue;
ShortDateFormat = "yyy";
edtyyy->Text = DateValue;
ShortDateFormat = "yyyy";
edtyyyy->Text = DateValue;
}
//---------------------------------------------------------------------------
Besides using any of these characters to display their corresponding portion of a date, you
can also combine these characters to display a semi or complete date. To do this, you will
need a symbol or character that separates the portions of a date. In the US English, the
most common character used to separate the portions of a date is the forward slash /.
Another character used is the dash -.
Using a set of combinations of the above characters, the operating system proposes a list
of possible formats for date display. To access this list, from the Regional Options
property page of the Regional and Language Options dialog box, you can click the arrow
of the Short Date Format combo box. The combinations are:
Here are examples of displaying the formats specified by the operating system:
753
Appendix
ShortDateFormat = "M/d/yyyy";
edtMdyyyy->Text = DateValue;
ShortDateFormat = "M/d/yy";
edtMdyy->Text = DateValue;
ShortDateFormat = "MM/dd/yy";
edtMMddyy->Text = DateValue;
ShortDateFormat = "MM/dd/yyyy";
edtMMddyyyy->Text = DateValue;
ShortDateFormat = "yy/MM/dd";
edtyyMMdd->Text = DateValue;
ShortDateFormat = "yyyy-MM-dd";
edtyyyyMMdd->Text = DateValue;
ShortDateFormat = "dd-MMM-yy";
edtddMMMyy->Text = DateValue;
}
//---------------------------------------------------------------------------
754
Appendix
YYY or YYYY: The triple y as yyy or the quadruple one as yyyy is used to display all
four digits of a year.
Using Dates
Displaying Dates
Any control that uses an AnsiString can display a date. From the declarations we have
seen, if you create an initialized date, you can use the DateToStr() function to display it.
If you use a date variable declared with the default construction, the control would
display the first date the compiler can recognize:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime Mine;
Edit1->Text = DateToStr(Mine);
}
//---------------------------------------------------------------------------
If the date is initialized with a valid date value, you can omit the conversion function:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime Mine("12/05/1990");
Edit1->Text = Mine;
}
//---------------------------------------------------------------------------
If you supply the integer values of the variable, the compiler would take care of
displaying the equivalent date:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime Mine(1990, 11, 26);
Edit1->Text = Mine;
}
//---------------------------------------------------------------------------
In the same way, if you initialize the variable with a number of days as an integer, the
compiler would calculate and display the corresponding date;
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime Mine = 33895;
Edit1->Text = Mine;
}
//---------------------------------------------------------------------------
755
Appendix
Decoding a Date
A date variable declared from the TDateTime class is made of a year, a month, and a day
values. Decoding a date consists of isolating or retrieving these components of a date
value. To perform such an operation, the TDateTime class is equipped with the
DecodeDate() method. Its syntax is:
void __fastcall DecodeDate(unsigned short* year, unsigned short* month, unsigned
short* day) const;
Each component is retrieved using a pointer to an unsigned short. The presence of
pointers allows you to pass empty variables whose value would be altered by the
function and returned with new values.
In the following example, the current date is stored in a TDateTime variable. Then the
year, month, and day of the variable are extracted before constructing a sentence to
display on a label:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TDateTime Today = Date();
unsigned short Year, Month, Day;
Today.DecodeDate(&Year, &Month, &Day);
Label1->Caption = "Today is the " + String(Day) +
" of month " + Month +
" of the year " + Year + ".";
}
//---------------------------------------------------------------------------
If you want to display a better English version of the sentence above, you can format the
date components to your liking. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
TDateTime Today = Date();
AnsiString Dayth, Monthth;
unsigned short Year, Month, Day;
Today.DecodeDate(&Year, &Month, &Day);
if( Day == 1 )
Dayth = "st";
else if( Day == 2 )
Dayth = "nd";
else if( Day == 3 )
Dayth = "rd";
else
Dayth = "th";
if( Month == 1 )
Monthth = "st";
else if( Month == 2 )
Monthth = "nd";
else if( Month == 3 )
756
Appendix
Monthth = "rd";
else
Monthth = "th";
Label1->Caption = "Today is the " + String(Day) + Dayth +
" of the " + Month + Monthth +
" month of the year " + Year + ".";
}
//---------------------------------------------------------------------------
The DecodeDate() function comes in two versions. Besides the TDateTimes, the VCL
provides another version whose syntax is:
void __fastcall DecodeDate(System::TDateTime Date, Word &Year, Word
&Month, Word &Day);
Since this version is class-independent, the first argument you must supply is a
TDateTime value or variable. This time, the year, the month, and the day values are
passed by reference, which also allows the function to return them altered.
Here is an example:
edtDay->Text = Day;
edtMonth->Text = Month;
edtYear->Text = Year;
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnCloseClick(TObject *Sender)
{
Close();
757
Appendix
}
//---------------------------------------------------------------------------
If you want to get or display the English name of the decoded month, you can write a
conditional switch whose cases would represent the months by their integral position.
You can also declare an AnsiString variable to hold the names of months and retrieve the
necessary one when needed. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDecodeClick(TObject *Sender)
{
TDateTime Typed = StrToDate(edtDate->Text);
Word Year, Month, Day;
Encoding a Date
Encoding a date consists of supplying the necessary components of a TDateTime to the
compiler to create a valid TDateTime value. The function used to perform this operation
is:
TDateTime __fastcall EncodeDate(Word Year, Word Month, Word Day);
This function takes three positive integers (unsigned short) that represent:
The following form is equipped with four Edit controls named edtDay, edtMonth,
edtYear and edtDate. When the user clicks the Encode button named btnEncode, the
758
Appendix
OnClick event retrieves the values of the day, the month, and the year from their
respective edit box. Then the compiler creates a date from those and displays it in the
Date edit box:
}
//---------------------------------------------------------------------------
759
Appendix
The following example starts by requesting a date value from the user using an
InputBox() function. Then the date is decoded to retrieve the year value. The year is
examined to find out whether it is a leap year, using the IsLeapYear() function. The
function displays a message box to show its findings:
//--------------------------------------------------------------------------void __fastcall TForm1::btnLeapYearClick(TObject *Sender)
{
unsigned short Year, Month, Day;
AnsiString Value =
InputBox("Date and Time", "Enter a date: ", "01/01/1900");
TDateTime Examiner = StrToDate(Value);
Examiner.DecodeDate(&Year, &Month, &Day);
AnsiString LeapYear =
IsLeapYear(Year) ? " is a leap year" : " is not a leap year";
ShowMessage("The date you typed was " + Value + "\n" +
AnsiString(Year) + LeapYear);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
760
Appendix
Of course, sometimes you will want to get or display the English name of the day. To do
this, you can write a switch conditional statement that would display a name accordingly.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnGetItClick(TObject *Sender)
{
TDateTime Value = StrToDate(edtDate->Text);
int Day = DayOfWeek(Value);
AnsiString DayName;
switch(Day)
{
case 1:
DayName = "Sunday";
break;
case 2:
DayName = "Monday";
break;
case 3:
DayName = "Tuesday";
break;
case 4:
DayName = "Wednesday";
break;
case 5:
DayName = "Thursday";
break;
case 6:
DayName = "Friday";
break;
case 7:
DayName = "Saturday";
}
edtDay->Text = DayName;
}
//---------------------------------------------------------------------------
761
Appendix
An alternative would be to declare an array of AnsiString strings to hold the names of the
week days, then retrieve the necessary one using its corresponding position. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnGetItClick(TObject *Sender)
{
TDateTime Value = StrToDate(edtDate->Text);
int Day = DayOfWeek(Value);
edtDay->Text = DayName[Day];
}
//---------------------------------------------------------------------------
The DayOfWeek() function comes in two versions. Besides the VCLs, the TDateTime
class also is equipped with this method. Its syntax is:
int __fastcall DayOfWeek() const;
This version does not take an argument. Instead, it is called by a TDateTime variable that
needs it. This function returns an integer that represents the weekly position of the day if
the execution is successful. The equivalent version of the above program would be:
//--------------------------------------------------------------------------void __fastcall TForm1::btnGetItClick(TObject *Sender)
{
TDateTime Value = StrToDate(edtDate->Text);
int Day = Value.DayOfWeek();
edtDay->Text = Day;
}
//---------------------------------------------------------------------------
Appendix
value; otherwise the execution would fail. The second argument, Months is an integer
that represents the number of months to be added to the first argument. If the addition is
successful, the IncMonth() function returns a new TDateTime value.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::edtMonthsExit(TObject *Sender)
{
TDateTime StartDate = edtStartDate->Text;
int Months = edtMonths->Text.ToInt();
TDateTime NextPeriod = IncMonth(StartDate, Months);
edtNextPeriod->Text = NextPeriod;
}
//---------------------------------------------------------------------------
The IncMonth() is used to both add and subtract months from a date. To subtract
months, pass the Months argument with a negative value. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::edtMonthsExit(TObject *Sender)
{
TDateTime StartDate, NextPeriod;
int Months;
if(edtMonths->Text != "")
{
StartDate = edtStartDate->Text;
Months = edtMonths->Text.ToInt();
NextPeriod = IncMonth(StartDate, Months);
edtNextPeriod->Text = NextPeriod;
}
else
edtNextPeriod->Text = edtStartDate->Text;
}
//---------------------------------------------------------------------------
Replacing a Date
void __fastcall ReplaceDate(TDateTime &Target, const TDateTime Source);
763
Appendix
The ReplaceDate() function allows replacing one date with another. On the function, the
Target argument is the new date whose value needs to be replaced by that of the Source
argument. Since the starting point of the TDateTime class is on 12/30/1899, if the Source
argument occurs before that the date, the ReplaceDate() function takes care of reconciling
the negative date. Here is an example of using the function:
}
//---------------------------------------------------------------------------
764
Appendix
This comparison is possible because the Equality operator == was overloaded in the
TDateTime class. Its syntax is:
bool __fastcall operator ==(const TDateTime& Target) const;
To compare two dates using the overloaded Equality operator, call the operator==() on
the desired date and supply the argument date value that is being compared against ar the
Target. The above could have been written:
//--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
if( Start.operator ==(End) )
pnlComparison->Caption = "Both dates are the same";
else
pnlComparison->Caption = "Those are different dates!";
}
//---------------------------------------------------------------------------
765
Appendix
766
Appendix
Alternatively, you can use the overloaded less than operator to find out when one date
is less than another. The syntax used is:
bool __fastcall operator <(const TDateTime& Target) const;
To perform the above less than comparison, you could implement the event as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
if( Start.operator<(End) )
pnlComparison->Caption = DateToStr(Start) + " occurs prior to " +
DateToStr(End);
else
pnlComparison->Caption = "I can't make up my mind";
}
//---------------------------------------------------------------------------
This comparison operator is useful because the less than or equal to operator <= was
overloaded in the TDateTime class. Its syntax is:
bool __fastcall operator <=(const TDateTime& Target) const;
Copyright 2003 FunctionX, Inc.
767
Appendix
Using the <= overloaded operator, The comparison in the above event could have been
written as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
if( Start.operator <=(End) )
pnlComparison->Caption = "Your film will be ready after 5 O'Clock";
else
pnlComparison->Caption = "Wrong date sequence";
}
//---------------------------------------------------------------------------
This greater than comparison between two date values is possible because its operator
was overloaded in the TDateTime class. Its syntax is:
bool __fastcall operator >(const TDateTime& Target) const;
Using the overloaded operator, the previous event could have been implemented as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
768
Appendix
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
if( Start.operator >(End) )
pnlComparison->Caption = DateToStr(Start) + " occurs after " +
DateToStr(End);
else
pnlComparison->Caption = "I can't make up my mind";
}
//---------------------------------------------------------------------------
Using this comparison, you can validation an intermediary operation. Even after finding
out whether the first date is greater than or equal to the second, you can further refine
your comparison inside of the comparison. In the following example, a message box
displays if the greater than or equal to comparison returns false:
//--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
if( Start >= End )
{
pnlComparison->Caption = "Testing for ""\"Greater Than Or Equal To""\"";
if( Start > End )
ShowMessage(DateToStr(Start) + " occurs after " + DateToStr(End));
else if( Start == End )
ShowMessage("Both dates occur at the same time");
}
else
pnlComparison->Caption = "I can't make up my mind";
}
//---------------------------------------------------------------------------
769
Appendix
This comparison operator is useful because the greater than or equal to operator <=
was overloaded in the TDateTime class. Its syntax is:
bool __fastcall operator >=(const TDateTime& Target) const;
To perform the comparison in the above event using the <= overloaded operator, call the
operator>=() method on the source date value and include the compared date as the
Target argument. The above event could be written as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
if( Start.operator >=(End) )
{
pnlComparison->Caption = "Testing for ""\"Greater Than Or Equal To""\"";
if( Start > End )
ShowMessage(DateToStr(Start) + " occurs after " + DateToStr(End));
else if( Start == End )
ShowMessage("Both dates occur at the same time");
}
else
pnlComparison->Caption = "I can't make up my mind";
}
//---------------------------------------------------------------------------
Operations on Dates
The TDateTime and other VCL functions allow you to perform various types of
operations on a date value. The TDateTime class is very sophisticated, especially when
copled with the Sysutils functions. Almost all types of operations and all types of
comparisons are possible. All arithmetic and all logic comparison operators were
overloaded to permit as much flexibility as possible. Some of the operations are possible
directly on date values. When not possible or difficult, the decoder and encoder functions
can be used to let the compiler work behind the scenes.
770
Appendix
Alternatively, the TDateTime has the assignment operator overloaded to allow assigning
a date variable to another. The syntaxes of the function are:
TDateTime& __fastcall operator =(const TDateTimeBase& rhs);
TDateTime& __fastcall operator =(const TDateTime& rhs);
TDateTime& __fastcall operator =(const double rhs);
TDateTime& __fastcall operator =(const int rhs);
To assign one date to another using the operator =() function, use a valid TDateTime
value or declare a TDateTime variable and call the operator=() overloaded function by
supplying the intended target TDateTime variable. The above event could be rewritten as;
//--------------------------------------------------------------------------void __fastcall TForm1::edtNbrOfDaysExit(TObject *Sender)
{
TDateTime Start, End;
int NbrOfDays;
Start = StrToDate(edtStartDate->Text);
NbrOfDays = StrToInt(edtNbrOfDays->Text);
if(NbrOfDays <= 1)
{
End.operator =(Start);
edtEndDate->Text = End;
771
Appendix
}
else
{
edtEndDate->Text = "";
edtEndDate->SetFocus();
}
}
//---------------------------------------------------------------------------
You can also get the number of days from the user by using another control on the
application. Here is an example:
772
Appendix
//---------------------------------------------------------------------------
The addition operation is possible on a date value because its operator is overloaded in
the TDateTime class. The TDateTime class provides a mechanism of adding a number of
days to a date value. The syntaxes of the overloaded operator are:
TDateTime __fastcall operator +(const TDateTimeBase& rhs) const;
TDateTime __fastcall operator +(const TDateTime& rhs) const;
TDateTime __fastcall operator +(const double rhs) const;
TDateTime __fastcall operator +(const int rhs) const;
When applied to a TDateTime value, the addition operator + adds a number of days to
a date. If the number added exceeds the end of year, the class will calculate and encode a
date that corresponds to the date of the subsequent year:
To add a number of months to a date value, decode the date to retrieve its year, month,
and day values. Add the intended number of months to your date and re-encode the date.
Here is an example:
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnExitClick(TObject *Sender)
{
Close();
773
Appendix
}
//---------------------------------------------------------------------------
To add a number of years to a date value, decode it to extract the year, month, and day
values. Add the integral number of years to the source year. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnAdditionClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtDate->Text);
int Years = StrToInt(edtValue->Text);
unsigned short Year, Month, Day;
Start.DecodeDate(&Year, &Month, &Day);
TDateTime End(Year+Years, Month, Day);
edtResult->Text = DateToStr(End);
}
//---------------------------------------------------------------------------
774
Appendix
Subtracting Dates
To get the number of days between two dates, perform the subtraction operation on their
values. To do this, you can declare a double precision number or an integer that would
store the subtracted number from the later date to the earlier. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
int Diff = End - Start;
edtDifference->Text = Diff;
}
//---------------------------------------------------------------------------
To get the difference of years between two dates, apply the subtraction operator on their
values to get the integral number of days. Then divide this number by 365. This
difference produces the number of years in ranges of 365 days. Here is an example:
As an alternative, you can decode both dates and subtract their year values; this would
produce the difference of years with regards to the years, not the real dates. For example,
Copyright 2003 FunctionX, Inc.
775
Appendix
the difference between 12/31/2001 and 01/01/2002 would produce a year. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
unsigned short StartYear, StartMonth, StartDay,
EndYear, EndMonth, EndDay;
Start.DecodeDate(&StartYear, &StartMonth, &StartDay);
End.DecodeDate(&EndYear, &EndMonth, &EndDay);
int Years = EndYear - StartYear;
edtYears->Text = Years;
}
//---------------------------------------------------------------------------
To get the difference of months between two dates, perform the subtraction operator on
their values to get the number of days elapsed and divide the result by 30. This would
produce a number of months. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
int Months = End - Start;
edtYears->Text = Months / 30;
}
//---------------------------------------------------------------------------
776
Appendix
}
//---------------------------------------------------------------------------
Decrementing a Date
To decrement a date value, declare a TDateTime variable and apply the operator on its
value. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
static TDateTime Start = StrToDate(edtStartDate->Text);
edtStartDate->Text = Start--;
}
//---------------------------------------------------------------------------
777
Appendix
The TDateTime class allows subtracting one day from a TDateTime value. This is done
using the overloaded decrement operator whose syntaxes are:
TDateTime& operator --();
TDateTime operator --(int);
To decrement a date value using the -- overloaded operator, you have two options. To use
the pre-decrement operator, as if you were using --Value, call the operator--() function.
This would apply the operator before recalling the variable. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
static TDateTime Start = StrToDate(edtStartDate->Text);
edtStartDate->Text = Start.operator --();
}
//---------------------------------------------------------------------------
To use the post-increment operator, which is the same as Value--, in which case the
statement is called before being incremented, use the operator--(int) method. The int
argument is not specific but you must supply it. Therefore, type any integer number
between the parentheses. Remember that the argument supplied is not the decrementing
value; it is only a witness but it is necessary. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
static TDateTime Start = StrToDate(edtStartDate->Text);
edtStartDate->Text = Start.operator --(2);
}
//---------------------------------------------------------------------------
Incrementing a Date
To increment a date value, declare a TDateTime variable and use the ++ operator on its
value. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnIncrementClick(TObject *Sender)
{
static TDateTime Original = StrToDate(edtDate->Text);
edtIncremented->Text = Original++;
}
//---------------------------------------------------------------------------
The TDateTime class allows you to add one day from a TDateTime value. This is done
using the overloaded increment operator with the following syntaxes:
778
Appendix
To use the post-increment operator, which is the same as Value++, in which case the
statement is called before being incremented, use the operator++(int). The int argument
is not specific but you must supply it. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnIncrementClick(TObject *Sender)
{
static TDateTime Original = StrToDate(edtDate->Text);
edtIncremented->Text = Original.operator ++(12);
}
//---------------------------------------------------------------------------
779
Appendix
780
Appendix
When the day has a numeric value that is less than 10, the default c and the d formats
display its value without the leading 0. To display the leading 0 as in 02 or 08, use the
dd format. Here is an example implemented using the TDateTime::FormatString()
method:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("dd");
}
//---------------------------------------------------------------------------
Using the FormatDateTime() function, you could have written the same event as
follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = FormatDateTime("dd", DateValue);
}
//---------------------------------------------------------------------------
781
Appendix
edtFormatString->Text = DateValue.FormatString("ddd");
}
//---------------------------------------------------------------------------
The same event using the FormatDateTime() function would be written as:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = FormatDateTime("ddd", DateValue);
}
//---------------------------------------------------------------------------
To display the weekday and the numeric day of the month, you can create a format that
combines both strings. When creating this string, the format must be separated inside the
string so the compiler would know which format to apply and where. To separate the
formats, you can use (almost) any character but you should conform to those used in your
regional settings. One of the most regularly used separators on dates is the comma but the
simplest separator is an empty space. Here is an example (using the
TDateTime::FormatString() method):
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("ddd dd");
}
//---------------------------------------------------------------------------
To display the complete name of a weekday, use the dddd format string. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("dddd");
}
//---------------------------------------------------------------------------
You can also display the weekday followed by the numeric day of the month. Here is an
example that uses the FormatDateTime() function:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
782
Appendix
In the same way you can combine a weekday short name followed by the combination of
day/month (or month/day) as you see fit. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = FormatDateTime("ddd m/dd", DateValue);
}
//---------------------------------------------------------------------------
783
Appendix
When using the m format, if the number of the month is less than 10, the compiler
would display it as 1, 2, 3, 4, 5, 6, 7, 8 or 9, without the leading 0. If you want to display
the leading zero for a month between 1 and 9, as 01 or 07, use the mm format. Here is
an example with the TDateTime::FormatString() method:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("mm");
}
//---------------------------------------------------------------------------
You can also use this format when constructing a combined date:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("dddd, mm/dd");
}
//---------------------------------------------------------------------------
This time, the name of the month would become more explicit in a combined format,
allowing the application to be more explicit. To create such a combined date, apply the
rules we have reviewed so far. The following TDateTime::FormatString()
implementation displays a date as short weekday-day-short month name combination:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
784
Appendix
}
//---------------------------------------------------------------------------
You can also use a comma and space to separate the name of the weekday from the other
components. The following event uses the FormatDateTime() function:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = FormatDateTime("dddd, dd mmm", DateValue);
}
//---------------------------------------------------------------------------
To display a complete name of a month, use the mmmm format. The name would
display as one of the following: January, February, March, April, May, June, July,
August, September, October, November, and December; confirming to the Regional
Settings of your computer. Here is an example that uses the
TDateTime::FormatString() method:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
AnsiString Formatter = "ddd, d mmmm";
edtFormatString->Text = DateValue.FormatString(Formatter);
}
//---------------------------------------------------------------------------
Another implementation that uses the FormatDateTime() function can display the
weekday-day-month combination with an empty space as the separator:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
AnsiString Formatter = "dddd, dd mmmm";
edtFormatString->Text = FormatDateTime(Formatter, DateValue);
785
Appendix
}
//---------------------------------------------------------------------------
To make the displays we have used so far a little more explicit, you can include the year
value in a combined date string, as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("ddd dd mmm yy");
}
//---------------------------------------------------------------------------
Therefore, you can apply any combination of the formats we have used so far to display a
date, as illustrated in the following FormatDateTime() function call:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
AnsiString Formatter = "dddd dd mmmm yy";
edtFormatString->Text = FormatDateTime(Formatter, DateValue);
}
//---------------------------------------------------------------------------
A year value represented with two digits is hardly explicit, unless you have a good reason
for using it. The alternative is to use all four digits to display a year. This format is
created with the yyy or the yyyy strings. Here is an example with the
TDateTime::FormatString() method:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("yyy");
}
//---------------------------------------------------------------------------
Since this format would be the only one with four digits in a combined string, it makes a
date easier to read. Once again, you can apply the rules we have used so far, to create and
display a combined date. The default format used by Microsoft Windows for the English
786
Appendix
language is as Sunday, January 27, 2002. You can use the TDateTime::FormatString()
method to create such a format as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
AnsiString Formatter = "dddd, mmmm dd, yyyy";
edtFormatString->Text = DateValue.FormatString(Formatter);
}
//---------------------------------------------------------------------------
Using these rules, you can display a date as you wish. The following FormatDateTime()
function display a date differently than the event above:
//--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
AnsiString Formatter = "dddd d mmmm yyyy";
edtFormatString->Text = FormatDateTime(Formatter, DateValue);
}
//---------------------------------------------------------------------------
Doing Time
The time is a unit that measures the number of hours, minutes, or seconds that divide a
day. A day is made of 24 non-spatial divisions called hours. An hour is made of 60 parts
called minutes and a minute is made of 60 seconds.
As done with dates, most of the operations performed on time values are centered around
the TDateTime class. This class is based on a double-precision number initialized at 0.00.
The constant 0.00 corresponds to 12/30/1899 at midnight. A double-precision number is
made of two sections: an integer part and a decimal side. The integral part is a natural
number with no decimal value, such as 8, 1450, or 32. For the TDateTime class, the
integral section represents the number of days that have elapsed since 12/30/1899.
On a double-precision number, such as 204.58, the decimal part starts with a period .
and is made of all digits on the right side of the period. For the TDateTime class, the
decimal part represents the number of seconds that have elapsed since midnight.
By default, the compiler refers to the Regional Settings of your computer to display the
time, separating it in hour, minute, second, and AM/PM. The default symbol to separate
the hour and the minute, or the minute and the second is :. To separate the seconds and
the AM/PM, the compiler leaves a one-character empty space between them.
787
Appendix
If not assigned a valued, this variable is initialized at midnight or 12:00:00 AM. You can
display its value in an Edit control as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime TimeValue;
Edit1->Text = TimeValue;
}
//---------------------------------------------------------------------------
You can initialize a time value with a double-precision number between 0.00000 and
0.99999. Here is an example:
TDateTime Value = 0.2185;
You can also get the value from an intermediary action or request it from the user. This
allows you, if necessary, to convert any floating-point number to a time value, as follows:
You can also use an independent floating-point number to initialize a time variable. Here
is an example:
788
Appendix
Still using the default constructor, if you know the time you want to initialze a variable
with, you can provide it. To do that, declare an instance of the TDateTime constructor
and type the time value between the double-quotes of the parentheses. If the time is
known only for the hour(s) and the minute(s), you can initialize it as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime TimeValue("08:22");
Label3->Caption = TimeValue;
}
//---------------------------------------------------------------------------
When using this formula, the hour value must be between 0 and 23. Any other value
outside of this range will cause an error. The minute value must range from 0 to 59;
otherwise, an error would be thrown. If the hour portion has a value between 0 and 11:59,
the time is set in the morning with the AM in the AM/PM section. If the hour portion is
between 12 and 23, the time is set in the afternoon. When displaying it, the compiler, by
default, calculates and displays the 0 to 12 portion and then displays PM in the AM/PM
section.
You can also initialize a time value using the Hour:Minute:Second formula as a string.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime TimeValue("20:22:52");
Label3->Caption = TimeValue;
}
//---------------------------------------------------------------------------
Once again, in the absence of an AM/PM section, the compiler would consider the hour
portion to evaluate whether the time occurs in the morning or in the afternoon. The value
of the seconds must be between 0 and 59; otherwise, an error will be thrown.
You can also initialize a time value by specifying the AM/PM portion as follows:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
789
Appendix
}
//---------------------------------------------------------------------------
The AM and the PM can be in uppercase or lowercase. In otherwords the AM/PM portion
can be represented as AM, Am, aM, am, PM, Pm, pM, or pm. Only the characters A and
P (uppercase or lowercase) are accepted as the first character. Only the M or m characters
are accepted as the second character. Any other combination or other character will cause
an error.
If you know the values of the hour, the minute, the second, and the millisecond, you can
use them to initialize a time variable. To do this, you must supply the arguments in order
following the constructor:
__fastcall TDateTime(unsigned short Hour, unsigned short Minute, unsigned short
Second, unsigned short Millisecond);
The hour value must be between 0 and 23. The minutes must be between 0 and 59. The
Second argument must have a value between 0 and 59. Whenever the seconds are not
important to represent the time, provide their value as 0. The milliseconds must range
from 0 to 999. If you do not know the millisecond value, provide it as 0. Here is an
example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime TimeValue(8, 20, 42, 605);
ShowMessage("The time considered is " + TimeValue);
}
//---------------------------------------------------------------------------
Since a double-precision number has a decimal section that represents the time of the
day, you can assign such a value to a TDateTime variable to initialize a time value. If the
integral part of the value is greater than 0, it would represents the number of days. If it is
0, only the time would be recognized as a fraction of the day. Using this, you can initia
The integral part is section y number
Appendix
the components of a time are separated with a colon :. The typical formats of a time
are:
If the string contains an invalid date, the conversion would fail and the program would
throw an error. If the conversion is successful, the function returns a valid time. Here is
an example:
//--------------------------------------------------------------------------void __fastcall TForm2::btnConvertClick(TObject *Sender)
{
TDateTime Value = StrToTime(edtSource->Text);
edtTarget->Text = Value;
}
//---------------------------------------------------------------------------
791
Appendix
The TDateTime class is also equipped with a method that can be used to convert a time
value to a string when necessary. The syntax used is:
AnsiString __fastcall TimeString() const;
To convert a time value to a string, declare a TDateTime variable and call the
TimeString() method. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime TimeValue("22:32:58");
AnsiString ToDisplay = TimeValue.TimeString();
Edit1->Text = ToDisplay;
}
//---------------------------------------------------------------------------
Alternatively, to convert a time value to a string, you can use the TimeToStr() function.
Its syntax is:
AnsiString __fastcall TimeToStr(System::TDateTime Date);
This function takes one argument, which is the time value that needs to be converted. If
the argument is not a valid time value, the conversion would fail and the program would
throw an error. If the conversion is successful, the function returns an AnsiString value.
Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnConvertClick(TObject *Sender)
{
AnsiString Current = TimeToStr(Time());
edtTarget->Text = Current;
}
//---------------------------------------------------------------------------
792
Appendix
Doing Time
Displaying the Time
The AnsiString class is highly compatible with the TDateTime class. This flexibility
allows any text-based control to be able to display a time value. Thanks to this feature,
you do not have to convert a time value in order to display. For example, to show the
current time on the caption of a form, you can just write:
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Caption = Time();
}
//---------------------------------------------------------------------------
This ability is independent of the format of the time; that is, it independent of the form of
initialization or source of the time value.
Decoding a Time
A time variable declared with the TDateTime class is made of an hour, a minute, a
second, and a millisecond portions. Decoding a time consists of isolating these
components from a valid time value. To perform such an operation, the TDateTime class
is equipped with the DecodeTime() method. Its syntax is:
void __fastcall DecodeTime(unsigned short* hour, unsigned short* min, unsigned
short* sec, unsigned short* msec) const;
793
Appendix
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnExitClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
794
Appendix
Besides the TDateTime::DecodeTime() method, the VCL provides a function that can be
used to decode a time value. Its syntax is:
void __fastcall DecodeTime(System::TDateTime Time, Word &Hour, Word &Min,
Word &Sec, Word &MSec);
The global DecodeTime() function is called in the same circumstances as the
TDateTime::DecodeTime() method except that it takes five arguments. The first is and
must be a valid TDateTime time value; this could be a time value in a recognizable
format or an already initialized variable. If this argument does not carry a valid time, the
function will fail and throw an error. The other four arguments are positive integers
(unsigned short) passed as reference. This allows the function to alter them and return
their changed values.
Encoding a Time
Encoding a time consists of supplying the necessary components of a TDateTime to the
compiler to create a valid time value. The function used to perform this operation is:
System::TDateTime __fastcall EncodeTime(Word Hour, Word Min, Word Sec,
Word MSec);
This function takes four positive integers (unsigned short) that represent:
Example: the following form is equipped with five Edit controls named edtHours,
edtMinutes, edtSeconds, edtMilliseconds, and edtTime. When the user clicks the Encode
button named btnEncode, the OnClick event retrieves the of the hour, the minute, the
second, and the millisecond values from their respective edit boxes. The the compiler
creates a time from those valules and displays the result in the Time edit box:
795
Appendix
}
//---------------------------------------------------------------------------
796
Appendix
The equality comparison works on time values thanks the overloaded equality operator
on the TDateTime class:
bool __fastcall operator ==(const TDateTime& rhs) const;
The equality comparison works on all components of a time value. If either the hour, the
minute, the second, or the AM/PM is not the same the operation renders false. If you
want to compare just the hours, you should decode the time values and then perform the
comparison on the hours.
The difference comparison works on all four entities of a time value. It examines the
hours, minutes, seconds, and AM/PM of the values provides on both sides of the
operator. If any of both similar components are different, the operation produces a true
result. This operation is based on the overloaded != operator whose syntax is:
bool __fastcall operator !=(const TDateTime& rhs) const;
797
Appendix
The less than comparison is based on its overloaded operator in the TDateTime class
using the following syntax:
bool __fastcall operator <(const TDateTime& rhs) const;
}
//---------------------------------------------------------------------------
The operator that allows this comparison uses the following syntax:
bool __fastcall operator <=(const TDateTime& rhs) const;
798
Appendix
{
TDateTime Start = StrToTime(edtStart->Text);
TDateTime End = StrToTime(edtEnd->Text);
if( Start > End )
{
lblMessage->Caption = "Your time sheet is not correct.";
edtStart->SetFocus();
}
}
//---------------------------------------------------------------------------
The greater than operator compare the hours, minutes, seconds, and AM/PM portions
of two dates and evaluates if the left date occurred after the right date; in this case, the
operator would produce a true Boolean value. This operation is based on its overloaded
operator from the TDateTime class. Its syntax is:
bool __fastcall operator >(const TDateTime& rhs) const;
799
Appendix
This function takes, as an argument, a string that specifies how the components of the
time value should display. The VCL provides an alternative function to apply the same
technique.; Its syntax is:
extern PACKAGE AnsiString __fastcall FormatDateTime(const AnsiString
Format, System::TDateTime Time);
When calling the FormatDateTime() function, you must pass two arguments. The Time
argument represents a time value or a variable that holds a valid time value. The Format
argument is a string that specifies how the time of the Time argument should display.
800
Appendix
To display the leading zero, use the hh format for the hour portion. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime RightNow = Time();
Edit1->Text = RightNow.FormatString("hh:nn:ss AM/PM");
}
//---------------------------------------------------------------------------
The rule to display or not display the leading zero for the minutes is the same. To avoid
the leading zero, use only one n when displaying the time. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnTimeClick(TObject *Sender)
{
TDateTime TimeX("08:05:52 AM");
// Displaying the minutes without the leading zero
edtStart->Text = TimeX.FormatString("HH:n:ss AM/PM");
// Displaying the minutes with the leading zero
edtEnd->Text = TimeX.FormatString("hh:nn:ss AM/PM");
}
//---------------------------------------------------------------------------
These same rules apply for the seconds portions of the time display.
The AM/PM portion is case sensitive. This allows you display it in uppercase or in
lowercase. To display the AM/PM section in lowercase, type am/pm or ampm in the
AM/PM section. On the other hand, to display it in uppercase, type it as AM/PM or
AMPM. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnTimeClick(TObject *Sender)
{
TDateTime TimeX("18:05:52");
// Displaying the AM/PM portion in lowercase
801
Appendix
}
//---------------------------------------------------------------------------
802
Appendix
{
Close();
}
//---------------------------------------------------------------------------
An alternative method to converting a date and time value to a string consists of using the
TDateTime::DateTimeString() method. Its syntax:
AnsiString __fastcall DateTimeString() const;
This function does not need an argument. To use it, declare a TDateTime date variable
and call the DateTimeString() method. Here is an example;
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime Current = Now();
AnsiString Right = Current.DateTimeString();
Edit1->Text = Right;
}
//---------------------------------------------------------------------------
803
Appendix
Index
A
abs() ................................................................. 95
Addition ......................................................... 590
alBottom ........................................................ 461
alClient................................................... 500, 502
algebra.............................................................. 91
Align .............................................................. 362
alLeft.......................................................... 691
TreeView ................................................... 691
AlignButton ................................................... 537
Alignment ...................................................... 502
AllCaps .......................................................... 524
AllowUp ........................................................ 438
alNone............................................................ 363
alTop .............................................................. 458
AM/PM .......................................................... 659
angle............................................................... 120
ANSI_CHARSET .......................................... 252
AnsiCompareStr() ............................................ 67
AnsiExtractQuotedStr() ................................... 72
AnsiLowerCase() ............................................. 63
AnsiQuotedStr()............................................... 71
AnsiSameCaption().......................................... 68
AnsiSameStr().................................................. 68
AnsiSameText() ............................................... 68
AnsiString ........................................................ 55
AnsiCompare()............................................. 66
AnsiCompareIC()......................................... 66
AnsiLastChar()............................................. 69
AppendStr() ................................................. 64
c_str()........................................................... 61
Delete() ........................................................ 70
IsEmpty() ..................................................... 56
Length() ....................................................... 57
LowerCase()................................................. 63
Pos()............................................................. 70
SetLength() .................................................. 57
sprintf() ........................................................ 93
SubString()................................................... 70
ToInt().......................................................... 91
ToIntDef().................................................... 92
Trim()........................................................... 59
TrimLeft() .................................................... 58
TrimRight().................................................. 59
UpperCase() ................................................. 61
AnsiUpperCase() ............................................. 62
ANTIALIASED_QUALITY ......................... 253
appearance ....................................................... 36
Apply ............................................................. 441
804
C
CalAlignment................................................. 660
CALLBACK.................................................. 343
Cartesian ................................................ 332, 358
case-sensitivity................................................. 65
cbChecked...................................................... 634
cbGrayed................................................ 609, 634
cbUnchecked.................................................. 634
ceil()................................................................. 96
Ceil() ................................................................ 96
CellRect()....................................................... 672
CharCase........................................................ 483
CHARFORMAT............................................ 523
CHARFORMAT2.......................................... 523
Checked ......................................................... 590
CHINESEBIG5_CHARSET.......................... 252
choice............................................................. 605
circle .............................................................. 120
circumference ................................................ 120
Civic............................................................... 546
cl3DDkShadow.............................................. 239
cl3DLight....................................................... 239
clActiveBorder............................................... 239
clActiveCaption ............................................. 239
clAppWorkSpace ........................................... 239
clAqua.................................................... 240, 327
Class Explorer.................................................. 26
Classes
AnsiString .................................................... 55
TApplication ................................................ 32
TCheckListBox .......................................... 637
TControl..................................................... 258
TCSpinButton ............................................ 542
TCSpinEdit ................................................ 545
TCustomComboBox .................................. 642
TCustomGrid ............................................. 681
TCustomLabel ........................................... 473
TCustomListBox........................................ 619
TCustomMemo .......................................... 499
TCustomTreeView..................................... 696
TDateTime ................................................. 653
TFindDialog............................................... 528
TFont.......................................................... 248
TFontDialog............................................... 257
TGraphicControl........................................ 473
TGraphicsObject........................................ 256
TLabel........................................................ 473
TList........................................................... 713
TListColumn.............................................. 702
TListItem ................................................... 702
TMainMenu ............................................... 452
TMaskEdit ................................................. 491
TMemo .............................................. 499, 502
TMonthCalendar ........................................ 652
TOrderedList.............................................. 742
Copyright 2003 FunctionX, Inc.
Appendix
Appendix
clMenuHighlight............................................ 239
clMenuText.................................................... 239
clMoneyGreen ............................................... 240
clNavy............................................................ 240
clNone............................................................ 240
clOlive............................................................ 240
Close ................................................................ 21
clPurple .......................................................... 240
clRed ...................................................... 240, 478
clScrollBar ..................................................... 239
clSilver ........................................................... 240
clSkyBlue............................................... 240, 667
clTeal ............................................................. 240
clWhite........................................................... 240
clWindow....................................................... 239
clWindowFrame............................................. 239
clWindowText ............................................... 239
clYellow......................................................... 240
Code Editor...................................................... 25
ColHeights ..................................................... 668
Color
CCalendar .................................................. 682
StringGrid .................................................. 667
COLOR_3DDKSHADOW............................ 239
COLOR_3DFACE......................................... 238
COLOR_3DHILIGHT................................... 239
COLOR_3DLIGHT ....................................... 239
COLOR_3DSHADOW ................................. 239
COLOR_ACTIVEBORDER ......................... 239
COLOR_ACTIVECAPTION ........................ 239
COLOR_APPWORKSPACE ........................ 239
COLOR_BACKGROUND............................ 239
COLOR_BTNHIGHLIGHT .......................... 239
COLOR_BTNHILIGHT................................ 239
COLOR_BTNTEXT...................................... 239
COLOR_CAPTIONTEXT ............................ 239
COLOR_DESKTOP...................................... 239
COLOR_GRADIENTACTIVECAPTION.... 239
COLOR_GRADIENTINACTIVECAPTION 239
COLOR_HIGHLIGHT .................................. 239
COLOR_HIGHLIGHTTEXT........................ 239
COLOR_HOTLIGHT.................................... 239
COLOR_INACTIVEBORDER ..................... 239
COLOR_INACTIVECAPTION.................... 239
COLOR_INACTIVECAPTIONTEXT.......... 239
COLOR_INFOBK ......................................... 239
COLOR_INFOTEXT .................................... 239
COLOR_MENU ............................................ 239
COLOR_MENUTEXT .................................. 239
COLOR_SCROLLBAR ................................ 239
COLOR_WINDOW ...................................... 239
COLOR_WINDOWFRAME......................... 239
COLOR_WINDOWTEXT ............................ 239
colorful........................................................... 649
COLORREF .................................................. 236
Columns......................................................... 596
806
Appendix
Appendix
dwExStyle...................................................... 323
DWORD ........................................................ 323
dwStyle .......................................................... 323
E
E350............................................................... 546
EASTEUROPE_CHARSET.......................... 252
ecLowerCase.................................................. 483
ecNormal........................................................ 483
EConvertError................................................ 661
ecUpperCase .................................................. 483
EDIT .............................................................. 325
EditStyle ........................................................ 687
Elantra............................................................ 546
EM_REDO .................................................... 522
EN_SETFOCUS ............................................ 497
Enabled .......................................................... 331
TColorGrid ................................................ 684
END_MESSAGE_MAP ................................ 345
Enumerator
TEditCharCase........................................... 483
Enumerators
TAlignment........................................ 474, 502
TButtonLayout........................................... 434
TCheckBoxState ........................................ 609
TComboBoxStyle ...................................... 641
TGridOrdering ........................................... 683
TProgressBarOrientation ........................... 561
TScrollBarKind.......................................... 580
TScrollCode............................................... 584
TTextLayout .............................................. 474
TTickMark ................................................. 550
TTrackBarOrientation................................ 548
TUDBtnType ............................................. 539
Escort ............................................................. 546
ETO_CLIPPED ............................................. 242
ETO_GLYPH_INDEX.................................. 242
ETO_NUMERICSLATIN ............................. 242
ETO_NUMERICSLOCAL............................ 242
ETO_OPAQUE ............................................. 242
ETO_PDY...................................................... 242
ETO_RTLREADING .................................... 242
Events
OnChange .......................................... 256, 497
OnClick.............................................. 427, 447
OnClickCheck............................................ 637
OnCreate ............................................ 355, 447
OnDblClick................................................ 247
OnDownClick ............................................ 542
OnExit() ..................................................... 497
OnGetMonthInfo ....................................... 655
OnKeyUp ................................................... 349
OnMouseEnter() ........................................ 480
OnMouseLeave .......................................... 480
OnMouseUp............................................... 352
OnUpClick ................................................. 542
808
DegToRad.................................................. 122
DoubleDecliningBalance ........................... 106
exp.............................................................. 101
expl ............................................................ 102
ExtTextOut................................................. 242
floor.............................................................. 97
Floor............................................................. 98
frexp ............................................................. 98
Frexp ............................................................ 99
frexpl............................................................ 98
FutureValue ............................................... 110
GetBkColor................................................ 241
GetBkMode................................................ 242
GetDesktopWindow................................... 357
GetParent ................................................... 329
GetSysColor............................................... 238
GetTickCount............................................. 556
GetWindowRect......................................... 340
InputBox ...................................................... 88
InputQuery ................................................... 89
InterestPayment ......................................... 113
InterestRate ................................................ 116
IntPower..................................................... 100
labs............................................................... 95
ldexp .......................................................... 102
Ldexp ......................................................... 102
ldexpl ......................................................... 102
LnXP1........................................................ 103
Log10 ......................................................... 103
Log2 ........................................................... 104
LogN .......................................................... 104
LowerCase ................................................... 63
MessageBox................................................. 78
MessageDlg ................................................. 83
MessageDlgPos............................................ 85
NetPresentValue ........................................ 119
NumberOfPeriods ...................................... 111
Payment ..................................................... 112
PostQuitMessage ....................................... 427
pow ............................................................ 100
Power ......................................................... 101
powl ........................................................... 100
PresentValue .............................................. 114
QuotedStr ..................................................... 72
RadToCycle ............................................... 123
RadToDeg.................................................. 123
SameText ..................................................... 65
SendMessage ............................................. 353
SendMessage()........................................... 493
SetBkColor ................................................ 241
SetBkMode ................................................ 241
SetParent .................................................... 329
SetTextColor...................................... 240, 251
ShellExecute()............................................ 479
ShowMessage .............................................. 75
ShowWindow............................................. 341
Copyright 2003 FunctionX, Inc.
Appendix
Appendix
Macros
GetBValue()............................................... 237
GetGValue()............................................... 237
GetRValue()............................................... 237
MAKEIPADDRESS .................................. 494
MAKEIPRANGE ...................................... 496
USEFORM .................................................. 34
MAKEIPADDRESS ...................................... 494
MAKEIPRANGE .......................................... 496
mantissa ........................................................... 98
Margin............................................................ 439
math.hpp .......................................................... 96
Max ................................................................ 580
MaxDate ........................................................ 660
Maximize ......................................................... 21
MaxLength............................................. 489, 501
MB_DEFBUTTON1 ......................................... 80
MB_DEFBUTTON2 ......................................... 80
MB_DEFBUTTON3 ......................................... 80
MB_DEFBUTTON4 ......................................... 80
MB_ICONASTERISK .................................... 79
MB_ICONERROR .......................................... 80
MB_ICONEXCLAMATION .......................... 79
MB_ICONHAND............................................ 80
MB_ICONINFORMATION............................ 79
MB_ICONQUESTION ................................... 80
MB_ICONSTOP.............................................. 80
MB_ICONWARNING .................................... 79
mbAbort........................................................... 84
mbAll ............................................................... 84
mbCancel ......................................................... 84
mbHelp ............................................................ 84
mbIgnore.......................................................... 84
mbLeft............................................................ 351
mbMiddle....................................................... 351
mbNo ............................................................... 84
mbNoToAll...................................................... 84
mbOK .............................................................. 84
mbRetry ........................................................... 84
mbRight ......................................................... 351
mbYes .............................................................. 84
mbYesToAll .................................................... 84
MDICLIENT ................................................. 325
menu template................................................ 452
message.......................................................... 343
MESSAGE_HANDLER................................ 345
MessageBox() .................................................. 78
MessageDlg()................................................... 83
MessageDlgPos() ............................................. 85
Messages
EM_REDO ................................................ 522
EN_SETFOCUS ........................................ 497
IPM_CLEARADDRESS ........................... 494
IPM_GETADDRESS ................................ 495
IPM_SETADDRESS ................................. 494
IPM_SETFOCUS ...................................... 496
Copyright 2003 FunctionX, Inc.
Appendix
IPM_SETRANGE...................................... 496
IPN_FIELDCHANGED ............................ 497
LB_SETHORIZONTALEXTENT .... 622, 633
LP_ADDSTRING...................................... 620
PBM_SETBARCOLOR ............................ 562
PBM_SETRANGE .................................... 562
PBM_SETRANGE32 ................................ 562
WM_MOVE ...................................... 344, 354
WM_QUIT................................................. 427
WM_SETTEXT......................................... 353
WMKeyDown............................................ 348
WMKeyPress ............................................. 349
WMKeyUp................................................. 349
Method ........................................................... 340
Microsoft.......................................................... 19
Microsoft Windows ......................................... 31
Microsoft Word.............................................. 440
milliseconds ................................................... 556
Min................................................................. 580
MinDate ......................................................... 660
Minimize .......................................................... 20
Modified......................................................... 501
MouseToCell()............................................... 673
mrAbort.......................................... 425, 428, 433
mrAll.............................................................. 425
mrCancel ................................................ 425, 433
mrIgnore................................................. 425, 433
mrNo ...................................................... 425, 433
mrNone .................................................. 425, 433
mrNoToAll............................................. 425, 433
mrOk ...................................................... 425, 433
mrRetry .................................................. 425, 433
mrYes..................................................... 425, 433
mrYesToAll ........................................... 425, 433
MSG............................................................... 343
Multiple Document Interface (MDI)................ 37
Multiplication................................................. 591
MultiSelect..................................................... 621
mutual-exclusive ............................................ 437
N
Name.............................................................. 324
Napierian........................................................ 103
natural logarithm ............................................ 103
Navigator ....................................................... 546
NetPresentValue().......................................... 119
new................................................................. 255
Next................................................................ 443
nHeight........................................................... 323
NONANTIALIASED_QUALITY................. 253
non-exclusive ................................................. 605
Notice3........................................................... 499
NULL............................................................. 251
Numbering ..................................................... 520
NumberOfPeriods()........................................ 111
nWidth ........................................................... 323
811
Appendix
O
Object Inspector............................................... 27
object-oriented programming (OOP) ............. 577
OEM_CHARSET .......................................... 252
OnAccept() ............................................ 508, 646
OnChange().................................... 256, 497, 643
OnClick.......................................................... 427
OnClickCheck() ............................................. 637
OnColumnMoved()........................................ 676
OnCreate() ............................................. 355, 643
OnDblClick............................................ 247, 674
OnDestroy() ................................................... 715
OnDrawCell() ........................................ 676, 677
OnEditButtonClick()...................................... 688
OnExit() ......................................................... 497
OnGetEditMask()........................................... 675
OnGetEdtText................................................ 675
OnKeyDown .......................................... 348, 674
OnKeyPress ................................................... 674
OnKeyUp ............................................... 349, 674
OnMouseDown.............................. 351, 352, 674
OnMouseMove .............................................. 674
OnMouseUp........................................... 352, 674
OnMouseWheelDown ................................... 674
OnRowMoved() ............................................. 676
OnScroll() ...................................................... 584
OnSelectCell() ............................................... 674
OnSelectionChange ....................................... 646
OnSetEditText()..................................... 675, 679
OnUserInput()................................................ 662
operating system ............................................ 649
Operating Systems ........................................... 19
Operators
: 347
+ 64
<<............................................................... 250
==................................................................. 56
delete.................................................. 255, 714
new............................................. 255, 341, 671
OR | ............................................................ 253
Orientation ............................................. 548, 561
OUT_CHARACTER_PRECIS...................... 253
OUT_DEFAULT_PRECIS............................ 253
OUT_DEVICE_PRECIS ............................... 253
OUT_OUTLINE_PRECIS ............................ 253
OUT_RASTER_PRECIS .............................. 253
OUT_STRING_PRECIS ............................... 253
OUT_STROKE_PRECIS .............................. 253
OUT_TT_ONLY_PRECIS............................ 253
OUT_TT_PRECIS......................................... 253
overflow......................................................... 101
P
PageSize......................................................... 581
PARAFORMAT ............................................ 524
812
BorderIcons.................................................. 53
BoundsRect................................................ 340
Caption............................................... 326, 424
ClientRect .................................................. 362
Enabled ...................................................... 331
Height ........................................................ 333
Hint ............................................................ 327
Left............................................................. 332
ModalResult............................................... 425
Position ...................................................... 362
ShowHint ................................................... 327
Showing ..................................................... 330
TabOrder.................................................... 331
TabStop...................................................... 331
Text ............................................................ 326
Top............................................................. 332
Visible........................................................ 330
Width ......................................................... 333
property page ................................................. 439
Q
QuotedStr() ...................................................... 72
R
radian ............................................................. 120
RadToCycle() ................................................ 123
RadToDeg() ................................................... 123
random ............................................................. 91
Rapid Application Development (RAD).......... 31
ratio ................................................................ 120
ReadOnly ....................................................... 500
RECT ............................................................. 337
Red................................................................. 235
reference .......................................................... 31
Regional Settings ..................................... 68, 649
Restore ............................................................. 21
RichEdit ......................................................... 325
RICHEDIT_CLASS ...................................... 325
Rio ................................................................. 546
rotation........................................................... 121
Run Time ......................................................... 36
RUSSIAN_CHARSET .................................. 252
S
SameText() ...................................................... 65
Save All ........................................................... 27
sbHorizontal................................................... 579
sbVertical....................................................... 579
SCROLLBAR........................................ 325, 577
ScrollBars ...................................................... 665
SelCount ........................................................ 621
SelectAll()...................................................... 485
SendMessage()............................................... 353
Sentra ............................................................. 546
SetBkColor().................................................. 241
Copyright 2003 FunctionX, Inc.
Appendix
SetBkMode().................................................. 241
SetFocus() ...................................................... 342
SetParams() .................................................... 583
SetParent() ..................................................... 329
Sets
TGridDrawState ......................................... 676
SetTextColor() ....................................... 240, 251
SetWindowLong().......................................... 694
Shape.............................................................. 445
SHIFTJIS_CHARSET ................................... 252
shortcut........................................................... 453
ShortDateFormat............................................ 628
ShowAccelChar ............................................. 483
Showing ......................................................... 330
ShowMessage .................................................. 75
ShowToday .................................................... 653
ShowWindow() .............................................. 341
Single Document Interface (SDI)..................... 37
SIZE ............................................................... 336
sLineBreak ....................................................... 76
SLNDepreciation()......................................... 107
Small Icons .................................................... 697
SmallChange .................................................. 581
Spacing........................................................... 439
SpeedButton................................................... 437
spin button...................................................... 533
sqrt()............................................................... 105
sqrtl().............................................................. 105
ssAlt ............................................................... 351
ssBoth............................................................. 576
ssCtrl .............................................................. 351
ssDouble......................................................... 351
ssHorizontal ................................................... 576
ssLeft.............................................................. 351
ssMiddle......................................................... 351
ssNone............................................................ 576
ssRight ........................................................... 351
ssShift..................................................... 348, 351
ssVertical ....................................................... 576
Standard Controls............................................. 37
StartOfWeek .................................................. 682
STATIC ................................................. 325, 481
static text ........................................................ 473
StaticText ....................................................... 481
statistics............................................................ 91
Statistics1 ....................................................... 698
status bar ........................................................ 328
Step ................................................................ 563
StepBy() ......................................................... 566
StepIt() ........................................................... 566
StringGrid
BorderStyle ................................................ 665
StrToFloat() ..................................................... 93
StrToInt() ......................................................... 92
Structures
TCarInventory............................................ 546
813
Appendix
TGridCoord................................................ 674
Subtraction ..................................................... 591
Sunday ........................................................... 653
SYDDepreciation() ........................................ 108
SYMBOL_CHARSET................................... 252
Syntaxes
InputBox().................................................... 88
USEFORM .................................................. 34
WinMain() ................................................... 31
WNDCLASS ............................................... 32
WNDCLASSEX .......................................... 32
system .............................................................. 20
SystemParametersInfo()................................. 357
T
TabOrder................................................ 331, 501
TabStop.......................................................... 331
taCenter.......................................................... 465
TActionEvent................................................. 471
TActionList.................................................... 466
Execute().................................................... 471
OnChange .................................................. 471
OnUpdate() ................................................ 471
taLeftJustify ........................................... 465, 590
TApplication .................................................... 32
CreateForm()................................................ 34
Hint ............................................................ 328
HintColor ................................................... 327
Initialize() .................................................... 32
MessageBox() .............................................. 80
Run() ............................................................ 35
taRightJustify ......................................... 465, 590
Taskbar ............................................................ 20
TBasicAction ................................................. 471
TBevel
bsTopLine .................................................. 445
TBitBtn
Enabled ...................................................... 722
Glyph ......................................................... 433
Kind ................................................... 432, 548
Layout ........................................................ 434
Margin........................................................ 434
NumGlyphs................................................ 434
OnClick() ................................................... 723
Spacing ...................................................... 434
TButton
Cancel ........................................................ 424
Default ............................................... 424, 501
ModalResult............................................... 425
OnClick() ................................................... 611
TButtonLayout............................................... 434
TCanvas ......................................................... 240
Font .................................................... 248, 253
TCCalendar
BorderStyle ................................................ 682
Color .......................................................... 682
814
Day............................................................. 682
GridLineWidth ........................................... 682
Month......................................................... 682
ReadOnly ................................................... 682
StartOfWeek .............................................. 682
UseCurrentDate.......................................... 682
Year............................................................ 682
TCheckBox .................................................... 611
Alignment .................................................. 608
AllowGrayed.............................................. 609
Caption....................................................... 608
Checked ..................................................... 607
GetControlsAlignment() ............................ 612
State ........................................................... 609
TCheckBoxState ............................................ 609
TCheckListBox
AllowGrayed.............................................. 634
Checked ..................................................... 634
Columns ..................................................... 633
Enabled ...................................................... 635
Header ........................................................ 633
HeaderBackgroundColor ........................... 634
HeaderColor............................................... 634
ItemEnabled ............................................... 635
Sorted ......................................................... 633
State ........................................................... 634
TColor............................................................ 235
TColorDialog
Color .......................................................... 244
TColorDialogOption ...................................... 245
TColorGrid
Background ................................................ 683
BackgroundEnabled ................................... 684
Enabled ...................................................... 684
Foreground ................................................. 683
ForegroundEnabled .................................... 684
GridOrdering.............................................. 683
TColorSelect .................................................. 507
TComboBox................................................... 642
DropDownCount........................................ 642
ItemIndex ................................................... 642
Items........................................................... 641
Name .......................................................... 641
Sorted ................................................. 641, 642
Style ........................................................... 641
TComboBoxStyle .......................................... 641
TControl................................................. 258, 612
Caption....................................................... 424
Hide() ......................................................... 342
Perform().................................................... 353
Show()........................................................ 341
WndProc().................................................. 354
TCSpinButton
OnDownClick().......................................... 542
OnUpClick() .............................................. 542
TCSpinEdit .................................................... 545
Copyright 2003 FunctionX, Inc.
Appendix
OnCreate().................................................. 686
OnDestroy() ............................................... 715
Position
poScreenCenter ...................................... 561
ShowHint ................................... 328, 463, 466
ShowModal() ..................................... 425, 611
TGraphicsObject ............................................ 256
THAI_CHARSET.......................................... 252
thumb ............................................................. 575
TImageList
Name .................................................. 699, 701
Times New Roman ........................................ 429
title bar ............................................................. 20
TKeyEvent ..................................................... 348
TKeyPress ...................................................... 348
TLabel
AutoResize................................................. 473
Caption....................................................... 473
Layout ........................................................ 474
Left............................................................. 477
Name .......................................................... 477
Top ............................................................. 477
Transparent ........................................ 474, 477
WordWrap.................................................. 474
tlBottom ......................................................... 474
tlCenter........................................................... 474
TList
Add() .................................................. 718, 737
Capacity ..................................................... 718
Clear() ........................................................ 741
Count.......................................................... 718
Delete() ...................................................... 739
First() ......................................................... 720
IndexOf().................................................... 720
Insert()........................................................ 737
Items................................................... 720, 739
Last() .......................................................... 720
Remove().................................................... 739
TListBox ........................................................ 622
Clear() ........................................................ 625
Columns ..................................................... 621
DockSite..................................................... 621
DragMode .................................................. 621
ExtendedSelect........................................... 621
Height......................................................... 622
ItemIndex ................................................... 621
Items........................................................... 620
MultiSelect................................................. 621
SelCount..................................................... 621
Selected ...................................................... 621
TListView
Height......................................................... 702
Items........................................................... 698
LargeImages............................................... 706
OnKeyDown()............................................ 739
SmallImages............................................... 706
815
Appendix
Appendix
Options....................................................... 669
goColMoving ......................................... 669
goRowMoving ....................................... 669
RowCount .................................................. 665
Rows .......................................................... 669
ScrollBars................................................... 665
TStringList ..................................................... 713
TStrings.................................................. 620, 713
Add() .......................................... 595, 620, 641
Delete() ...................................................... 624
TTextAttributes
Bold............................................................ 520
TTickMark ..................................................... 550
TTimer
Enabled ...................................................... 555
Interval ....................................................... 555
TToolBar........................................................ 458
EdgeBorders............................................... 727
Flat ............................................................. 727
Height......................................................... 727
Images ........................................................ 727
TTrackBar
Max ............................................................ 549
Min............................................................. 549
OnChange()................................................ 551
Orientation ................................................. 548
SliderVisible .............................................. 549
ThumbLength............................................. 549
TickMarks .................................................. 550
TTrackBarOrientation
trHorizontal ................................................ 548
TTreeView
Align .......................................................... 691
Items........................................................... 691
TUDBtnType ................................................. 539
TUpDown
AlignButton................................................ 537
ArrowKeys................................................. 537
Associate .................................................... 537
Increment ................................................... 536
Max ............................................................ 536
Min............................................................. 536
OnChanging() ............................................ 539
OnChangingEx() ........................................ 539
OnClick() ................................................... 539
OnMouseUp() ............................................ 539
Orientation ................................................. 535
Position ...................................................... 536
Thousands .................................................. 536
Wrap........................................................... 536
TURKISH_CHARSET .................................. 252
TValueListEditor
ItemProps ................................................... 685
OnEditButtonClick() .................................. 688
OnGetPickList() ......................................... 686
TVisualListEditor
817
Appendix
818
CHARFORMAT2...................................... 523
PARAFORMAT ........................................ 524
PARAFORMAT2 ...................................... 525
WindowProc .................................................. 355
Windows .......................................................... 19
Menu Designer........................................... 453
Windows 95 ..................................................... 37
Windows Styles
WS_DISABLED........................................ 331
WS_TABSTOP.......................................... 332
WS_VISIBLE ............................................ 331
WinMain() ....................................................... 31
WM_DESTROY............................................ 427
WM_MOVE .......................................... 344, 354
WM_QUIT..................................................... 427
WM_SETTEXT..................................... 326, 353
WMKeyDown................................................ 348
WMKeyPress ................................................. 349
WMKeyUp..................................................... 349
WNDCLASS.................................................... 32
WNDCLASSEX .............................................. 32
WNDPROC ..................................................... 32
WndProc()...................................................... 354
WordPerfect ................................................... 451
WordWrap ............................................. 474, 502
WorkAreaHeight............................................ 361
WorkAreaRect ............................................... 361
Wrap............................................................... 536
WS_CHILD ................................................... 329
WS_DISABLED............................................ 331
WS_TABSTOP.............................................. 332
WS_VISIBLE ................................................ 331
819