Anda di halaman 1dari 820

FunctionX Practical Learning Series

Table of Contents Borland C++ Builder Programming



2 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Table of Contents

Copyright 2003 FunctionX, Inc. 3


Table of Contents

PART I INTRODUCTION TO BORLAND C++ BUILDER...................... 18
CHAPTER 1: THE BORLAND C++ BUILDER IDE.................................. 19
1.1 An Integrated Development Environment ............................................ 19
1.1.1 Overview ...................................................................................... 19
1.1.2 Integrated Development Environment .......................................... 20
1.1.3 The Title Bar................................................................................. 20
1.1.4 The Main Menu ............................................................................ 21
1.1.5 The Toolbars................................................................................. 22
1.1.6 The Component Palette................................................................. 24
1.1.7 The Default Form......................................................................... 25
1.1.8 The Code Editor............................................................................ 25
1.1.9 The Class Explorer ....................................................................... 26
1.1.10 . The Object Inspector .................................................................... 27
1.2 Introduction to C++ Builder Projects.................................................... 27
1.2.1 Program Execution ....................................................................... 27
1.2.2 Saving a Project ............................................................................ 27
1.3 Borland C++ Builder Help.................................................................... 28
1.3.1 Overview ...................................................................................... 28
1.3.2 Online Help................................................................................... 28
1.3.3 Internet Help................................................................................. 28
CHAPTER 2: CONTROLS FUNDAMENTALS.......................................... 31
2.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 Borland C++ Builder Programming

4 Copyright 2003 FunctionX, Inc.

3.1 The Fundamentals of Strings ................................................................ 55
3.1.1 String Construction and Declaration............................................. 55
3.1.2 String Initialization ....................................................................... 55
3.2 General Purpose String Functions ........................................................ 56
3.2.1 String Emptiness........................................................................... 56
3.2.2 The Length of a String.................................................................. 57
3.2.3 String Trimming ........................................................................... 58
3.3 String Conversions................................................................................ 60
3.3.1 C/C++ Data Types Conversion to AnsiString .............................. 60
3.3.2 AnsiString and C-Strings.............................................................. 61
3.3.3 Strings Cases: Lowercase to Uppercase Conversion .................... 61
3.3.4 Strings Cases: Uppercase to Lowercase Conversion .................... 63
3.4 Strings Addition.................................................................................... 64
3.4.1 The Addition Operator.................................................................. 64
3.4.2 Appending Strings ........................................................................ 64
3.5 Strings Comparison Functions.............................................................. 65
3.5.1 Introduction .................................................................................. 65
3.5.2 String Comparison With Case-Insensitivity ................................. 65
3.5.3 String Comparison With Case-Sensitivity.................................... 66
3.5.4 Strings Boolean Comparisons....................................................... 67
3.6 Characters and Sub-Strings................................................................... 69
3.6.1 The Last Character of a String...................................................... 69
3.6.2 Character Deletion........................................................................ 70
3.6.3 Substring Creation ........................................................................ 70
3.6.4 The Position of a Sub String......................................................... 70
3.6.5 Character or Substring Replacement ............................................ 71
3.7 String Quotations.................................................................................. 71
3.7.1 Regular String to Quoted String Conversion................................ 71
3.7.2 Quoted String to Regular String Conversion................................ 72
3.7.3 String Quotes Removal ................................................................. 72
CHAPTER 4: MESSAGE BOXES................................................................. 75
4.1 Fundamental Messages Boxes .............................................................. 75
4.1.1 Overview ...................................................................................... 75
4.1.2 Message Showing......................................................................... 75
4.1.3 The Win32 Message Box.............................................................. 78
4.2 VCL Custom Message Boxes ............................................................... 83
4.2.1 The Message Box as a Dialog....................................................... 83
4.2.2 The Message Box and its Position................................................ 85
4.2.3 Message Created From a Dialog................................................... 86
4.2.4 The Input Dialog Box ................................................................... 88
4.2.5 The InputQuery Request ............................................................... 89
CHAPTER 5: MATH FUNCTIONS.............................................................. 91
5.1 The Math Libraries ............................................................................... 91
5.1.1 Introduction .................................................................................. 91
5.1.2 String to Integer Conversion......................................................... 91
5.1.3 Integer to String Conversion......................................................... 92
5.1.4 String to Floating-Point Conversion............................................. 92
5.1.5 Number Formatting to Display Decimals ..................................... 93
5.2 Arithmetic Functions ............................................................................ 94
5.2.1 Absolute Values............................................................................ 94
5.2.2 The Ceiling of a Number .............................................................. 95
5.2.3 The Floor of a Number ................................................................. 97
5.2.4 The Exponent of a Number........................................................... 98
Borland C++ Builder Programming Table of Contents

Copyright 2003 FunctionX, Inc. 5

5.2.5 The Power of a Number................................................................ 99
5.2.6 The Exponential.......................................................................... 101
5.2.7 The Square Root ......................................................................... 105
5.3 Business Functions ............................................................................. 105
5.3.1 Introduction ................................................................................ 105
5.3.2 Double Declining Balance.......................................................... 106
5.3.3 Straight Line Depreciation.......................................................... 107
5.3.4 Sum of the Year Digits Depreciation.......................................... 108
5.4 Finance Functions............................................................................... 109
5.4.1 Introduction ................................................................................ 109
5.4.2 The Future Value of an Investment ............................................ 110
5.4.3 The Number of Periods of an Investment ................................... 111
5.4.4 Making an Investment or Paying a Loan .................................... 112
5.4.5 The Amount Paid as Principal .................................................... 113
5.4.6 The Present Value of an Investment ........................................... 114
5.4.7 The Amount Paid As Interest...................................................... 115
5.4.8 The Interest Rate......................................................................... 116
5.4.9 The Internal Rate of Return ........................................................ 117
5.4.10 . The Net Present Value................................................................ 119
5.5 Measure-Based Functions................................................................... 120
5.5.1 Introduction ................................................................................ 120
5.5.2 The Pi Constant .......................................................................... 121
5.5.3 Cycle To Radius Conversion ...................................................... 121
5.5.4 Degrees To Radius Conversion .................................................. 122
5.5.5 Radius To Cycle Conversion ...................................................... 123
5.5.6 Radius To Degrees Conversion .................................................. 123
5.6 Statistics.............................................................................................. 124
5.6.1 The Maximum Integer Value of a Series.................................... 124
5.6.2 The Maximum Value of a Series ................................................ 124
5.6.3 The Mean or Average Value of a Series..................................... 125
5.6.4 The Minimum Integral Value of a Series ................................... 125
5.6.5 The Minimum Value of a Series................................................. 126
5.6.6 The Sum of Values of a Series.................................................... 126
5.6.7 The Sum of Integers of a Series.................................................. 127
5.6.8 The Sum Of Squares of a Series ................................................. 128
5.6.9 The Sums and Squares of a Series .............................................. 129
5.7 Trigonometric Functions .................................................................... 129
5.7.1 The Cosine of a Value ................................................................ 130
5.7.2 The Sine of a Value .................................................................... 130
5.7.3 Tangents...................................................................................... 131
CHAPTER 6: ACCESSORIES FOR FILE PROCESSING...................... 133
6.1 Files .................................................................................................... 133
6.1.1 Introduction ................................................................................ 133
6.1.2 Characteristics of a File .............................................................. 133
6.1.3 Introduction to Common File Dialog Boxes............................... 134
6.2 The Save As Dialog Box .................................................................... 134
6.2.1 Overview of the Save As Dialog Box......................................... 134
6.2.2 Save As Dialog Box Creation..................................................... 136
6.2.3 Characteristics of the Save As Dialog Box................................. 137
6.3 The Open File Dialog Box.................................................................. 141
6.3.1 Introduction ................................................................................ 141
6.3.2 Open File Dialog Box Creation .................................................. 141
6.3.3 Characteristics of an Open Dialog Box ...................................... 142
6.4 The Browse For Folder Dialog Box.................................................... 144
Table of Contents Borland C++ Builder Programming

6 Copyright 2003 FunctionX, Inc.

6.4.1 Introduction ................................................................................ 144
6.4.2 Creation of a Browse for Folder Dialog Box.............................. 145
6.5 The Select Directory Dialog Box........................................................ 146
6.5.1 Introduction ................................................................................ 146
6.5.2 Creation of the Select Directory Dialog Box.............................. 146
6.6 The Print Dialog Box.......................................................................... 148
6.6.1 Printing: An Overview................................................................ 148
6.6.2 The Process of Printing............................................................... 149
6.7 The Print Setup Dialog Box................................................................ 151
6.7.1 Overview of the Print Setup Dialog Box .................................... 151
6.7.2 Creationg of the Print Setup Dialog box..................................... 152
CHAPTER 7: FILE PROCESSING............................................................. 153
7.1 C File Processing................................................................................ 153
7.1.1 C How To Process Files ............................................................. 153
7.1.2 Opening and/or Saving Files....................................................... 154
7.1.3 Reading From and Writing to Files ............................................ 156
7.2 C++ File Streaming ............................................................................ 159
7.2.1 Overview .................................................................................... 159
7.2.2 Saving a File ............................................................................... 159
7.2.3 Opening a File ............................................................................ 163
7.3 VCL File Streaming............................................................................ 167
7.3.1 Introduction ................................................................................ 167
7.3.2 Saving Controls Contents ........................................................... 167
7.3.3 Loading Controls Contents ......................................................... 169
7.4 VCL File Buffering............................................................................. 170
7.4.1 Introduction ................................................................................ 170
7.4.2 Values Buffering......................................................................... 170
7.4.3 Value Reading ............................................................................ 171
7.5 Win32 File Processing........................................................................ 172
7.5.1 File Creation ............................................................................... 172
7.5.2 File Saving.................................................................................. 175
7.5.3 File Opening ............................................................................... 178
CHAPTER 8: STRINGS LISTS................................................................... 183
8.1 Introduction to Lists............................................................................ 183
8.1.1 Overview .................................................................................... 183
8.1.2 Usage of Lists ............................................................................. 184
8.2 The TStrings Class.............................................................................. 185
8.2.1 Introduction ................................................................................ 185
8.2.2 Strings Addition and Insertion to a List ...................................... 186
8.2.3 String Removal From a List........................................................ 188
8.2.4 Strings and Their Positions in a List ........................................... 188
8.2.5 Groups of Strings........................................................................ 194
8.3 List of Strings and File Management.................................................. 198
8.3.1 Stream Saving............................................................................. 199
8.3.2 Stream Opening .......................................................................... 199
8.4 The TStringList Class ......................................................................... 200
8.4.1 Introduction ................................................................................ 200
8.4.2 The String List Object ................................................................ 200
8.4.3 How TStringList != TStrings...................................................... 202
8.4.4 List Creation and Management with TStringList ....................... 202
PART II THE DEVICE CONTEXT AND ITS USEFULNESS................. 206
Borland C++ Builder Programming Table of Contents

Copyright 2003 FunctionX, Inc. 7

CHAPTER 9: THE GRAPHICAL DEVICE INTERFACE...................... 207
9.1 Introduction to the GDI ...................................................................... 207
9.1.1 The Device Context .................................................................... 207
9.1.2 The Canvas ................................................................................. 207
9.2 Drawing Lines and Shapes ................................................................. 208
9.2.1 Lines ........................................................................................... 208
9.2.2 Polylines ..................................................................................... 210
9.2.3 Multiple Polylines....................................................................... 212
9.2.4 Polygons ..................................................................................... 214
9.2.5 Multiple Polygons....................................................................... 214
9.2.6 Rectangles and Squares .............................................................. 216
9.2.7 A Rectangle With Edges............................................................. 218
9.2.8 Ellipses and Circles..................................................................... 219
9.2.9 Round Rectangles and Round Squares ....................................... 221
9.2.10 . Pies ............................................................................................. 222
9.2.11 . Arcs ............................................................................................ 223
9.2.12 . The Arc's Direction..................................................................... 225
9.2.13 . Angular Arcs .............................................................................. 227
9.2.14 . Chords ........................................................................................ 228
9.2.15 . Bzier Curves ............................................................................. 229
9.3 Text Drawing Techniques................................................................... 232
9.3.1 Text Outing................................................................................. 232
9.3.2 Text Confined to a Rectangle ..................................................... 233
9.3.3 The Dimensions of a Drawn String ............................................ 233
9.3.4 Text Drawing and Alignment ..................................................... 234
CHAPTER 10: GDI ACCESSORIES.......................................................... 235
10.1 Colors ................................................................................................ 235
10.1.1 . Overview.................................................................................... 235
10.1.2 . The Color as a Data Type ........................................................... 235
10.1.3 . Color Decoding .......................................................................... 237
10.1.4 . Color Identification .................................................................... 237
10.1.5 . Color Palettes.............................................................................. 240
10.2 Drawing with Colors.......................................................................... 240
10.2.1 . Text Drawing with Colors .......................................................... 240
10.2.2 . Text Background Color .............................................................. 241
10.3 The Color Dialog Box ....................................................................... 243
10.3.1 . Description of the Color Dialog Box.......................................... 243
10.3.2 . Making a Color Dialog Box Available....................................... 244
10.4 Fonts .................................................................................................. 247
10.4.1 . Introduction to Fonts .................................................................. 248
10.4.2 . Font Creation or Selection.......................................................... 248
10.4.3 . Font Properties............................................................................ 249
10.4.4 . Win32 Support of Fonts ............................................................. 251
10.4.5 . Font Retrieval ............................................................................. 255
10.4.6 . Font Methods.............................................................................. 255
10.4.7 . Font Messages and Events.......................................................... 256
10.5 The Font Dialog Box ......................................................................... 256
10.5.1 . Introduction ................................................................................ 256
10.5.2 . Allowing Font Formatting.......................................................... 257
CHAPTER 11: GDI TOOLS......................................................................... 261
11.1 Pens.................................................................................................... 261
11.1.1 . The Fundamentals of a Pen ........................................................ 261
Table of Contents Borland C++ Builder Programming

8 Copyright 2003 FunctionX, Inc.

11.1.2 . Creating and Selecting a Pen...................................................... 261
11.1.3 . Win32 Support of Pens............................................................... 262
11.1.4 . Characteristics of a Pen .............................................................. 262
11.1.5 . Retrieving a Pen ......................................................................... 265
11.2 Brushes .............................................................................................. 265
11.2.1 . Introduction ................................................................................ 265
11.2.2 . Win32 Support of Brushes ......................................................... 265
11.2.3 . Solid Brushes.............................................................................. 266
11.2.4 . Hatched Brushes......................................................................... 268
11.2.5 . Logical Brushes .......................................................................... 270
11.3 Using Pens and Brushes: The Image Editor....................................... 271
11.3.1 . Introduction ................................................................................ 271
11.3.2 . Starting Image Editor.................................................................. 271
11.3.3 . Using the Image Editor............................................................... 272
11.4 Icons .................................................................................................. 273
11.4.1 . Introduction ................................................................................ 273
11.4.2 . Creating Icons............................................................................. 274
11.5 Cursors............................................................................................... 278
11.5.1 . Introduction ................................................................................ 278
11.5.2 . Creating Cursors......................................................................... 279
11.6 Other Techniques of Creating Icons and Cursors .............................. 282
11.6.1 . Icons and Cursors Design........................................................... 282
11.6.2 . Transforming an Icon or a Cursor .............................................. 286
11.7 Applications Resources ..................................................................... 289
11.7.1 . Introduction ................................................................................ 289
11.7.2 . Creating a Resource File............................................................. 289
CHAPTER 12: BITMAPS............................................................................. 293
12.1 Bitmaps Fundamentals....................................................................... 293
12.1.1 . Introduction ................................................................................ 293
12.1.2 . Bitmap Creation.......................................................................... 294
12.1.3 . Bitmap Design on Image Editor ................................................. 294
12.1.4 . Bitmap Creation: Windows Paint ............................................... 296
12.2 The VCL Support of Bitmaps............................................................ 297
12.2.1 . Introduction ................................................................................ 297
12.2.2 . Bitmap Drawing ......................................................................... 298
12.2.3 . Bitmap Loading From a File ...................................................... 299
12.2.4 . Bitmap Loading From a Resource File....................................... 301
12.2.5 . Bitmap Loading From a Resource Identifier .............................. 303
12.2.6 . Characteristics of Bitmaps.......................................................... 306
12.2.7 . Pattern Brushes........................................................................... 308
12.3 Win32 Support for Bitmaps............................................................... 310
12.3.1 . Introduction ................................................................................ 310
12.3.2 . Bitmap Creation.......................................................................... 311
12.4 Image Lists ........................................................................................ 311
12.4.1 . Overview.................................................................................... 311
12.4.2 . The Pictures of an Image List ..................................................... 311
12.4.3 . Image List Creation and Characteristics..................................... 312
12.4.4 . Image List Methods.................................................................... 315
PART IV WINDOWS CONTROLS ............................................................ 320
CHAPTER 13: CHARACTERISTICS OF CHILD CONTROLS ............ 321
13.1 Control Creation ................................................................................ 321
13.1.1 . Introduction ................................................................................ 321
Borland C++ Builder Programming Table of Contents

Copyright 2003 FunctionX, Inc. 9

13.1.2 . Techniques of Creating Controls: Win32 ................................... 321
13.1.3 . Techniques of Creating Controls: VCL...................................... 322
13.1.4 . Techniques of Creating Controls: Class Derivation ................... 323
13.2 Characteristics and Properties of Controls......................................... 323
13.2.1 . Introduction ................................................................................ 323
13.2.2 . The Controls Handle ................................................................. 324
13.2.3 . Control's Names.......................................................................... 324
13.2.4 . Controls Text and Caption.......................................................... 326
13.2.5 . Controls Hints and Tool Tips ..................................................... 326
13.2.6 . Controls Styles: Childhood......................................................... 328
13.2.7 . Controls Styles: Visibility .......................................................... 330
13.2.8 . Controls Styles: Availability ...................................................... 331
13.2.9 . Tab Ordering .............................................................................. 331
13.2.10 Controls Location ....................................................................... 332
13.2.11 Controls Dimensions .................................................................. 333
13.2.12 The Bounding Rectangle of a Control ........................................ 335
13.3 Controls Methods .............................................................................. 340
13.3.1 . Overview of Methods ................................................................. 340
13.3.2 . Windows Visibility ................................................................... 341
13.3.3 . Focus .......................................................................................... 342
13.4 Controls Messages and Events .......................................................... 343
13.4.1 . Overview.................................................................................... 343
13.4.2 . A Map of Messages .................................................................... 345
13.4.3 . Messages Characteristics............................................................ 345
13.4.4 . Event Implementation................................................................. 346
13.5 Keyboard Messages ........................................................................... 347
13.5.1 . Introduction ................................................................................ 347
13.5.2 . The Key Down Message............................................................. 348
13.5.3 . The Key Up Message ................................................................. 349
13.5.4 . The Key Press Message.............................................................. 349
13.6 Mouse Messages................................................................................ 351
13.6.1 . Introduction ................................................................................ 351
13.6.2 . The Mouse Down Message ........................................................ 351
13.6.3 . The Mouse-Up Message............................................................. 352
13.6.4 . The Mouse Move Message......................................................... 352
13.7 Programmer Defined Messages ......................................................... 352
13.7.1 . Introduction ................................................................................ 352
13.7.2 . Windows Functions .................................................................... 353
13.7.3 . Prefoming Messages................................................................... 353
13.7.4 . Custom Message Implementation............................................... 354
13.7.5 . Methods and Messages Combinations........................................ 355
CHAPTER 14: PARENT CONTROLS....................................................... 357
14.1 Characteristics of Parent Controls ..................................................... 357
14.1.1 . The Windows Desktop.............................................................. 357
14.1.2 . Applications Containers ............................................................ 357
14.1.3 . The Parents Location................................................................. 358
14.2 Control Alignment and Constraint..................................................... 360
14.2.1 . The Client Area .......................................................................... 360
14.2.2 . Controls Alignment in the Client Area...................................... 362
14.2.3 . The Client Areas Constraints .................................................... 365
14.2.4 . Control Anchoring...................................................................... 365
14.2.5 . Child Controls and DragnDrop Operations.............................. 366
CHAPTER 15: FORMS AND DIALOG BOXES ....................................... 367
Table of Contents Borland C++ Builder Programming

10 Copyright 2003 FunctionX, Inc.

15.1 Characteristics of Forms .................................................................... 367
15.1.1 . Introduction ................................................................................ 367
15.1.2 . The System Icon......................................................................... 367
15.1.3 . The System Menu....................................................................... 368
15.1.4 . The Caption ................................................................................ 369
15.1.5 . The System Buttons.................................................................... 370
15.1.6 . Form and Dialog Box Positioning .............................................. 373
15.1.7 . The Borders ................................................................................ 373
15.1.8 . The Window State of a Form...................................................... 375
15.1.9 . The Body of a Form or a Dialog Box......................................... 376
15.1.10 Forms Transparency.................................................................. 376
15.2 Form Methods.................................................................................... 377
15.2.1 . Form Creation............................................................................. 377
15.2.2 . Form Closure.............................................................................. 377
15.3 Forms Messages and Events.............................................................. 377
15.3.1 . Form Creation............................................................................. 377
15.3.2 . Form Showing ............................................................................ 378
15.3.3 . Form Activation and Deactivation.............................................. 378
15.3.4 . Window Painting........................................................................ 379
15.3.5 . Window Sizing ........................................................................... 380
15.3.6 . Form Closure.............................................................................. 380
15.3.7 . Form Destruction........................................................................ 381
15.4 Application of Forms......................................................................... 381
15.4.1 . Multiple Forms ........................................................................... 381
15.4.2 . Dynamic Forms .......................................................................... 385
15.4.3 . Customizing Dynamic Forms..................................................... 386
15.5 The Multiple Document Interface (MDI) .......................................... 388
15.5.1 . Introduction to MDI-Based Applications ................................... 388
15.5.2 . MDI Creation.............................................................................. 389
15.6 Dialog Boxes ..................................................................................... 390
15.6.1 . Introduction ................................................................................ 390
15.6.2 . Dialog Box Creation................................................................... 391
15.6.3 . Modal Dialog Boxes................................................................... 392
15.6.4 . Modeless Dialog Boxes .............................................................. 392
15.6.5 . C++ Builder Template Dialog Boxes ......................................... 393
CHAPTER 16: CONTROLS CONTAINERS............................................. 395
16.1 The Form........................................................................................... 395
16.2 The Frame.......................................................................................... 395
16.2.1 . Introduction ................................................................................ 395
16.2.2 . Frame Creation ........................................................................... 395
16.3 The Data Module ............................................................................... 399
16.3.1 . Introduction ................................................................................ 399
16.3.2 . Data Module Creation ................................................................ 399
16.4 The Panel Control .............................................................................. 400
16.4.1 . Introduction ................................................................................ 400
16.4.2 . Characteristics of a Panel ........................................................... 401
16.5 Property Sheets and Property Pages .................................................. 404
16.5.1 . Overview.................................................................................... 404
16.5.2 . Property Sheet Creation.............................................................. 406
16.5.3 . Property Pages Creation ............................................................. 409
16.6 Wizard Pages ..................................................................................... 413
16.6.1 . Overview.................................................................................... 413
16.6.2 . Wizard Creation.......................................................................... 413
Borland C++ Builder Programming Table of Contents

Copyright 2003 FunctionX, Inc. 11

CHAPTER 17: AESTHETIC AND GRAPHICS CONTROLS................. 415
17.1 Bevels ................................................................................................ 415
17.1.1 . Overview.................................................................................... 415
17.1.2 . Characteristics of Bevel.............................................................. 415
17.1.3 . Bevel Methods............................................................................ 419
17.2 The Image Control ............................................................................. 419
17.2.1 . Introduction ................................................................................ 419
17.2.2 . Image Control Fundamentals...................................................... 419
17.3 The Paint Box Control ....................................................................... 422
17.3.1 . Introduction ................................................................................ 422
17.3.2 . Characteristics of a Paint Box..................................................... 422
CHAPTER 18: COMMAND CONTROLS ................................................. 423
18.1 Command Buttons ............................................................................. 423
18.1.1 . Overview.................................................................................... 423
18.1.2 . Button Properties........................................................................ 423
18.1.3 . Button Events ............................................................................. 427
18.1.4 . Button Methods .......................................................................... 432
18.2 Bitmap Buttons .................................................................................. 432
18.2.1 . Introduction ................................................................................ 432
18.2.2 . Bitmap Button Characteristics.................................................... 432
18.3 Speed Buttons .................................................................................... 437
18.3.1 . Introduction ................................................................................ 437
18.3.2 . Speed Buttons Characteristics .................................................... 437
18.4 Property Sheet and Wizards Buttons ................................................. 439
18.4.1 . Buttons on a Property Sheet ....................................................... 439
18.4.2 . Property Sheet Buttons Implementation..................................... 440
18.4.3 . Wizards Buttons ......................................................................... 443
18.4.4 . Wizard Implementation .............................................................. 445
CHAPTER 19: COLLECTIONS-BASED CONTROLS............................ 451
19.1 The Main Menu ................................................................................. 451
19.1.1 . Overview.................................................................................... 451
19.1.2 . Main Menu Creation................................................................... 452
19.1.3 . Coding a Main Menu Item.......................................................... 456
19.1.4 . Popup and Context-Sensitive Menus.......................................... 457
19.2 Toolbars............................................................................................. 458
19.2.1 . Introduction ................................................................................ 458
19.2.2 . Toolbar Programming................................................................. 460
19.3 Status Bars ......................................................................................... 461
19.3.1 . Introduction ................................................................................ 461
19.3.2 . Characteristics of a Status Bar.................................................... 461
19.3.3 . Status Bar Panels ........................................................................ 464
19.4 Action Lists........................................................................................ 465
19.4.1 . Introduction ................................................................................ 465
19.4.2 . The List of Actions..................................................................... 466
19.4.3 . Action Lists Messages and Events ............................................. 471
CHAPTER 20: TEXT-BASED CONTROLS.............................................. 473
20.1 Labels ................................................................................................ 473
20.1.1 . Introduction ................................................................................ 473
20.1.2 . Label Characteristics .................................................................. 473
20.1.3 . Label Methods ............................................................................ 479
20.1.4 . Label Messages and Events........................................................ 479
Table of Contents Borland C++ Builder Programming

12 Copyright 2003 FunctionX, Inc.

20.2 The Static Text Control ..................................................................... 481
20.2.1 . Introduction ................................................................................ 481
20.2.2 . Characteristics of a Static Text ................................................... 481
20.3 Edit Boxes.......................................................................................... 482
20.3.1 . Introduction ................................................................................ 482
20.3.2 . Edit Box Characteristics ............................................................. 482
20.3.3 . The Edit Control and its Functionality ....................................... 485
20.3.4 . Edit Control Events .................................................................... 485
20.4 The MaskEdit Control ....................................................................... 486
20.4.1 . Introduction ................................................................................ 486
20.4.2 . MaskEdit Characteristics............................................................ 486
20.4.3 . MaskEdit Methods...................................................................... 491
20.4.4 . MaskEdit Events......................................................................... 492
20.5 The IP Address Control ..................................................................... 492
20.5.1 . Introduction ................................................................................ 492
20.5.2 . Operations on an IP Address Control ......................................... 492
20.5.3 . IP Address Control Events ......................................................... 497
CHAPTER 21: TEXT-BASED APPLICATIONS ...................................... 499
21.1 The Memo Control ............................................................................ 499
21.1.1 . Overview.................................................................................... 499
21.1.2 . Characteristics of a Memo Control............................................. 500
21.1.3 . Memo Methods........................................................................... 502
21.1.4 . Memo Events.............................................................................. 504
21.2 The Rich Text .................................................................................... 504
21.2.1 . Introduction ................................................................................ 504
21.2.2 . Rich Text Implementation.......................................................... 519
21.2.3 . Rich Text Management .............................................................. 522
21.2.4 . Text Formatting.......................................................................... 523
21.2.5 . Paragraph Formatting ................................................................. 524
21.3 The Find Dialog Box ......................................................................... 527
21.3.1 . Introduction ................................................................................ 527
21.3.2 . Searching for Text ...................................................................... 528
21.3.3 . Word and Expression Search...................................................... 528
21.4 The Replace Dialog Box.................................................................... 529
21.4.1 . Overview.................................................................................... 529
21.4.2 . Making Text Replacement Possible ........................................... 530
CHAPTER 22: TRACK-BASED CONTROLS .......................................... 533
22.1 The UpDown Control ........................................................................ 533
22.1.1 . Overview.................................................................................... 533
22.1.2 . Characteristics of an UpDown Control....................................... 535
22.1.3 . The UpDown Control Methods .................................................. 538
22.1.4 . The UpDown Control Events ..................................................... 539
22.2 The Spin Button................................................................................. 540
22.2.1 . Characteristics of a Spin Button ................................................. 541
22.2.2 . The Spin Button Methods........................................................... 542
22.2.3 . The Spin Button Events.............................................................. 542
22.3 The Spin Edit Control ........................................................................ 544
22.3.1 . Introduction ................................................................................ 544
22.3.2 . Characteristics of the SpinEdit Control ...................................... 544
22.3.3 . The Spin Edit Methods............................................................... 545
22.4 Track Bars.......................................................................................... 545
22.4.1 . Introduction ................................................................................ 545
22.4.2 . Characteristics of a Track Bar .................................................... 549
Borland C++ Builder Programming Table of Contents

Copyright 2003 FunctionX, Inc. 13

22.4.3 . Track Bar Events ........................................................................ 551
CHAPTER 23: PROGRESS-BASED CONTROLS ................................... 553
23.1 Timers................................................................................................ 553
23.1.1 . Introduction ................................................................................ 553
23.1.2 . Characteristics of a Timer........................................................... 554
23.1.3 . The Tick Counter........................................................................ 556
23.2 Progress Bars ..................................................................................... 560
23.2.1 . Overview.................................................................................... 560
23.2.2 . Progress Bar Properties .............................................................. 561
23.2.3 . Progress Bar Methods and Messages ......................................... 566
23.3 Scroll Bars ......................................................................................... 574
23.3.1 . Introduction ................................................................................ 574
23.3.2 . Automatically-Added Scroll Bars............................................... 575
23.3.3 . Text-Based Applications and Scroll Bars................................... 576
23.4 The Scroll Bar Control....................................................................... 577
23.4.1 . Introduction ................................................................................ 577
23.4.2 . Characteristics of the Scroll Bar Control .................................... 579
23.4.3 . Methods to Manage a Scroll Bar ................................................ 582
23.4.4 . Scroll Bar Events........................................................................ 583
CHAPTER 24: SELECTION-BASED CONTROLS.................................. 589
24.1 Radio Buttons .................................................................................... 589
24.1.1 . Introduction ................................................................................ 589
24.1.2 . Characteristics of Radio Buttons ................................................ 589
24.1.3 . Radio Buttons Methods .............................................................. 592
24.1.4 . Radio Button Events................................................................... 592
24.2 The Radio Group Control .................................................................. 593
24.2.1 . Introduction ................................................................................ 594
24.2.2 . Characteristics of the RadioGroup Control ................................ 594
24.2.3 . RadioGroup Methods ................................................................. 604
24.2.4 . RadioGroup Messages and Events ............................................. 605
24.3 Check Boxes ...................................................................................... 605
24.3.1 . Introduction ................................................................................ 605
24.3.2 . Characteristics of Check Boxes.................................................. 607
24.3.3 . Check Box Methods ................................................................... 611
24.3.4 . Check Box Events ...................................................................... 612
CHAPTER 25: LIST-BASED CONTROLS................................................ 619
25.1 List Boxes.......................................................................................... 619
25.1.1 . Overview.................................................................................... 619
25.1.2 . List Box Creation ....................................................................... 619
25.1.3 . List Box Properties ..................................................................... 620
25.1.4 . List Box Methods ....................................................................... 622
25.1.5 . Operations on List Boxes ........................................................... 625
25.2 Check List Boxes............................................................................... 630
25.2.1 . Overview.................................................................................... 630
25.2.2 . Characteristics of a Checked List Box........................................ 632
25.2.3 . Methods to Manage a Check List Box........................................ 637
25.3 Combo Boxes .................................................................................... 640
25.3.1 . Introduction ................................................................................ 640
25.3.2 . Characteristics of a Combo Box................................................. 641
25.3.3 . Methods of Combo Box Management........................................ 642
25.3.4 . Operations on Combo Box Using Events ................................... 643
Table of Contents Borland C++ Builder Programming

14 Copyright 2003 FunctionX, Inc.

CHAPTER 26: GRID-BASED CONTROLS .............................................. 649
26.1 The Win32 Calendar.......................................................................... 649
26.1.1 . Introduction ................................................................................ 649
26.1.2 . Calendar Properties .................................................................... 652
26.1.3 . Calendar Methods and Events .................................................... 654
26.2 The Date and Time Picker ................................................................. 657
26.2.1 . Overview.................................................................................... 657
26.2.2 . The Time Picker ......................................................................... 658
26.2.3 . The Date Picker .......................................................................... 659
26.2.4 . Date Time Picker Events ............................................................ 662
26.3 The String Grid Control..................................................................... 664
26.3.1 . Overview of Grids ...................................................................... 664
26.3.2 . String Grid Properties................................................................. 665
26.3.3 . Cells Properties........................................................................... 666
26.3.4 . StringGrid Methods .................................................................... 671
26.3.5 . StringGrid Events ....................................................................... 674
26.4 The CCalendar Control...................................................................... 681
26.4.1 . Introduction ................................................................................ 681
26.4.2 . Using a CCalendar...................................................................... 681
26.5 The Color Grid Control ..................................................................... 682
26.5.1 . Overview.................................................................................... 682
26.5.2 . Using a Color Grid Control ........................................................ 683
26.6 The Value List Editor ........................................................................ 684
26.6.1 . Introduction ................................................................................ 684
26.6.2 . The Visual List Editor Control ................................................... 684
26.6.3 . Combo Boxes on a Value List Editor ......................................... 684
26.6.4 . Ellipsis Buttons on a Value List Editor ...................................... 687
CHAPTER 27: LISTS VIEW CONTROLS................................................ 689
27.1 Tree Views......................................................................................... 689
27.1.1 . Overview.................................................................................... 689
27.1.2 . Tree View Design....................................................................... 690
27.1.3 . Dynamic Tree Views.................................................................. 694
27.1.4 . Adding Images to Nodes ............................................................ 696
27.2 List Views.......................................................................................... 697
27.2.1 . Introduction ................................................................................ 697
27.2.2 . List View Design........................................................................ 697
27.2.3 . Dynamic List Views ................................................................... 702
27.3 Splitter Bars ....................................................................................... 710
27.3.1 . Introduction ................................................................................ 710
27.3.2 . Creating a Splitter....................................................................... 710
CHAPTER 28: CREATING AND USING LISTS...................................... 713
28.1 The TList Class.................................................................................. 713
28.1.1 . Introduction ................................................................................ 713
28.1.2 . List Preparation .......................................................................... 713
28.1.3 . Preparing an Object for a List..................................................... 715
28.1.4 . List Build-Up.............................................................................. 718
28.1.5 . List Navigation ........................................................................... 720
28.1.6 . Operations on a List.................................................................... 726
28.1.7 . A List and its New Items ............................................................ 731
28.1.8 . List Item Insertion ...................................................................... 737
28.1.9 . Item Removal From a List .......................................................... 739
28.1.10 List Clearance............................................................................. 741
Borland C++ Builder Programming Table of Contents

Copyright 2003 FunctionX, Inc. 15

28.2 The VCL's Collection of List............................................................. 742
28.2.1 . Introduction ................................................................................ 742
28.2.2 . The TOrderedList Class.............................................................. 742
28.2.3 . The TQueue Class ...................................................................... 742
28.2.4 . The TStack Class........................................................................ 742
APPENDICES................................................................................................ 744
A/ DATE AND TIME FUNCTIONS............................................................ 745
Dates 745
Declaring a Date...................................................................................... 745
The Date() Function ................................................................................ 745
Date, String, and Numeric Conversions ....................................................... 746
Converting a String to Date..................................................................... 746
Converting a Date to a String .................................................................. 747
Converting a Date to a Double-Precision Number .................................. 748
Converting a Date to an Integer............................................................... 749
The Computers System of Displaying Dates .............................................. 750
The Short Date Format ............................................................................ 751
The Long Date Format ............................................................................ 754
Using Dates .................................................................................................. 755
Displaying Dates ..................................................................................... 755
Decoding a Date ...................................................................................... 756
Encoding a Date ...................................................................................... 758
Finding Out the Leap Year ...................................................................... 759
The Day of the Week............................................................................... 760
Increasing Months on a Date................................................................... 762
Replacing a Date ..................................................................................... 763
Comparison Operations on Dates................................................................. 764
The Comparison for Equality .................................................................. 764
The Comparison for Inequality ............................................................... 765
The Comparison for Inferiority ............................................................... 766
The Comparison for Inferiority or Equality............................................. 767
The Comparison for Superiorty............................................................... 768
The Comparison for Superiority or Equality........................................... 769
Operations on Dates ..................................................................................... 770
Assigning One Date to Another .............................................................. 770
Adding Values to a Date.......................................................................... 772
Assigning an Added Date........................................................................ 774
Subtracting Dates .................................................................................... 775
Assigning a Subtracted Date ................................................................... 777
Decrementing a Date ............................................................................... 777
Incrementing a Date ................................................................................ 778
Formatting and Controlling the Display of Dates......................................... 779
The Default Display ................................................................................ 780
Displaying the Numeric Day................................................................... 780
Displaying Weekday Names ................................................................... 781
Displaying Numeric Months ................................................................... 783
Displaying Months Names ...................................................................... 784
Displaying the Year................................................................................. 786
Doing Time .................................................................................................. 787
Declaring Time Variables........................................................................ 788
The Time() Function ............................................................................... 790
Converting a String to Time .................................................................... 790
Converting a Time Value to a String....................................................... 791
Table of Contents Borland C++ Builder Programming

16 Copyright 2003 FunctionX, Inc.

Converting a Time Value to a Double-Precision Number....................... 792
Doing Time .................................................................................................. 793
Displaying the Time ................................................................................ 793
Decoding a Time ..................................................................................... 793
Encoding a Time ..................................................................................... 795
Replacing a Time Value .......................................................................... 796
Comparison Operations On Time Values..................................................... 796
The Comparison for Equality .................................................................. 797
The Comparison for Difference............................................................... 797
The Comparison for Previous Occurrence .............................................. 798
The Comparison for Previous or Equal Occurrence................................ 798
The Comparison for Subsequent Occurrence .......................................... 798
The Comparison for Latter or Same Time............................................... 799
Controlling Time Display............................................................................. 799
Displaying in Default Format .................................................................. 800
Displaying the Leading Zero................................................................... 800
Combining Date and Time .......................................................................... 802
Fundamental Functions: Now() ............................................................... 802
Converting a String to Date and Time..................................................... 802
Converting a Date and Time to a String.................................................. 803
INDEX............................................................................................................. 804


Borland C++ Builder Programming

Copyright 2003 FunctionX, Inc. 17

Table of Contents Borland C++ Builder Programming

18 Copyright 2003 FunctionX, Inc.




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.



Borland C++ Builder Programming Chapter 1: The Borland C++ Builder IDE

Copyright 2003 FunctionX, Inc. 19


Chapter 1: The Borland C++ Builder IDE


1.1 An Integrated Development Environment

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)

Launching Borland C++ Builder
To start Borland C++ Builder, click Start -> Programs -> Borland C++ Builder 6 ->
C++ Builder 6

Chapter 1: The Borland C++ Builder IDE Borland C++ Builder Programming

20 Copyright 2003 FunctionX, Inc.


Figure 1: Borland C++ Builder IDE

1.1.2 Integrated Development Environment
An Integrated Development Environment (IDE) is an application that provides a friendly
interface for creating computer programs.

Bcbs IDE is structurally a classic application. On top, there is a title bar that displays the
name of the application and the program currently running. The title bar itself is made of
three sections.

1.1.3 The Title Bar
1. Click the applications system icon .
The system icon is used to identify the application you are using. Almost every
application has its own system icon. The system icon holds its own list of actions; for
example, it can be used to move, minimize, maximize or close (when double-
clicked) a window.
2. To see an example, while the system menu is displaying, click Minimize.
3. To bring back the IDE, on the Taskbar, click C++ Builder

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
Borland C++ Builder Programming Chapter 1: The Borland C++ Builder IDE

Copyright 2003 FunctionX, Inc. 21

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


1.1.4 The Main Menu
Under the title bar, there is a range of words located on a gray bar; this is called the
menu.


In this book, the word Main Menu refers to the menu on top of the IDE

To use a menu, you click one of the words and the menu expands.

1. Click File. There are four main types of menus you will encounter.

When clicked, the behavior of a menu that stands
alone depends on the actions prior to clicking it. Under the File menu, examples
include Save, Close All or Exit. For example, if you click Close All, Bcb will find
whether the project had been saved already. If it were, the project would be closed;
otherwise, you would be asked whether you want to save it.
2. To see an example, click Save.
3. When you are asked to save, click Cancel
4. A menu that is disabled is not accessible at the moment.
This kind of menu depends on another action or the availability of something else.
To see an example, click Edit and notice Undelete, Redo, Cut, Copy, and Paste.
5. A menu with three dots means an action is
required in order to apply its setting(s). Usually, this menu would call a dialog box
where the user would have to make a decision.
As an example, on the main menu, click Tools and click Editor Options
6. On the Editor Options dialog, click OK
7. A menu with an arrow holds submenu. To use
such a menu, position the mouse on it to display its submenu.
For example, on the main menu, click Edit and position the mouse on Flip Children.
8. To dismiss the menu, click Edit
9. To dismiss the menu, click Edit again
This book uses the -> arrow for the menu requests.
From now on, in this book,
Request Means
Chapter 1: The Borland C++ Builder IDE Borland C++ Builder Programming

22 Copyright 2003 FunctionX, Inc.

Edit -> Copy Click Edit then click Copy
View -> Toolbars -> Custom
Click View, position the mouse on Toolbars,
and then click Custom

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.

From now on, in this book,
Press Means
T Press the t key
Alt, G Press and release Alt. Then press G
Ctrl + H
Press and hold Ctrl. While you are still holding Ctrl, press H
once. Then release Ctrl
Ctrl + Shift + E
Press and hold Ctrl. Then press and hold Shift. Then press E
once. Release Ctrl and Shift

1.1.5 The Toolbars
A toolbar is an object made of buttons. These buttons provide the same features you
would get from the menu, only faster. Under the menu, the IDE is equipped with various
toolbars. For example, to create a new project, you could click File -> New on the
Borland C++ Builder Programming Chapter 1: The Borland C++ Builder IDE

Copyright 2003 FunctionX, Inc. 23

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. Drag the grippers of one toolbar down and right
2. Notice that the toolbar has moved
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. To dismiss the menu, press Esc.

In this book, every toolbar is referred to by its name






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 and see the hint
Chapter 1: The Borland C++ Builder IDE Borland C++ Builder Programming

24 Copyright 2003 FunctionX, Inc.


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. Press Esc to dismiss the New Items dialog box.
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.

1.1.6 The Component Palette
On the right side of the toolbars, there is a bar with multiple tabs; this is called the
Component Palette:



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. For example, click the Win32 tab.
Whenever the Component Palette cannot display all of its tabs; there are two
horizontal arrows that allow you to reveal the hidden tabs.
To reveal more tabs, click the right pointing arrow.
2. Click the ActiveX tab
3. Click the Standard tab
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.
Borland C++ Builder Programming Chapter 1: The Borland C++ Builder IDE

Copyright 2003 FunctionX, Inc. 25


1.1.7 The Default Form
Under the Component Palette, there is a gray box called a form. The form is an object
that allows you to create an application that would allow the user of your program to
interact with the computer. When you start Borland C++ Builder, it creates a readily
available form for you. This form, although not highly functional, is already part of a
complete application.

An application can consist of one form or as many forms as necessary. Notice that the
current form has its own application icon, a title bar, the system buttons, and it has a
body, which is the area with grids.

1. 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. Once the project is ready, it displays the ready form.
4. To close the form, click its close button.
5. A form is made of two parts, its physical part called the form and its code section
called a unit.
6. To toggle between the form and its unit, press F12.
7. Notice that the form goes to the back.

1.1.8 The Code Editor
Besides designing forms (and applications), one of your most regular jobs will consist of
writing code that directs the computer as to what to do, when, and how. This is done in an
appropriate window called the Code Editor.

The Code Editor is featured text editor adapted for coding purposes. It is programmed to
recognize the parts of a program that are recognized by C++ or not. To access the Code
Editor, if you have a form opened, you can press F12. The Code Editor manages your
jobs by organizing its files into property pages (also called tabs). If your project contains
more than one file, you can click the desired tab to access one of the files.

The basic building block of a program is called a C++ file. Whenever you start Bcb, it
creates a starting project that has a C++ file called Unit1 while the project is called
Project1. Eventually, you will change these names to those you like. A typical code of a
Chapter 1: The Borland C++ Builder IDE Borland C++ Builder Programming

26 Copyright 2003 FunctionX, Inc.

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.


Figure 2: The Code Editor

1. To display the header file, press Ctrl + F6
2. Notice that the header file is called Unit1.h
3. To toggle between the form and its unit, press F12
4. To bring back the Code Editor, press F12
5. Click Unit1.cpp
6. Click Unit1
7. To bring back the form, press F12.

1.1.9 The Class Explorer
What is called an object in real world is also referred to as an object in C++, and an
object is build using a class. To organize the objects involved in a program, C++ Builder
uses a special window called the Class Explorer. As its name implies, it is used to
navigate to various objects.

The Class Explorer is positioned on the left side (or section) of the Code Editor. It is
organized in a tree view format with the name of the project as the root. To view the
objects that are part of a project, you can expand the tree.

You can close or hide the Class Explorer any time and bring it back at will. You can also
permanently hide it or for a while.

Borland C++ Builder Programming Chapter 1: The Borland C++ Builder IDE

Copyright 2003 FunctionX, Inc. 27

1.1.10 The Object Inspector
One of the best (if not the best) features of Borland C++ Builder (and other compilers of
the company) is its ease of use and accessibility of designing objects. This is even
enhanced by the presence of another special window called the Object Inspector. This is a
window that lists the characteristics of the object that you are using to design an
application.

We will review the various uses of the Object Inspector when we can take advantage of
them.

1.2 Introduction to C++ Builder Projects

1.2.1 Program Execution
A program would not mean much unless it accomplishes the desired purpose. To examine
how your development is proceeding, as a beginning programmer, you should regularly
ask C++ to show you the result.

There are three ways you can execute a program in Borland C++ Builder. To execute a
program, you can press F9. You can also use the main menu where you would click Run
-> Run. On the toolbar, you can also click the Run button

1. On the Debug toolbar, click the Run button . As you see, the program does not
do much because we did not write a formal assignment.
2. To close the window, click its system Close button .

1.2.2 Saving a Project
A program in Borland C++ Builder is called a project. As an application, it is saved in a
few steps. To save a project, on the Standard toolbar, you can click the Save All button.

1. On the Standard toolbar, click Save All .
2. Type Exercise to replace the name of the Unit1.
3. Click the arrow of the Save In combo box and click the (C:) drive.
4. Click the Create New Folder button .
5. Type Exercise01 and press Enter.
6. Double-click Exercise1 to display it in the Save In combo box.
7. Click Save
8. Type Exercise01 to replace the name of the project.
9. Click Save.

Chapter 1: The Borland C++ Builder IDE Borland C++ Builder Programming

28 Copyright 2003 FunctionX, Inc.

1.3 Borland C++ Builder Help

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.

1.3.2 Online Help
2 On the main menu, click Help -> C++ Builder Help.
3 In the Contents tab, double-click Programming With C++ Builder to expand it.
4 Double-click C++ Builder Programming Fundamentals
5 Double-click Types of Events
6 Notice that the help window displays, click the advance button.
7 After reading, click Help Topics
8 Click the Index tab.
9 Type OnMo
10 Notice that the OnMouseDown display in the list.
11 Press Enter.
12 After reading the text, click the TControl link.
13 After reading, click the Back button.
14 Click See Also.
15 Double-click TControl::OnClick
16 After reading, click Example
17 To close the Help system, click its Close button.

1.3.3 Internet Help
Perform the following exercise if you have access to the Internet.

Borland C++ Builder Programming Chapter 1: The Borland C++ Builder IDE

Copyright 2003 FunctionX, Inc. 29

1 Log on to http://www.borland.com
2 Look for and click C++ Builder.
3 After finding some information and reading, change the address in the Address box
to http://msdn.microsoft.com and press Enter.
4 Position the mouse on Libraries and click Developer


Chapter 2: Controls Fundamentals Borland C++ Builder Programming

30 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 31


Chapter 2: Controls Fundamentals


2.1 Windows Fundamentals

2.1.1 Introduction to the Win32 Library
A library is a set of published documents. It could consist of a piece of paper, a book, or a
group of books used as a (written) reference for a specific purpose. The programs that are
on a Microsoft Windows operating system follow a set of rules and suggestions defined
in a library called Win32 (Windows 32-bits). Win32 is a group of functions, objects,
variables, and constants that govern how the Microsoft Windows (95 and above)
operating systems function. As a reference, it allows individuals and companies to know
what is necessary in order to create or develop applications for this specific platform.

Over all, it is not strictly necessary to know Win32 thoroughly to write programs; but it is
highly recommended to know most of the intricacies of its implementations. Our opinion
is that you must know as much as possible about Win32 to be an effective Windows
programmer.

In order to create a Win32 application in Borland C++ Builder, you would call the New
Items dialog box. From the General property page, you can click the Console Application
icon and click OK. From the Console Wizard dialog box, click the C++ radio button and
make sure the Console Application check box is unchecked.

As every C++ application specifies the main() function as its entry point, every Windows
application must have a function called WinMain. If you start a Win32 application using
the Console Wizard, Borland C++ Builder would display syntax of the WinMain()
function. This is:

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)

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)

2.1.2 C++ Builder Application Fundamentals
To make application development easier, that is, to provide Rapid Application
Development (RAD), the Visual Component Library (VCL) is equipped with various
classes. To start a computer application in a Win32 environment, you must create an
Chapter 2: Controls Fundamentals Borland C++ Builder Programming

32 Copyright 2003 FunctionX, Inc.

application. This is done using the WNDCLASS or the WNDCLASSEX structure.
These two structures are defined as follows:

typedef struct _WNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;
typedef struct _WNDCLASSEX {
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;

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);

This creates an empty application without much to do.

2.1.3 Applications Instance
If you start an application such as Notepad, you are said to have created an instance of the
application. In the same way, when you declare a variable of a class, an instance of the
class is created and made available to the project. In fact, when you create an application,
you must create an instance of that application so it can be made available to the
operating system. Such an instance must be passed to the WinMain() function. Once the
instance exists, it can be accessed either by any control of the same application or by the
operating system for any reason.

When you create a VCL application, a variable of type TApplication is declared and the
instance of the application is globally made available to anything that needs it. The
instance of the application is called HInstance.
Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 33


Practical Learning: Creating an Application
1. Start Borland C++ Builder and, on the Standard toolbar, click the New button
2. On the New Items dialog box, click Console Wizard

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. In the body of the WinMain() function, call the TApplication::Initialize() method
before the return line:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <windows.h>
#pragma hdrstop
Chapter 2: Controls Fundamentals Borland C++ Builder Programming

34 Copyright 2003 FunctionX, Inc.


//---------------------------------------------------------------------------

#pragma argsused
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevI nstance, LPSTR
lpCmdLine, int nCmdShow)
{
Application->Initialize();

return 0;
}
//---------------------------------------------------------------------------
7. To save the application, on the Standard toolbar, click the Save All button
8. Click the Create New Folder button
9. Type Exercise1 and press Enter twice to display it in the Save combo box
10. Click Save twice

2.1.4 Introduction to User Interface Objects
After creating an application, you can create an object that the user will look at when
interacting with the computer. Such an object is called a window. Most of the things you
see on the screen or on an application are window objects. As various as they are, there
are different techniques used to create them.

The primary window and the most frequent object you will use in your applications is the
form. Therefore, after creating an application, the next step you probably take is to create
a form. Because C++ Builder provides a rapid application development (RAD)
environment, a form is related in two steps. First you must provide its resource. This is
done by visually adding a form available from clicking the New Form button on the
Standard toolbar or by clicking File -> New -> Form on the main menu.

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
Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 35

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).

Practical Learning: Adding a Form to an Application
1. 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. Press F12 to access the Code Editor window.
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 hPrevI nstance,
LPSTR lpCmdLine, int nCmdShow)
{
Application->Initialize();
Application->CreateForm(__classid(TForm2), &Form2);
Application->Run();

return 0;
}
//---------------------------------------------------------------------------
5. To test the application, press F9
Chapter 2: Controls Fundamentals Borland C++ Builder Programming

36 Copyright 2003 FunctionX, Inc.



6. Close it and return to Bcb

2.1.5 Design and Run Times
To create your applications, there are two settings you will be using. If a control is
displaying on the screen and you are designing it, this is referred to as Design Time. This
means that you have the ability to manipulate the control. You can visually set the
controls appearance, its location, its size, and other necessary or available
characteristics. The design view is usually the most used and the easiest because you can
glance at a control, have a realistic display of the control and configure its properties. The
visual design is the technique that allows you to visually add a control and manipulate its
display. This is the most common, the most regularly used, and the easiest technique.

The other system you will be using to control a window is with code, writing the
program. This is done by typing commands or instructions using the keyboard. This is
considered, or referred to, as Run Time. This is the only way you can control an objects
behavior while the user is interacting with the computer and your program.

2.1 Techniques of Creating Controls

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.
Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 37


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.

2.1.2 Control Design
When creating an application, you will be adding Windows controls, also called controls,
to it. Most of the controls you will use reside on the Component Palette:



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.

Chapter 2: Controls Fundamentals Borland C++ Builder Programming

38 Copyright 2003 FunctionX, Inc.

Practical Learning: Introduction to Form Design
1. 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. From the Standard tab, double-click Button
4. Notice that a button control is added to the form
5. To add another control, double-click Edit
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. To add another control, on the Component Palette, click Memo .
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.
Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 39

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.

2.1.3 User Interface Design
Creating a good-looking application is a combination of art, intuition, and logic. Not all
IDEs provide the same features but Borland can boast for what is available on C++
Builder: a host of controls to fit almost any assignment. We will learn how to configure
various controls as we move on. The first thing to do is to get familiar with what can ease
the users experience with your application.

When designing an application, you will usually position the desired controls on a form.
This is when you decide which control you need, where it will be positioned on the form
and with regard to other controls, what appearance it should have, and what behavior
should be customized.

When Borland C++ Builder starts, it creates a starting form. Once you have a form, you
can place the other controls that would participate in your application. The easiest way to
add a control to a form is to locate it on the Component Palette, click it and click on the
form.

Practical Learning: Adding Controls
1. 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. On the Component Palette, click the Standard tab
3. Click the Edit control .
4. Click on the center-top section of the form (it is not important how it is positioned):


Chapter 2: Controls Fundamentals Borland C++ Builder Programming

40 Copyright 2003 FunctionX, Inc.

5. On the Component Palette, click the Edit control again
6. This time, click on the middle left section of the form (again, the position is not
important):

7. Once again, on the Standard tab, click the Edit control .
8. This time, click in the lower-left section of the form and hold the mouse down.
9. Drag in the opposite directions to draw a tall and wide rectangle:

10. Release the mouse
11. Notice that the control gets the same height as the other edit boxes.
12. On the Standard tab again, click the Memo control .
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.
Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 41

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.
Chapter 2: Controls Fundamentals Borland C++ Builder Programming

42 Copyright 2003 FunctionX, Inc.

25. Click inside the panel but just above the Edit2 edit box.


2.1.4 Controls Selection
While you are designing a form and it is hosting controls, it is important to always know
what particular control is selected. Whenever you make changes, they are applied to the
control that has focus.


A control that is selected or highlighted is described as having focus.

There are two main techniques used to give focus to a control. On the form, click the
desired control. On the other hand, you can select the desired control on the combo box
on the upper section of the Object Inspector. You can also select a control by clicking its
name in the Object TreeView.

To select a form, click an unoccupied area on the form. If the form is hidden, you can
press F12 to toggle between the form and the code. If your application has more than one
form, to display one of them, on the main menu, you can click View -> Forms and select
the desired form from the list.

Practical Learning: Selecting Controls
1. To display one of the forms, on the main menu, click View -> Forms
2. From the View Form dialog box, click Form1
Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 43


3. Click OK
4. To select one of the controls, click the memo box. Also notice the Name field in the
Object Inspector:

5. To select another control, in the Object TreeView, click Edit3
6. Notice that, on the form, the Edit3 control receives focus.
7. To select another control, click the arrow on the upper section of the Object
Inspector and click Edit2

8. Notice the new selected name in the Object Inspector.
9. On the form, click Edit1 to select it.
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.
Chapter 2: Controls Fundamentals Borland C++ Builder Programming

44 Copyright 2003 FunctionX, Inc.

16. To select two controls, on the form, click Edit2.
17. Press and hold Shift.
18. While you are still holding Shift, click Edit3 and click the ActionList button.
19. Release Shift. Notice that the controls have handles on their corners, indicating that
they are selected. The Object Inspector displays properties that are common to the
selected controls.
20. To make another selection, just under the lowest edit box on the form, click and drag
up and right as to draw a rectangle that would touch Edit2, Edit3, and Memo1:

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.

2.1.5 Controls Moving
Any control on a form can be moved to a desired location either to accommodate other
controls or based on new demands. To move a control, click and hold your mouse on it;
then drag your mouse to the desired location, and release the mouse. To move a group of
controls, first select them; then click one of the selected controls and drag to the desired
location, and release the mouse. If the controls are hosted by another control, you can
move all of them when moving the container.

To move a control or a group of controls by one pixel, first make the selection. Press and
hold Ctrl. Then press one of the arrow keys. Once you are satisfied with the location,
release the Ctrl key.

To move a control or a group of controls by one grid unit, first make the selection. Press
and hold Shift + Ctrl. Then press one of the arrow keys. Once you are satisfied with the
location, release the Ctrl key.

There are two valuable dialog boxes available to move controls. To solve alignment
issues, access the Align dialog box from the main menu by clicking Edit -> Align
When dealing with relative dimensions, call the Size dialog by clicking Edit -> Size

Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 45

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.

Practical Learning: Moving Controls
1. 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. Press m to select Move. Notice the shape of the mouse pointer.
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. Click and hold your mouse on the panel.
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:
Chapter 2: Controls Fundamentals Borland C++ Builder Programming

46 Copyright 2003 FunctionX, Inc.


20. Click OK.
21. On the form, right-click one of the selected controls, position your mouse on Position
and click Align
22. On the Alignment dialog, on the Horizontal section, click Left Sides. On the Vertical
section, click the Space Equally radio button and click OK.
23. Press Esc to deselect the group and select their container.
24. On the form, move the Edit1control to the center section of the form. Move Edit2 to
the lower right section of the form. Move Edit3 to the top section of the form.
25. Select Edit1, Edit2, and Edit3.
26. On the main menu, click Edit -> Size
27. On the Size dialog box, in the Width section, click Shrink to Smallest:

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
Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 47


2.1.6 Control Resizing
Most controls, including the form, can be resized using guiding mouse cursors. To resize
a control, first select it. Except for the form, whenever a control is selected, there are
eight handles around it. To resize the control, position your mouse on one of the handles.
The mouse pointer will change, indicating in what direction you can move to resize the
control.

Cursor 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.

Practical Learning: Resizing Controls
1. To resize the form, position your mouse on its right border until the mouse turns into
double horizontal arrow .
2. Click and drag left for one inch.
3. Release the mouse.
4. Position the mouse on the lower right corner
5. Click and drag in the upper right direction, to acceptable dimensions.
6. Release the mouse.
7. To resize a control, on the form, click ActionList .
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.

Chapter 2: Controls Fundamentals Borland C++ Builder Programming

48 Copyright 2003 FunctionX, Inc.

2.1.7 Controls Navigation
Controls navigation has to deal with not only the alignment of controls on a form but also
the logical process the user should follow when using your product. The Tab key on your
keyboard is the primary means for users to navigate from one control to another. The tab
order is the sequence of controls that the keyboard follows when the user presses the Tab
key. Sometimes, after creating a form, this navigation sequence does not follow the right
logic. You usually have to adjust that sequence. This is done from the Edit Tab Order
dialog box.

Practical Learning: Navigating a Form
1. 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. From the Component Palette, click Edit and click on the form to add Edit1
4. Add other edit controls as Edit2, Edit3, and Edit4.
5. From the Standard tab of the Component Palette, click Button to add a button
control to the form
6. Add one more button.
7. Add two more Edit controls as Edit5 and Edit6
8. Move and resize your controls and the form as follows:

9. To test the form, press F9.
10. Notice that Edit1 is highlighted, meaning it has focus.
11. Press Tab and notice that when the second edit box receives focus, its content is
highlighted:
Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 49


12. Press Tab a few times until Button1 is highlighted:

13. Press Tab a few times to review the sequence of navigation.
14. Close the form.
15. To adjust the navigation sequence, right-click an empty area on the form and flick
Tab Order
16. On the Edit Tab Order dialog, click Button1: TButton
17. Click the down pointing button twice .
18. Click Edit6: TEdit
19. Click the up pointing button .
20. Rearrange the list on the Edit Tab Order dialog as follows:
Chapter 2: Controls Fundamentals Borland C++ Builder Programming

50 Copyright 2003 FunctionX, Inc.


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 Runtime Creation of Controls

2.2.1 Window Creation
As seen above, in a RAD environment, the primary means of adding a control to an
application consists of visually adding it to a container such as a form. In the world of
Win32 programming, a control can only be created programmatically. In a VCL
application, you can either add a control at design time or programmatically create it.

To create a control, the Win32 library provides two main functions: CreateWindow()
and CreateWindowEx().
2.2.2 VCL Control Creation
To programmatically create a control in a VCL application, you must know its base class.
This is fortunately very easy to find out. If the control is one of the types of objects of the
Component Palette, find its hint and simply start it with T. For example, the class of the
control called Edit is named TEdit. All of the other classes follow the same rule.

The VCL requires that a dynamic control be declared as a pointer.

2.3 Windows Controls and Their Properties

Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 51

2.3.1 Overview of Controls Properties
A property is a piece of information that characterizes a control. It could be related to its
location or size. It could be its color, its identification, or any visual aspect that gives it
presence on the screen.

The properties of an object can be changed either at design time or at run time. You can
also manipulate these characteristics both at design and at run times. This means that you
can set some properties at design time and some others at run time.

To manipulate the properties of a control at design time, first click the desired property
from the Component Palette. Then add it to the form or to a container control. To change
the properties of a control at design time, on the form, click the control to select it. Then
use the Object Inspector:



2.3.2 Properties Categories
Each field on the Object Inspector has two sections: the propertys name and the
property's value:

Chapter 2: Controls Fundamentals Borland C++ Builder Programming

52 Copyright 2003 FunctionX, Inc.



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:



Text Fields: There are fields that expect you to type
a value. Most of these fields have a default value. To change the value of the property,
click the name of the property and press Enter. While some properties, such as the
Caption, would allow anything, some other fields expect a specific type of text, such as a
numeric value. If you type an invalid value, you would receive an error.



You can click OK to dismiss the error dialog and type a new valid value.
Borland C++ Builder Programming Chapter 2: Controls Fundamentals

Copyright 2003 FunctionX, Inc. 53


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 double-
click 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 double-
click 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.




Chapter 2: Controls Fundamentals Borland C++ Builder Programming

54 Copyright 2003 FunctionX, Inc.




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.


Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 55


Chapter 3: Strings


3.1 The Fundamentals of Strings

3.1.1 String Construction and Declaration
String operations in C++ Builder are mainly performed using a class called AnsiString.
The AnsiString class is not derived from TObject. Therefore, it has a high level of
independence and flexibility from the application or the control that wants to use it.

It is simply incredible the level of work and support provided by the VCL to its strings
and text-related operations. Almost any possible operation that can be performed on a
string is supported. There are so many functions that we will review only those that are
most used in this book but all functions that were created in the libraries are highly
valuable and can save you a tremendous amount of code writing and headache.

Many controls use AnsiString properties. All controls that use a caption (forms, panels,
labels, etc) have their Caption property set as an AnsiString value. Many other controls
such as the edit box use the AnsiString class as the basis of their text. Based on these two
facts, you have already used and implemented AnsiString values. In some other
scenarios you will need to declare and possibly initialize a string before using it.

To declare a string, use the AnsiString word followed by a valid C++ name. Here is an
example:

AnsiString Country;

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();

3.1.2 String Initialization
There are two main ways you can initialize an AnsiString variable. After declaring it,
you can assign the desired value to the variable using the assignment operator. Here is an
example:

AnsiString Country;
Country = Madagascar;

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");
Chapter 3: Strings Borland C++ Builder Programming

56 Copyright 2003 FunctionX, Inc.


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;
}
//---------------------------------------------------------------------------

You can also use it to fill out an edit control:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString City = "Antananrivo";
Edit1->Text = City;
}
//---------------------------------------------------------------------------

3.2 General Purpose String Functions

3.2.1 String Emptiness
General purpose functions are those that perform on strings regardless of any other
consideration. For example, before performing any operation on a string, sometimes you
will need first to find out whether the string contains something or is empty. Eventually,
you will decide what to do if the string is empty.

There are two main ways you can check whether the content of a text-based control is
empty. You can just use the AnsiString ==overloaded operator. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Original = Edit1->Text;

if(Original == "")
Edit2->Text = "Why is Edit1 empty?";
}
//---------------------------------------------------------------------------

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:

Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 57

//---------------------------------------------------------------------------
void __fastcall TOKBottomDlg::OKBtnClick(TObject *Sender)
{
String UserName = edtUserName->Text;

if( Edit1.IsEmpty() )
Panel1->Caption = "Please provide a User Name";
if( Edit2->Text == "" )
Panel1->Caption = "You need to type a password";
if( Edit3->Text.IsEmpty() )
Panel1->Caption = "Your account is not complete";

edtUserName->SetFocus();
}
//---------------------------------------------------------------------------

3.2.2 The Length of a String
When a string has been initialized or at least is not empty, it has a length. There are
various ways you can get or control the length of a string.

To find the length of a string, if the string is from the C string class, you can first convert
it using the AnsiString::c_str() function, then use the strlen() function to get its length:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
char* S = Edit1->Text.c_str();
Edit2->Text = strlen(S);
}
//---------------------------------------------------------------------------

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
Chapter 3: Strings Borland C++ Builder Programming

58 Copyright 2003 FunctionX, Inc.

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);
}
//---------------------------------------------------
//---------------------------------------------------
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
AnsiString S = Edit1->Text;
Edit2->Text = S.SetLength(4);
}
//---------------------------------------------------


3.2.3 String Trimming
Trimming a string is an operation that gets rid of leading or ending spaces in a string. To
remove any (empty) space on the left side of a string, you can use the
AnsiString::TrimLeft() method. Its syntax is:

AnsiString __fastcall TrimLeft() const;

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);

As opposed to the AnsiString::TrimLeft() method, the (global) TrimLeft() function
takes one argument which is the string that needs to be left trimmed. The function returns
a new string that is the same as the original omitting the leading space (if any exists):

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit2->Text = TrimLeft(Edit1->Text);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 59

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;

Here is an example of using this method:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit2->Text = Edit1->Text.Trim();
}
//---------------------------------------------------------------------------

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);
}
//---------------------------------------------------------------------------
Chapter 3: Strings Borland C++ Builder Programming

60 Copyright 2003 FunctionX, Inc.


3.3 String Conversions

3.3.1 C/ C++ Data Types Conversion to AnsiString
A value that the user types in a control such as an edit box is considered a string. This is
because the compiler cannot assume the kind of value the user or the client of an Edit
control would supply. For this reason, after a value has been provided to a control that
uses the AnsiString as the basis of its content, if you want to perform any mathematical
operation on the string you must convert the string to a valid data type.

The AnsiString class provides a lot of constructors that allow you to create a string of
any kind. For example you can use it to declare:

A character:
AnsiString Symbol = 'H';

An integer:
AnsiString Int = 120;

A long integer:
AnsiString Longer = -73495745;

A floating-point value:
AnsiString WeeklyEarnings = 675.15;

A double-precision number:
AnsiString WeeklyEarnings = 675.15;
AnsiString Silver = 2.15e28;

A string:
AnsiString GirlFriend = "Micheline Phoon";

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:

A character:
char Sign = 'q';
Edit1->Text = AnsiString(Sign);

An interger:
Integer Number = 808;
Caption->Text = AnsiString(Number);

Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 61

A long integer:
long Value = 497783L;
Panel1->Caption = AnsiString(Value);

A floating-point value:
Float Distance = 1205.62;
Label1->Caption = AnsiString(Distance);

A double-precision number:
Double YearlyIncome = 24588;
Edit1->Text = AnsiString(YearlyIncome);

A string:
AnsiString Food = "Peanut Butter";
Button2->Caption = AnsiString(Food);

3.3.2 AnsiString and C-Strings
The AnsiString class is configured to recognize null-terminated strings of the classic C
string functions. The AnsiString class has a constructor that can convert a null-
terminated C string to AnsiString. Thanks to this constructor, the AnsiString(const
char* Source), you can declare a C string and use it as you see fit:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
char Status[] = "Employee Status";
Caption = Status;
char *FullName = "Antoine Williams";
Edit1->Text = FullName;
}
//---------------------------------------------------------------------------

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.

3.3.3 Strings Cases: Lowercase to Uppercase Conversion
There are various techniques you can use to convert a string from lowercase to uppercase
and vice-versa. An alphabetical character is recognized as being in lowercase if it is one
of the following characters: a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z.
On the other hand, a character qualifies as uppercase if it is one of A, B, C, D, E, F, G, H,
I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z. All the other symbols are ignored
even if on the keyboard you would press Shift to type them.

To convert a lowercase character or string to uppercase, you can use the
AnsiString::UpperCase() member function. Its syntax is:

AnsiString __fastcall UpperCase() const;
Chapter 3: Strings Borland C++ Builder Programming

62 Copyright 2003 FunctionX, Inc.


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 = "J ames 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 = "J ames 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)
//---------------------------------------------------
void __fastcall TForm1::Button1Click(TObject
*Sender)
Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 63

{
String S1 = "La maison? a a t brle!";
String S2 = UpperCase(S1);
Edit1->Text = S2;
}
//---------------------------------------------------
{
String S1 = "La maison? a a t brle!";
String S2 = AnsiUpperCase(S1);
Edit1->Text = S2;
}
//---------------------------------------------------


3.3.4 Strings Cases: Uppercase to Lowercase Conversion
You can use the AnsiString::LowerCase() method to convert an uppercase character or
string to lowercase. Its syntax is:

AnsiString __fastcall LowerCase() const;

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
Chapter 3: Strings Borland C++ Builder Programming

64 Copyright 2003 FunctionX, Inc.

AnsiString::UpperCase() method but uses the same syntax as the UpperCase()
function:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnConvertClick(TObject *Sender)
{
String S1 = "La version Franaise de Borland C++ Builder est l. "
"Elle est arrive!";
edtConvert->Text = AnsiLowerCase(S1);
}
//---------------------------------------------------------------------------

3.4 Strings Addition

3.4.1 The Addition Operator
To add one AnsiString value to another, you use the addition operator (it was overloaded
to respond appropriately). The operation would produce a new string that combines the
first and the second string. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Edit3->Text = Edit1->Text + Edit2->Text;
}
//---------------------------------------------------------------------------

You can then use the addition operation to add as many strings as you see fit.

3.4.2 Appending Strings
Appending two strings consists of adding one string to another string. This operation
usually involves two strings: a destination and a source strings. To append two strings,
besides the addition operator +, you can use the AppendStr() member function. Its
syntax is:

void __fastcall AppendStr(AnsiString &Destination, const AnsiString Source);

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;
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 65

3.5 Strings Comparison Functions

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 case-
sensitivity. 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.

3.5.2 String Comparison With Case-Insensitivity
The SameText() function is used to compare two strings. Its syntax is:

bool __fastcall SameText(const AnsiString String1, const AnsiString String2);

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();
}
Chapter 3: Strings Borland C++ Builder Programming

66 Copyright 2003 FunctionX, Inc.

else
{
Panel1->Caption = "Your account has been setup.";
Close();
}
}
//---------------------------------------------------------------------------

The AnsiString::AnsiCompareIC() method performs a comparison of two strings,
considering the Regional Settings. Like SameText(), this function does not care about
case-sensitivity.

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString String1 = Edit1->Text;
AnsiString String2 = Edit2->Text;

if(String1.AnsiCompareIC(String2) < 0)
Edit3->Text = "True";
else if(String1.AnsiCompareIC(String2) > 0)
Edit3->Text = "False";
else
Edit3->Text = "Equal";
}
//---------------------------------------------------------------------------

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:

a negative value if the First string is less than the Second
a positive value if the First string is greater than the Second
0 if both strings are the same

3.5.3 String Comparison With Case-Sensitivity
The AnsiString::AnsiCompare() method is used to compare two strings with regards to
case sensitivity. This function, when performed on dates and currency values, considers
the Regional Settings of the users computer. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString String1 = Edit1->Text;
AnsiString String2 = Edit2->Text;

if(String1.AnsiCompare(String2) < 0)
Edit3->Text = "True";
else if(String1.AnsiCompare(String2) > 0)
Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 67

Edit3->Text = "False";
else
Edit3->Text = "Equal";
}
//---------------------------------------------------------------------------



Besides the AnsiString::AnsiCompare() method you can use the AnsiCompareStr()
function to compare strings. Like the AnsiString::AnsiCompare() method, this function
takes into consideration the Windows Regional Settings. Its syntax is:

int __fastcall AnsiCompare(const AnsiString& OtherString) const;

The function considers its own string and compares it to the string argument it takes. This
function returns:

a negative value if your string is less than the OtherString
a positive value if your string is greater than the OtherString
0 if both strings are the same

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:

A negative value if the First string is less than the Second
A positive value if the First string is greater than the Second
0 if both strings are the same

3.5.4 Strings Boolean Comparisons
The AnsiString class and the sysutils library provide techniques of comparing strings.
The functions we have used to perform comparisons returned integral values at the end of
their comparisons. Sometimes, when performing specific algorithms, such as comparing
passwords, performing mathematical calculations, performing spell checks in text
documents, etc, you will only need to know whether two strings are equal. This type of
Chapter 3: Strings Borland C++ Builder Programming

68 Copyright 2003 FunctionX, Inc.

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)
Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 69

{
String Caption1 = lblCaption1->Caption;
String Caption2 = lblCaption2->Caption;

if(AnsiSameCaption(Caption1, Caption2))
Panel1->Caption = "Same captions";
else
Panel1->Caption = "Completely Different";
}
//---------------------------------------------------------------------------

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:

Equal: ==
Not Equal: !=
Less Than <
Less Than Or Equal To <=
Greater Than >
Greater Than Or Equal To >=

3.6 Characters and Sub-Strings

3.6.1 The Last Character of a String
There are many operations you can perform on individual characters of an AnsiString
variable. These include checking for a character, finding the position of a character, or
deleting a character or characters. These operations can be valuable when creating objects
such as login dialog boxes.

To find the last character of a string, use the AnsiString::AnsiLastChar() method. Its
syntax is:

char* __fastcall AnsiLastChar() const;

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:
Chapter 3: Strings Borland C++ Builder Programming

70 Copyright 2003 FunctionX, Inc.



3.6.2 Character Deletion
Sometimes you will want to get rid of a character in a string. This is done using the
AnsiString::Delete() method. The syntax is:

AnsiString& __fastcall Delete(int Index, int Count);

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());

3.6.3 Substring Creation
A sub string is a string that is created from, or is included in, another string. C++ and
C++ Builder allow you to find a sub string in an original string, to get the position of a
sub string in a string, etc.

With a string, you can create a new string retrieved from the original using the
AnsiString::SubString() method. Its syntax is:

AnsiString __fastcall SubString(int StartPosition, int HowManyChars) const;

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 The Position of a Sub String
The AnsiString class allows you to analyze a string and find out whether it contains a
certain sub string. If it does, you can get the position of the substring, using the
AnsiString::Pos() method. Its syntax is:

int __fastcall Pos(const AnsiString& SubString) const;
Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 71


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);

3.6.5 Character or Substring Replacement
When performing your algorithms or other specific types of operations on strings, you
may want to find out whether a certain character or group of characters has been provided
in a string. If so, you may want to replace it with a different character or with a new sub
string. This operation is handled by the StringReplace() function. Its syntax is:

AnsiString __fastcall StringReplace(const AnsiString Original,
const AnsiString LookFor,
const AnsiString ReplaceWith,
TReplaceFlags FlagToUse);

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

3.7.1 Regular String to Quoted String Conversion
In the strict of string routines, a quote is a character or symbol used to delimit a string. It
sets the beginning and end of a string. In the English language, a quote is represented
with the double-quote symbol . The VCL is equipped with functions used to insert or
remove quotes from a string.

The AnsiQuotedStr() function is used to convert a string into a quoted string. Its
syntax is;

AnsiString __fastcall AnsiQuotedStr(const AnsiString Source, char Quote);

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:

Chapter 3: Strings Borland C++ Builder Programming

72 Copyright 2003 FunctionX, Inc.



//---------------------------------------------------------------------------
void __fastcall TForm1::edtQuotedExit(TObject *Sender)
{
char *BookTitle = edtQuoted->Text.c_str();
char Quote = '"';
AnsiString Quoted = AnsiQuotedStr(BookTitle, Quote);
edtBookTitle->Text = Quoted;
}
//---------------------------------------------------------------------------

3.7.2 Quoted String to Regular String Conversion
The QuotedStr() function is used to add a single-quote on both sides of a string. Its
syntax is:

AnsiString __fastcall QuotedStr(const AnsiString S);

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;
}
//---------------------------------------------------------------------------

3.7.3 String Quotes Removal
When a string is provided with quotes and you want to remove the quotes, use the
AnsiExtractQuotedStr() function. Its syntax is:

AnsiString __fastcall AnsiExtractQuotedStr(char * &Source, char Quote);

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 = '"';
Borland C++ Builder Programming Chapter 3: Strings

Copyright 2003 FunctionX, Inc. 73

AnsiString RemoveQuotes = AnsiExtractQuotedStr(BookTitle, Quote);
edtBookTitle->Text = RemoveQuotes;
}
//---------------------------------------------------------------------------



Chapter 4: Message Boxes Borland C++ Builder Programming

74 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 4: Message Boxes

Copyright 2003 FunctionX, Inc. 75


Chapter 4: Message Boxes


4.1 Fundamental Messages Boxes

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.

Practical Learning: Preparing the Message Boxes Application
1. Create a new project with its starting form
2. Change its Caption to Message Boxes Configurations
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.

4.1.2 Message Showing
The ShowMessage() function provides the most fundamental of Borlands message
boxes. This function takes one string argument and does not return any value. It is used to
display a message to the user who acknowledges it by clicking the OK button. The syntax
of the ShowMessage() function is

void __fastcall ShowMessage(const AnsiString Message);

Chapter 4: Message Boxes Borland C++ Builder Programming

76 Copyright 2003 FunctionX, Inc.

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);
}
//---------------------------------------------------------------------------

The string can also be a combination of other strings:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ShowMessage("The name " + AnsiString("\"") +
edtMessage->Text + AnsiString("\"") + " is not in our records.");
}
//---------------------------------------------------------------------------

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);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 4: Message Boxes

Copyright 2003 FunctionX, Inc. 77



Practical Learning: Using the ShowMessage() Function
1. 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. Double-click the Show Msg button to access its Click event
4. Press Tab and implement it as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::btnShowMsgClick(TObject *Sender)
{
ShowMessage("Please fill out your Time Sheet before leaving.");
}
//---------------------------------------------------------------------------
5. To test the program, press F9
6. Click the Show Msg button:

7. Click OK and close the form.
8. To save the project, on the main menu, click File -> Save All
9. Locate the folder where the exercises are installed.
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:
Chapter 4: Message Boxes Borland C++ Builder Programming

78 Copyright 2003 FunctionX, Inc.

15. And return to Bcb

4.1.3 The Win32 Message Box
The MessageBox() function is derived from Win32. Its syntax is:

int __fastcall MessageBox(const char * Message, const char * Caption, int Flags);



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:
Borland C++ Builder Programming Chapter 4: Message Boxes

Copyright 2003 FunctionX, Inc. 79


//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Application->MessageBox("Make sure the music is playing.",
"CD PLayer Instructions", MB_OK);
}
//---------------------------------------------------------------------------

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

Warning the user of an action
performed on the application
MB_ICONINFORMATION
MB_ICONASTERISK

Informing the user of a non-critical
situation
Chapter 4: Message Boxes Borland C++ Builder Programming

80 Copyright 2003 FunctionX, Inc.

MB_ICONQUESTION

Asking a question that expects a Yes
or No, or a Yes, No, or Cancel answer
MB_ICONSTOP
MB_ICONERROR
MB_ICONHAND

A critical situation or error has
occurred. This icon is appropriate
when informing the user of a
termination or deniability of an action

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
I f the message box has more
than one button, the default
button would be
MB_DEFBUTTON1 The first button
MB_DEFBUTTON2 The second button
MB_DEFBUTTON3 The third button
MB_DEFBUTTON4 The fourth button

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",
Borland C++ Builder Programming Chapter 4: Message Boxes

Copyright 2003 FunctionX, Inc. 81

MB_YESNOCANCEL | MB_DEFBUTTON3 | MB_ICONQUESTION);
}
//---------------------------------------------------------------------------

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!";
}
//---------------------------------------------------------------------------

Chapter 4: Message Boxes Borland C++ Builder Programming

82 Copyright 2003 FunctionX, Inc.

Practical Learning: Using the MessageBox() function
1. On the Component Palette, click Button and click on the form
2. Change the Caption of the new button to Simple &1 and its name to btnSimpleMsg
3. Double-click the Simple 1 button to access its click event.
4. Press Tab and type:
Application->MessageBox("This operation can only be performed by an administrator.",
NULL, NULL);
5. To test the form, press F9.
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. Click OK and close the form.
8. Press F12 to display the form
9. Add another button to the form.
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);
13. Test the form and return to Bcb
14. Add another button to the form.
15. Change its caption to &Question and its name to btnQuestion
16. Double-click the Question button.
17. Press Tab and type:
Application->MessageBox("Do you hear any music now or any sound at all?",
"CD Player Instructions",
MB_YESNOCANCEL | MB_ICONQUESTION);
18. Test the form:

19. Return to Bcb
20. Add another button to the form
21. Change its caption to &Default and its name to btnDefault
Borland C++ Builder Programming Chapter 4: Message Boxes

Copyright 2003 FunctionX, Inc. 83

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);
}
//---------------------------------------------------------------------------
23. Test the form:

24. Return to Bcb

4.2 VCL Custom Message Boxes

4.2.1 The Message Box as a Dialog
The MessageDlg() function is Borlands enhanced message box and it provides a good
alternative to the Win32s MessageBox() function:



The syntax of the MessageDlg() function is:

int __fastcall MessageDlg(const AnsiString Message, TMsgDlgType IconType,
TMsgDlgButtons Buttons, int HelpContext);

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:

Chapter 4: Message Boxes Borland C++ Builder Programming

84 Copyright 2003 FunctionX, Inc.

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 Button
mbYes

mbRetry

mbNo

mbIgnore

mbOK

mbAll

mbCancel

mbNoToAll

mbAbort


mbYesToAll

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.

Practical Learning: Using the MessageDlg() Function
1. Add a button to the form.
2. Change its caption to Msg Di&alog 1 and its name to btnMsgDialog1
3. Double-click the Msg Dlg 1 button
4. Press Tab and type:
MessageDlg("All songs on the CD have been copied. Now it will be ejected.",
mtInformation, TMsgDlgButtons() << mbOK, 0);
5. Test the form
6. Add a button to the form.
7. Change its caption to Msg Dia&log 2 and its name to btnMsgDlg2
8. Double-click the Msg Dialog2 button and implement it as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::btnMsgDlg2Click(TObject *Sender)
{
Borland C++ Builder Programming Chapter 4: Message Boxes

Copyright 2003 FunctionX, Inc. 85

MessageDlg("The file " + AnsiString("\"") +
edtMessage->Text + AnsiString("\"") +
" that you are trying to upload "
"already exists on the server.\n"
"If you continue, you will replace the recent versions "
"of the files on the server.\n"
"Would you like to upload anyway?",
mtConfirmation, TMsgDlgButtons() << mbNoToAll
<< mbNo << mbYes
<< mbYesToAll, 0);
}
//---------------------------------------------------------------------------
9. To test the form, press F9
10. In the Message edit box, type canonderby.asp
11. Click the Msg Dialog 2 button:

12. Return to Bcb

4.2.2 The Message Box and its Position
The MessageDlgPos() function provides extra possibilities to the programmer. It behaves
exactly like the MessageDlg() function. To create a message box based on this function,
use the syntax:

int __fastcall MessageDlgPos(const AnsiString Msg, TMsgDlgType DlgType,
TMsgDlgButtons Buttons, int HelpCtx, int X, int Y);

Besides the same arguments as the MessageDlg() function, The MessageDlgPos()
function allows you to specify the coordinates used to display the dialog box. The X
argument is an integer value that specifies the distance between the left border of the
screen and the left border of the dialog box. The Y argument represents the height from
the top border of the screen to the top border of the dialog box. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
MessageDlgPos("The right side of the main form displays "
"a \"Read-Only\" list of currently registered students.\n"
"This only includes students with good records.",
mtInformation, TMsgDlgButtons() << mbRetry
<< mbI gnore, 0, 20, 120);
}
//---------------------------------------------------------------------------

Chapter 4: Message Boxes Borland C++ Builder Programming

86 Copyright 2003 FunctionX, Inc.

4.2.3 Message Created From a Dialog
If you have a prototype message box that you are planning to use over and over again in
your application, you create it at once, implement it, then use in different locations in
your application. Such a common message box is created using the
CreateMessageDialog() function. The CreateMessageDialog() function does not allow
you to create a new message box. It provides a technique of creating a central dialog box
that combines the arguments and flags of the other message boxes. The syntax of the
CreateMessageDialog() function is:

Forms::TForm* __fastcall CreateMessageDialog(const AnsiString Msg,
TMsgDlgType IconType,
TMsgDlgButtons ButtonType);

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)
Borland C++ Builder Programming Chapter 4: Message Boxes

Copyright 2003 FunctionX, Inc. 87

: 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)
{
Chapter 4: Message Boxes Borland C++ Builder Programming

88 Copyright 2003 FunctionX, Inc.

Mine->ShowModal();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Mine->ShowModal();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Panel1Click(TObject *Sender)
{
Mine->ShowModal();
}
//---------------------------------------------------------------------------



4.2.4 The Input Dialog Box
The InputBox() function allows you to display a message box that would request a piece
of information from the user. The message box is equipped with an edit box and two
buttons. The edit box accepts a string from the user. The user can type anything but it is
up to you to use the content of that edit box as your program needs it. When the user
clicks OK, the Input Box returns the content of its edit box. If the user clicks Cancel or
presses Esc, the content of its edit box is dismissed.

The syntax of the InputBox() function is

AnsiString __fastcall InputBox(const AnsiString Caption,
const AnsiString Prompt, const AnsiString Default);

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
Borland C++ Builder Programming Chapter 4: Message Boxes

Copyright 2003 FunctionX, Inc. 89

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.

Practical Learning: Using the InputBox Dialog
1. Add a button to the form
2. Double-click it. Press Tab and type
InputBox('Student Registration', 'Type the Student''s Gender', '');
3. Press F9 to test the form. Click the new button. Notice that the content of its edit box
is empty
4. Close the Input Box and the form

4.2.5 The InputQuery Request
Like the InputBox() function, the InputQuery() function is used to display a prompting
dialog box to the user. The syntax of this function is:

function InputQuery(const Caption, Prompt: string; var Value: string): Boolean;

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.

Chapter 4: Message Boxes Borland C++ Builder Programming

90 Copyright 2003 FunctionX, Inc.

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;



Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 91


Chapter 5: Math Functions


5.1 The Math Libraries

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.
5.1.2 String to Integer Conversion
If you want to use the value of a string as an integer, you can use the
AnsiString::ToInt() method. Its syntax is:

int __fastcall ToInt() const;

This member function converts an AnsiString variable to a valid integer. In the following
example, the contents of two edit boxes are converted to integers and a subtraction is
performed:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Number1 = Edit1->Text.ToInt();
int Number2 = Edit2->Text.ToInt();
int Subtract = Number1 - Number2;

Edit3->Text = Subtract;
}
//---------------------------------------------------------------------------

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
Chapter 5: Math Functions Borland C++ Builder Programming

92 Copyright 2003 FunctionX, Inc.

AnsiString::ToIntDef() method allows you to supply a default value if the conversion
fails. Here is an example that uses it:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Value1 = Edit1->Text.ToIntDef(0);
int Value2 = Edit2->Text.ToIntDef(1);
int Remainder = Value1 % Value2;
Edit3->Text = Remainder;
}
//---------------------------------------------------------------------------

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;
}
//---------------------------------------------------------------------------

5.1.3 Integer to String Conversion
To convert an integer to an AnsiString, you can use the IntToStr() function. Its syntax is

AnsiString __fastcall IntToStr(int Value);
or
AnsiString __fastcall IntToStr(__int64 Value);

This function takes an integer as the argument and returns a string.

5.1.4 String to Floating-Point Conversion
To convert the value of a string to floating number, use the AnsiString::ToDouble()
method. Its syntax is:

double __fastcall ToDouble() const;

Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
double Value1 = Edit1->Text.ToDouble();
double Value2 = Edit2->Text.ToDouble();
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 93

double Value3 = Value1 + Value2;
Edit3->Text = Value3;
}
//---------------------------------------------------------------------------

Another function used to convert a string to a floating-point value is the StrToFloat()
function whose syntax is:

Extended __fastcall StrToFloat(const AnsiString S);

This function takes one argument, which is the string to convert. The function returns a
long double-precision value. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
double Value1 = StrToFloat(Edit1->Text);
double Value2 = StrToFloat(Edit2->Text);
double Value3 = Value1 + Value2;
Edit3->Text = Value3;
}
//---------------------------------------------------------------------------

5.1.5 Number Formatting to Display Decimals
The AnsiString class is equipped with a method used to format a floating-point number
and specify the number of decimal places. Actually, the AnsiString::sprintf() function
imitates the Cs printf() string system of displaying strings, integers, and floating
variables. In mathematical operations, you can use it to control how to display a decimal
number. The syntax of the method is:

AnsiString& __cdecl sprintf(const char* format, ...);

Following the C system of passing arguments, this member function can at least display a
string. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Edit2->Text = Edit2->Text.sprintf("The best movie of the year");
}
//---------------------------------------------------------------------------

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;
Chapter 5: Math Functions Borland C++ Builder Programming

94 Copyright 2003 FunctionX, Inc.

Volume = Radius * Radius * Radius * 4.00 * M_PI / 3;

edtDiameter->Text = Diameter;
edtCircumference->Text = Circumference;
edtArea->Text = Area;
edtVolume->Text = Volume;
}
//---------------------------------------------------------------------------


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

5.2.1 Absolute Values

The abs Function
The decimal numeric system counts from minus infinity to infinity. This means that
numbers are usually negative or positive, depending on their position from 0, which is
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 95

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);
}
//---------------------------------------------------------------------------


The labs Function
If you want to find the absolute value of a number that is larger than the regular integer,
you can use the labs() function. Its syntax is:

long labs(long int x);

This function takes a long integer as argument and returns its equivalent absolute value:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnLongAbsoluteClick(TObject *Sender)
{
int Longer = StrToInt(edtNumber->Text);
edtResult->Text = labs(Longer);
}
//---------------------------------------------------------------------------

5.2.2 The Ceiling of a Number
Consider a floating number such as 12.155. As you can see, this number is between
integer 12 and integer 13.







In the same way, consider a number such as 24.06. As this number is negative, it is
between 24 and 25, with 24 being greater.

12.155
13
12
Chapter 5: Math Functions Borland C++ Builder Programming

96 Copyright 2003 FunctionX, Inc.

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.

The ceil() Function
In C++, the function used to obtain the ceiling of a number uses the following syntax:

double ceil(double Value);

The function takes one argument, which is the floating number to be evaluated, and the
function returns a double-precision number that is the integer that is greater than or equal
to Value. Here is an example:

//---------------------------------------------------------------------------
#include <iostream>
using namespace std;
#pragma hdrstop
//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char* argv[])
{
double Value1 = 155.55;
double Value2 = -24.06;

cout << "The ceiling of " << Value1 << " is " << ceil(Value1) << endl;
cout << "The ceiling of " << Value2 << " is " << ceil(Value2) << endl;

cout << "\nPress any key to continue...";
getchar();
return 0;
}
//---------------------------------------------------------------------------

This would produce:

The ceiling of -24.06 is -24

Press any key to continue...


The Ceil() Function
In C++ Builder, the function used to get the ceiling of a number is:

int __fastcall Ceil(Extended Value);

The Ceil() function takes an argument that represents a long double value. The function
returns the greater or equal integer of Value. To use the Ceil() function, include the
math.hpp header to your program. Here is an example:

//---------------------------------------------------------------------------
#include <iostream.h>
#include <math.hpp>
#pragma hdrstop
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 97


//---------------------------------------------------------------------------

#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;
}
//---------------------------------------------------------------------------

This would produce:

The ceiling of 312.44 is 313
The ceiling of -4002.35 is -4002

Press any key to continue...

5.2.3 The Floor of a Number
Consider two floating numbers such as 128.44 and -36.72. The number 128.44 is between
128 and 129 with 128 being the lower. The number 36.72 is between 37 and 36 with
37 being the lower. The lowest but closest integer value of a number is referred to as its
floor. Based on this, the floor of 128.44 is 128. The floor of 36.72 is 37.

The floor() Function
In C++, to obtain the floor of a number, use the following function:

double floor(double Value);

The floor() function takes the considered value as the argument and returns the integer
that is less than or equal to Value. Here is an example:

double Value1 = 1540.25;
double Value2 = -360.04;

cout << "The floor of " << Value1 << " is " << floor(Value1) << endl;
cout << "The floor of " << Value2 << " is " << floor(Value2) << endl;

This would produce:

The floor of 1540.25 is 1540
The floor of -360.04 is -361

Press any key to continue...


Chapter 5: Math Functions Borland C++ Builder Programming

98 Copyright 2003 FunctionX, Inc.

The Floor() Function
When using C++ Builder, you can use the Floor() function to find the floor of a number.
The syntax of the function is:

int __fastcall Floor(Extended Value);

The Value argument of the function represents the number that is being considered. The
function returns the integer that is less than or equal to Value. Here is an example:

Extended Value1 = 312.44;
Extended Value2 = -4002.35;

cout << "The floor of " << Value1 << " is " << Floor(Value1) << endl;
cout << "The floor of " << Value2 << " is " << Floor(Value2) << endl;

This would produce:

The floor of 312.44 is 312
The floor of -4002.35 is -4003

Press any key to continue...

5.2.4 The Exponent of a Number

The frexp() and the frexpl() Functions
double frexp(double Number, int *Exp);
long double frexpl(long double Number, int *Exp);

The C++ frexp() and frexpl() functions are used to get the mantissa and the exponent
portions of a floating-point number. Each of these functions takes two arguments. The
Number argument represents the value that will be examined. For the frexp() function,
this value is a double-precision number. If the number is larger, then use the frexpl()
version whose argument is a long double. The Exp argument is passed as a pointer to an
integer. This allows the function to return a second value.

After execution, the function returns the mantissa such that:

Mantissa = frexp(Number, Exp);

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 * 2
Exp


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 :
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 99


//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
double Mant, Number = edtNumber->Text.ToDouble();
int Exp;

Mant = frexp(Number, &Exp);
edtMantissa->Text = Mant;
edtExponent->Text = Exp;
}
//---------------------------------------------------------------------------




The Frexp() Function
void __fastcall Frexp(Extended Number, Extended &Mantissa, int &Exp);

The Frexp() function is the VCLs version of the frexp() function. This function takes
three arguments. The number to be examined is the Number argument passed as a long
double. The number to be returned, also a long double, is the Mnatissa argument, also
passed by reference. The Exp argument, also passed as a reference, is returned as the
exponent value. The numbers are dealt with according to the formula:

Number = Mantissa * 2
Exp


//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Extended Number, Mant;
int Exp;

Number = StrToFloat(Edit1->Text);
Frexp(Number, Mant, Exp);

Edit2->Text = FloatToStr(Mant);
Edit3->Text = Exp;
}
//---------------------------------------------------------------------------

5.2.5 The Power of a Number

Chapter 5: Math Functions Borland C++ Builder Programming

100 Copyright 2003 FunctionX, Inc.

The C++ pow() Functions

double pow(double Source, double Raise);
long double powl(long double Source, long double Raise);

The pow() function is used to calculate the value of one number or expression raised to
the power of another number. This follows the formula: ReturnValue = x
y

The pow() function takes two required arguments. The first argument, x, is used as the
base number to be evaluated. The second argument, y, also called the exponent, will raise
x to this value. The powl() function performs the same calculation on long double
numbers and returns a long double.

In the following example, a form is equipped with a Button control and an Edit control.
When the user clicks the button, the constant 205.38 is raised to the power of 4.12. The
result displays in the edit box:

//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.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)
{
const double Source = 205.38;
const double Exp = 4.12;
double Result = pow(Source, Exp);

Edit1->Text = Result;
}
//---------------------------------------------------------------------------

The IntPower() function

Extended __fastcall IntPower(Extended Base, int Exponent);

The VCLs IntPower() function is used to raise a number, Base, to the integral Exponent
power. The first argument of this function, Base, can be an integer, a float, a double-
precision number or a long double. The Exponent argument is the factor about which the
Base number will be raised.

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 101

Extended Number, Base;
int Exp;

Base = StrToFloat(Edit1->Text);
Exp = StrToInt(Edit2->Text);
Number = IntPower(Base, Exp);

Edit3->Text = FloatToStr(Number);
}
//---------------------------------------------------------------------------

The Power() Function

Extended __fastcall Power(Extended Base, Extended Exponent);

The Power() function takes a number (any number, including integers, floating, double
or long double-precision numbers) as the Base argument and raises it to the power of the
Exponent argument, which also can be any number (int, float, double, long double).

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Source = 205;
float Exp = 5.25;
double Result = Power(Source, Exp);

Edit1->Text = Result;
}
//---------------------------------------------------------------------------

5.2.6 The Exponential
double exp(double x);

The exp() function calculates the exponential value of a number. The argument, a double-
precision value, represents the number to be evaluated.

If the value of x is less than -708.395996093 (approximately), the result is reset to 0 and
qualifies as underflow. If the value of the argument x is greater than 709.78222656
(approximately), the result is INF and qualified as overflow:

//---------------------------------------------------------------------------
#include <iostream.h>
#pragma hdrstop
//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char* argv[])
{
cout << "\nThe exponential of " << 709.78222656
<< " is " << exp(709.78222656);

cout << "\n\nPress any key to continue...";
getchar();
return 0;
}
Chapter 5: Math Functions Borland C++ Builder Programming

102 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------

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

double ldexp(double x, int y);
long double ldexpl(long double x, int y);

The C/C++ ldexp() function takes the mantissa and the exponent numbers and returns a
floating number. The function uses the formula:

Result = x * 2
y


Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
double x, Result;
int y;

x = StrToFloat(Edit1->Text);
y = StrToInt(Edit2->Text);
Result = ldexp(x, y);

Edit3->Text = FloatToStr(Result);
}
//---------------------------------------------------------------------------

The ldexp() function works on double-precision numbers while the ldexpl() uses long
doubles.

The Ldexp() Function

Extended __fastcall Ldexp(Extended X, int P);

The VCLs Ldexp() function is used to calculate a number that is derived from a known
mantissa and an exponent numbers. To perform this calculation, the function uses the
formula:

Result = X * 2
P

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
float Source = 450.04;
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 103

float Exp = 10.25;
double Result = Ldexp(Source, Exp);

Edit1->Text = Result;
}
//---------------------------------------------------------------------------

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 = log
10
x which is equivalent to x = 10
y


Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Extended X, Result;

X = StrToFloat(Edit1->Text);
Result = Log10(X);
Edit2->Text = FloatToStr(Result);
}
//---------------------------------------------------------------------------

Chapter 5: Math Functions Borland C++ Builder Programming

104 Copyright 2003 FunctionX, Inc.

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 = log
2
x. This is the same as x = 2
y


//---------------------------------------------------------------------------
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 = log
b
x which is the same as x = b
y


For the LogN() function, this formula would be:

LogN(Base, Number) = Number
Base
;

//---------------------------------------------------------------------------
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);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 105

5.2.7 The Square Root
There are two forms of calculating the square root of a Real positive number. When
using any of these functions, make sure you include the math.h header file to your
project.

The sqrt() function is used to calculate the square root of a double-precision number. Its
syntax is:

double sqrt(double x);

This function takes one argument as a positive floating number. After the calcultion, the
function returns the square root of x:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
double Number = Edit1->Text.ToDouble();
double Result = sqrt(Number);

Edit2->Text = Result;
}
//---------------------------------------------------------------------------


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,
Chapter 5: Math Functions Borland C++ Builder Programming

106 Copyright 2003 FunctionX, Inc.

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.

5.3.2 Double Declining Balance
The Double Declining Balance is a technique used to calculate the depreciating value of
an asset. The function used to perform this calculation is the DoubleDecliningBalance()
and its syntax is:

Extended __fastcall DoubleDecliningBalance(Extended Cost, Extended Salvage, int
Life, int Period);

The first parameter, Cost, represents the initial value of the item. The Salvage parameter
is the estimated value of the asset when it will have lost all its productive value. The Cost
and the Salvage values must be given in a monetary value. The value of Life is the length
of the lifetime of the item; this could be the number of months for a car or the number of
years for a house, for example. The Period is a factor for which the depreciation is
calculated. For the Double Declining Balance, this Period argument is usually 2.

In the following example, a form is equipped with five Edit controls named edtCost,
edtSalvage, edtLife, edtPeriod, and edtDepreciation. After entering the necessay values
and pressing Enter, the OnClick event of the Calculate button retrieves the values and
calls the DoubleDecliningBalnace() function to calculate the Depreciation and display it
the appropriate edit box:



//---------------------------------------------------------------------------
#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)
{
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 107

}
//---------------------------------------------------------------------------
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);
Extended Depreciation = DoubleDecliningBalance(Cost, Salvage, Life, Period);

edtDepreciation->Text = FloatToStr(Depreciation);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnExitClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
5.3.3 Straight Line Depreciation
The VCL provides another function used to calculate the depreciation of an item. This
time, the depreciation is considered on one period of the life of the item. The function
used, SLNDepreciation(), is:

Extended __fastcall SLNDepreciation(const Extended Cost, const Extended Salvage,
int Life);

The Cost argument is the original amount paid for an item (refrigerator, mechanics
toolbox, high-volume printer, etc). The Life parameter represents the period during which
the asset is (or was) useful; it is usually measured in years. The Salvage parameter, also
called the scrap value, is the value that the item will have (or is having) at the end of Life.

To perform this operation, the VCL uses the following formula:






//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended Depreciation, Cost, Salvage;
int Life;

Chapter 5: Math Functions Borland C++ Builder Programming

108 Copyright 2003 FunctionX, Inc.

Cost = StrToFloat(edtCost->Text);
Salvage = StrToFloat(edtSalvage->Text);
Life = StrToInt(edtLife->Text);
Depreciation = SLNDepreciation(Cost, Salvage, Life);

edtDepreciation->Text = FloatToStrF(Depreciation, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------

5.3.4 Sum of the Year Digits Depreciation
The Sum-Of-The-Years-Digits provides another technique for calculating the
depreciation of an item. Imagine that a restaurant bought a commercial refrigerator (cold
chamber) for $18,000 and wants to estimate its depreciation after 5 years using the Sum-
Of-Years-Digits technique. Each year is assigned a number, also called a tag, using a
consecutive count. This means that the first year is appended 1, the second is 2, etc. This
way, the depreciation is not uniformly applied to all years.

Year => 1, 2, 3, 4, and 5.

The total count is made for these tags. For our refrigerator example, this would be

Sum = 1 + 2 + 3 + 4 + 5 = 15

Each year is divided by this Sum, also called the sum of years, used as the common
denominator:



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 Fraction * Amount = Depreciation
1 5/15 * $18,000.00 = $6,000.00
2 4/15 * $18,000.00 = $4,800.00
3 3/15 * $18,000.00 = $3,600.00
4 2/15 * $18,000.00 = $2,400.00
5 1/15 * $18,000.00 = $1,200.00
Total Depreciation = $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
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 109

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 4
th
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 8
th
year. If the asset is worthless in the 8
th
year,
the depreciation would be 0.

Here is an example:



//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended DeprecYear1, DeprecYearN, Cost, Salvage;
int Life;

Cost = StrToFloat(edtCost->Text);
Salvage = StrToFloat(edtSalvage->Text);
Life = StrToInt(edtLife->Text);
DeprecYear1 = SYDDepreciation(Cost, Salvage, Life, 1);
DeprecYearN = SYDDepreciation(Cost, Salvage, Life, Life);

edtYear1->Text = FloatToStrF(DeprecYear1, ffCurrency, 8, 2);
edtYearN->Text = FloatToStrF(DeprecYearN, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------


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.
Chapter 5: Math Functions Borland C++ Builder Programming

110 Copyright 2003 FunctionX, Inc.

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 };

5.4.2 The Future Value of an Investment
The FutureValue() function is used to calculate the future value of an investment. The
syntax of this function is:

Extended __fastcall FutureValue(Extended Rate, int NPeriods, Extended Payment,
Extended PresentValue, TPaymentTime PaymentTime);

Here is an example:

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 111



//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended Present, Future, Payment, TheRate;
int Period;

Present = StrToFloat(edtPresent->Text);
Payment = StrToFloat(edtPayment->Text);
Period = StrToInt(edtPeriod->Text);
TheRate = StrToFloat(edtRate->Text) / 12;
double Rate = TheRate /100;
Future = FutureValue(Rate, Period, Payment, Present, ptEndOfPeriod);

edtFuture->Text = FloatToStrF(Future, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnExitClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------

5.4.3 The Number of Periods of an Investment
The NumberOfPeriods() function calculates the number of periodic payments of an
investment. Its syntax is:

Extended __fastcall PeriodPayment(constExtended Rate, int Period, int NPeriods,
const Extended PresentValue, const Extended FutureValue, TPaymentTime
PaymentTime);

Here is an example:

Chapter 5: Math Functions Borland C++ Builder Programming

112 Copyright 2003 FunctionX, Inc.



//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended Present, Future, TheRate, Payments, NPeriod;

Present = StrToFloat(edtLoan->Text);
Future = StrToFloat(edtFuture->Text);
Payments = StrToFloat(edtPayments->Text);
TheRate = StrToFloat(edtRate->Text) / 12;

double Rate = TheRate / 100;
// Apply the function
NPeriod = NumberOfPeriods(Rate, -Payments, -Present,
Future, ptStartOfPeriod);
// Since the number of periods is really an integer, find its ceiling
Extended Actual = Ceil(NPeriod);
// Display the number of periods
edtNPeriods->Text = FloatToStr(Actual);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnExitClick(TObject *Sender)
{
PostQuitMessage(0);
}
//---------------------------------------------------------------------------

5.4.4 Making an Investment or Paying a Loan
The Payment() function is used to calculate the regular payment of an investment. Its
syntax is:

Extended __fastcall Payment(Extended Rate, int NPeriods, constExtended PresentValue,
const Extended FutureValue, TPaymentTime PaymentTime);

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:

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 113



//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended Present, Future, TheRate, Payments, NPeriod;

Present = StrToFloat(edtLoan->Text);
Future = StrToFloat(edtFuture->Text);
TheRate = StrToFloat(edtRate->Text) / 12;
NPeriod = StrToFloat(edtNPeriods->Text);

double Rate = TheRate / 100;
// Apply the function
Payments = Payment(Rate, NPeriod, -Present,
Future, ptStartOfPeriod);
// Display the payments
edtPayments->Text = FloatToStrF(Payments, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------

5.4.5 The Amount Paid as Principal
While the InterestPayment() function calculates the amount paid as interest for a loan,
the PeriodPayment() function calculates the actual amount that applies to the balance of
the loan. This is referred to as the principal. Its syntax is:

Extended __fastcall PeriodPayment(constExtended Rate, int Period, int NPeriods,
const Extended PresentValue, const Extended FutureValue, TPaymentTime
PaymentTime);

Here is an example:

Chapter 5: Math Functions Borland C++ Builder Programming

114 Copyright 2003 FunctionX, Inc.



//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended Present, Future, TheRate, PPayment;
int Periods, NPeriod;

Present = StrToFloat(edtLoan->Text);
Future = StrToFloat(edtFuture->Text);
TheRate = StrToFloat(edtRate->Text) / 12;
Periods = StrToInt(edtPeriod->Text);
NPeriod = StrToInt(edtNPeriods->Text);

double Rate = TheRate / 100;
// Apply the function
PPayment = PeriodPayment(Rate, Periods, NPeriod,
-Present, Future, ptStartOfPeriod);
// Display the payment
edtPPMT->Text = FloatToStrF(PPayment, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------

5.4.6 The Present Value of an Investment
The PresentValue() function calculates the total amount that future investments are
worth currently. Its syntax is:

Extended __fastcall PresentValue(constExtended Rate,
int NPeriods,
const Extended Payment,
const Extended FutureValue,
TPaymentTime PaymentTime);

Here is an example:

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 115



//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended Present, Future, TheRate, Payments;
int NPeriod;

Future = StrToFloat(edtFuture->Text);
Payments = StrToFloat(edtPayments->Text);
TheRate = StrToFloat(edtRate->Text) / 12;
NPeriod = StrToInt(edtNPeriods->Text);

double Rate = TheRate / 100;
// Apply the function
Present = PresentValue(Rate, NPeriod,
-Payments, -Future, ptStartOfPeriod);
// Display the payment
edtPresent->Text = FloatToStrF(Present, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------

5.4.7 The Amount Paid As Interest
The InterestPayment() function is used to calculate the amount paid as interest for a
loan. Its syntax is:

Extended __fastcall InterestPayment(const Extended Rate,
int Period,
int NPeriods,
const Extended PresentValue,
const Extended FutureValue,
TPaymentTime PaymentTime);

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:
Chapter 5: Math Functions Borland C++ Builder Programming

116 Copyright 2003 FunctionX, Inc.




//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended Present, Future, Payment, TheRate;
int Periods, NPeriod;

Present = StrToFloat(edtPresent->Text);
Future = StrToFloat(edtFuture->Text);
Periods = StrToInt(edtPeriod->Text);
NPeriod = StrToInt(edtNPeriods->Text);
TheRate = StrToFloat(edtRate->Text) / 12;
double Rate = TheRate /100;

Payment = InterestPayment(Rate, Periods, NPeriod,
Present, Future, ptEndOfPeriod);

edtPayments->Text = FloatToStrF(Payment, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------

5.4.8 The Interest Rate
The InterestRate() function is used to find the interest applied to a loan. Its syntax is:

Extended __fastcall InterestRate(int NPeriods,
constExtended Payment,
const Extended PresentValue,
const Extended FutureValue,
TPaymentTime PaymentTime);

All of the arguments are the same as described for the InterestPayment() function. Here
is an example:

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 117



//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
Extended Present, Future, Payments, Rate;
int NPeriod;

Present = StrToFloat(edtPresent->Text);
Future = StrToFloat(edtFuture->Text);
Payments = StrToFloat(edtPayments->Text);
NPeriod = StrToInt(edtNPeriods->Text);

Rate = InterestRate(NPeriod, Payments, Present,
Future, ptEndOfPeriod) * 12 * 100;
AnsiString Value = FloatToStrF(Rate, ffGeneral, 3, 2);
edtRate->Text = Value + "%";
}
//---------------------------------------------------------------------------

5.4.9 The Internal Rate of Return
The InternalRateOfReturn() function is used to calculate an internal rate of return
based on a series of investments. Its syntax is:

Extended __fastcall InternalRateOfReturn(constExtended Guess,
const double * CashFlows,
const int CashFlows_Size);

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:
Chapter 5: Math Functions Borland C++ Builder Programming

118 Copyright 2003 FunctionX, Inc.




//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
double Goal;
double Month1, Month2, Month3, Month4, Month5, Month6;
Extended InterestGuess;
int Periods;

// Retrieve the estimate financial goal to achieve
Goal = edtGoal->Text.ToDouble();
// Retrieve the monthly investments
Month1 = edtMonth1->Text.ToDouble();
Month2 = edtMonth2->Text.ToDouble();
Month3 = edtMonth3->Text.ToDouble();
Month4 = edtMonth4->Text.ToDouble();
Month5 = edtMonth5->Text.ToDouble();
Month6 = edtMonth6->Text.ToDouble();
// Guess how much percentage
InterestGuess = StrToFloat(edtGuess->Text) / 100;

double Months[] = {-Goal,Month1,Month2,Month3,Month4,Month5,Month6};
Periods = (sizeof(Months) / sizeof(double)) - 1;
double IRR = InternalRateOfReturn(-InterestGuess, Months, Periods) * 100;

// Format the number to display only two decimals
AnsiString Value = FloatToStrF(IRR, ffGeneral, 3, 2);
// Display the result with a percent sign
edtIRR->Text = Value + "%";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnExitClick(TObject *Sender)
{
exit(0);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 119

5.4.10 The Net Present Value
The NetPresentValue() function uses a series of cash flows to calculate the present value
of an investment. Its syntax is:

Extended __fastcall NetPresentValue(const Extended Rate,
const double * CashFlows,
const int CashFlows_Size,
TPaymentTime PaymentTime);

The CashFlows is an array of payments made on an investment. Because it uses a series
of payments, any payment made in the past should have a positive value (because it was
made already). Any future payment should have a negative value (because it has not been
made yet). The CashFlows should be passed as an array. The CashFlows_Size is the
number of payments 1, which is also the dimension of the array 1.

The Rate is the rate of discount during one period of the investment.

The PaymentTime specifies whether the payment occurs at the beginning or end of the
period. It uses the TPaymentTime enumerator.

Here is an example:



//---------------------------------------------------------------------------
void __fastcall TForm1::btnCalculateClick(TObject *Sender)
{
double Goal;
double Month1, Month2, Month3, Month4, Month5, Month6;
Extended Rate;
int Periods;

// Retrieve the estimate financial goal to achieve
Goal = edtGoal->Text.ToDouble();
// Retrieve the monthly investments
Month1 = edtMonth1->Text.ToDouble();
Month2 = edtMonth2->Text.ToDouble();
Month3 = edtMonth3->Text.ToDouble();
Month4 = edtMonth4->Text.ToDouble();
Month5 = edtMonth5->Text.ToDouble();
Chapter 5: Math Functions Borland C++ Builder Programming

120 Copyright 2003 FunctionX, Inc.

Month6 = edtMonth6->Text.ToDouble();
Rate = StrToFloat(edtRate->Text) / 100;

double Months[] = { Month1, Month2, Month3, Month4, Month5, Month6 };
Periods = (sizeof(Months) / sizeof(double)) - 1;
double NPV = NetPresentValue(Rate, Months, Periods, ptEndOfPeriod) - Goal;

// Format the number to display as currency
edtNPV->Text = FloatToStrF(NPV, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------

5.5 Measure-Based Functions

5.5.1 Introduction










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.


C
Note
Equidistant
means same
distance
A
B
R
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 121



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>

5.5.2 The Pi Constant
The word #, also written as Pi, is a constant number used in various mathematical
calculations. Its approximate value is 3.1415926535897932. The calculator of Windows
represents it as 3.1415926535897932384626433832795. Borland had included its value
in the math.h library as M_PI 3.14159265358979323846.

A diameter is two times the radius. In geometry, it is written as 2R. In C++, it is written
as 2 * R or R * 2 (because the multiplication is symmetric). The circumference of a circle
is calculated by multiplying the diameter to Pi, which is 2R#, or 2 * R * # or 2 * R * Pi.

A radian is 2R#/R radians or 2R#/R rad, which is the same as 2# rad or 2 * Pi rad.

To perform conversions between the degree and the radian, you can use the formula:

360 = 2# rad which is equivalent to 1 rad = 360 / 2# = 57.3

5.5.3 Cycle To Radius Conversion
Extended __fastcall CycleToRad(Extended Cycles);

The CycleToRad() function is used to convert the measurement of an angle from radians
to cycles. This function is equivalent to using the formula Radian = 2Pi * Cycle.

Here is an example:

//---------------------------------------------------------------------------
#include <MATH.HPP>
#include <vcl.h>
#pragma hdrstop

A
B
"
Chapter 5: Math Functions Borland C++ Builder Programming

122 Copyright 2003 FunctionX, Inc.

#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);
}
//---------------------------------------------------------------------------



5.5.4 Degrees To Radius Conversion
Extended __fastcall DegToRad(Extended Degrees);

The DegToRad() function is used to calculate the equivalent value of an angle from
degrees to radians. This function follows the formula:

2Pi rad = 360 which is 1 rad = 360 / 2Pi = 180 / Pi = 57.3

Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnConversionClick(TObject *Sender)
{
Extended Deg = StrToFloat(edtDegrees->Text);
Extended Rad = DegToRad(Deg);
edtRadians->Text = FloatToStr(Rad);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 123



5.5.5 Radius To Cycle Conversion
Extended __fastcall RadToCycle(Extended Radians);

The RadToCycle() function is used to convert the measurement of an angle from radians
to cycles. This function is equivalent to using the formula Cycle = Radian / 2Pi.

Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnConversionClick(TObject *Sender)
{
Extended Rad = StrToFloat(edtRadian->Text);
Extended Cyc = RadToCycle(Rad);

edtCycle->Text = FloatToStr(Cyc);
}
//---------------------------------------------------------------------------



5.5.6 Radius To Degrees Conversion

Extended __fastcall RadToDeg(Extended Radians);

The RadToDeg() function is used to calculate the equivalent value of an angle from
radians to degrees. This function applies the formula:

360 = 2 * Pi which is 1 = 2 * Pi

Here is an example:

Chapter 5: Math Functions Borland C++ Builder Programming

124 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------
void __fastcall TForm1::btnConversionClick(TObject *Sender)
{
Extended Rad = StrToFloat(edtRadians->Text);
Extended Deg = RadToDeg(Rad);
edtDegrees->Text = FloatToStr(Deg);
}
//---------------------------------------------------------------------------



5.6 Statistics

5.6.1 The Maximum Integer Value of a Series

int __fastcall MaxIntValue(const int * Data, const int Data_Size);

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);
}
//---------------------------------------------------------------------------

5.6.2 The Maximum Value of a Series

double __fastcall MaxValue(const double * Data, const int Data_Size);
Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 125


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;
}
//---------------------------------------------------------------------------

5.6.3 The Mean or Average Value of a Series

Extended __fastcall Mean(const double * Data, const int Data_Size);

The Mean() function considers an array of numbers and calcuates the average value of
those numbers. The function takes two arguments. The first, Data, is the name of the
array of numbers. These number could integers or floating numbers. The second
argument, Data_Size represents the number-1 of members of the array. You can type an
integral number as the Data_Size or you can use the sizeof operator to get the dimension
of the array and subtract 1 from it. After the calculation, the function returns a long
double-precision number as the average of the numbers.

Here is an example:

//---------------------------------------------------------------------------
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 Average = Mean(Values, Size);

Edit1->Text = Average;
}
//---------------------------------------------------------------------------

5.6.4 The Minimum Integral Value of a Series

Chapter 5: Math Functions Borland C++ Builder Programming

126 Copyright 2003 FunctionX, Inc.

int __fastcall MinIntValue(const int * Data, const int Data_Size);

The MinIntValue() function calculates the minimum value of an array of integers. The
Data argument of the function is the name of the array. The second argument, Data_Size
is the number-1 of members of the array.

To get the minimum value of a group of integers, declare an integral array of numbers.
You can initialize the 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 members.

Here is an example that uses the MaxIntValue() function:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Numbers[] = { 15, 408, 72, 995, 32 };
int Size = (sizeof(Numbers)/sizeof(double)) - 1;

double MinInteger = MinIntValue(Numbers, Size);

Edit1->Text = MinInteger;
}
//---------------------------------------------------------------------------

5.6.5 The Minimum Value of a Series

double __fastcall MinValue(const double * Data, const int Data_Size);

The MinValue() function gets a numeric value that represents the minimum value of the
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 MinValue() function, declare an array that involves the necessary numbers.
You can initialize such a variable or request the values from the user. To calculate the
minimum 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
that uses the MinValue() 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 Minimum = MinValue(Values,Size);
Edit1->Text = Minimum;
}
//---------------------------------------------------------------------------

5.6.6 The Sum of Values of a Series

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 127

Extended __fastcall Sum(const double * Data, const int Data_Size);

The Sum() function is used to calculate the sum value of a group of numbers. The first
argument of this function, Data, is the name of an array that holds the numbers
considered. The Data_Size argument isthe dimension of the array minus 1.

To get the sum of a group of numbers, declare an array to hold the necessary numbers.
The numbers can be integers or double precision values. You can initialize the array with
these numbers or request their values from the user.

Here is an example of calculating a total number of grades of a student:

//---------------------------------------------------------------------------
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 = Sum(Grades, Size);
Edit1->Text = FloatToStr(Total);
}
//---------------------------------------------------------------------------

5.6.7 The Sum of Integers of a Series

int __fastcall SumInt(const int * Data, const int Data_Size);

The SumInt() function is used to calculate the total of a group of integral numbers. This
function takes two arguments. The first, Data, is the name of the array that holds the
numbers. The numbers must be integers. If you want to calculate a total of floating-point
numbers, use the Sum() function. The second argument, Data_Size is the size of the array
minus one.

Here is an example that simulates a company inventory to count business assets:

//---------------------------------------------------------------------------
#include <iostream.h>
#include <math.hpp>
#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused

int main(int argc, char* argv[])
{
int Tables, Chairs, BookShelves, TrashCans,
Desktops, Laptops, Printers, FaxMachines,
Books, Pens, Pencils, Others;

cout << "Company Inventory\nType the number of items of each category\n";
cout << "Desktops: "; cin >> Desktops;
cout << "Laptops: "; cin >> Laptops;
cout << "Printers: "; cin >> Printers;
Chapter 5: Math Functions Borland C++ Builder Programming

128 Copyright 2003 FunctionX, Inc.

cout << "Fax Machines: "; cin >> FaxMachines;
cout << "Chairs: "; cin >> Chairs;
cout << "Tables: "; cin >> Tables;
cout << "Book Shelves: "; cin >> BookShelves;
cout << "Books: "; cin >> Books;
cout << "Trash Cans: "; cin >> TrashCans;
cout << "Pens: "; cin >> Pens;
cout << "Pencils: "; cin >> Pencils;
cout << "Others: "; cin >> Others;

int Assets[] = { Tables, Chairs, BookShelves, TrashCans,
Desktops, Laptops, Printers, FaxMachines,
Books, Pens, Pencils, Others };
int Items = (sizeof(Assets)/sizeof(int)) - 1;
int AllAssets = SumInt(Assets, Items);

cout << "\nTotal Number of Items: " << AllAssets;

cout << "\n\nPress Enter to send the inventory...";
getchar();
return 0;
}
//---------------------------------------------------------------------------

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...

5.6.8 The Sum Of Squares of a Series

Extended __fastcall SumOfSquares(const double * Data, const int Data_Size);

The SumOfSquares() function performs a double operation on an array. First it
calculates the square S of each member of the array; then it calculates the total of the
individual S values. The first argument of the function, Data, is the name of the array,
the second argument, Data_Size represents the dimension of the array minus 1.

To calculate the total of the squares, declare an array variable. You can initialize the array
by providing the necessary list of numbers. Otherwise, request the different numbers
from the user. The function will take care of the rest.

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 129

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.6.9 The Sums and Squares of a Series
void __fastcall SumsAndSquares(const double * Data, const int Data_Size, Extended
&Sum, Extended &SumOfSquares);

The SumsAndSquares() function performs two operations and returns two values. Using
a group of numbers, the function calculates their total, Sum. It also calculates the square
S of each number then calculates the total of the S values, which produces a
SumOfSquares. The first argument, Data, is the array of the numbers considered. The
Data_Size argument is the number of items in the array minus 1. The Sum and the
SumOfSquares arguments are passed by reference, which allows the function to return
these last two values.

Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Extended Total, TotalOfSquares;
int Size;
double Values[] = { 32.12, 208.45, 14.80, 95.25,
30.32, 102.55, 88.20, 100.05 };

Size = (sizeof(Values)/sizeof(double)) - 1;
SumsAndSquares(Values, Size, Total, TotalOfSquares);

Edit1->Text = FloatToStr(Total);
Edit2->Text = FloatToStr(TotalOfSquares);
}
//---------------------------------------------------------------------------
5.7 Trigonometric Functions

Chapter 5: Math Functions Borland C++ Builder Programming

130 Copyright 2003 FunctionX, Inc.

5.7.1 The Cosine of a Value

Cosine Functions
double cos(double x);
long double cosl(long double x);

The cos() function calculates the cosine of a number.

Consider AB the length of A to B, also referred to as the hypotenuse. Also consider AC
the length of A to C which is the side adjacent to point A. The cosine of the angle at point
A is the ratio AC/AB. That is, the ratio of the adjacent length, AC, over the length of the
hypotenuse, AB.



The returned value, the ratio, is a double-precision number between 1 and 1.

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 cosine and displays it in the same Edit control:

//---------------------------------------------------------------------------
void __fastcall TForm1::edtValueKeyPress(TObject *Sender, char &Key)
{
if( Key == VK_RETURN )
{
double Value = edtValue->Text.ToDouble();
double Cosinus = cos(Value);
edtValue->Text = Cosinus;
}
}
//---------------------------------------------------------------------------




5.7.2 The Sine of a Value

The sin and sinl Functions
double sin(double x);
long double sinl(long double x);

The sin() function calculates the sine of a number.

Borland C++ Builder Programming Chapter 5: Math Functions

Copyright 2003 FunctionX, Inc. 131

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)
Chapter 5: Math Functions Borland C++ Builder Programming

132 Copyright 2003 FunctionX, Inc.

{
if( Key == VK_RETURN )
{
double Value = edtValue->Text.ToDouble();
double Tangent = tan(Value);
edtValue->Text = Tangent;
}
}
//---------------------------------------------------------------------------




The Arc Tangent Functions

double atan(double x);

The atan() function is used to calculate the arc tangent of a number.


In geometry, consider BC the length of B to C. Also consider AC the length of A to C.
The arc tangent is the ratio of BC/AC.

The atan() function takes one argument, x, that represents the angle BA AC. After the
evaluation, the function returns a double-precision number between PI/2 and PI/2.

If the number to be evaluated is larger than a double, use the atanl() function:

long double atanl(long double x);

This function takes a long double argument and returns a long double.



Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 133


Chapter 6: Accessories for File Processing


6.1 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.

6.1.2 Characteristics of a File
In order to manage files stored in a computer, each file must be able to provide basic
pieces of information about itself. This basic information is specified when the file is
created but can change during the life time of a file.

To create a file, a user must first decide where it would be located: this is a requirement.
A file can be located on the root drive. Alternatively, a file can be positioned inside of an
existing folder. Based on security settings, a user may not be able to create a file just
anywhere in the (file system of the) computer. Once the user has decided where the file
would reside, there are various means of creating files that the users are trained to use.
When creating a file, the user must give it a name following the rules of the operating
system combined with those of the file system.


At the time of this writing, the rules for file names were on the MSDN web site at
Windows Development\Windows Base Services\Files and I/O\SDK
Documentation\Storage\Storage Overview\File Management\Creating, Deleting, and
Maintaining Files\Naming a File (because it is a web site and not a book, its pages can
change anytime).

Chapter 6: Accessories for File Processing Borland C++ Builder Programming

134 Copyright 2003 FunctionX, Inc.

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.1.3 Introduction to Common File Dialog Boxes
Because files on a computer can be stored in various places, Microsoft Windows provides
various means of creating, locating, and managing files through objects called Windows
Common Dialog Boxes. Indeed, these dialog boxes are part of the operating system and
are equipped with all the necessary operations pertinent to their functionality. To support
this, Borland C++ Builder ships these ready-made dialog boxes so that, instead of, or
before creating a new commonly used dialog box, first find out if C++ Builder already
provides an object that can do the job. The objects of C++ Builder are highly efficient
and were tested enough to be reliable. This means that whenever possible, you should use
them.

To use a standard Windows dialog box, from the Dialogs tab of the Component Palette,
click the desired dialogs button and click anywhere on the form. The position of the
control on the form has no importance because it is only a representative. It will not
appear when the form is running. Once the desired dialogs icon is on the form, place a
button that will be used to call the dialog. A dialog is called using the DialogName-
>Execute() method. You can find out what button the user clicked when closing the
dialog, and act accordingly.

Practical Learning: Introducing Common Dialogs
1. Start Borland C++ Builder or create a new project with its default form
2. Save it in a new folder named FileProcess1
3. Save the unit as Exercise and save the project as FileProc
4. Change the Caption to File Processing

6.2 The Save As Dialog Box

6.2.1 Overview of the Save As Dialog Box
Most of the applications users open display an empty document. In other words, users are
supposed to create files. Once a file has been created, a user would usually want to store
the contents of that file on a media (hard drive, floppy disk, etc). Microsoft Windows
provides a common dialog box for this purpose: The Save As dialog box:

Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 135



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 8-
bit 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
Save In Combo Box
The Folder in which
the file will be saved
The File Name
Save As Type
This allows the
user to set an
extension
Chapter 6: Accessories for File Processing Borland C++ Builder Programming

136 Copyright 2003 FunctionX, Inc.

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).

6.2.2 Save As Dialog Box Creation
In the VCL, the Save As dialog box is performed using the TSaveDialog class. To
visually add a file saving capability to your application, on the Dialogs property page of
the Component Palette, you can click the SaveDialog button and click on a form.

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;
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 137

6.2.3 Characteristics of the Save As Dialog Box
To make sure that your application can open the allowed types of files for your
application, depending on your goals, you should create a list of extensions that you want
the users to be able to open. The allowed extensions form a group called a filter. The
filter is like a funnel that selects the good items. For a text-based application, you may
allow only text files, that is, files with a txt extension. For a rich text-based application,
you may allow only Rich Text Format files, which are files with rtf extension. On the
other hand, if you are creating an application for web files, you can allow as many file
extensions as necessary, such as htm, html, php, asp, etc. As you may realize, text files
or web files are all text-based files. This means that if you create a text-based or rich-text
based application, you should allow the users to decide whether the file they are trying to
open can be "read" by a text-based control. To provide this ability, you can specify an
unknown extension specified as All Files.

To create a list of allowable extensions for your SaveDialog object, use the Filter
property from the Object Inspector. At run time, you can create a list of file extensions as
a string. If the Save dialog box will need only one extension, you can create the string
using the following syntax:

Prompt|Extension

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|"
Chapter 6: Accessories for File Processing Borland C++ Builder Programming

138 Copyright 2003 FunctionX, Inc.

"Perl Script (*.pl)|*.pl|"
"All Files";
}
//---------------------------------------------------------------------------

This would produce:



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:

Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 139

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.

Chapter 6: Accessories for File Processing Borland C++ Builder Programming

140 Copyright 2003 FunctionX, Inc.

Practical Learning: Using the Save As Dialog Box
1. On the Dialogs tab of the Component Palette, click the SaveDialog button and
click the form
2. While the Save Dialog1 button is still selected on the dialog, in the Object Inspector,
click the DefaultExt field and type rtf
3. Click Filter and click its ellipsis button
4. Complete the Filter Editor dialog box as follows:

5. Click OK
6. Click Title, type Save File As and press Enter
7. Click an empty area on the form to select it
8. In the Object Inspector, click the Events tab and double-click OnDblClick
9. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDblClick(TObject *Sender)
{
if( SaveDialog1->Execute() == True )
{
ShowMessage("The Save button was clicked or Enter key was pressed"
"\nThe file would have been saved as " +
SaveDialog1->FileName);
}
else
ShowMessage("The Cancel button was clicked or Esc was pressed");
}
//---------------------------------------------------------------------------
10. Execute the application. To test the Save As dialog box, double-click anywhere on
the form
Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 141



11. Close the application and return to Bcb


6.3 The Open File Dialog Box

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:



6.3.2 Open File Dialog Box Creation
To provide the means of opening files, you can use the TOpenDialog class. The easiest
way to use it is to click the OpenDialog button from the Dialogs tab of the Component
Palette and click on the form. The OpenDialog icon can be positioned anywhere on the
form because it would not be seen at run time. After placing it on the form, you can use
the Object Inspector to configure it.

Chapter 6: Accessories for File Processing Borland C++ Builder Programming

142 Copyright 2003 FunctionX, Inc.

If you prefer to dynamically create an Open dialog box, declare a pointer to
TOpenDialog and use the new operator to call its constructor and specify its owner. The
technique is the same we applied for the TSaveDialog class. Here is an example:

TOpenDialog *OpenMe = new TOpenDialog(Form1);

6.3.3 Characteristics of an Open Dialog Box
One of the most important properties of an Open dialog box is the file it presents to the
user. This is represented by the FileName property. If you want a default file to
bespecified when the dialog box comes up, you can specify this in the FileName property
of the Object Inspector. If you need to use this property, you should make sure the file
can be found. If the file is located in the same folder as the application, you can provide
just its name. If the file is located somewhere in the hard drive, you should provide its
complete path. Most of the time, you will not be concerned with this property if you are
creating an application that will allow the user to open any file of her choice. Once a file
is located, it can be accessed using the TOpenDialog::FileName property.

To make your application more effective, you should know what types of files your
application can open. This is taken care by specifying a list of extensions for the
application. To control the types of files that your application can open, specify their
extensions using the Filter Property. The Filter string is created exactly like that of a
SaveDialog control as we saw earlier.

Like the SaveDialog control, the default extension is the one the dialog box would first
filter during file opening. If you want the Open File dialog to easily recognize a default
type of file when the dialog box opens, you can specify the extension's type using the
DefaultExt property.

For convenience, or for security reasons, Open File dialog boxes of applications are
sometimes asked to first look for files in a specific location when the Open File dialog
box comes up. This default folder is specified using the InitialDir property.

The essence of using the Open File dialog box is to be able to open a file. This job is
handled by the Execute()method which is easily called using a pointer to the OpenDialog
object you are using.

Practical Learning: Using the Open Dialog Box
1. On the Dialogs tab of the Component, click the OpenDialog button and click
the form
2. While the OpenDialog1 icon is still selected on the form, in the Object Inspector,
click DefaultExt and type rtf
3. Click Filter and click its ellipsis button
4. Complete the Filter Editor dialog box as follows:
Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 143


5. Click OK
6. Click Title, type Open an Existing Document and press Enter
7. Click an unoccupied area on the form to select it and, in the Object Inspector, click
the Events property page
8. Double-click the OnMouseDown field and implement the event as follows:
//---------------------------------------------------------------------------

void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if( Button == mbRight )
{
if( OpenDialog1->Execute() == True )
{
ShowMessage("The Open button was clicked or the Enter key was pressed"
"\nThe " + OpenDialog1->FileName +
" file would have been opened.");
}
else
ShowMessage("The Cancel button was clicked or Esc was pressed");
}
}
//---------------------------------------------------------------------------
9. Test the application:
Chapter 6: Accessories for File Processing Borland C++ Builder Programming

144 Copyright 2003 FunctionX, Inc.


10. After using it, close it and return to Bcb.

6.4 The Browse For Folder Dialog Box

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:



Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 145

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.

6.4.2 Creation of a Browse for Folder Dialog Box
The Browse For Folder dialog box is made available through the SelectDirectory()
function. Its syntax is:

bool __fastcall SelectDirectory(const AnsiString Caption,
const WideString Root, AnsiString &Directory);

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;
}
//---------------------------------------------------------------------------


Chapter 6: Accessories for File Processing Borland C++ Builder Programming

146 Copyright 2003 FunctionX, Inc.

6.5 The Select Directory Dialog Box

6.5.1 Introduction
An overloaded version of the SelectDirectory() function allows performing a different
type of folder selection:



6.5.2 Creation of the Select Directory Dialog Box
To make available a Select Directory dialog box to your application, you can call the
SelectDirectory() function using the following syntax:

bool __fastcall SelectDirectory(AnsiString &Directory,
TSelectDirOpts Options,
int HelpCtx);

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);
}
//---------------------------------------------------------------------------

On the computer used for this exercise, the function produced:

Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 147



Notice the absence of a Directory Name edit box.

Once again, the user can change the displaying folder. This time, to proceed, the user
would double-click a folder in the Directories tree view to expand the folder. Once the
user has located the desired folder, he must click it to highlight it. After selecting a folder,
the user would click OK. In this case the function would return true and the path of the
folder. Two options not available on the first CreateDirectory() version can be used
here. If you want to display the Directory Name edit box, set the TSelectDirOpts option
set to at least:

TSelectDirOpts() << sdAllowCreate

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:

Chapter 6: Accessories for File Processing Borland C++ Builder Programming

148 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
AnsiString Directory = "C:\\Program Files\\Borland\\Delphi4\\Projects";
const int HelpMe = 12;
SelectDirectory(Directory, TSelectDirOpts() << sdAllowCreate
<< sdPerformCreate << sdPrompt, HelpMe);
}
//---------------------------------------------------------------------------


6.6 The Print Dialog Box

6.6.1 Printing: An Overview
Another operation users perform on a file is to print it. Printing is the ability to render, on
paper, the result of a control's contents. This is performed using an external device called
a peripheral. To do this, users need access to a printer device. There are two main ways
users print a document or file. They can ask the application they are using to send the
document directly to a printer, or they can use a dialog box to decide how the printing
should be done.

To directly send a document to the printer, you need to make sure that the control, whose
value needs to be printed, supports printing. To accommodate the users of such an
application, you can provide a menu item or a button they would click. An example of
such a button would be . To print, the user can click this button. That is the case when
using WordPad; its Standard toolbar is equipped with such a button. With this type of
printing, when the user decides to print, the whole document would be printed "as is", in
color if the document is colored and if the printer supports colors. If there are more than
one printer, the computer would use what is known as the default printer.

If you want users to be able to configure or customize the printing process, Microsoft
Windows provides a common dialog box called Print:



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
Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 149

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.

6.6.2 The Process of Printing
There are various ways you can deal with printing (printing is actually one of the most
difficult tasks to program). The first thing you should do is to let the users know that
printing is available on your application. This is usually done by providing a menu item
called Print or a button on a toolbar with a printer-like bitmap.

The easiest and fastest way to send a document to the printer, if the control that holds the
document directly supports printing, is by calling the Print() method. For example, since
the TRichEdit class and its associated control the RichEdit support printing, you can
simply call the TRichEdit::Print() method. This method takes one argument as the name
of the document that needs to be printed. The argument can be the name of the file. If you
do not have a specific name, you can type anything or specify an empty string. Here is an
example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
RichEdit1->Print("Mother and Father");
}
//---------------------------------------------------------------------------

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.
Chapter 6: Accessories for File Processing Borland C++ Builder Programming

150 Copyright 2003 FunctionX, Inc.


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("");
}
//---------------------------------------------------------------------------
Borland C++ Builder Programming Chapter 6: Accessories for File Processing

Copyright 2003 FunctionX, Inc. 151


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 The Print Setup Dialog Box

6.7.1 Overview of the Print Setup Dialog Box
As opposed to directly printing a file, a user may want to perform some preliminary
preparation on the file or the printer. Microsoft Windows provides another dialog box
used to control printing. It is called Print Setup:



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:

Chapter 6: Accessories for File Processing Borland C++ Builder Programming

152 Copyright 2003 FunctionX, Inc.





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.

6.7.2 Creationg of the Print Setup Dialog box
In VCL applications, the Print Setup dialog box is provided by the TPrinterSetupDialog
class. To make a Print Setup dialog available, at design time, from the Dialogs property
sheet of the Component Palette, click the PrintSetupDialog button and click on the form.
Because the options of the Print Setup dialog box are configured and controlled by the
driver of the printer selected, there is no significant configuration you need to perform.


Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 153


Chapter 7: File Processing


7.1 C File Processing

7.1.1 C How To Process Files
File processing is traditionally performed using the FILE class. In the strict C sense,
FILE is a structure and it is defined in the stdio.h header file. This object is equipped
with variables used to indicate what operation would be performed. To use this structure,
you can first declare an instance of the FILE structure. Here is an example:

FILE *Starter;

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.

Practical Learning: Preparing for File Processing
Open the StdGrades project from the Students Grades1 folder from the resources
that accompany this book and display the main form


Chapter 7: File Processing Borland C++ Builder Programming

154 Copyright 2003 FunctionX, Inc.

7.1.2 Opening and/ or Saving Files
To create a new file, open an existing file, or save a file, you use the fopen() function. Its
syntax is:

FILE *fopen( const char *FileName, const char *Mode );

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 If the file already exists If the file does not exist
r Opens an existing file
for reading only
it would be opened and can be
read. After the file is opened, the
user cannot add data to it
the operation would fail
w Saves a new file the file's contents would be
deleted and replaced by the new
content
a new file is created and can be
written to
a Opens an existing
file, saves new file,
or saves a existing
file that has been
modified
the file is opened and can be
modified or updated. New
information written to the file
would be added to the end of the
file
a new file is created and can be
written to
r+ Opens an existing file the file is opened and its existing
data can be modified or updated
the operation would fail
w+ Creates new file or
saves an existing one
he file is opened, its contents
would be deleted and replaced
with the new contents
a new file is created and can be
written to
a+ Creates a new file or
modifies an existing
one
it is opened and its contents can
be updated. New information
written to the file would be
added to the end of the file
a new file is created and can be
written to

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);

Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 155

To use this function, you must let it know what instance of the FILE object you are
closing.

Practical Learning: Opening and Saving Files
1. From the Dialogs tab of the Component Palette, click the OpenDialog button
and click anywhere in the form
2. On the Object Inspector, change its DefaultExt to rcd
3. In its Filter field, type Student Record (*.rcd)|*.rcd|All Files
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() )
Chapter 7: File Processing Borland C++ Builder Programming

156 Copyright 2003 FunctionX, Inc.

{
FOpen = fopen(OpenDialog1->FileName.c_str(), "r+");

if( FOpen == NULL )
{
ShowMessage("The file could not be opened");
return;
}
}

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. Save the project

7.1.3 Reading From and Writing to Files
To save a file, you must write data to its contents. This operation is performed using the
fprintf() or the fwprintf() functions. Their syntaxes are:

int fprintf(FILE *stream, const char *format, ...);
int fwprintf(FILE *stream, const wchar_t *format, ...);

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:

Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 157

Character Used for
c A single character
d An integer
e A floating-point number
f A floating-point number
g A floating-point number
h A short integer
i A decimal, a hexadecimal, or an octal integer
o An octal integer
s A string followed by a white space character
u An unsigned decimal integer
x 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.

Practical Learning: Reading to and Writing From Files
1. 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, "%s", FirstName);
fscanf(FOpen, "%s", LastName);
fscanf(FOpen, "%s", DOB);
fscanf(FOpen, "%d", &Gender);
Chapter 7: File Processing Borland C++ Builder Programming

158 Copyright 2003 FunctionX, Inc.

fscanf(FOpen, "%s", English);
fscanf(FOpen, "%s", Language2);
fscanf(FOpen, "%s", History);
fscanf(FOpen, "%s", Geography);
fscanf(FOpen, "%s", Sciences);
fscanf(FOpen, "%s", 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, "%s\n", FirstName);
fprintf(FSave, "%s\n", LastName);
fprintf(FSave, "%s\n", DOB);
fprintf(FSave, "%d\n", Gender);
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 159

fprintf(FSave, "%.2f\n", English);
fprintf(FSave, "%.2f\n", Language2);
fprintf(FSave, "%.2f\n", History);
fprintf(FSave, "%.2f\n", Geography);
fprintf(FSave, "%.2f\n", Sciences);
fprintf(FSave, "%.2f\n", Sports);
}

fclose(FSave);
}
//---------------------------------------------------------------------------
3. Save and test the application

4. After using it, close it and return to Bcb

7.2 C++ File Streaming

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.
7.2.2 Saving a File
Saving a file consists of writing data to disk. To do this, first declare an instance of the
ofstream class using one of its constructors from the following syntaxes:
Chapter 7: File Processing Borland C++ Builder Programming

160 Copyright 2003 FunctionX, Inc.


ofstream(const char* FileName, int FileMode);
ofstream();

The ofstream(const char* FileName, int FileMode) constructor provides a complete
mechanism for creating a file. It does this with the help of its two arguments. The first
argument, FileName, is a string that specifies the name of the file that needs to be saved.
The second argument, FileMode, specifies what kind of operation you want to perform
on the file. It can be one of the following:

Mode Description
ios::app If FileName is a new file, data is written to it.
If FileName already exists and contains data, then it is opened, the
compiler goes to the end of the file and adds the new data to it.
ios::ate If FileName is a new file, data is written to it and subsequently added
to the end of the file.
If FileName already exists and contains data, then it is opened and
data is written in the current position.
ios::in If FileName is a new file, then it gets created fine as an empty file.
If FileName already exists, then it is opened and its content is made
available for processing
ios::out If FileName is a new file, then it gets created fine as an empty file.
Once/Since it gets created empty, you can write data to it.
If FileName already exists, then it is opened, its content is destroyed,
and the file becomes as new. Therefore you can create new data to
write to it. Then, if you save the file, which is the main purpose of this
mode, the new content is saved it.
*This operation is typically used when you want to save a file
ios::trunc If FileName already exists, its content is destroyed and the file
becomes as new
ios::nocreate If FileName is a new file, the operation fails because it cannot create a
new file.
If FileName already exists, then it is opened and its content is made
available for processing
ios::noreplace If FileName is a new file, then it gets created fine.
If FileName already exists and you try to open it, this operation would
fail because it cannot create a file of the same name in the same
location.

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() )
{
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 161

ofstream Students(SaveDialog1->FileName.c_str(), ios::out);
Students << FirstName << "\n" << LastName << "\n" << Age;
}
}

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();

Practical Learning: Saving a File
1. 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
Chapter 7: File Processing Borland C++ Builder Programming

162 Copyright 2003 FunctionX, Inc.

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. In the FileName field, type Untitled
5. In the Filter box, type
Body Tag Documents (*.btd)|*.btd|Text Files (*.txt)|*.txt|All Files|*.*
6. In the Title box, type Save Current Format Tag
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
12. Implement the new event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::btnSaveClick(TObject *Sender)
{
DWORD RedBG, GreenBG, BlueBG,
RedText, GreenText, BlueText,
RedLink, GreenLink, BlueLink,
RedALink, GreenALink, BlueALink,
RedVLink, GreenVLink, BlueVLink;

RedBG = GetRValue(mmoPreview->Color);
GreenBG = GetGValue(mmoPreview->Color);
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 163

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();
}
}
//---------------------------------------------------------------------------
13. Press F9 to test the application
14. To test it, change the colors of the attributes (Background, Text, Link, Active Link,
and Visited Link). Then click Save and set the name to Firmament
15. Close the application and return to Bcb
16. Save All
7.2.3 Opening a File
Besides saving, another operation you can perform consists of opening an already
existing file to have access to its contents. To do this, C++ provides the ifstream class.
Like ofstream, the ifstream class provides various constructors you can use, two of
which are particularly important. If you have enough information about the file you want
to open, you can use the following constructor:
Chapter 7: File Processing Borland C++ Builder Programming

164 Copyright 2003 FunctionX, Inc.


ifstream( const char* FileName, int FileMode);

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;
}
}
//---------------------------------------------------------------------------

Practical Learning: Opening a File
1. Display the main form, frmMain. From the Dialogs tab of the Component Palette,
double-click the OpenDialog button
2. While the OpenDialog button is still selected on the form, on the Object Inspector,
change the DefaultExt to btd
3. In the FileName field, type Untitled
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 165

4. In the Filter box, type
Body Tag Documents (*.btd)|*.btd|Text Files (*.txt)|*.txt|All Files|*.*
5. In the Title box, type Open a Tag Format
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
Chapter 7: File Processing Borland C++ Builder Programming

166 Copyright 2003 FunctionX, Inc.

>> RedVLink >> GreenVLink >> BlueVLink;

FormatToOpen.close();

scrRed->Position = 255 - RedBG;
scrGreen->Position = 255 - GreenBG;
scrBlue->Position = 255 - BlueBG;

edtNumRed->Text = RedBG;
edtNumGreen->Text = GreenBG;
edtNumBlue->Text = BlueBG;

edtHexaRed->Text = IntToHex(__int64(RedBG), 2);
edtHexaGreen->Text = I ntToHex(__int64(GreenBG), 2);
edtHexaBlue->Text = IntToHex(__int64(BlueBG), 2);

edtBackground->Text = "#" +
AnsiString(IntToHex(__int64(RedBG), 2)) +
AnsiString(IntToHex(__int64(GreenBG), 2)) +
AnsiString(IntToHex(__int64(BlueBG), 2));
edtText->Text = "#" +
AnsiString(IntToHex(__int64(RedText), 2)) +
AnsiString(IntToHex(__int64(GreenText), 2)) +
AnsiString(IntToHex(__int64(BlueText), 2));
edtLink->Text = "#" +
AnsiString(IntToHex(__int64(RedLink), 2)) +
AnsiString(IntToHex(__int64(GreenLink), 2)) +
AnsiString(IntToHex(__int64(BlueLink), 2));
edtALink->Text = "#" +
AnsiString(IntToHex(__int64(RedALink), 2)) +
AnsiString(IntToHex(__int64(GreenALink), 2)) +
AnsiString(IntToHex(__int64(BlueALink), 2));
edtVLink->Text = "#" +
AnsiString(IntToHex(__int64(RedVLink), 2)) +
AnsiString(IntToHex(__int64(GreenVLink), 2)) +
AnsiString(IntToHex(__int64(BlueVLink), 2));

edtPreviewText->Font->Color = TColor(RGB(RedText, GreenText, BlueText));
edtPreviewLink->Font->Color = TColor(RGB(RedLink, GreenLink, BlueLink));
edtPreviewALink->Font->Color = TColor(RGB(RedALink, GreenALink,
BlueALink));
edtPreviewVLink->Font->Color = TColor(RGB(RedVLink, GreenVLink,
BlueVLink));

pnlPreview->Color = TColor(RGB(RedBG, GreenBG, BlueBG));
mmoPreview->Color = TColor(RGB(RedBG, GreenBG, BlueBG));
edtPreviewText->Color = TColor(RGB(RedBG, GreenBG, BlueBG));
edtPreviewLink->Color = TColor(RGB(RedBG, GreenBG, BlueBG));
edtPreviewALink->Color= TColor(RGB(RedBG, GreenBG, BlueBG));
edtPreviewVLink->Color= TColor(RGB(RedBG, GreenBG, BlueBG));
grpBodyAttributes->ItemIndex = 0;
}
}
//---------------------------------------------------------------------------
10. Press F9 to test the application
11. Open the previously saved format by clicking the Open... button
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 167


12. After using the application, close it and return to Bcb

7.3 VCL File Streaming

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.

7.3.2 Saving Controls Contents
In order to save the contents of controls, first declare a pointer to TFileStream using its
constructor and specify where the file would be saved. You can specify this path directly
in the constructor if you know exactly where the file should be located. This can be done
if you are writing a program that stores the default file at a specific location and you
know where the file should be saved. Otherwise, you can use the SaveDialog control to
let the user specify where to save the file.
Chapter 7: File Processing Borland C++ Builder Programming

168 Copyright 2003 FunctionX, Inc.


When saving a file, the Mode argument of the constructor can be passed as one of the
following constants:

Write Mode Description
fmCreate If the user is saving a new file, this is used to save it as new
fmOpenWrite
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
fmOpenReadWrite
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 Only the current application can access the file
fmShareDenyWrite
The file can be opened by other applications but they cannot
modify its contents
fmShareDenyRead
The file can be modified by other applications but they cannot
open it
fmShareDenyNone
There is no restriction on what the other applications can do with
the current file


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:



Here is an example of saving the contents of both controls to a file:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnSaveClick(TObject *Sender)
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 169

{
// Decalre a pointer to TFileStream
TFileStream *FStream;

// Let the user call the Save Dialog
if( SaveDialog1->Execute() )
{
// Use the constructor of the TFileStream to create a file
try {
FStream = new TFileStream(SaveDialog1->FileName, fmCreate);
// In the pointer to FStream, add the contents of the Memo
FStream->WriteComponent(Memo1);
// and the content of the Edit controls
FStream->WriteComponent(Edit1);
}
__finally
{
// Since the pointer was created, delete it,
// whether it was used or not
delete FStream;
}
}
}
//---------------------------------------------------------------------------

7.3.3 Loading Controls Contents
When saving the contents of controls using TFileStream, the file is arranged so the class
can locate data for each object. Based on this, you can use TFileStream to open a file
that was created with this class. To do this, once again, declare a pointer to TFileStream
and initialize the file using the constructor. If you already know where the file is located,
you can simply provide it to the constructor. Otherwise, you can use the Open dialog box
and let the user select the file.

When opening a file, you can use one of the following modes as the Mode argument:

Read Mode Description
fmOpenRead This is used to open a file but the user cannot modify and then save
it.
fmOpenReadWrite This allows opening an existing file, modifying, and saving it.

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);
Chapter 7: File Processing Borland C++ Builder Programming

170 Copyright 2003 FunctionX, Inc.

}
__finally
{
delete FStream;
}
}
}
//---------------------------------------------------------------------------

7.4 VCL File Buffering

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:



7.4.2 Values Buffering
To create a file, you can use the TFileStream class reviewed earlier, using the same rules.
To write data to a file, you can call the TFileStream::WriteBuffer() method. Its syntax
is:

void __fastcall WriteBuffer(const void *Buffer, int Count);

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;
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 171

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;
}
}
}
//---------------------------------------------------------------------------

7.4.3 Value Reading
Data reading in this context is performed using the ReadBuffer() method of the
TFileStream class. Its syntax is:

void __fastcall ReadBuffer(void *Buffer, int Count);

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);

Chapter 7: File Processing Borland C++ Builder Programming

172 Copyright 2003 FunctionX, Inc.

edtFullName->Text = FullName;
edtDOB->Text = DateToStr(DOB);
cboGender->ItemIndex = StrToInt(Gender);
}
__finally
{
delete Streamer;
}
}
}
//---------------------------------------------------------------------------

7.5 Win32 File Processing

7.5.1 File Creation
In your VCL applications, even on console applications created on the Microsoft
Windows operating system, besides the C, the C++, and the VCL means of saving and
opening files, the Win32 library provides its mechanism of file processing.

In order to use a file, you must obtain a handle for it. You can obtain a file handle by
calling the CreateFile() function. Its syntax is:

HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);

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:

0: Information about a device (floppy, CD, DVD, hard disk, etc) will be
retrieved without a file being accessed
DELETE: The file can be deleted
READ_CONTROL: The read rights of the file can be accessed or read from
STANDARD_RIGHTS_EXECUTE: Same as READ_CONTROL
STANDARD_RIGHTS_READ: Same as READ_CONTROL
STANDARD_RIGHTS_WRITE: Same as READ_CONTROL
SYNCHRONIZE: The file can be synchronized
WRITE_DAC: The discretionary access control list (DACL) can be modified
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 173

WRITE_OWNER: The owner of the file can be changed
FILE_EXECUTE: The file can be executed. For example, if the file being
accessed is an application, it can be launched
FILE_READ_DATA: The data of the file can be read from
FILE_WRITE_DATA: Data can be written to the file
FILE_APPEND_DATA: data can be added to end of the file
FILE_READ_ATTRIBUTES: The attributes of the file can be accessed or
viewed
FILE_READ_EA: The extended attributes of the file can be read from
FILE_WRITE_ATTRIBUTES: The attributes of the file can be modified
FILE_WRITE_EA: The extended attributes of the file can be written to
STANDARD_RIGHTS_READ: The read rights of the file can be read
STANDARD_RIGHTS_WRITE: The write rights of the file can be modified

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 DELETE,
READ_CONTROL, WRITE_DAC, and WRITE_OWNER
STANDARD_RIGHTS_ALL: Combines DELETE, READ_CONTROL,
WRITE_DAC, WRITE_OWNER, and SYNCHRONIZE
FILE_ALL_ACCESS: The user will be able to perform any allowed operation
on the file
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

The dwShareMode argument controls how the file can be shared. Its possibles values are:
0: The file will not be shared
FILE_SHARE_DELETE: (Available only on Windows NT and 2000) The file
can be shared and accessed by more than one user. Also, a user can request to
delete it. If a user attempts to delete this file, his or her access rights will be
checked
FILE_SHARE_READ: The file can be accessed only if the user is allowed to
read from it
FILE_SHARE_WRITE: The file can be accessed only if the user is allowed to
write to it
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.

Chapter 7: File Processing Borland C++ Builder Programming

174 Copyright 2003 FunctionX, Inc.

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
TRUNCATE_EXISTING: If the file does not exist, the CreateFile() function
would fail. If the file already exists, it must be opened with a
GENERIC_WRITE dwDesiredAccess flag and subsequently, its size would be
reduced to 0 bytes
The dwFlagsAndAtributes argument specifies the attribute(s) to apply to the file. It can
have one or a combination of the following values:
FILE_ATTRIBUTE_ARCHIVE: The file will be marked as Archive
FILE_ATTRIBUTE_ENCRYPTED: The file will is encrypted
FILE_ATTRIBUTE_HIDDEN: The file will be marked as Hidden and
consequently cannot display in Windows Explorer, My Computer, or any file
viewing tool
FILE_ATTRIBUTE_NORMAL: The flag must be used by itself and means
that the file has ordinary attributes
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: The file will not be
indexed
FILE_ATTRIBUTE_OFFLINE: The file will be accessed offline
FILE_ATTRIBUTE_READONLY: The file will be marked as Read-Only.
Users can open and read its contents but cannot modify or delete it
FILE_ATTRIBUTE_SYSTEM: The file is part of the operating system
FILE_ATTRIBUTE_TEMPORARY: The file will be marked as a temporary
object
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.

Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 175

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.

Practical Learning: Processing File with Win32
1. 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

7.5.2 File Saving
As its name suggests, the CreateFile() function is used to create a stream. Actually, it
initiates a file object. After calling, since it returns a file handle, your next action is to
decide what to do with this handle and one of the actions you can take is to store a newly
created file into a drive.

To save a file in Win32, you can call the WriteFile() function. Of course, in order to save
a file, you must first retrieve the value to be saved. This value can be made of a single
variables or a complex class. Therefore, you must know what the value to save is made of
because it must be supplied to the WriteFile() function. The syntax of this file is:

BOOL WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);
Chapter 7: File Processing Borland C++ Builder Programming

176 Copyright 2003 FunctionX, Inc.


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.

Practical Learning: Saving a File
1. From the Dialogs tab of the Component Palette, click the SaveDialog button
and click on the form
2. Change its properties as follows:
DefaultExt: fst
Filter: Fast Food Files (*.fst)|*.fst|Text Files (*.txt)|*.txt|All Files
Title: Save Customer's Order
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;
}
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 177


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. Test the application with a customer order and save it
Chapter 7: File Processing Borland C++ Builder Programming

178 Copyright 2003 FunctionX, Inc.


5. Close the form and return to Bcb
6. Save All

7.5.3 File Opening
Besides saving a file, another operation you can perform on streams using the Win32
approach is to open an existing file. Reading a file is performed sing the ReadFile()
function. Its syntax is:

BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);

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.

Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 179

Practical Learning: Opening a File
1. Display the main form. From the Dialogs tab of the Component Palette, click the
OpenDialog button and click the form
2. Change its properties as follows:
DefaultExt: fst
Filter: Fast Food Files (*.fst)|*.fst|Text Files (*.txt)|*.txt|All Files
Title: Open a Previous Order

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 )
Chapter 7: File Processing Borland C++ Builder Programming

180 Copyright 2003 FunctionX, Inc.

{
ShowMessage("Invalid file size");
return;
}

bResult = ReadFile(hFile, &CustOrder, dFileSize, &dBytesRead, NULL);


if( bResult == FALSE )
{
ShowMessage("The file could not be read");
return;
}

edtClerk->Text = CustOrder.strClerk;
edtOrderDate->Text = CustOrder.dteOrderDate;
if( CustOrder.iBread == 0 )
rdoBun->Checked = True;
else if( CustOrder.iBread == 1 )
rdoRoll->Checked = True;

if( CustOrder.iMeat == 0 )
rdoBeefPatty->Checked = True;
else if( CustOrder.iMeat == 1 )
rdoGrilledChicken->Checked = True;
else if( CustOrder.iMeat == 2 )
rdoChickenBreast->Checked = True;

bLettuce = CustOrder.bLettuce;
bOnion = CustOrder.bOnion;
bTomato = CustOrder.bTomato;
bPickles = CustOrder.bPickles;

if( (bLettuce == False) &&
(bOnion == False) &&
(bTomato == False) &&
(bPickles == False) )
chkRegulars->State = cbUnchecked;
else if( (bLettuce == True) &&
(bOnion == True) &&
(bTomato == True) &&
(bPickles == True) )
chkRegulars->State = cbChecked;
else
chkRegulars->State = cbGrayed;

chkCheese->Checked = CustOrder.bCheese;
chkBacon->Checked = CustOrder.bBacon;
if( CustOrder.iIngredients == 0 )
rdoMayonnaise->Checked = True;
else if( CustOrder.iIngredients == 1 )
rdoKetchup->Checked = True;
else if( CustOrder.iIngredients == 2 )
rdoMustard->Checked = True;

chkSweetenerClick(Sender);
EvaluatePrice();
}
__finally
Borland C++ Builder Programming Chapter 7: File Processing

Copyright 2003 FunctionX, Inc. 181

{
if( hFile != INVALID_HANDLE_VALUE )
CloseHandle(hFile);
}
}
}
//---------------------------------------------------------------------------
4. Test the application and open the previous order saved to file
5. After using the form, close it and return to Bcb
6. Save All



Chapter 8: Strings Lists Borland C++ Builder Programming

182 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 183


Chapter 8: Strings Lists


8.1 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:

Chapter 8: Strings Lists Borland C++ Builder Programming

184 Copyright 2003 FunctionX, Inc.



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.

8.1.2 Usage of Lists
There are various reasons for using lists. Lists provide a uniform way of displaying a
group of similar items to the user. Depending on how it is configured, a certain list could
be used to display a simple list of items to the user. This could serve to provide static
information to the user. An example is the list of Fonts of MS Windows:

The Fonts window provides a static list that
allows the user to view and examine the list of
fonts installed on the local computer.
Although the user can be provided with
limited interaction with the list, the list cannot
be modified by, or receive input from, the
user.



Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 185

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 The TStrings Class

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.

Chapter 8: Strings Lists Borland C++ Builder Programming

186 Copyright 2003 FunctionX, Inc.

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.

8.2.2 Strings Addition and Insertion to a List
The operations performed using the TStrings class consist of creating a list of items,
inserting new ones, deleting others, counting the items, changing their positions, or
switching the items to another list. The primary operation you as the programmer will
perform on a new list is to fill it with the items the user can use. At design time, this is
usually easy to do because most list-based controls provide a dialog box used to create an
initial list. If you have to programmatically create the list, depending on the control, you
would be interested to know not only whether the item was added to the list but also what
position the item is occupying in the list. To create such a list, call the TStrings::Add()
method. Its syntax is:

int __fastcall Add(const AnsiString Source);

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
Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 187

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.

Chapter 8: Strings Lists Borland C++ Builder Programming

188 Copyright 2003 FunctionX, Inc.

8.2.3 String Removal From a List
Besides adding, appending, or inserting, you can delete an item from a list. This is
performed using the Delete() method whose syntax is:

void __fastcall Delete(int Index)


To delete an item, the Delete() methodError!
Bookmark not defined. needs to know its
position. Once again, the list is zero-based: the
1st item is at position 0, the 2nd at 1, etc. If
the Index argument points to an item that does
not exist, for example either you set it to a
value greater than the number of items or all
items have been deleted, nothing would
happen (no error would be thrown). In the
following example, the 3rd item of a combo
box is deleted:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnDeleteClick(TObject *Sender)
{
cbxNetwork->Items->Delete(2);
}
//---------------------------------------------------------------------------

8.2.4 Strings and Their Positions in a List
To (effectively) use the Delete() method, you should supply the position of the item you
want to delete. Sometimes such a position would be invalid. The best thing to do is to
first inquire whether the item you want to delete exists in the list. For this and many other
reasons, you can ask the compiler to check the existence of a certain item in a list. This
operation is performed using the IndexOf() method. Its syntax is:

int __fastcall IndexOf(const AnsiString Source);

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");
Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 189


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
Chapter 8: Strings Lists Borland C++ Builder Programming

190 Copyright 2003 FunctionX, Inc.

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):

Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 191



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)
Chapter 8: Strings Lists Borland C++ Builder Programming

192 Copyright 2003 FunctionX, Inc.

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:
Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 193

//---------------------------------------------------------------------------
void __fastcall TForm1::btnMoveClick(TObject *Sender)
{
CheckListBox1->Items->Move(1, 4);
}
//---------------------------------------------------------------------------



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);
}
//---------------------------------------------------------------------------




Chapter 8: Strings Lists Borland C++ Builder Programming

194 Copyright 2003 FunctionX, Inc.

8.2.5 Groups of Strings
Instead of adding one item at a time to a string, there are various techniques available for
filling out a list with all of the needed items. If you want to fill out a Memo or a RichEdit
controls with a file, which is just a list of strings, use the TStrings::LoadFromFile()
method. Its syntax is:

void __fastcall LoadFromFile(const AnsiString FileName);

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);

Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 195

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);

The above event could also be written as:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnTransferFileClick(TObject *Sender)
{
Memo2->Lines->Assign(Memo1->Lines);
}
//---------------------------------------------------------------------------

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);

Chapter 8: Strings Lists Borland C++ Builder Programming

196 Copyright 2003 FunctionX, Inc.

This function requires a TStrings list of strings. The
conversion is performed on two fronts. First, both lists
should have the same number of strings. If both lists have
different number of items, the function returns false. If
they have the same number of strings, then the compiler
examines the strings one line at a time, for each list. If two
strings of the same position are not the same, the function
returns false. This means that even if both lists have the
same strings, the comparison can still render negative.


Here is an example of implementing this method:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnCompareListsClick(TObject *Sender)
{
if( !ListBox1->Items->Equals(ListBox2->Items) )
ShowMessage("Both lists are not the same\n"
"You should synchronize them before exiting");
}
//---------------------------------------------------------------------------



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);

Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 197

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);
}
//---------------------------------------------------------------------------

This transfer of a list of strings from a ListBox, a ComboBox, a CheckListBox or a
RadioGroup controls to a text-based control such as a memo, a RichEdit or a label can
also effectively be accomplished using the Text property. When you ask it to use the Text
property to perform a transfer, the compiler scans the list of items. At the end of each
item, the compiler adds a carriage return (this is equivalent to the Enter key) and add the
next string to the next line. This allows the compiler to create an AnsiString object made
of all of the strings. Here is an example:



//---------------------------------------------------------------------------
void __fastcall TForm2::btnTransferClick(TObject *Sender)
{
Memo1->Lines->Add(ListBox1->Items->Text);
Edit1->Text = ListBox1->Items->Text;
Label1->Caption = ListBox1->Items->Text;
}
//---------------------------------------------------------------------------

Chapter 8: Strings Lists Borland C++ Builder Programming

198 Copyright 2003 FunctionX, Inc.

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 List of Strings and File Management

Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 199

8.3.1 Stream Saving
To provide better management of its strings especially as related to the controls that make
intensive use of strings, the TStrings class provides the ability to save a list of strings to a
portable medium. The primary means of saving a list of strings is performed using the
TStrings::SaveToFile() method. Its syntax is:

virtual void __fastcall SaveToFile(const AnsiString FileName);

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.

Practical Learning: Saving a List of Strings
1. 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. Execute the application and type a few lines:

4. On the toolbar, click the Save button and save the file with a recognizable name
5. Close the application and return to Bcb

8.3.2 Stream Opening
The TStrings class also allows you to retrieve a list of strings from file. This is made
possible using the TStrings::LoadFromFile(). Its syntax is:
Chapter 8: Strings Lists Borland C++ Builder Programming

200 Copyright 2003 FunctionX, Inc.


virtual void __fastcall LoadFromFile(const AnsiString FileName);

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.

Practical Learning: Saving a List of Strings
7. 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. Close the application and return to Bcb
10. Save All

8.4 The TStringList Class

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.

8.4.2 The String List Object
Because the TStringList class is not a property of any control, whenever you need it, you
must create it dynamically. And because it is not a control, you only need the new
Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 201

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
Chapter 8: Strings Lists Borland C++ Builder Programming

202 Copyright 2003 FunctionX, Inc.

{
__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.

8.4.3 How TStringList != TStrings
The fundamental difference between the TStrings and the TStringList classes is that,
besides the first being the parent of the second, you cannot create an instance of a
TStrings class. Using the properties of inheritance, you can perform any operation on a
TStringList variable that you would perform on a TStrings object.

Like a TStrings list, the primary means of filling out a TStringList object is by using
either the Add() or the Append() methods. The Append() method is only inherited. The
syntax of the TStringList::Add() method is:

int __fastcall Add(const AnsiString Source);

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.

8.4.4 List Creation and Management with TStringList
When it comes to creating a list, everything depends on what the list would be used for,
what you want the list to accomplish, how much, and what kind of interaction the users
would have with the list. As we saw already, there are three main categories of lists. The
easiest lists tend to be those that only display items; the only options to the user could be
to change how the list displays. The 2nd categories in difficulty are those the user would
use to perform selections; these types are common in (desktop) database applications.
Borland C++ Builder Programming Chapter 8: Strings Lists

Copyright 2003 FunctionX, Inc. 203

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.



Chapter 8: Strings Lists Borland C++ Builder Programming

204 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming

Copyright 2003 FunctionX, Inc. 205

Chapter 8: Strings Lists Borland C++ Builder Programming

206 Copyright 2003 FunctionX, Inc.



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.





Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 207


Chapter 9: The Graphical Device
Interface


9.1 Introduction to the GDI

9.1.1 The Device Context
A device context is an ensemble of the tools needed to draw lines, shapes, and other
graphics. It includes the platform you draw on, the dimensioning of the platform, the
orientation and other variations of your drawing, the tools you need to draw on the
platform, the colors, and various other accessories that can complete your imagination.
To provide support for drawing on the Windows operating system, Microsoft created the
Graphical Device Interface, abbreviated as GDI. It is a set of classes, functions, variables,
and constants that group all or most of everything you need to draw on an application.
The GDI is provided as a library called Gdi.dll and is already installed on your computer.
9.1.2 The Canvas
In a Win32 application, in order to draw, you must create a device context. This can be
taken care of by declaring a variable of type HDC. To keep track of the various drawings,
the device context uses a coordinate system that has its origin (0, 0) on the top-left corner
of the desktop:


Figure 3: Origin of the GDI coordinate system

Anything that is positioned on the screen is based on this origin. This coordinate system
can get the location of an object using a horizontal and a vertical measurement. The
horizontal measures are based on an x axis that moves from the origin to the right
direction. The vertical measures use a y axis that moves from the origin to the bottom
direction:
Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

208 Copyright 2003 FunctionX, Inc.


Figure 4: Axes of the GDI coordinate system
This means that, if you start drawing something such as a line, it would start on the origin
and continue where you want it to stop. To significantly simplify drawing and make it
compatible with the VCL, Borland created a class called TCanvas. TCanvas is based on
TPersistent, which is derived from TObject. To further make it easy to draw, every
control that needs drawing already has a TCanvas variable available. This means that
you usually will not have to declare a TCanvas variable before drawing.

In a VCL application, a canvas is the object on which you draw but the TCanvas class
actually encompasses everything that can be used to draw. This includes the platform and
the tools. Because TCanvas does not implement everything that is possibly available on
Win32 drawing, this class provides a handle that allows TCanvas to use an HDC
variable to.

9.2 Drawing Lines and Shapes

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);

Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 209

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);
}
//---------------------------------------------------------------------------

Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

210 Copyright 2003 FunctionX, Inc.



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] = Point(20, 50);
Pt[1] = Point(180, 50);
Pt[2] = Point(180, 20);
Pt[3] = Point(230, 70);
Pt[4] = Point(180, 120);
Pt[5] = Point(180, 90);
Pt[6] = Point(20, 90);

Canvas->Polyline(Pt, 7);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 211



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] = Point(20, 50);
Pt[1] = Point(180, 50);
Pt[2] = Point(180, 20);
Pt[3] = Point(230, 70);
Pt[4] = Point(180, 120);
Pt[5] = Point(180, 90);
Pt[6] = Point(20, 90);

HDC hDC = Canvas->Handle;
PolylineTo(hDC, Pt, 7);
}
//---------------------------------------------------------------------------



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
Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

212 Copyright 2003 FunctionX, Inc.

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] = Point(20, 50);
Pt[1] = Point(180, 50);
Pt[2] = Point(180, 20);
Pt[3] = Point(230, 70);
Pt[4] = Point(180, 120);
Pt[5] = Point(180, 90);
Pt[6] = Point(20, 90);

HDC hDC = Canvas->Handle;

Canvas->MoveTo(20, 30);
PolylineTo(hDC, Pt, 7);
Canvas->LineTo(20, 110);
}
//---------------------------------------------------------------------------



9.2.3 Multiple Polylines
The above polylines were used each as a single entity. That is, a polyline is a combination
of lines. If you want to draw various polylines in one step, you can use the Win32 API's
PolyPolyline() function. By definition, PolyPolyline() is used to draw a series of
polylines. Its syntax is:

BOOL PolyPolyline(HDC hdc, CONST POINT *lppt, CONST DWORD *lpdwPolyPoints,
DWORD cCount);

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
Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 213

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);
}
//---------------------------------------------------------------------------



Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

214 Copyright 2003 FunctionX, Inc.

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] = Point(20, 50);
Pt[1] = Point(180, 50);
Pt[2] = Point(180, 20);
Pt[3] = Point(230, 70);
Pt[4] = Point(180, 120);
Pt[5] = Point(180, 90);
Pt[6] = Point(20, 90);

Canvas->Polygon(Pt, 7);
}
//---------------------------------------------------------------------------



9.2.5 Multiple Polygons
If you want to draw a series of polygons, you can use the PolyPolygon() function whose
syntax is:

BOOL PolyPolygon(HDC hdc, CONST POINT *lpPoints,
CONST INT *lpPolyCounts, int nCount);

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
Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 215

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);
}
//---------------------------------------------------------------------------

Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

216 Copyright 2003 FunctionX, Inc.




9.2.6 Rectangles and Squares
A rectangle is a geometric figure made of four sides that compose four right angles. Like
the line, to draw a rectangle, you must define where it starts and where it ends. This can
be illustrated as follows:



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);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 217



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|.

Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

218 Copyright 2003 FunctionX, Inc.

9.2.7 A Rectangle With Edges
The Win32 library provides another function you can use to draw a rectangle. This time
you can control how the edges of the rectangle would be drawn. The function used is
called DrawEdge and its syntax is:

BOOL DrawEdge(HDC hdc, LPRECT qrc, UINT edge, UINT grfFlags);

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 Description
BDR_RAISEDINNER The interior edge will be raised
BDR_SUNKENINNER The interior edge will be sunken
BDR_RAISEDOUTER The exterior edge will be raised
BDR_SUNKENOUTER 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 Used For
EDGE_DUMP BDR_RAISEDOUTER | BDR_SUNKENINNER
EDGE_ETCHED BDR_SUNKENOUTER | BDR_RAISEDINNER
EDGE_RAISED BDR_RAISEDOUTER | BDR_RAISEDINNER
EDGE_SUNKEN BDR_SUNKENOUTER | BDR_SUNKENINNER

The grfFlags value specifies what edge(s) would be drawn. It can have one of the
following values:

Value Description
BF_RECT The entire rectangle will be drawn
BF_TOP Only the top side will be drawn
BF_LEFT Only the left side will be drawn
BF_BOTTOM Only the bottom side will be drawn
BF_RIGHT Only the right side will be drawn
BF_TOPLEFT
Both the top and the left sides will be
drawn
BF_BOTTOMLEFT
Both the bottom and the left sides will be
drawn
BF_TOPRIGHT
Both the top and the right sides will be
drawn
BF_BOTTOMRIGHT
Both the bottom and the right sides will be
drawn
BF_DIAGONAL_ENDBOTTOMLEFT
A diagonal line will be drawn from the top-
right to the bottom-left corners
BF_DIAGONAL_ENDBOTTOMRIGHT
A diagonal line will be drawn from the top-
left to the bottom-right corners
Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 219

BF_DIAGONAL_ENDTOPLEFT
A diagonal line will be drawn from the
bottom-right to the top-left corners
BF_DIAGONAL_ENDTOPRIGHT
A diagonal line will be drawn from the
bottom-left to the top-right corners

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);
}
//---------------------------------------------------------------------------



9.2.8 Ellipses and Circles
An ellipse is a closed continuous line whose points are positioned so that two points
exactly opposite each other have the exact same distant from a point called the center. It
can be illustrated as follows:



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);

Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

220 Copyright 2003 FunctionX, Inc.

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);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 221



A circle is an ellipse whose all points have the same distance with regards to a central
point.

9.2.9 Round Rectangles and Round Squares
A rectangle qualifies as round if its corners do not form straight angles but rounded
corners. It can be illustrated as follows:


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);
}
//---------------------------------------------------------------------------

Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

222 Copyright 2003 FunctionX, Inc.



A round square is a square whose corners are rounded.

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:
Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 223


//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Pie(40, 20, 226, 144, 155, 32, 202, 115);
}
//---------------------------------------------------------------------------



9.2.11Arcs
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:

Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

224 Copyright 2003 FunctionX, Inc.



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;

Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 225

TRect Recto(20, 20, 226, 144);
TPoint Pt1(202, 115);
TPoint Pt2(105, 32);

Canvas->MoveTo(207, 155);
ArcTo(hDC,
Recto.Left, Recto.Top, Recto.Width(), Recto.Height(),
Pt1.x, Pt1.y, Pt2.x, Pt2.y);
}
//---------------------------------------------------------------------------



9.2.12 The Arc's Direction
Here is and arc we drew earlier with a call to Arc():

//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Arc(20, 20, 226, 144, 202, 115, 105, 32);
}
//---------------------------------------------------------------------------



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:
Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

226 Copyright 2003 FunctionX, Inc.


int SetArcDirection(HDC hdc, int ArcDirection);

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 Orientation
AD_CLOCKWISE The figure is drawn clockwise
AD_COUNTERCLOCKWISE The figure is drawn counterclockwise

The default value of the direction is AD_COUNTERCLOCKWISE. Therefore, this
would be used if you do not specify a direction. Here is an example that uses the same
values as above with a different orientation:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
HDC hDC = Canvas->Handle;

SetArcDirection(hDC, AD_CLOCKWISE);

Canvas->Arc(20, 20, 226, 144, 202, 115, 105, 32);
Canvas->Arc(20, 20, 226, 144, 202, 115, 105, 32);
}
//---------------------------------------------------------------------------



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);
}
//---------------------------------------------------------------------------
Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 227




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);

This function returns the current arc direction as AD_CLOCKWISE or
AD_COUNTERCLOCKWISE.

9.2.13 Angular Arcs
You can (also) draw an arc using the AngleArc() function. Its syntax is:
Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

228 Copyright 2003 FunctionX, Inc.


BOOL AngleArc(HDC hdc, int X, int Y, DWORD dwRadius,
FLOAT eStartAngle, FLOAT eSweepAngle);

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:

Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 229



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);
}
//---------------------------------------------------------------------------


9.2.15 Bzier Curves
A bzier line is an arc that is strictly based on a set number of points instead of on an
ellipse. A bzier curve uses at least four points to draw on. A bzier line with four points
can be illustrated as follows:

Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

230 Copyright 2003 FunctionX, Inc.



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);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 231



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);
}
//---------------------------------------------------------------------------



PolyBezierTo(): The TCanvas::PolyBezier() method requires at least four points to
draw its curve. This is because it needs to know where to start drawing. Another way you
can control where the curve would start is by using the TCanvas::PolyBezierTo()
method. Its syntax is:

void __fastcall PolyBezierTo(const TPoint * Points, const int Points_Size);
Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

232 Copyright 2003 FunctionX, Inc.

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 Text Drawing Techniques

9.3.1 Text Outing
To write text on a canvas, you can call the TCanvas::TextOut() method. Its syntax is:

void __fastcall TextOut(int X, int Y, const AnsiString Text);

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");
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 9: The Graphical Device Interface

Copyright 2003 FunctionX, Inc. 233

9.3.2 Text Confined to a Rectangle
To create text that must only fit inside or a rectangle, you can call the
TCanvas::TextRect() methodError! Bookmark not defined.. Its syntax is:

void __fastcall TextRect(const Types::TRect &Rect, int X, int Y, const AnsiString Text);

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");
}
//---------------------------------------------------------------------------

9.3.3 The Dimensions of a Drawn String
Many drawing functions require you to know the dimensions of the string that is being
drawn or the string that needs to be drawn. To support this, the VCL provides various
functions. To know the width occupied by a string, you can call the
TCanvas::TextWidth() method. Its syntax is:

int __fastcall TextWidth(const AnsiString Text);

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.

Chapter 9: The Graphical Device Interface Borland C++ Builder Programming

234 Copyright 2003 FunctionX, Inc.

9.3.4 Text Drawing and Alignment
The Win32 library provides a function that can be used to draw text that is
proportionately centered with regards to either the width or the height of the canvas on
which it is positioned. The function used is DrawText() and its syntax is:

int DrawText(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat);

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.




Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 235


Chapter 10: GDI Accessories


10.1 Colors

10.1.1Overview
The color is one the most fundamental aspects used to enhance the aesthetic appearance
of an object. It is a non-spatial abstract that is added to an object to modify some of its
visual aspects. The Win32 library provides various functions to deal with colors.

To provide support for colors, the VCL is equipped with the TColor enumerator for the
actions you can use to take advantage of the various aspects of colors.

Three numeric values are used to create or specify a color. Each one of these values is 8
bits. The first number is called red. The second is called green. The third is called blue:

Bits
Red 7 6 5 4 3 2 1 0
Green 7 6 5 4 3 2 1 0
Blue 7 6 5 4 3 2 1 0

Converted to decimal, each one of these numbers would produce:

2
7
+ 2
6
+ 2
5
+ 2
4
+ 2
3
+ 2
2
+ 2
1
+ 2
0

= 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1
= 255

Therefore, each number can have a value that ranges from 0 to 255 in the decimal system.
These three numbers are combined to produce a single value as follows:

Converted to decimal, this number has a value of 255 * 255 * 255 = 16581375. This
means that we can have approximately 16 million colors available.

10.1.2 The Color as a Data Type
Microsoft Windows characterizes a color as a 32-bit long integer value. Therefore, a
color is actually a combination of 32 bits. The bits of the most significant byte (the left
byte) are reserved for the operating system's internal use and must be set to 0. Based on
this, each color is characterized by its combination of a red, a green, and a blue values.

23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Blue Green Red
Chapter 10: GDI Accessories Borland C++ Builder Programming

236 Copyright 2003 FunctionX, Inc.

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);
}
//---------------------------------------------------------------------------

Although the above number (1637623) is a legitimate color value, it is difficult to
identify and predict as its red, green, and blue values are not known. To create a color
value, the Win32 API provides the RGB macro. Its syntax is:

COLORREF RGB(BYTE byRed, BYTE byGreen, BYTE byBlue);

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
Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 237

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.

10.1.3 Color Decoding
Whether a color was initialized with a 32-bit long integer, the RGB macro, or a valid
color name, if you want to retrieve the red, green, and blue values of a color, you can use
the GetRValue(), the GetGValue(), and/or the GetBValue() macros to extract the value
of each. The syntaxes of these macros are:

BYTE GetRValue(DWORD rgb);
BYTE GetGValue(DWORD rgb);
BYTE GetBValue(DWORD rgb);

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.

10.1.4 Color Identification
When all three red, green, and blue numbers of a color have their lowest value, which is
0, the color is referred to black. When the numbers are at their highest value, which is
255, the color qualifies as white. To help with color naming, the VCL provides a list of
color identifiers in the graphics.hpp header file. These names can be used throughout
any VCL application where a color would be used. To see a list of these colors, on the
Object Inspector, click the Color (or any color-related) field and click the arrow of its
combo box. The names of colors start with cl.

There are two categories of color names you will use in your applications: those used or
configured by the operating system and those defined by the VCL. The colors whose
values are controlled by the operating system are set using the Appearance tab of the
Display program of Control Panel:

Chapter 10: GDI Accessories Borland C++ Builder Programming

238 Copyright 2003 FunctionX, Inc.



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 System Color - nIndex TColor Color Name
3D Objects Background COLOR_3DFACE clBtnFace
Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 239

COLOR_BTNFACE
3D Objects: Top and Left
Edges
COLOR_3DHILIGHT
COLOR_3DHIGHLIGHT
COLOR_BTNHILIGHT
COLOR_BTNHIGHLIGHT
clBtnHighlight
3D Objects: Right and
Bottom Edges
COLOR_3DDKSHADOW cl3DDkShadow
3D Effect of Buttons and
Dialog Boxes:
Top and left edges
COLOR_3DLIGHT cl3DLight
3D Effect of Buttons and
Dialog Boxes: Right and
bottom edges
COLOR_3DSHADOW
COLOR_BTNSHADOW
clBtnShadow
Background of Buttons and
Dialog Boxes
COLOR_BTNFACE clBtnFace
Text of Buttons COLOR_BTNTEXT clBtnText
General Text on Windows COLOR_WINDOWTEXT
Active Title Bar COLOR_ACTIVECAPTION clActiveCaption
Active Window Border COLOR_ACTIVEBORDER clActiveBorder
Inactive Window Border COLOR_INACTIVEBORDER clInactiveBorder
Application Background COLOR_BACKGROUND clBackground
Desktop General COLOR_DESKTOP clBackground
Desktop Background COLOR_BACKGROUND clBackground
Inactive Title Bar
Background
COLOR_INACTIVECAPTION clInactiveCaption
Inactive Title Bar Text COLOR_INACTIVECAPTIONTEXT clInactiveCaptionText
Background of MDI COLOR_APPWORKSPACE clAppWorkSpace
Menu Bar clMenuBar
Menu Background COLOR_MENU clMenu
Menu Text COLOR_MENUTEXT clMenuText
Menu Highlight clMenuHighlight
Scrollbar COLOR_SCROLLBAR clScrollBar
Selected Items Background COLOR_HIGHLIGHT clHighlight
Selected Items Text COLOR_HIGHLIGHTTEXT clHighlightText
ToolTip Background COLOR_INFOBK clInfoBk
ToolTip Text COLOR_INFOTEXT clInfoText
Window: Text on Caption COLOR_CAPTIONTEXT clCaptionText
Window Background COLOR_WINDOW clWindow
Window Frame COLOR_WINDOWFRAME clWindowFrame
Window Text COLOR_WINDOWTEXT clWindowText
Right Side of a Gradient
Active Window Title Bar
COLOR_GRADIENTACTIVECAPTION clGradientActiveCaption
Right Side of a Gradient
Inactive Window Title Bar
COLOR_GRADIENTINACTIVECAPTION clGradientInactiveCaption
Color of Hot-Track Item
(See Tree View)
COLOR_HOTLIGHT clHotLight

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
Chapter 10: GDI Accessories Borland C++ Builder Programming

240 Copyright 2003 FunctionX, Inc.

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.

10.1.5 Color Palettes
Device independence is the ability for an application to draw its intended figures, text,
shapes, and display colors regardless of the device on which the drawing is performed.
One way to take care of this is to manage colors at the operating system level so that
Microsoft Windows can select the right color to render an object or portion of it. In some
cases, a device, such as a monitor or a printer, may need to take care of the coloring
details of the job(s) it is asked to perform.

A color palette is a list of colors that a device can display. For example, one device may
be able to handle only two colors. Such is the case for a black and white printer. Another
device could be able to use more colors than that. To control this situation, Microsoft
Windows keeps track of the color palette of each device installed on the computer.

There are two types of color palettes. The default color palette is a list of colors that the
operating system would use on a device unless notified otherwise. There are typically 20
reserved colors as default. A logical palette is a palette that an application creates for a
specific device context.

10.2 Drawing with Colors

10.2.1Text Drawing with Colors
The text drawing functions we used or reviewed in the previous lesson have some limited
control over the text they are asked to draw. For example, they cannot specify or control
the color of their text. To exercise such control, you would need other TCanvas methods
or other Win32 functions.

When drawing on a device context, the TCanvas methods or Win32 functions consult the
current color that has previously been selected. By default, the selected color is black for
most operations, including drawing. If you want to use a different color, you must select
it first. To select a color to apply when drawing text, you can call the SetTextColor()
function. Its syntax is:

COLORREF SetTextColor(HDC hdc, COLORREF crColor);

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");
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 241

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.

10.2.2 Text Background Color
If you want to highlight the text, which is equivalent to changing its background, you can
call the SetBkColor() function. Its syntax is:

COLORREF SetBkColor(HDC hdc, COLORREF crColor);

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, "J ohnny 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:

OPAQUE: the background would be drawn using the crColor value
Chapter 10: GDI Accessories Borland C++ Builder Programming

242 Copyright 2003 FunctionX, Inc.

TRANSPARENT: the background would not be drawn

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
ETO_GLYPH_INDEX: The lpString string refers to an array of strings from the
GetCharacterPlacement() function
ETO_NUMERICSLATIN: The lpString value is a formatted using Latin language
digits
ETO_NUMERICSLOCAL: The lpString string uses Regional Settings rules to
format its value
ETO_OPAQUE: A function such as SetBkColor() would be used to fill the
rectangle
ETO_PDY: The lpDx parameter may contain pairs of values
ETO_RTLREADING: For Middle-East Windows versions that read text right to
left
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, "J ohnny Carson", 13, NULL);
}
Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 243

//---------------------------------------------------------------------------



10.3 The Color Dialog Box

10.3.1Description of the Color Dialog Box
To provide the selection of colors on Microsoft Windows applications, the operating
system provides a common dialog box appropriate for such tasks. The Color dialog box is
used by various reasons to let the user set or change a color of an object such as the
background color of a control or the color used to paint an object. When it displays, by
default, the dialog box appears as follows:


Figure 5: The Color Dialog Box

This displays a constant list of colors to the user. If none of the available colors is
appropriate for the task at hand, the user can click the Define Custom Colors button to
expand the dialog box:

Chapter 10: GDI Accessories Borland C++ Builder Programming

244 Copyright 2003 FunctionX, Inc.


Figure 6: The Expanded Dialog Box

The expanded Color dialog box allows the user to either select one of the preset colors or
to custom create a color by specifying its red, green, and blue values.

The user can change the color in four different areas. The top left section displays a list of
48 predefined colors. If the desired color is not in that section, the user can click and drag
the mouse in the multi-colored palette. The user can also drag the right bar that displays a
range based on the color of the palette; the user can scroll up and down by dragging the
arrow. For more precision, the user can type the Red, Green and Blue values. Ech uses a
integral value that ranges from 1 to 255.

10.3.2 Making a Color Dialog Box Available
To provide the Color dialog box to your application, from the Dialogs tab of the
Component Palette, you can click the ColorDialog button and click anywhere on
the form.

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.
Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 245


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:


Figure 7: Dialog Boxes - String List Editor

The most important method of the Color dialog is the Execute() member function. This
method occurs when the user clicks OK or presses Enter after selecting a color. You can
use it to get the selected color and use it as you see fit. The following example displays a
Color dialog when the user clicks a button on the form. When the user clicks OK on the
Color dialog, the selected color is applied to the background of the form:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ColorDialog1->Execute();
Color = ColorDialog1->Color;
Chapter 10: GDI Accessories Borland C++ Builder Programming

246 Copyright 2003 FunctionX, Inc.

}
//---------------------------------------------------------------------------

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;
}
//---------------------------------------------------------------------------

The TColorDialog constructor is used to dynamically create an instance of the
ColorDialog control at runtime in case you cannot design it. To do this, declare a
TColorDialog class in an event or function as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnCreateColorClick(TObject *Sender)
{
TColorDialog* Dlg = new TColorDialog(this);
}
//---------------------------------------------------------------------------

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:

Practical Learning: Allowing Color Changing
1. Start a new project with the default form
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
Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 247


4. Notice that the background color of the form has changed
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. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDblClick(TObject *Sender)
{
TColorDialog *DlgColor = new TColorDialog(this);

try {
if( DlgColor->Execute() )
Color = DlgColor->Color;
}
__finally
{
delete DlgColor;
}
}
//---------------------------------------------------------------------------
11. Test the application and double-click the form
12. Select a color and click OK
13. Close the form and return to Bcb

10.4 Fonts

Chapter 10: GDI Accessories Borland C++ Builder Programming

248 Copyright 2003 FunctionX, Inc.

10.4.1Introduction to Fonts
A font is a technique of representing symbols drawn on a device context. A font is
designed by an artist but usually follows a specific pattern. For example a font designed
to produce symbols readable in the English language must be designed by a set of
predetermined and agreed upon symbols. These English symbols are grouped in an entity
called the English alphabet. When designing such a font, the symbols created must
conform to that language. This also implies that one font can be significantly different
from another and a font is not necessarily a series of readable symbols.

Just like everything else in the computer, a font must have a name. To accommodate the
visual needs, a font is also designed to assume different sizes.

Before drawing text on a device context, a font must have been installed. Microsoft
Windows installs many fonts during setup. To handle its various assignments, the
operating system uses a particular font known as the System Font. This is the font used to
display the menu items and other labels for resources in applications. If you want to use a
different font to draw text in your application, you must select it.

The Win32 library makes fonts available through the HFONT handle. The VCL provides
font support through the TFont class.

10.4.2 Font Creation or Selection
When Windows starts, it creates and selects a font to use throughout your application. If
you do not like that font, you can select another. Selecting a font, as well as selecting any
other GDI object we will use from now on, is equivalent to specifying the characteristics
of a GDI object you want to use. To do this, you must first create the object, unless it
exists already.

To create a font, you can declare a TFont variable. If you want to use a temporary font in
an event, you can declare the variable locally. If you plan to refer to the same font object
in more than one event, you should declare it globally in the header file of the parent
object that will make it available to necessary controls.

After declaring a TFont variable, you must initialize it. This can be done by assigning the
desired values to its member variables. You do not have to specify a value for each
characteristic of the font. If you omit a property, its default value would be used.

To perform text drawing on a device context, the TCanvas class is equipped with a Font
member variable. This variable has a set of default values. For example, the color of text
is set to black. To specify different font characteristics, simply call the Font member
variable and initialize any of its own member variables as you see fit. In the same way, if
you first declare a TFont variable, initialize it, and want to use it in the device context,
simply assign it to the TCanvas::Font variable. This would be done as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
TFont *NewFont = new TFont;

Canvas->Font = NewFont;
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 249

You can also create a font using one of the many Win32 font related functions.

10.4.3 Font Properties
Although sometimes represented as if it were one entity object, a font can be a complex
concept made of various characteristics such as its width, weight, and size, etc. Therefore,
to make better use of fonts, you should be familiar with their appearance, especially if
you plan to perform any artistic text drawing.

The name of a font is the most commonly used characteristic. It is used by the operating
system and the application to identify it. The names of fonts installed on your computer
can be seen in the Fonts window accessible from Control Panel:



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.

Chapter 10: GDI Accessories Borland C++ Builder Programming

250 Copyright 2003 FunctionX, Inc.

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 Value Example
Bold fsBold This text is bold
Italic fsItalic Italicized section
Underline fsUnderline The words are underlined
Strikeout fsStrikeOut 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";
Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 251

Memo1->Font->Size = 10;
Memo1->Font->Color = clBlue;
Memo1->Font->Style = TFontStyles() << fsBold << fsUnderline
<< fsI talic << 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");
}
//---------------------------------------------------------------------------



10.4.4 Win32 Support of Fonts
As the main library of Windows applications, Win32 provides font support through
various functions. Most of these functions return an HFONT value. To use them, call any
of the Win32 functions and make sure you retrieve its return value. Then assign that
value to the Handle member variable of the TCanvas class.

If you have a TCanvas variable that can be used, convert the value of the height to
logical units. If you do not have this value, set it to NULL.

The Win32 functions that are related to fonts do not control the color applied when
drawing text on a device context. To provide color, you can call the SetTextColor()
function as we saw in the previous lesson.

One of the most complete means of creating a font is by using the CreateFont() function.
Its syntax is:

HFONT CreateFont(int nHeight,
int nWidth,
Chapter 10: GDI Accessories Borland C++ Builder Programming

252 Copyright 2003 FunctionX, Inc.

int nEscapement,
int nOrientation,
int fnWeight,
DWORD fdwItalic,
DWORD fdwUnderline,
DWORD fdwStrikeOut,
DWORD fdwCharSet,
DWORD fdwOutputPrecision,
DWORD fdwClipPrecision,
DWORD fdwQuality,
DWORD fdwPitchAndFamily,
LPCTSTR lpszFace);

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 Value Constant Value
FW_DONTCARE 0 FW_THIN 100
FW_EXTRALIGHT 200 FW_ULTRALIGHT 200
FW_LIGHT 300
FW_NORMAL 400 FW_REGULAR 400
FW_MEDIUM 500
FW_SEMIBOLD 600 FW_DEMIBOLD 600
FW_BOLD 700
FW_EXTRABOLD 800 FW_ULTRABOLD 800
FW_BLACK 900 FW_HEAVY 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.
Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 253


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 Description
FF_DECORATIVE Used for a decorative or fancy fonts
FF_DONTCARE Let the compiler specify
FF_MODERN Modern fonts that have a constant width
FF_ROMAN Serif fonts with variable width
FF_SCRIPT Script-like fonts
FF_SWISS Sans serif fonts with variable width

The lpszFace string is the name of the font used.

Once you have created a font, you can assign its return HFONT value to the handle of
the TCanvas::Font member variable and then use it as you see fit. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
HFONT font;

font = CreateFont(46, 28, 215, 0,
FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,
"Times New Roman");

Canvas->Font->Handle = font;

Canvas->TextOut(20, 128, "Euzhan Palcy");
}
//---------------------------------------------------------------------------

Chapter 10: GDI Accessories Borland C++ Builder Programming

254 Copyright 2003 FunctionX, Inc.



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
Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 255

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, "J ames Kolowski");
}
//---------------------------------------------------------------------------



10.4.5 Font Retrieval
At any specific time, a font is selected in the device context. This font could be the
default font set by the operating system, which is usually MS Sans Serif. You may have
changed it because of the requirements of your application. If you want to find out what
font is currently selected on the canvas, simple declare a TFont variable and initialize it
with the TCanvas::Font member variable. This could be done as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormDblClick(TObject *Sender)
{
TFont *CurFont = Canvas->Font;
}
//---------------------------------------------------------------------------

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.

10.4.6 Font Methods
The TFont class is equipped with a constructor that can be used to declare its variable.
Like every TObject descendent, a TFont variable must be declared using the new
operator. After using it, you can delete it using the delete operator.
Chapter 10: GDI Accessories Borland C++ Builder Programming

256 Copyright 2003 FunctionX, Inc.


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.

10.4.7 Font Messages and Events
On most graphical applications created in an environment such as C++ Builder, device
context objects come and go regularly to make the application less boring. Many, if not
most, of the device context objects we will use are derived from the TGraphicsObject
class. When a device object changes, the parent class fires the OnChange() event.
OnChange() is a TNotifyEvent type of event.


10.5 The Font Dialog Box

10.5.1Introduction
To support easy selection of font, Microsoft Windows provides the Font dialog box:

Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 257



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.
10.5.2 Allowing Font Formatting
VCL applications can provide the Font dialog box through the TFontDialog class. To
make it available, at design time, from the Dialogs tab of the Component Palette, you can
click FontDialog and click on the form.

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.
Chapter 10: GDI Accessories Borland C++ Builder Programming

258 Copyright 2003 FunctionX, Inc.


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:

Borland C++ Builder Programming Chapter 10: GDI Accessories

Copyright 2003 FunctionX, Inc. 259

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if( dlgFont->Execute() )
{
Memo1->Font->Name = dlgFont->Font->Name;
}
}
//---------------------------------------------------------------------------

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;
}
}
//---------------------------------------------------------------------------





Chapter 11: GDI Tools Borland C++ Builder Programming

260 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 261


Chapter 11: GDI Tools

11.1 Pens

11.1.1The Fundamentals of a Pen
In the previous lesson, we mentioned that, in order to draw, two primary objects are
needed: a platform and a tool. So far, we were using the platform, called a device context.
We introduced the main device context represented by, or accessed with, the TCanvas
class. To draw, we have been using a pointer to TCanvas. A TCanvas variable does not
just give us access to the device context, it also initializes it.

A pen is a tool used to draw lines and curves on a device context. In graphics
programming, a pen is also used to draw the borders of a geometric closed shape such as
a rectangle or a polygon. To make it an efficient tool, a pen must produce some
characteristics on the lines it is asked to draw. These characteristics can range from the
width of the line drawn to their colors, from the pattern applied to the level of visibility of
the lines. To manage these properties, Microsoft Windows considers two types of pens:
cosmetic and geometric.

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.

11.1.2 Creating and Selecting a Pen
As mentioned already, the device context is a combination of the platform on which the
drawing is performed and the necessary tools to draw on it. To make drawing quick,
when selecting a device context, which is already done for all objects that have a
TCanvas member variable, the device context is initialized with some default values.
One of these is a pen. This is why we have been able to draw shapes so far, without
realizing that the device context was already equipped with a pen for us. A pen is like any
other device context tool. It is equipped with characters that define its behavior and
control its role on the canvas.

The VCL supports pens through a class called TPen. When an object that has a TCanvas
member variable comes up, it is equipped with a pen already and you can use it as you
see fit. To use that pen, simply access the Pen member variable of the TCanvas class. If
you want to explicitly create a pen, you can declare a TPen variable using the new
operator. Whether using the TCanvas::Pen member variable or a TPen variable, once
you have a pen, you can change its characteristics through its own member variables.

Chapter 11: GDI Tools Borland C++ Builder Programming

262 Copyright 2003 FunctionX, Inc.

11.1.3 Win32 Support of Pens
The Win32 library defines a pen as HPEN, a handle to a pen. The Win32 library supports
pens through various functions. To make sure an HPEN can be used on a canvas, the
TCanvas class of the VCL is equipped with a Handle member variable. This variable is
used to get a handle to a Win32 HPEN and make it available to a VCL object.

To create a pen in Win32, you can call the CreatePen() function. Its syntax is:

HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);

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.

11.1.4 Characteristics of a Pen
The color of a pen is one of its most visual characteristics. To support colors, the TPen
class provides a Color property. Its value follows the same rules we reviewed for a color
object. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Pen->Color = clRed;
Canvas->MoveTo(20, 15);
Canvas->LineTo(255, 82);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 263



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 CreatePen() and LOGPEN Illustration Description
psSolid PS_SOLID

A continuous solid line
psDash PS_DASH

A continuous line with dashed
interruptions
psDot PS_DOT

A line with a dot interruption at
every other pixel
psDashDot PS_DASHDOT

A combination of alternating dashed
and dotted points
psDashDotDot PS_DASHDOTDOT

A combination of dash and double
dotted interruptions
psClear PS_NULL No visible line
psInsideFrame PS_INSIDEFRAME

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:

PS_COSMETIC: used to create a cosmetic pen
PS_GEOMTERIC: used to create a geometric pen

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)
{
Chapter 11: GDI Tools Borland C++ Builder Programming

264 Copyright 2003 FunctionX, Inc.

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);
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 265



11.1.5 Retrieving a Pen
If you want to know the currently selected pen used on a device context, simple declare a
pointer to TPen an initialize it with the TCanvas::Pen of the current Canvas.

11.2 Brushes

11.2.1Introduction
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.

11.2.2 Win32 Support of Brushes
Because there can be so many variations of brushes, the Win32 library provides various
functions for creating or managing brushes. Nevertheless, the Win32 API considers a
brush to be a handle to a brush and it is defined as HBRUSH. Each of the functions used
to create a brush returns HBRUSH.

Besides using a function to create a brush, the Win32 library provides the LOGBRUSH
structure that can be used to create a logical brush by specifying its characteristics.

The TBrush class is equipped with a Handle member variable. After using one of the
Win32 functions used to create a brush object, retrieve its HBRUSH value and initialize
the TBrush::Handle member variable with it. After this initialization, the brush is ready
to be used by the TCanvas class.

Chapter 11: GDI Tools Borland C++ Builder Programming

266 Copyright 2003 FunctionX, Inc.

11.2.3 Solid Brushes
A brush is referred to as solid if it is made of a color simply used to fill a closed shaped.
The TBrush class is equipped with a Color member variable. To create a solid brush,
simply assign a TColor value to the TBrush::Color variable. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Brush->Color = static_cast<TColor>(RGB(250, 25, 5));

Canvas->Rectangle(20, 20, 250, 125);

}
//---------------------------------------------------------------------------



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);

Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 267

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);
Chapter 11: GDI Tools Borland C++ Builder Programming

268 Copyright 2003 FunctionX, Inc.

Pt[2] = Point(155, 155);

Canvas->Brush->Color = clYellow;
Canvas->Polygon(Pt, 2);

// Right Triangle
Pt[0] = Point(170, 80);
Pt[1] = Point(170, 140);
Pt[2] = Point(230, 110);

Canvas->Brush->Color = clBlue;
Canvas->Polygon(Pt, 2);
}
//---------------------------------------------------------------------------



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.

11.2.4 Hatched Brushes
A hatched brush is one that uses a drawn pattern to regularly fill an area. Microsoft
Windows provides 6 preset patterns for such a brush.

To create a hatched brush, the TBrush class is equipped with the Style property. Style
can have the following values: bsSolid, bsClear, bsBDiagonal, bsFDiagonal, bsCross,
bsDiagCross, bsHorizontal, and bsVertical.

The Win32 API supports hatched brushes through the CreateHatchBrush() function. Its
syntax is:

HBRUSH CreateHatchBrush(int fnStyle, COLORREF clrref );

Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 269

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.

Practical Learning: Displaying Brush Hatches
1. Start a new Application with the default form
2. Change the forms Caption to Hatched Brushes
3. Access the OnPaint event of the form and implement it as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Brush->Color = static_cast<TColor>(RGB(0, 0, 255));
Canvas->Brush->Style = bsBDiagonal;
Canvas->RoundRect( 20, 30, 160, 80, 10, 10);

Canvas->Brush->Style = bsFDiagonal;
Canvas->Brush->Color = static_cast<TColor>(RGB(0, 128, 192));
Canvas->RoundRect(180, 30, 320, 80, 10, 10);

Canvas->Brush->Style = bsDiagCross;
Canvas->Brush->Color = static_cast<TColor>(RGB(0, 128, 0));
Canvas->RoundRect(340, 30, 480, 80, 10, 10);

Canvas->Brush->Style = bsVertical;
Canvas->Brush->Color = static_cast<TColor>(RGB(0, 128, 0));
Canvas->RoundRect(20, 120, 160, 170, 10, 10);

Canvas->Brush->Style = bsHorizontal;
Canvas->Brush->Color = static_cast<TColor>(RGB(255, 128, 0));
Canvas->RoundRect(180, 120, 320, 170, 10, 10);

Canvas->Brush->Style = bsCross;
Canvas->Brush->Color = static_cast<TColor>(RGB(200, 0, 0));
Canvas->RoundRect(340, 120, 480, 170, 10, 10);

Canvas->Font->Style = TFontStyles() << fsBold;
Canvas->Font->Color = static_cast<TColor>(RGB(0, 0, 255));
Canvas->TextOut(40, 10, "HS_BDIAGONAL");
Canvas->Font->Color = static_cast<TColor>(RGB(0, 128, 192));
Canvas->TextOut(205, 10, " bsBDiagonal");
Canvas->Font->Color = static_cast<TColor>(RGB(0, 128, 0));
Canvas->TextOut(355, 10, " bsDiagCross");
Canvas->Font->Color = static_cast<TColor>(RGB(255, 0, 255));
Canvas->TextOut(44, 100, " bsVertical");
Canvas->Font->Color = static_cast<TColor>(RGB(255, 128, 0));
Canvas->TextOut(195, 100, " bsHorizontal");
Canvas->Font->Color = static_cast<TColor>(RGB(200, 0, 0));
Canvas->TextOut(370, 100, " bsCross");
}
//---------------------------------------------------------------------------
4. Test the application
Chapter 11: GDI Tools Borland C++ Builder Programming

270 Copyright 2003 FunctionX, Inc.


5. Close it and return to Bcb

11.2.5 Logical Brushes
The Win32 library provides the LOGBRUSH structure to create a brush by specifying its
characteristics. The LOGBRUSH structure is defined as follows:

typedef struct tagLOGBRUSH {
UINT lbStyle;
COLORREF lbColor;
LONG lbHatch;
} LOGBRUSH, *PLOGBRUSH;

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);
}
Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 271

//---------------------------------------------------------------------------



11.3 Using Pens and Brushes: The Image Editor

11.3.1Introduction
C++ Builder ships with a graphic application called Image Editor. Image Editor is used to
create or manipulate small to medium, various types of, pictures needed in computer and
graphic applications. These graphics are divided in categories that have different roles.
Image Editor provides pens and brushes used to design its objects

11.3.2 Starting Image Editor
There are various ways you can launch Image Editor:

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
Image Editor is installed in the same location as C++ Builder. Therefore, in
Windows Explorer or My Computer, locate C:\Program
Files\Borland\Cbuilder6\Bin, and double-click imagedit or imagedit.exe.

Chapter 11: GDI Tools Borland C++ Builder Programming

272 Copyright 2003 FunctionX, Inc.



11.3.3 Using the Image Editor
When Image Editor appears, it is mainly made of four areas.

On top, there is the title bar that displays Image Editor and the main menu. The title bar
has the same classic look shared by Windows applications. Under the title bar, the menu,
here called the main menu, allows you to perform all regular operations of an application.
Image Editor is a Multiple Document Interface (MDI) application. This means that it
allows you to open or work on different child windows. By default, when Image Editor
starts, it does not create a new document. To create a graphic, you will have to let Image
Editor know what kind of graphic you want to work on. Once you open or start a new
document, the menu would change according to the type of graphic you are using.

To open an existing document, on the main menu, you can click File -> Open and
locate the desired document. To create a new document, on the main menu, you would
click File -> New and select the type of document you want.

The menu is used as on all other documents. For example, if you make a mistake on a
graphic and want to dismiss the last action, you can click Edit -> Undo or press Ctrl + Z.
In the same way, you can copy by clicking Edit -> Copy or pressing Ctrl + C. In other
words, most of the shortcuts you are familiar with are available.

On the left side of the application, the Tools Palette displays buttons that will be used to
create new graphics or manipulate existing ones

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 tools such as the Pencil, lines, and the geometric
shapes (rectangle, round rectangle, and ellipse) allow
you to specify a width for their lines.

Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 273

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 tools such as the Pencil, lines, and the geometric
shapes (rectangle, round rectangle, and ellipse) allow
you to specify a width for their lines.


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.
If you change the default line width of a tool, the selected width or
thickness stays in memory and can be applied to a tool that does not
obviously display this 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.
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)



Under the Color Palette, there is the Status Bar.

After using Image Editor, you can close it. You have a few alternatives:

On the main menu, you can click File -> Exit
The shortcut to close such an application as Image Editor is Alt + F4
To close Image Editor, you can press either Alt, f, x or Alt + X

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.1Introduction
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.
Chapter 11: GDI Tools Borland C++ Builder Programming

274 Copyright 2003 FunctionX, Inc.


Icons are used to represent folders in Windows Explorer and My Computer:



11.4.2 Creating Icons
To create an icon, on the main menu of Image Editor, you can click File -> New ->
Icon File (.ico). This would call the Icon Properties dialog box, which allows you to
specify the icon as a 16x16 or 32x32 pixel graphic. You can also design an icon that
consists of 2 or 16 colors. After creating and designing your icon you must save it. An
icon is a Windows file whose extension is .ico

Practical Learning: Creating Icons
1. Start Borland C++ Builder if necessary.
On the main menu, click Tools -> Image Editor
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. Press Ctrl + I three times to zoom
6. On the Tools Palette, click the Rectangle button
7. On the Color Palette, click the gray button (2nd column, 1st row)
8. On the drawing area, draw a rectangle as follows:
Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 275


9. On the Tools Palette, click the Fill button
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:
Chapter 11: GDI Tools Borland C++ Builder Programming

276 Copyright 2003 FunctionX, Inc.


19. On the Tool Palette, click the Line tool
20. Save the icon as Belgium in the Icons folder of our exercises and return to C++
Builder.
21. Create a new Application and change the forms caption to Applications Resources
22. Save the project in a New Folder named Applications Resources
23. Save the unit as Main and sanve the project as AppResources
24. To use the Belgium icon we have just created, on the main menu, click Project ->
Options...
25. In the Project Options dialog box, click the Application property page and click the
Load Icon button
26. Locate the Icons folder in which the Belgium icon was saved and display it in the
Look In combo box

27. Click Belgium and click Open
Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 277


28. On the Project Options, click OK
29. To execute your project, on the main menu, click Run -> Run
30. When the main form displays, notice that it uses the small (16x16) Belgium icon

31. After viewing the form, close it
32. Open Windows Explorer or My Documents and display the contents of the
Applications Resources folder. Display the content in Small Icons and Large Icons
views
Chapter 11: GDI Tools Borland C++ Builder Programming

278 Copyright 2003 FunctionX, Inc.




33. Notice that the executable file uses the the right icon for each display.
34. Return to Bcb
11.5 Cursors

11.5.1Introduction
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:
Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 279




11.5.2 Creating Cursors
Microsoft Windows installs a wide array of cursors for various occasions. Besides the
cursors provided by Windows, Borland C++ Builder installs additional cursors that can
accommodate even more scenarios. If these are still not enough, you can create your own
cursors. Using your own, custom cursors involves more steps than using bitmaps and
icons.

To create your own cursor, on the main menu of Image Editor, you can click File -> New
-> Cursor File (.cur). A starting but empty cursor would be displayed. After designing a
cursor, you must save it. It has an extension of .cur.

Essentially, a cursor uses only two colors, black or white. This is because a cursor is only
used as an indicator of the presence or position of the mouse pointer on the screen. Based
on this (limitation), you ought to be creative. The minimum you can give a cursor is a
shape. This can be a square, a rectangle, a circle, an ellipse, a triangle, or any shape of
your choice. You can make the cursor fully black by painting it with that color. If you
decide to make the cursor completely white, make sure you draw the borders of the
cursor. By playing with the frequency of pixels and varying the frequencies of black and
white, you can create variances of gray.

Between the black and white colors, two gray degrees are provided to you. In reality
these two colors are used to give a transparency to the cursor so the background can be
seen when the mouse passes over a section of the document.
Chapter 11: GDI Tools Borland C++ Builder Programming

280 Copyright 2003 FunctionX, Inc.


Practical Learning: Creating a Cursor
1. On the main menu of Image Editor, click File -> New -> Cursor File (.cur)
2. On the Tools Palette, click the Fill button and right-click the drawing area to
give it a white background
3. On the Tools Palette, click the Line tool
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:
Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 281


10. On the Tools Palette, click Fill
11. On the Color Palette, click the button with a red color and a green S
12. On the drawing area, click the right triangle
13. On the Color Palette, click the button with a green background and a red S
14. On the drawing area, click in the left triangle

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
Chapter 11: GDI Tools Borland C++ Builder Programming

282 Copyright 2003 FunctionX, Inc.


23. Click OK
24. To test the cursor, on the main menu, click Cursor -> Test
25. Draw various shapes

26. After previewing the cursor, click Close
27. Save the cursor as Push in the Cursors folder but do not close the cursor window

11.6 Other Techniques of Creating Icons and Cursors


11.6.1Icons and Cursors Design
Sometimes in your application, you will want to use the same picture for a bitmap, an
icon, and a cursor. Although each category has some predefined rules regarding its
design, you can still cleverly use a common design among them. This can be done by
simply copying one graphic from one category and pasting it into another category. All
you have to do is to adapt the pasted picture to the category you are designing. Of course,
there are some rules you will submit to.

If you want to use the same design for a bitmap and an icon, it must be designed with a
maximum width and height of 32 pixels. This means that you can use 16, 24, or 32 pixels
width and height and you must use a maximum of 16 colors. If you want to use the same
design for a bitmap, an icon, and a cursor, you should use a graphic that fits in 32 pixels
width and height. Although you can interchangeably copy and paste between a bitmap
and an icon, when pasting the same graphic into a cursor, keep in mind that you would be
restricted to 2 colors only.

Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 283

Practical Learning: Sharing Graphics


In the following exercise, the instructions are approximate and you should not take them
exactly at face value. As long as you draw a line that approximately resembles the
screenshot, you are fine. You do not have to have exactly the same result as the
corresponding screenshot.
1. On the main menu of Image Editor, click File -> New Cursor (.cur)
If necessary, press Ctrl + I a few times to zoom in
2. On the Tools Palette, click Curve
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:
Chapter 11: GDI Tools Borland C++ Builder Programming

284 Copyright 2003 FunctionX, Inc.


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:
Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 285


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
Chapter 11: GDI Tools Borland C++ Builder Programming

286 Copyright 2003 FunctionX, Inc.

25. On the Tools Palette, click Pencil
26. On the Color Palette, click the dark red (3rd column, 1st row)
27. In the drawing area, click all black pixels to change their color to dark red
28. Use the Fill tool and the dark Olive color (4th column, 1st row) the change the areas
on both sides of the middle line of the icon
29. Click the Line tool and select the white color
30. In the drawing area, without touching the dark red pixels, draw a checkered area on
the right side of the middle line:



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

11.6.2 Transforming an Icon or a Cursor
Besides creating an object from scratch or modifying an existing one we have seen in a
few examples so far, you can play with various pictures on your computer or from other
resources and get very creative bitmaps, icons, or cursors. This technique consists of
taking an object that, by default, in not a bitmap, icon, or cursor, and transforming it into
one.

Microsoft Windows installs a few fonts for its internal use and yours. Besides these fonts,
you can also purchase new ones. Some of these fonts have curious types of characters
you can use for your graphics objects. You can also find icons on the Internet and
transform them.

If you find a character of a font that you want to use as an icon or a cursor, you can select
it. You should be able to paste it into Image Editor but Image Editor does not faithfully
retrieve objects from the clipboard. Therefore, you should first paste it into Microsoft
Paint, copy it from Microsoft Paint, and then paste it into the type of graphic you want to
create in Image Editor. The only thing left to do is to customize the appearance of your
object.

Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 287

Practical Learning: Transforming Objects for Graphics
1. 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. Press Ctrl + A to select the symbol you have just typed
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. Using your mouse and the Select tool , select only the symbol you just pasted:
Chapter 11: GDI Tools Borland C++ Builder Programming

288 Copyright 2003 FunctionX, Inc.


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)
10. Accept the 32 x 32 size and click OK
11. If necessary, press Ctrl + I a few times to zoom enough
Press Ctrl + V to paste the picture of the keyboard
While the picture is still selected, drag and position it to leave three empty lines at
the bottom and one empty line on the right side:
12. Using the Tool and the Color Palettes, design the icon as follows:

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
Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 289

14. Copy the character from Paint to the clipboard
15. On the toolbar of Image Editor, click New. Accept the 16 x 16 size and click OK
16. Zoom in and Paste.
17. Design the icon as follows:

18. Save the icon as Keyword

11.7 Applications Resources

11.7.1Introduction
In the programming world, a resource is any external object that you can use to complete
your application. As you have seen so far, we had to use an external application to create
icons and cursors. For a regular application, a resource can be picture, a sound file, a
dialog box, a cursor, a menu, an icon, anything. Most of the time, when you need one of
these, first check if you can get it inside C++ Builder; that will be the case for all dialog
boxes and menus we will use in this book. Some other resources just have to be gotten
from another application. For example, although you can program a music application in
C++ Builder, you cannot create a music file using it; you would need an external
application.

There is no strict rule on what a resource is or is not, except that it is a file with an
extension. For a programming resource file, it (primarily) has an extension of rc dcr that
helps the compiler identify it. In order to use it in your application, the file has to be
compiled into a format that the compiler can understand. Fortunately, you can do this
compilation and include the file into your application from C++ Builder. You must first
create and gather the necessary resources, then make them available to your application.

11.7.2 Creating a Resource File
Although there are, and can be, various types of resources, here we will cover only icons
and cursors. A resource for an application can include icons, pictures (bitmaps), and
cursors. To create such a resource, on the main menu of Image Editor, you can click File
-> New... You can click either Component Resource File (.dcr) or Resource File (.res).
Once you have a resource file, you can add the objects by right-clicking, New, and
clicking the category of object you want to include.
Chapter 11: GDI Tools Borland C++ Builder Programming

290 Copyright 2003 FunctionX, Inc.


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.

Practical Learning: Creating and Using a Resource
1. In the main menu of Image Editor, click File -> New... -> Resource File (.res)
2. On the main menu, click Resource -> New -> Cursor
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. On the main menu, click Window -> Untitled1.res
9. In the child window, double-click POINTME and press Ctrl + V to paste.
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
Borland C++ Builder Programming Chapter 11: GDI Tools

Copyright 2003 FunctionX, Inc. 291


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

24. Press F12 to display the form
25. On the Component Palette, click Standard and click Panel
26. On the form, draw a rectangle from the top-left side to the middle-center of the form
27. In the Component Palette, click Memo and draw a rectangle on the right side
of the existing panel on the form
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");
Chapter 11: GDI Tools Borland C++ Builder Programming

292 Copyright 2003 FunctionX, Inc.

Screen->Cursors[WriteI tDown] = LoadCursor(HInstance, "SCRIPTER");
}
//---------------------------------------------------------------------------
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);
}
//---------------------------------------------------------------------------
32. To test your application, press F9



33. Close the project



Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 293


Chapter 12: Bitmaps


12.1 Bitmaps Fundamentals

12.1.1Introduction
A bitmap is a graphic object used to display a picture on a window or to store it in the
computer memory as a file. It is the primary type of graphic used for various occasions.
For example, a bitmap can be used as a background for a window. That is the case for the
Pinball game that ships with some versions of Microsoft Windows:



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:

Borland C++ Builder Programming

294 Copyright 2003 FunctionX, Inc.




Probably the most regular use of bitmaps is as small graphics on toolbars:


Figure 8: Bitmaps on a toolbar

There are three main ways you create or add a bitmap to your application. You can create
an array of byte values that describe the bitmap. You can design a bitmap using a low-
level bitmap application like Image Editor, or you can use a professional picture.

12.1.2 Bitmap Creation
There are three types of bitmaps we will be creating for our lessons. The simplest consists
of designing a regular picture made of regular colors from the Image Editor. Another
technique consists of declaring an array of bits that describes the bitmap; then translate
this array into a handle to bitmap before actually using it. The last technique, which
requires some design or importing, consists of using a more advance picture in an
application.

Creating a bitmap and making it available to an application is just one aspect. The goal is
to use such a bitmap or to decide what to use it for. Normally, the way you create a
bitmap has some influence on where and how that bitmap is used. For example, bitmaps
created in Image Editor using only its tools are appropriate to display on top of controls
that need small bitmaps. Those bitmaps and those created from an array of bits are also
the prime candidates to use as drawing brushes. Bitmaps that are professional looking
pictures and that tend to be taller or wider are usually used to display illustrative pictures.
In reality, any of the bitmaps can be used in any scenario of your choice.

12.1.3 Bitmap Design on Image Editor
Image Editor provides a good environment to create small bitmaps that would be used on
controls that need them. Such bitmaps usually have a size of 16x16 pixels, 32x32 pixels,
or a ratio of width and height appropriate for the control that needs the bitmap. We will
use bitmaps on bitmap buttons, toolbars, and list-based controls, etc.
Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 295


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

Practical Learning: Creating a Bitmap
1. 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:
Borland C++ Builder Programming

296 Copyright 2003 FunctionX, Inc.



8. Save it as Diamond1

12.1.4 Bitmap Creation: Windows Paint
Paint (or PaintBrush) is an application that gets installed with Microsoft Windows. It
provides a cheap solution to creating or maipulating bitmaps. It is a little more flexible
than Image Editor. For example, if you try opening a color-intensive picture in Image
Editor, you would receive an error:



The same picture can be opened in Paint:

Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 297



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).

12.2 The VCL Support of Bitmaps

12.2.1Introduction
The VCL provides support for bitmaps through the TBitmap class from the Graphics
namespace. Some classes, such as TBrush, already have a Bitmap member variable that
you can use initialize appropriately. In most cases, you will need to declare a pointer to
Borland C++ Builder Programming

298 Copyright 2003 FunctionX, Inc.

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.

12.2.2 Bitmap Drawing
Once the file is ready, you can use it in your application. For example, you can display it
on a form. Because the form is equipped with a canvas, to display a picture, you can call
the TCanvas::Draw() method. Its syntax is:

void __fastcall Draw(int X, int Y, TGraphic* Graphic);

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;
Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 299

}
}
//---------------------------------------------------------------------------

12.2.3 Bitmap Loading From a File
In order to use a bitmap in your application, you must import it from its file to the
application. The easiest way to do this consists of calling the
TGraphic::LoadFromFile() method. Its syntax is:

virtual void __fastcall LoadFromFile(const AnsiString FileName);

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:

Borland C++ Builder Programming

300 Copyright 2003 FunctionX, Inc.



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;
}
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 301



12.2.4 Bitmap Loading From a Resource File
Another technique you can use to open a bitmap consists of retrieving it from a resource
file. Before doing this, you must have a resource file with .res extension. You can directly
create a resource file from Image Editor but once again, you would have to deal with that
applications limitations. Alternatively, you can first create a Windows resource file that
has the .rc extension and create a header file that lists its resources. The header file is
used to specify a constant number ofr each resource. For this example, the header file can
be named resource.h. In the file, a constant can be defined as follows:

#define DEUXFILES 1000

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"
//---------------------------------------------------------------------------
Borland C++ Builder Programming

302 Copyright 2003 FunctionX, Inc.

#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;
}
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 303



12.2.5 Bitmap Loading From a Resource Identifier
One more alternative you have into preparing a picture to display in your application
consists of using an identifier from a resource file. Since the steps are exactly the same as
those used above, we will follow them in a Practical Learning session.

Practical Learning: Loading a Bitmap From Resource
1. Create a new project with its default form
2. Save the application in a new folder named Bitmaps1
3. Save the unit as Exercise and save the project as Bitmaps1
4. Change the forms caption to Picture Display
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. In the empty file, type #define FOODITEM 101
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
Borland C++ Builder Programming

304 Copyright 2003 FunctionX, Inc.

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:

15. Click Save
16. On the main menu, click Project -> Add to Project
17. In the Files of Type combo box, select Resource file (*.rc) and, in the list of files,
click ExoRes.rc

11. Click Open
12. While the ExoRes.rc tab is displaying, to compile it, on the main menu, click Project
-> Compile Unit
Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 305


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;
}
Borland C++ Builder Programming

306 Copyright 2003 FunctionX, Inc.

}
//---------------------------------------------------------------------------
16. Test the application

17. Close it and return to Bcb

12.2.6 Characteristics of Bitmaps
In most circumstances, in order to use a bitmap, you will need to declare a pointer to
TBitmap. Because the TBitmap class has only a default constructor, after declaring the
pointer, you will need to load a bitmap into it before using it. Once the variable contains a
bitmap, you can use it as you see fit. If there is no bitmap stored into it and you attempt to
use, you may receive an error or an unreliable result. If you want to check first whether
the variable contains a bitmap, you can check its Empty Boolean value. If the variable
contains a bitmap, this property would be true. Otherwise, it would be false.

Once a bitmap has been loaded, as a visible object, it has dimensions represented by a
width and a height. These are its Width and its Height properties. You can either retrieve
these values or change them. A bitmap also uses a set of colors known as its palette. To
get or set the characteristics of these colors, you can access the Palette property of the
TBitmap variable.

When a bitmap displays, it uses all allowable colors and occupies the whole area
allocated to its Width and Height values. If you want the bitmap to display only certain
parts, set its Transparent property to true.

When designing a picture that would display with transparency, you should use one color
to paint the areas that would not display. For example, the doughnut in the following
exercise was surrounded by a white color. When it displays with the Transparent
property set to true, the white area surrounding it is seen through.

So far, we have seen first how to declare a bitmap variable, second how to load it into an
application. Once such a bitmap is ready, you can use it in various ways as we saw that
Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 307

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.

Practical Learning: Using a Bitmap Properties
1. From the resources that accompany this book, copy doughnut1.bmp and
doughnut2.bmp. Then paste them in the folder of this project
2. Change the resource.h file as follows:
#define FOODITEM 101
#define DOUGHNUT1 102
#define DOUGHNUT2 103
3. Change the ExoRes.rc file as follows:
#include "resource.h"

FOODITEM BITMAP "food1.bmp"
DOUGHNUT1 BITMAP "doughnut1.bmp"
DOUGHNUT2 BITMAP "doughnut2.bmp"
4. Recompile the ExoRes.rc file to update the res file
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;
}
}
Borland C++ Builder Programming

308 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------
6. Test the application

7. Close it and return to Bcb
8. Save All

12.2.7 Pattern Brushes
A pattern brush is one that uses a bitmap instead of a plain color like the solid brush.
Therefore, to use a pattern brush, you must first create a bitmap. There are two main
options to do this. You can first load a bitmap from a file as we have done so far. Once
the bitmap is ready, assign its pointer to the Bitmap property of the Brush member
variable of the TCanvas class you are using.

Alternatively, to create a pattern brush, you can declare and initialize an array of BYTE
values that describe the bitmap.

Practical Learning: Using a Pattern Brush
1. Create a new project with its default form
2. Save it in a new folder named PatternBrush1
3. Save the unit as Exercise and the project PatBrush
4. Using Image Editor, create a 38 x38 pixels bitmap and design it as follows:
Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 309


5. Save it in the folder of the current project as Diamond
6. Create a 32x32 pixels icon and design it as follows:

7. Associate a 16x16 pixels icon to it and design it as follows:

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:
//---------------------------------------------------------------------------
Borland C++ Builder Programming

310 Copyright 2003 FunctionX, Inc.

void __fastcall TForm1::FormPaint(TObject *Sender)
{
Graphics::TBitmap *BmpDiamond = new Graphics::TBitmap;

BmpDiamond->LoadFromFile("Diamond.bmp");
Canvas->Pen->Color = clBlue;
Canvas->Brush->Bitmap = BmpDiamond;
Canvas->Rectangle(0, 0, 346, 190);
}
//---------------------------------------------------------------------------
11. Test the application

12. Close it and return to Bcb
13. Save All

12.3 Win32 Support for Bitmaps

12.3.1Introduction
The primary means of using a bitmap in your application is obviously through the
TBitmap class with its properties and methods. If the VCL does not natively provide the
bitmap functionality you are using for, you can use one of the Win32 functions. The
Win32 library supports bitmaps through various functions. Most of these functions return
a handle to BITMAP, implemented as HBITMAP. To use the return value of one of
these, the TBitmap class provides a Handle member variable particularly easy to use,
just all Handle member variables provided by the TWinControl class to its children.
Therefore, anything you would have done on the HBITMAP handle, to use it in your
application, simply assign its variable to the TCanvas::Handle and it is made ready.

A bitmap, like any other GDI object, must be created first, either as a resource file or by
definition. Once it exists, before using it, it must be selected into a device context.
Fortunately, the device context is already largely implemented in VCL objects through
the TCanvas class that is made a member variable of all classes that need a device
context.

Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 311

12.3.2 Bitmap Creation
The most basic bitmap is created from an array of constant values that describe the
bitmap. This type is created using the CreateBitmap() function. Its syntax is:

HBITMAP CreateBitmap(int nWidth, int nHeight, UINT cPlanes,
UINT cBitsPerPel, CONST VOID *lpvBits);

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);
}
//---------------------------------------------------------------------------


12.4 Image Lists

12.4.1Overview
An image list is an array of pictures of the same size. The pictures are created as a single
icon or bitmap and each icon or bitmap can be located using its index. The array is zero-
based, meaning that the first picture has an index of 0. The second has an index of 1, etc.

An image list is not a traditional control. It does not display to the user who in fact is
never aware of it. It is used to complement a control that needs a series of pictures for its
own display.

12.4.2 The Pictures of an Image List
The image list as a control does not create the necessary images for the list. It only stores
them for eventual retrieval. Therefore, before creating a list of images, you must first
Borland C++ Builder Programming

312 Copyright 2003 FunctionX, Inc.

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:




12.4.3 Image List Creation and Characteristics
The VCL provides support for image lists through the TImageList class. There are two
main ways you can create a list of images. If you are setting up a list for icons or small
Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 313

bitmaps that will be used by another control, you can use a friendly dialog box that
allows you to create the list. To do this, you would click the ImageList button from
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);
}
//---------------------------------------------------------------------------
Borland C++ Builder Programming

314 Copyright 2003 FunctionX, Inc.


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 Description
itImage The picture is single
itMask The picture uses a mask

Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 315

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.

12.4.4 Image List Methods
To actually create an image list, you must add icons or bitmaps to it. If you are using the
Image List Editor dialog box, this is visually done using the buttons on the dialog box. If
you are programmatically creating the list of images, you can add each icon or picture
individually if they were created as separate entities. You can also add a single long
bitmap that is made of various pictures.

To add a bitmap to an image list, call the Add() method. Its syntax is:

int __fastcall Add(Graphics::TBitmap* Image, Graphics::TBitmap* Mask);

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:

Borland C++ Builder Programming

316 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------
__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;
SingleI mage->Masked = True;
SingleImage->ImageType = itImage;
SingleImage->BkColor = clBlack;
SingleI mage->AddMasked(SingleBitmap, clBlack);

. . .

}
//---------------------------------------------------------------------------

To retrieve the bitmap stored at a specific position in the list, call the GetBitmap()
method. Its syntax is:

void __fastcall GetBitmap(int I ndex, 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];
Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 317


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);

MultiBitmaps[1] = new Graphics::TBitmap;
MultiBitmaps[1]->LoadFromFile("Picture2.bmp");
MultiImages->Width = MultiBitmaps[1]->Width;
MultiImages->Height = MultiBitmaps[1]->Height;
MultiImages->Add(MultiBitmaps[1], NULL);

MultiBitmaps[2] = new Graphics::TBitmap;
MultiBitmaps[2]->LoadFromFile("Picture3.bmp");
MultiImages->Width = MultiBitmaps[2]->Width;
MultiImages->Height = MultiBitmaps[2]->Height;
MultiImages->Add(MultiBitmaps[2], NULL);

MultiBitmaps[3] = new Graphics::TBitmap;
MultiBitmaps[3]->LoadFromFile("Picture4.bmp");
MultiImages->Width = MultiBitmaps[3]->Width;
MultiImages->Height = MultiBitmaps[3]->Height;
MultiImages->Add(MultiBitmaps[3], NULL);

MultiImages->Height = MultiBitmaps[0]->Height;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Image1Click(TObject *Sender)
{
static int ImgCounter = 0;

Graphics::TBitmap *Bmp = new Graphics::TBitmap;

if( ImgCounter <= SingleI mage->Count )
{
SingleImage->GetBitmap(ImgCounter++, Bmp);
Image1->Picture->Bitmap = Bmp;
}
else
ImgCounter = 0;
}
//---------------------------------------------------------------------------
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;
Borland C++ Builder Programming

318 Copyright 2003 FunctionX, Inc.

}
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 Image parameter is the icon that needs to be added.

Each of these methods (Add() and AddIcon()) returns the index of the bitmap or icon
that was added if the method succeeded. If it fails, it returns 1 indicating that the icon or
bitmap was not added.

Once the bitmaps or icons have been added, if you want to find out how many images are
in the list, get the TImageList::Count property.

If you want to remove a picture from the image list, call the TImageList::Delete()
method. Its syntax:

void __fastcall Delete(int Index);

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);

The Index value specifies the index bitmap to replace.

The Image parameter is the new bitmap. If the bitmap is masked, the second parameter,
Mask, specifies what bitmap will serve as mask.

If you want to replace an icon, call the ReplaceIcon() method.

Once an image list is ready, you can use it directly in an application or make it available
to a control that can use it. One way you can use an image list is to display one or more of
Borland C++ Builder Programming Chapter 12: Bitmaps

Copyright 2003 FunctionX, Inc. 319

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;
}
//---------------------------------------------------------------------------




Borland C++ Builder Programming

320 Copyright 2003 FunctionX, Inc.


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).



Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 321


Chapter 13: Characteristics of Child
Controls

13.1 Control Creation

13.1.1Introduction
The Win32 library defines a window as a rectangle object that displays on the screen. In
order to give presence to such an object, its window must be explicitly created. There are
two main categories of windows you will need to create for your application: parents and
children:
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.

13.1.2 Techniques of Creating Controls: Win32
As seen in the previous lesson, there are two main techniques of making a control
available to an application: The control can be visually added from the Component
Palette or it can be programmatically created. To create a control using the Win32
approach, you can call either the CreateWindow() or the CreateWindowEx() function.

Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

322 Copyright 2003 FunctionX, Inc.

13.1.3 Techniques of Creating Controls: VCL
If for any reason you cannot or would not visually add a control at design time, you can
create it programmatically. There are various techniques you can use. You can first
decide what class will be used as the basis of your control. Once you have the object you
want to use, declare a pointer to its class using the new operator. When dynamically
creating a control, you must specify its owner. The owner will be responsible hosting or
carrying the child control. The name of the owner is passed to the constructor of the
control. This would appear as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
TSomeControl *ctlShowMe = new TSomeControl (Mom);
}
//---------------------------------------------------------------------------

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;
}
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 323

//---------------------------------------------------------------------------

13.1.4 Techniques of Creating Controls: Class Derivation
If you are using a VCL control, you must know the name of the class on which the
control is based (and each control is based on a particular class). If you have examined
the types of classes available but none implements the behavior you need, you can first
locate one that is close to the behavior you need, then use it as a base to derive a new
class. To derive a class from an existing VCL control, you must at least declare a
constructor that will specify the owner of the control. An example would be:

//---------------------------------------------------------------------------
class TPlatform : public TPanel
{
public:
__fastcall TPlatform(TComponent *Owner);
};
//---------------------------------------------------------------------------

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).

13.2 Characteristics and Properties of Controls

13.2.1Introduction
There are three main categories of controls we will use in this book: existing VCL
controls, existing Win32 controls, and custom controls that are not inherently available
from the other categories. We will always prefer VCL controls because that is the subject
of this book. If a control cannot do what we want, which will hardly happen, then we can
use one of the Win32s. In extreme cases, we may have to create a new control.

Once again, to create a Win32 control, you can use either the CreateWindow() of the
CreateWindowEx() function. They are defined as follows:

HWND CreateWindow(
LPCTSTR lpClassName,
HWND CreateWindowEx(
DWORD dwExStyle,
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

324 Copyright 2003 FunctionX, Inc.

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
);

13.2.2 The Controls Handle
A handle is a numeric value used internally by Microsoft Windows to identify a control
or a class. Every control must have a handle. You have a choice of using it or not but the
compiler uses it.

As you can see, the Win32 library defines a window as a handle to a window object,
known as HWND. After the window has been created, the CreateWindow() or the
CreateWindowEx() function returns an HWND variable. You can use this return value
later on to access the created window, for example to change some of its characteristics.

To access the handle the handle to a control, when creating it, make sure you return its
HWND value. This would be done as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
HWND hWndButton = CreateWindow( . . . );
}
//---------------------------------------------------------------------------

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.

13.2.3 Control's Names
To create a control, the primary piece of information you must provide is its name. This
allows you and the compiler to know what control you are referring to when the program
is running. Specifying the name of a control may depend on the technique you decide to
use to create the control.

After adding a control to a form, it automatically receives a name. In the Object
Inspector, the controls name displays in the Name field. The newly added control
reflects the name of its button on the Component Palette and adds an incremental
number. For example, if you click the Edit button on the Component Palette and click the
form, the control would be named Edit1. If you add a second Edit control, it would be
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 325

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 Description
STATIC A static control can be used to display text, a drawing, or a
picture
EDIT As its name suggests, an edit control is used to display text to the
user, to request text from the user, or both
RichEdit 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
RICHEDIT_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
LISTBOX A list box is a control that displays items, such as text, arranged
so each item, displays on its own line
COMBOBOX 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
SCROLLBAR 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
BUTTON A button is an object that the user clicks to initiate an action
MDICLIENT 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", );
}
//---------------------------------------------------------------------------

Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

326 Copyright 2003 FunctionX, Inc.

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.

13.2.4 Controls Text and Caption
Some controls, as we will see, are text-based, meaning they are meant to display or
sometimes request text from the user. For such controls, this text is referred to as caption
while it is simply called text for some other controls. This property is not available for all
controls.

If a control displays text, it then has a Caption or a Text field in the Object Inspector.
After adding such a control to a form, its Caption or its Text field would hold the same
text as its name. At design time, to change the text of the control, click either its Caption
or its Text field and type the desired value. For most controls, there are no strict rules to
follow for this text. Therefore, it is your responsibility to type the right value.

The text provided in a Caption or Text fields of a text-based control can only be set as
is during design. If you want the text to change while the application is running, you can
format it. For example, such a control can display the current time or the name of the user
who is logged in. These format attributes cannot be set at deign time. To change the text
of a text-based control at run time, either assign a simple string or provide a formatted
string to either the Caption or the Text property. Here are two examples:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Panel1->Caption = "Let it Go";
Edit1->Text = Time();
}
//---------------------------------------------------------------------------

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", );
}
//---------------------------------------------------------------------------

13.2.5 Controls Hints and Tool Tips
A tool tip is a small yellowish box that displays on a control when the user rests the
mouse on such an object for a few seconds. A tool tip is a form of help that allows the
user to get a quick and brief characteristic about the control. In VCL applications, a tool
tip is called a Hint.

Hints in VCL applications can be applied on different levels, on all controls or on just
some of them. A hint can be made of just the text that displays when the user rests the
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 327

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)
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

328 Copyright 2003 FunctionX, Inc.

{
Screen->HintFont->Name = "Times New Roman";
Screen->HintFont->Size = 14;
}
//---------------------------------------------------------------------------



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.

13.2.6 Controls Styles: Childhood
A style is a characteristic that defines the appearance, and can set the behavior, of a
control. The styles are varied from one control to another although they share some of the
characteristics common to most Windows controls.

All of the controls you will create need to be hosted by another control. During design,
once you position a control on a form, it automatically gets the status of child. If you are
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 329

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");
}
//---------------------------------------------------------------------------

Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

330 Copyright 2003 FunctionX, Inc.

13.2.7 Controls Styles: Visibility
A control is referred to as visible if it can be visually located on the screen. A user can
use a control only if he or she can see it. As a programmer, you have the role of deciding
whether a control must be seen or not and when.

At design time, when you add a control to a parent, it is visible by default. This is
because its Visible property is set to true in the Object Inspector. In the same way, if you
programmatically create a control, by default, it is made visible once you specify its
owner and parent as the (main) form.

If you do not want a control to primarily appear when the form comes up, you can either
set its Visible property to false or set its parents visible property to false. Equivalently, at
run time, to hide a control, assign a false value to either its Visible property or its parents
Visible property. Remember that, as stated already, when a parent gets hidden, it also
hides its children. On the other hand, a parent can be made visible but hide one or some
of its children.

To check whether a control is visible at one time, apply a conditional statement (if or
while) to its Visible property and check whether its value is true or false. Alternatively,
to check whether a control is visible or not, check its Showing state. If the control is
visible, its Showing value would be true. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if( Panel1->Showing )
Panel1->Visible = False;
}
//---------------------------------------------------------------------------

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()
{
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 331

Form2->Visible = !Form2->Visible;
}
//---------------------------------------------------------------------------


To made a control visible when using either the CreateWindow() or the
CreateWindowEx() functions, add the WS_VISIBLE style to its dwStyle argument.

Some controls cannot be seen by the user. Examples are object from the Dialogs tab of
the Component Palette and some other controls such as the image list. These controls do
not have a Visible field in the Object Inspector.

13.2.8 Controls Styles: Availability
For the user to use a control, it must allow it. For example, if a control is supposed to
receive text, the user can enter characters in it only if this is made possible. To make a
control available to the user, the object must be enabled.

By default, after adding a control to a form, it is enabled and its Enabled property in the
Object Inspector is set to true. An enabled control displays its text or other characteristics
in their normal settings. If you want to disable a control, set its Enabled property to false.
In the following picture, the two top controls (the edit box and the button) are disabled:



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.

13.2.9 Tab Ordering
As we saw with control design, a user can navigate through controls using the Tab key.
When that key has been pressed, the focus moves from one control to the next. By their
designs, not all controls can receive focus and not all controls can participate in tab
navigation. Even controls that can receive focus must be explicitly included in the tab
sequence.

At design time, the participation to tab sequencing is controlled by the TabStop property
on the Object Inspector. Fortunately, every VCL control that can receive focus is already
configured to have this Boolean property set to true. If you want to remove a control from
this sequence, set its TabStop value to false.

If a control has the TabOrder property set to true, to arrange the navigation order of
controls, we saw that you can use the Edit Tab Order dialog box:
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

332 Copyright 2003 FunctionX, Inc.



Figure 9: Dialog Boxes - Edit Tab Order

Alternatively, at design time, you can click a control on the form. Then, on the object
Inspector, change the value of its TabOrder field. The value must be a positive short
integer.

If you are creating the control using either the CreateWindow() or the
CreateWindowEx() functions, to include it in a tab sequence, add the WS_TABSTOP
style.

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:


Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 333


Figure 10: The location of a control

When you click a control on the Component Palette and click its parent window, the Left
and Top values are set where the mouse landed. To change the location of a control, as
we saw in the previous lesson, you can click and drag it to the new desired location.
Alternatively, you can type the desired value in either the Left or the Top fields on the
Object Inspector. At design time, if you drag a control to move it, C++ Builder updates
the values of its location. If you save the project, the compiler would also save the
locations of all objects, including the form, so that the next time you run the application,
the compiler would remember to display the form where it was last positioned at design
time.


To programmatically move a control, which is equivalent to changing the values of the
Left or the Top properties at run time, assign the desired respective values. If you set a
negative value for the Left field, the left border of the control would be hidden. In the
same way, a negative Top value would hide the top border of the control. Make sure you
use valid integer values; otherwise you would receive an error when you compile the
project.

If you are using the CreateWindow() or the CreateWindowEx() functions, to set the left
distance, pass the desired value as the X argument. On the other hand, to specify the top
distance, pass the desired value for the Y argument.

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:
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

334 Copyright 2003 FunctionX, Inc.



Figure 11: The location and dimension of a control

If you click a controls button on the Component Palette and click its parent window, the
control assumes some default dimensions in the body of the parent. As we saw in the
previous lesson, to change the dimensions of a control, you can drag one of its borders or
corners. Alternatively, on the Object Inspector, you can type the desired values in either
or both the Height and the Width fields.

If the control has already been created, you can resize it at run time. To change the
dimensions programmatically, simply assign the desired value to the Height and/or
Width property of your choice. Here is an example that changes the width of the form:

//---------------------------------------------------------------------------
void __fastcall TForm1::ChangingFormWidth()
{
Width = 248;
}
//---------------------------------------------------------------------------

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)
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 335

{
HWND hWndButton = CreateWindow("Button", "Submit",
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
10, 10, 124, 25, );
}
//---------------------------------------------------------------------------

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 Bounding Rectangle of a Control
Because a controls location can be identified with two values, it can also be illustrated as
a geometric point on the coordinate system of the screen. A point is a pixel on the
monitor screen, on a form, or on any object of your application. A point is represented by
its coordinates with regard to the object that "owns" the point:


Figure 12: The Windows coordinate system

To identify the concept of a point, the Win32 library provides the POINT structure. This
structure is defined as follows:

typedef struct tagPOINT {
LONG x;
LONG y;
} POINT, *PPOINT;

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)
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

336 Copyright 2003 FunctionX, Inc.

{
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:

Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 337


Figure 13: Windows representation of a rectangle

We saw already that each object of your application, including a form, is visually defined
by its location and its dimensions on the screen for a form or on the object that owns it.
These two aspects are controlled by a geometric rectangle that defines the location and
the dimensions of the object.

In Win32, from the above right rectangle, the syntax of the RECT structure is defined as
follows:

typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT;

The RECT structure can be illustrated as follows:

Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

338 Copyright 2003 FunctionX, Inc.


Figure 14: The RECT Structure

Notice that the left and the top are the same as the Left and the Top values of a control
but the right and the bottom values are not the width and the height of a control. This is
not an anomaly. It was done like that on purpose.

You can use the Win32 RECT structure wherever you need to define a rectangle in your
VCL applications. Besides the RECT structure, the VCL provides its own class to
represent a rectangle. It is called TRect and it is defined as follows:

struct TRect

{
TRect() {}
TRect(const TPoint& TL, const TPoint& BR) { left=TL.x; top=TL.y;
right=BR.x; bottom=BR.y; }
TRect(int l, int t, int r, int b) { left=l; top=t; right=r; bottom=b; }
TRect(RECT& r)
{
left = r.left;
top = r.top;
right = r.right;
bottom = r.bottom;
}
int Width () const { return right - left; }
int Height() const { return bottom - top ; }

bool operator ==(const TRect& rc) const
{
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 339

return left == rc.left && top==rc.top &&
right == rc.right && bottom==rc.bottom;
}
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 };
};

The TRect class can be illustrated as follows:


Figure 15: The TRect class

It is important to notice that, to conform to the RECT structure, the TRect class provides
two constructors: TRect(int l, int t, int, r, int b) and TRect(RECT& r). The first is used
to simply reproduce the member variables of the RECT structure and the second is used
to create a copy of RECT. Once these two constructors have been defined making the
RECT member variables available, the class declares two new methods. The Width()
member function returns the distance from the left to the right borders of the rectangle.
The Height() method performs the equivalent calculation vertically.

One of the ways you can use the TRect class consists of declaring a rectangle using its
default constructor and then specifying a value for each on of its members. Here is an
example:
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

340 Copyright 2003 FunctionX, Inc.


//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TRect Recto = TRect;
Recto.Top = 100;
Recto.Left = 120;
Recto.Right = 420;
Recto.Bottom = 288;
}
//---------------------------------------------------------------------------

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);

Using the concept of a screen defined rectangle, a control is included in a rectangle
known as the BoundsRect property. Therefore, at any time, to get the location and the
dimensions of a control, call its BoundsRect property, which produces a TRect value.

Here is the final code of the Win32 control we were creating:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
CreateWindow("Button", "Submit",
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
10, 10, 124, 25,
Handle, NULL, HInstance, NULL);
}
//---------------------------------------------------------------------------

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.


13.3 Controls Methods

13.3.1Overview of Methods
As in C++, a method is a function created as a member of a class. Methods are used to
access or manipulate the characteristics of an object, a variable, or a pointer to a class.
There are mainly two categories of methods you will use in your classes:
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 341

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
o 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.

13.3.2 Windows Visibility
We saw that, in order to use a control, it must be visible. The visibility of a control can be
made by setting its Visible property to true. Alternatively, to display a window, you can
call the TControl::Show() method. Its syntax is:

void __fastcall Show();

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)
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

342 Copyright 2003 FunctionX, Inc.

{
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:



Here is another example of a button-based control whose
caption is surrounded with a dotted line.

A text-based control indicates that it has focus by
displaying a blinking cursor:



A list-based control indicates that it has
focus when one of its items has a
surrounding dotted rectangle.

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();

Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 343

13.4 Controls Messages and Events

13.4.1Overview
An application is made of various objects or controls. During the lifetime of the
application, its controls regularly send messages to the operating system to do something
on their behalf. These messages must be processed appropriately. Also, most of the time,
more than one application is running on the computer. The controls of such an
application also send messages to the operating system. As the operating system is
constantly asked to perform these assignments, because there can be so many requests
presented unpredictably, the operating system leaves it up to the controls to specify what
they want, when they want it, and what behavior or result they expect.

A message, like a letter you send to somebody, must provide a few pieces of information
in order to be processed. For a Win32 application, these pieces of information are stored
in a structure called MSG. It is defined as follows:

typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG;

For a Win32 application, each message is processed by a function called a window
procedure. A window procedure is a pointer to function, therefore declared as
CALLBACK, and it returns a positive 32-bit number. Therefore, a MSG variable must
be passed to the window procedure for processing.

In a Win32 application, a window procedure that processes a message requires 3 pieces
of information with the last piece divided in two (which produces 4 pieces of
information):

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;
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

344 Copyright 2003 FunctionX, Inc.

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
{
Word WParamLo;
Word WParamHi;
Word LParamLo;
Word LParamHi;
Word ResultLo;
Word ResultHi;

};
struct
{
int WParam;
int LParam;
int Result;
};

};
} ;

A TMessage message must provide two pieces of information, as the TMessage
structure shows: the name of the message and one group of information. Unlike the
Win32 MSG structure, in a VCL application, the object that sends the message is already
known (because it is the one that sent the message). The accompanying items of the
message are coded into either the top or the bottom structures. The two anonymous
structures inside the union indicate that you use either the top or the bottom structure but
not both.

Once a control has composed a message, it must send it to the right target, which could
be the operating system. In order to send a message, a control must create an event. The
control is also said to fire an event. To make a distinction between the two, a message's
name will usually be written in uppercase. An example would be WM_MOVE, which
stands for Window Message Move. The name of an event usually starts with On, which
indicates an action.

Remember, the message is what needs to be sent. The event is the action of sending the
message.

Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 345

13.4.2 A Map of Messages
As mentioned already, the messages of a Win32 application are processed by a window
procedure. For a VCL application, you can use the same approach. Alternatively, you can
use a technique referred to as creating a map of messages.

For the compiler to manage messages, they should be listed in a public section of the
class definition. The messages are included in a list referred to as a message map. The list
of messages, that is, the message map, starts with the BEGIN_MESSAGE_MAP and
ends with the END_MESSAGE_MAP macros. The END_MESSAGE_MAP macro
takes an argument, which is the original class that holds the primary implementation of
the messages.

Between the BEGIN_MESSAGE_MAP and the END_MESSAGE_MAP macros, each
message is declared using either the MESSAGE_HANDLER or the
VCL_MESSAGE_HANDLER macros. Their syntaxes are:

MESSAGE_HANDLER(VCLMessageName, TMessage, EventName);
VCL_MESSAGE_HANDLER(WindowsMsgName, VCLMessageName, EventName);

There are various categories of messages the operating system receives. Some of them
come from the keyboard, some from the mouse, and some others from various other
sources. For example, some messages are sent by the application itself while some other
messages are controlled by the operating system.

13.4.3 Messages Characteristics
The most commonly sent messages have already been coded in the objects of the VCL
controls so much that you will hardly need to define new messages, at least not in the
beginning of your C++ Builder programming adventure. Most of what you will do
consists of implementing the desired behavior when a particular message is sent.

Therefore, to start, you should know what messages are available, when, and how they
work. As mentioned already, each control sends its own messages when necessary. Based
on this, some messages are unique to some controls according to their roles. Some other
messages are common to various controls, as they tend to provide similar actions. To
manage such various configurations, the VCL considers the messages in two broad
categories. Those that take an argument and those that do not. The VCL defines events as
function pointers (or pointers to function).

As it happens, some messages do not require much information to be performed. For
example, suppose your heart sends a message to the arm and states, Raise your hand. In
this case, suppose everything is alright, the arm does not ask, how do I raise my hand?.
It simply does. This type of message would be sent without any accompanying
information. Consider another message where the arm carries some water and says to the
mouth, Swallow the following water. The mouth would need the water that needs to be
swallowed. Therefore, the message must be accompanied by additional information,
which is considered an argument. Consider one more message where the heart says to the
tongue, Taste the following food but do not swallow it. In order to process this
message, the tongue would need the food and something to indicate that the food must
not be swallowed. In this case, the message must be accompanied by two pieces of
information.

Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

346 Copyright 2003 FunctionX, Inc.

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);

The Sender argument is the control that is sending the messages.

Besides TNotifyEvent, the other events carry different and appropriate names.

13.4.4 Event Implementation
Although there are different ways you can implement an event, there are two main ways
you can initiate its coding. If the control has a default event and if you double-click it, the
compiler would initiate the default event. Another technique you can use is to click the
Events tab of the Object Inspector. This would display a list of the events associated with
the selected control:


The list is divided in two columns. The name of each event is
displayed on the left side. You can click the name of an event to
reveal a combo box. If a similar event has already been written, you
can click the arrow of the combo box and select it from the list:



Similar events are those that share a behavior. Otherwise, to initiate
an event double-click the field on the right column of the name of
the desired event.


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.

Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 347

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.


13.5 Keyboard Messages

13.5.1Introduction
A keyboard is a hardware object attached to the computer. By
default, it is used to enter recognizable symbols, letters, and other
characters on a control. Each key on the keyboard displays a
symbol, a letter, or a combination of those, to give an indication of
what the key could be used for.

The user typically presses a key, which sends a signal to a program.
The signal is analyzed to find its meaning. If the program or control that has focus is
equipped to deal with the signal, it may produce the expected result. If the program or
control cannot figure out what to do, it ignores the action.

Each key has a code that the operating system can recognize. This code is known as the
virtual key code and they are as follows:

Virtual Key Used for Virtual Key Used for
VK_F1 F1 VK_F2 F2
VK_F3 F3 VK_F4 F4
VK_F5 F5 VK_F6 F6
VK_F7 F7 VK_F8 F8
VK_F9 F9 VK_F10 F10
VK_F11 F11 VK_F12 F12
VK_SCROLL Scroll Lock VK_SNAPSHOT Prt Scrn (Depends on keyboard)
VK_PAUSE Pause/Break VK_TAB Tab
VK_BACK Backspace VK_CAPITAL Caps Lock
VK_SHIFT Shift VK_CONTROL Ctrl
VK_MENU Alt VK_ESCAPE Escape
VK_RETURN Enter VK_SPACE Space Bar
VK_INSERT Insert VK_HOME Home
VK_PRIOR Page Up VK_DELETE Delete
VK_END End VK_NEXT Page Down
VK_UP Up Arrow Key VK_RIGHT Right Arrow Key
VK_DOWN Down Arrow Key VK_LEFT Left Arrow Key
VK_LWIN Left Windows Key VK_RWIN Right Windows Key
VK_APPS Applications Key
The following keys apply to the Numeric Keypad
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

348 Copyright 2003 FunctionX, Inc.

VK_NUMLOCK Num Lock
VK_NUMPAD0 0 VK_NUMPAD1 1
VK_NUMPAD2 2 VK_NUMPAD3 3
VK_NUMPAD4 4 VK_NUMPAD5 5
VK_NUMPAD6 6 VK_NUMPAD7 7
VK_NUMPAD8 8 VK_NUMPAD9 9
VK_DIVIDE / VK_MULTIPLY *
VK_SUBTRACT - VK_ADD +
VK_SEPARATOR VK_DECIMAL .

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.

Practical Learning: Introducing Keyboard Messages
1. Start a new project with the default form
2. Save it in a new folder called Keyboard1
3. Save the Unit as Exercise and save the project as Keyboard

13.5.2 The Key Down Message
When a keyboard key is pressed, a message called WMKeyDown is sent. WMKeyDown
is a TKeyEvent message that produces the OnKeyDown event. Its syntax is:

void __fastcall OnKeyDown(System::TObject* Sender, Word &Key,
Classes::TShiftState Shift);

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 Description
ssShift One of the Shift keys was pressed
ssAlt One of the Alt keys was pressed
ssCtrl 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.

Practical Learning: Sending Key Down Messages
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:
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 349

//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
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':
// If the user press Ctrl + K
if( Shift.Contains(ssCtrl) )
ShowMessage("K was pressed");
break;
}
}
//---------------------------------------------------------------------------

3. Test the application and return to Bcb
13.5.3 The Key Up Message
As opposed to the key down message that is sent when a key is down, the WMKeyUp
message is sent when the user releases the key. Like WMKeyDown, WMKeyUp is a
TKeyEvent message. It produces the OnKeyUp event whose syntax is:

void __fastcall OnKeyUp(System::TObject* Sender, Word &Key,
Classes::TShiftState Shift);

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 + $

13.5.4 The Key Press Message
When the user presses a key, the WMKeyPress message is sent. Unlike the other two
keyboard messages, the key pressed for this event should (must) be a character key.
WMKeyPress produces the OnKeyPress event whose syntax is:

void __fastcall OnLKeyPress(System::TObject* Sender, char &Key);

The Key argument must be a letter or a recognizable symbol. Lowercase alphabetic
characters, digits, and the lower base characters such as ; , [ ] - = / are recognized as
they are. For an uppercase letter or an upper base symbols, the user must press Shift + the
key. The character would be identified as one entity. This means that the symbol % typed
with Shift + 5 is considered as one character.

Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

350 Copyright 2003 FunctionX, Inc.

Practical Learning: Sending Key Press Messages
1. 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;
}
}
//---------------------------------------------------------------------------

3. Test the application and press the Delete or the F1 keys. Notice that nothing happens
4. Press k

5. Click OK and press Shift + K
6. Close the application and return Bcb

Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 351

13.6 Mouse Messages

13.6.1Introduction
The mouse is another object that is attached to the computer allowing the user to interact
with the machine. The mouse and the keyboard can each accomplish some tasks that are
not normally available on the other and both can accomplish some tasks the same way.

The mouse is equipped with two, three, or more buttons. When a mouse has two buttons,
one is usually located on the left and the other is located on the right. When a mouse has
three buttons, one is in the middle of the other two. The mouse is used to select a point or
position on the screen. Once the user has located an item, which could also be an empty
space, a letter or a word, he or she would position the mouse pointer on it. To actually use
the mouse, the user would press the left, the middle (if any), or the right button. If the
user presses the left button once, this action is called Click. If the user presses the right
mouse button, the action is referred to as Right-Click. If the user presses the left button
twice and very fast, the action is called Double-Click.

Mouse events are implemented as TMouseEvent and TMouseMoveEvent function
pointers.

13.6.2 The Mouse Down Message
Imagine the user has located a position or an item on a document and presses one of the
mouse buttons. While the button is pressed and is down, a button-down message is sent.
The syntax of this event is as follows:

void __fastcall OnMouseDown(System::TObject* Sender, TMouseButton Button,
Classes::TShiftState Shift, int X, int Y);

The Button argument specifies what button was clicked. The buttons of the mouse are
identified by the TMouseButton enumerator whose members are:

Value Description
mbLeft The left mouse button was clicked
mbRight The right mouse button was clicked
mbMiddle 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 Description
ssShift One of the Shift keys was pressed
ssAlt One of the Alt keys was pressed
ssCtrl One of the Ctrl keys was pressed
ssLeft The left mouse button was held down
ssRight The right mouse button was held down
ssMiddle The middle mouse button was held down
ssDouble The Button was double-clicked

Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

352 Copyright 2003 FunctionX, Inc.

The X and the Y argument represent the TPoint(X, Y) point where the mouse was
clicked.

Practical Learning: Sending a Mouse Down Message
1. 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. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
ShowMessage("Mouse at (" + IntToStr(X) + ", " + IntToStr(Y) + ")");
}
//---------------------------------------------------------------------------
3. Test the program and click various parts of the form
4. Close it and return to Bcb

13.6.3 The Mouse-Up Message
After pressing a mouse button, the user usually releases it. While the button is being
released, a button-up message is sent and it depends on the button, left or right, that was
down. The event produced is OnMouseUp.

The OnMouseUp event uses the same syntax as the OnMouseDown event and processes
the same arguments.

13.6.4 The Mouse Move Message
Whenever the mouse is positioned and being moved on top of a control, a mouse event is
sent. This event is implemented as a TMouseMoveEvent function pointer and its syntax
is:

void __fastcall OnMouseMove(System::TObject* Sender,
Classes::TShiftState Shift, int X, int Y);

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.

13.7 Programmer Defined Messages

13.7.1Introduction
The list of methods and messages available for the objects used in your applications is
very impressive because the VCL tries to help process as many messages as possible.
Unfortunately, in some areas, the VCL tends to address only the most commonly used
messages, which is still very long, as we will see throughout this book.

Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 353

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.

13.7.2 Windows Functions
Another way you will manipulate controls consists of calling a Win32 API function.
There are two main categories of functions you will call: those that must identify the
control that is calling the function and those that do not. If the function requires the
control that called it, you can specify the control using its handle. If the function does not
need this piece of information, then you can omit it. We will see various types of both
categories of functions.

Most of the messages we will use in our applications are implemented in various classes
of the VCL. Some others are not. Some of the messages are available in the Win32
library and you can use them in your application. This is made possible by calling the
SendMessage() function. Its syntax is:

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

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);
}
//---------------------------------------------------------------------------

13.7.3 Prefoming Messages
Besides the SendMessage() function, the TControl class provides the Perform() method
that allows you to send a message any time. Its syntax is:

int __fastcall Perform(unsigned Msg, int WParam, int LParam);
Chapter 13: Characteristics of Child Controls Borland C++ Builder Programming

354 Copyright 2003 FunctionX, Inc.


The TControl::Perform() method functions like the SendMessage() function except
that, since it is a VCL function, it omits the handle to the control because the control that
calls it would be specified already. The first argument, Msg, is the identifier of the
message that needs to be processed. It is exactly like the second argument of the
SendMessage() function. Like the SendMessage() function, the values of WParam and
LParam depend on the message. Here is an example used to close a form:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Perform(WM_CLOSE, 0, 0);
}
//---------------------------------------------------------------------------

13.7.4 Custom Message Implementation
To customize an existing message of a control or to create a new message that is not
available for your control, the TControl class provides the WndProc() method. Its
syntax is:

virtual void __fastcall WndProc(Messages::TMessage &Message);

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);
}
//---------------------------------------------------------------------------
Borland C++ Builder Programming Chapter 13: Characteristics of Child Controls

Copyright 2003 FunctionX, Inc. 355


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;
}
//---------------------------------------------------------------------------

13.7.5 Methods and Messages Combinations
The events of Windows controls are designed to be executed at specific periods in
response to some occurrence. For example, only when the user presses a mouse button
can a message related to this action would occur. Yet, one of the most influential ways of
creating an effective program is to anticipate users actions as much as possible. This
allows you to correctly respond. To support this idea, the VCL provides a system of
combining controls messages and their associated events.

When the user initiates an action to send a message, the control that owns the action
sends the message and an accompanying method. This method, although belonging to the
control, allows other controls or other parts of the program to access the event as if they
controlled when such an event would occur. For example, if the user clicks a control, the
control composes and sends a click message. Because the control is also equipped with a
Click() method, another control of the application can call its Click() event and perform
the same action. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Perform(WM_CLOSE, 0, 0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Panel1MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
Button1->Click();
}
//---------------------------------------------------------------------------


Chapter 14: Parent Controls Borland C++ Builder Programming

356 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 14: Parent Controls

Copyright 2003 FunctionX, Inc. 357


Chapter 14: Parent Controls


14.1 Characteristics of Parent Controls

14.1.1The Windows Desktop
When you start the computer, the first object the operating system creates is the desktop
window. This is a wide rectangular area that covers the entire monitor screen. Based on
the settings of the Control Panel, the operating system uses either a color or a picture (a
bitmap) to fill the window. Once the desktop is ready, other objects or controls can be
placed on it.

To provide some information about the desktop, the VCL is equipped with the TScreen
class. This allows you to get such details as the screen dimensions (its width and height),
the form that is displaying on the screen, the type of keyboard being used, etc. To make
the screen information available to you any time, every application declares a TScreen
global variable so you do not have to declare your own.

Alternatively, to get access to the desktop, you can call the GetDesktopWindow()
function. Its syntax is:

HWND GetDesktopWindow(VOID);

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().

14.1.2 Applications Containers
A control is referred to as container if it can host other controls. While the desktop is the
biggest container of the computer, to develop your applications, you will use your own
controls that can act as parent to other controls. The VCL provides various objects that
can play this role. The list includes the form, the frame, the group box, the panel, the
scroll box, the control bar, the tab control, the page control, the status bar, the toolbar, the
cool bar, the page scroller, the tabbed notebook, etc. The list is impressive and these
parent controls are usually used for different purposes. In fact, the only real characteristic
they share is their ability to host other controls.

Except for the form (and consequently the dialog box), most, if not all, of the other
containers must be hosted by a form. Therefore, after starting a project or once you get a
form, you can place a control container on it (the form). Once placed on a form, these
parent windows can host their own controls. Therefore, to use a container, you can either
Chapter 14: Parent Controls Borland C++ Builder Programming

358 Copyright 2003 FunctionX, Inc.

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.
14.1.3 The Parents Location
As seen with child controls, except for the desktop window, every visual object either on
the screen or in your application must be located. The location of an object depends on its
parent and some other considerations.

The main object or frame of an application, which is usually the first form of a project,
when it appears on the screen, is located with regards to the screen. Such a form is
located on a Cartesian coordinate system whose origin is on the top-left corner of the
screen. The horizontal axis moves from the left border to the right. The vertical axis
moves from the top border down.

The distance from the left border of the screen to the form is the Left measurement. The
distance from the top border of the screen to the top border of the form is its left
measurement.

This can be illustrated as follows:



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
Borland C++ Builder Programming Chapter 14: Parent Controls

Copyright 2003 FunctionX, Inc. 359

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:


Figure 16: A control positioned in a parent window

In the same way, an object positioned on a parent can be seen only if its dimensions are
confined to the body of the parent. To manage the display of visible windows, each
parent provides a section called the Client Area. For the desktop screen, the client area is
the whole screen. For a form, the client area is the body of the form without the title bar.
Most other containers provide their whole body as the client area:

Chapter 14: Parent Controls Borland C++ Builder Programming

360 Copyright 2003 FunctionX, Inc.


Figure 17: The Client Area

14.2 Control Alignment and Constraint

14.2.1The Client Area
The desktop window provides a rectangular area that it can use to display the computers
applications. This area is also used to host other objects. Although an objects borders can
span beyond the borders of the desktop, only the area of an object covered by the desktop
can be seen. Here is an example of a window whose right side cannot be seen:


Figure 18: A section of a window that cannot be displayed on the screen
Borland C++ Builder Programming Chapter 14: Parent Controls

Copyright 2003 FunctionX, Inc. 361

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());
}
//---------------------------------------------------------------------------


Figure 19: Result - DesktopRect
The TScreen class also provides such aspects as the location and width of the desktop in
the forms of DesktopLeft, DesktopTop, DesktopWidth, and DesktopHeight.

The DesktopRect and the DesktopHeight provide the height of the desktop including the
task bar. If you want to know the actual area that is made available on the desktop, which
does not include the taskbar, use the TScreen::WorkAreaRect or the
TScreen::WorkAreaHeight respectively. Otherwise, you can also get the left, the top,
and width measures using the WorkAreaLeft, the WorkAreaTop, and the
WorkAreaWidth respectively. In reality, the WorkArea_ measures can be more useful if
your application takes advantage of more than one monitor. In this case the WorkArea_
measures provide information about the desktop of the primary monitor.

Besides the TScreen class, you can use the GetDesktopWindow() function to get a
handle to the desktop and find the dimensions of that window. Here is an example that
displays the screen resolution:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
Chapter 14: Parent Controls Borland C++ Builder Programming

362 Copyright 2003 FunctionX, Inc.

{
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.

14.2.2 Controls Alignment in the Client Area
Besides the Top, Left, and Position properties, you can also control the position of a
control in the client area of its container using the Align property. This property and its
values have the following effects:

Borland C++ Builder Programming Chapter 14: Parent Controls

Copyright 2003 FunctionX, Inc. 363

By default, the Align property is
set to alNone. In this case, the
control can assume any position
with regard to its host control



When the Align property is set
to alLeft, the control will be
fixed to the left border of its
host.


A control whose Align
property is set to alTop can
serve as a toolbar or a
floatable window on the top
section of its host.



Chapter 14: Parent Controls Borland C++ Builder Programming

364 Copyright 2003 FunctionX, Inc.


If the control has the Align
property set to alRight, the
control would be anchored to
the right border of its
container.


A control can be used as a status
bar that always displays on the
bottom section of the form. In
this case, its Align property
would be set to alBottom.




If a controls Align property is
set to alClient, it would occupy
the client area of the container.
If another control on the same
container already has one of the
previous alignments, except
alNone, the control whose
Align property is set to alClient
would occupy only the
remaining unoccupied area.


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.

Borland C++ Builder Programming Chapter 14: Parent Controls

Copyright 2003 FunctionX, Inc. 365

14.2.3 The Client Areas Constraints
The user of your application will have the ability to resize the form when using your
application. If the form is not a classic dialog box, by default, the user can resize it to any
dimensions allowed by the screen resolution; the minimum is to have the system buttons
and just a tiny part of the caption.

You can set the minimum height, the maximum height, the minimum width, and the
maximum width allowed on the control if the control gets resized. This property is
controlled by the Constraints property. The Constraints property is a TSizeConstraints
class, which is inherited from the TPersistent class. Although it is a class, you should
never declare it. The control that uses a Constraints property will create and initialize an
instance of the class. The MinWidth, MinHeight, MaxWidth, and MaxHeight are
integer values you can set using the keyboard. To set the constraints of a control that has
this property, click the + button of the Constraints field in the Object Inspector, click the
field desired and type an integer value. To programmatically control the constraints of a
control, call the TSizeConstraints::Property and type the desired value. In the
following example, when the user tries to resize the Form1 form, the minimum and
maximum dimensions of the form are already set and would not change:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
Form1->Constraints->MinHeight = 300;
Form1->Constraints->MinWidth = 500;
Form1->Constraints->MaxHeight = 400;
Form1->Constraints->MaxWidth = 565;
}
//---------------------------------------------------------------------------

14.2.4 Control Anchoring
If you position a (visual) control on a form, if the control is positioned on the top left
section of the form, when the user resizes the form, the controls position would appear
static, it would not move. This could be a concern if the control is positioned on the right,
the bottom or the lower right section of the form. When the user resizes the form, the
buttons position would not be updated. Sometimes you will want the control to have the
same location and/or distance with regard to the bottom, the right, and/or the lower right
corner of the form. This ability is controlled by the Anchors property.

The anchoring of a control to its parent or host container is controlled using a set property
derived from the TAnchors enumerator. By default, when you add a control to a form, it
is glued to the top left corner of its container. Since this property is a Set, you can set
the controls position with regards to its containers right and bottom borders. The
possible values of the Anchors property are:

Value The controls location will not change
akTop With regards to its parents top border
akLeft With regards to its parents left border
akRight With regards to its parents right border
akBottom With regards to its parents bottom border

Chapter 14: Parent Controls Borland C++ Builder Programming

366 Copyright 2003 FunctionX, Inc.

14.2.5 Child Controls and DragnDrop Operations
One of the most useful actions performed on a control consists of involving it in drag and
drop operations. Some controls can serve as the source or the destination on such an
operation. Drag and drop operations are performed using specific properties of child
controls associated with their parents.

The DockSite property uses a Boolean value to indicate that the control can serve as the
host for a dragndrop operation. The DragKind property specifies how the control
participates in the dragndrop operation. If the control serves as a docking host, you
should set its DragKind property to dkDock. By contrast, if the control will itself be
used when dragging, set its DragKind property to dkDrag.

If a control is draggable, you can use the DragMode property to specify how the
dragging operation on the control would be initiated. If you want the dragging operation
to be possible when the user clicks the control, set its DragMode property to
dmAutomatic. Otherwise, some controls, depending on the result you are trying to
achieve, should be available for dragging only depending on an intermediary action. In
such a case, you can write your program so that the dragging operation on a control
would be available only after the application or another event or control has called the
TControl::BeginDrag() method. In this case, you can set the DragMode property to
dmManual.




Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 367


Chapter 15: Forms and Dialog Boxes

15.1 Characteristics of Forms

15.1.1Introduction
The form is the primary object of a VCL application. It is rectangular window whose
main purpose is to carry, host, or hold other controls to allow the user to interact with the
computer. Although there are various types of parent containers we will use, the form is
the commonly and regularly used.

When C++ Builder starts, it creates a starting form and initializes an application. You can
use such as form as you see fit. If you need additional forms, you can create them
visually by clicking File -> New -> Form on the main menu. You can also click the New
Form button on the Standard toolbar. Alternatively, you can display the New Items dialog
box from where you would select the Form icon.

Another technique you can use is to create a form programmatically. To do this, declare a
pointer to the TForm class using the new operator. Like every other control, you must
specify the owner of the form that will be responsible for destroying it. This owner is
usually the form that is calling it
15.1.2 The System Icon
The top section of a form or a dialog box is called a title bar:


Figure 20: The Title Bar


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:

Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

368 Copyright 2003 FunctionX, Inc.


Figure 21: Dialog Boxes - Load Picture

After selecting the icon and clicking Open, the icon would be made available in the
Picture Editor dialog box:


Figure 22: Dialog Boxes - Picture Editor

After clicking OK, the new icon would be used as the system icon of the form:


Figure 23: The title bar with a custom icon

15.1.3 The System Menu
The icon used on the title bar possesses a menu called the system menu. This menu
displays if you click the icon:
Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 369



Figure 24: The System Menu

The system menu of the icon's title bar allows you to perform the common actions of a
regular Windows form. If you do not want to display the icon and its menu, refer to the
section on the BorderIcons below.

15.1.4 The Caption
On the right side of the system icon, there is long bar that is actually usually referred to as
the title bar. This section displays a word or a group of words known as the caption of the
form. By default, the title bar displays the name of the form. To customize the text
displayed on the title bar, on the Object Inspector, change the text of the Caption
property.

At design time, you can only set the caption as text. The caption can be any type of
string. At run time, you can control and programmatically display anything on the caption
of the form. It can consist of an expression or the result of a calculation. To change the
caption at run time, you can use a function, a method, or an event. Just type Caption =
and the sentence you want. For example, you can display todays date on the caption as
follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::ChangingFormCaption()
{
Caption = "Today is " + Date();
}
//---------------------------------------------------------------------------

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;
}
//---------------------------------------------------------------------------

Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

370 Copyright 2003 FunctionX, Inc.

15.1.5 The System Buttons
On the right side caption, there are three system buttons that allow minimizing ,
maximizing , restoring or closing the form as a window. The presence,
absence, or appearance of the system buttons is controlled by the BorderIcons property.
To control this property, you can click its + button to expand it to display its four possible
values: biSystemMenu, biMinimize, biMaximimize, and biHelp:


Figure 25: The BorderIcons property in the Object Inspector

The BorderIcons property is a (Pascal-style) set, which means it can use one or more
values in any allowed combination. Each member of the set can have a true or false
value. If you set the biSystemMenu property to true, regardless of the values of the other
fields of the BorderIcons property, the form would have neither the system icon (which
also makes the system menu unavailable) nor the system buttons. If a form does not have
a system close button, you should make sure the user has a way to close the form:

This dialog box does
not present system
buttons. The user
would not have any
inherent way to close
it unless the
programmer
explicitly presents a
button to close it,
which was taken care
of by the presence of
the OK and Cancel
buttons.


Figure 26: A window without the system buttons

Using a combination of the biSystemMenu, the biMinimize and the biMaximimize
properties, you can control the availability of the system buttons:

Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 371

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


Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

372 Copyright 2003 FunctionX, Inc.

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)
Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 373

{
BorderIcons = TBorderIcons() << biSystemMenu << biMinimize >> biMaximize;
}
//---------------------------------------------------------------------------

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.


15.1.6 Form and Dialog Box Positioning
One of the ways you can use the title bar is to move the form at design time. The location
of a form is set at design time because the VCL provides another property that follows
the directives of the design. At design time, you can position the form anywhere on your
screen and when you run the application, the form would appear following that position.
This is based on the fact that the position of the form is controlled by the TPosition
enumerator. The default position of the form is recognized as the way it was designed if
you do not change the Position property. The default value of the Position property is
poDesigned.

15.1.7 The Borders
One of the most visual aspects of an object's appearance is its border. A border is a line
that sets the visual limits of an object. Most objects, including a form, have four borders:
left, top, right, and bottom. In VCL applications, the borders of a form are controlled by a
property called BorderStyle. This is also the main property that specifies whether you
are creating a regular form or a dialog box.

To control the appearance of the borders of your form, on the Object Inspector, if you
click BorderStyle, you would see that it is a combo box property. The BorderStyle
property is an enumerator and you can choose one of its values needed for your form.

Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

374 Copyright 2003 FunctionX, Inc.

By default, a form is designed to be resizable using
the bsSizeable value. In this case the user can
change its width and its height by dragging one of its
borders or corners.




If you set the BorderStyle property to bsDialog, the
user will not be able to resize the dialog. This is the
default and the mostly used characteristics for a
dialog window. You should always use it if you
want the window to be a dialog box.


If you set the BorderStyle property to bsDialog and
have a Help file you want to use on the dialog box,
you can set the biHelp of the BorderIcons property
to true. This would display a Whats This button on
the title bar.


A BorderStyle set with bsSingle looks like one set
with the bsSizeable. The difference is that the user
cannot resize it.




A floating window is a form used to let the user
move it around while she is working on the main
form or dialog. This form is usually modeless,
which means the user does not have to close it to
continue working. If you want to create a floating
modeless window, you should set the forms
BorderStyle to bsSizeToolWin. The window has a
short title bar and it can be resized.

Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 375

Like the sizable tool window, a form with
bsToolWindow has a short title bar and is also a
prime candidate for a floating window. Unlike the
form with bsSizeToolWin, the user cannot resize a
bsToolWindow form.



A Form whose BorderStyle is set to bsNone has
neither a title bar nor borders.

To change a property programmatically, assign one of the above values to the
BorderStyle variable. Here is an example that would transform the form in a dialog box:

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
BorderStyle = bsDialog;
}
//---------------------------------------------------------------------------

15.1.8 The Window State of a Form
When a form appears as it was designed, it is said to be "normal". C++ Builder allows
you to have the form minimized or maximized when the application is launched. This
ability is controlled by the WindowState property. The default value of this property is
wsNormal, which means the form would appear in a normal fashion. If you want the form
to be minimized or maximized at startup, in the Object Inspector, select the desired value
for the WindowState property.

To control the windows state programmatically, simply assign the wsMaximized or the
wsMinimized value to the WindowState property. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
WindowState = wsMaximized;
}
//---------------------------------------------------------------------------

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)
Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

376 Copyright 2003 FunctionX, Inc.

{
if( WindowState == wsNormal )
WindowState = wsMaximized;
else if( WindowState == wsMaximized )
WindowState = wsMinimized;
else
WindowState = wsMaximized;
}
//---------------------------------------------------------------------------

15.1.9 The Body of a Form or a Dialog Box
The area that a form makes available to its control is its body. As the main container of
Windows controls, a form provides some particular properties to the controls it hosts. The
client area of a form is represented by the ClientRect property. This property can be used
to get the dimensions of the body of the form. Like the TControl::ClientRect property,
the TForm::ClientRect property provides the width and the eight of the client area,
omitting the location (left and top) which is set to the client origin (0, 0) located on the
top-left corner of the body of the form. Alternatively, to get the width of the client area of
the form, you can call the ClientWidth property. To get the height of the client area, use
the ClientHeight. The client aspects can be illustrated as follows:




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.

Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 377

15.2 Form Methods

15.2.1Form Creation
The form in implemented by the TForm class which itself is derived from
TCustomForm. Like every other control, the form is equipped with a constructor that
allows you to dynamically create it.

15.2.2 Form Closure
When the user has finished using a form, he or she must be able to close it. Closing a
form is made possible by a simple call to the Close() method. Its syntax is:

void __fastcall Close();

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.

15.3 Forms Messages and Events

15.3.1Form Creation
When an application made of a form is launched, the form must be created to display to
the user. At the form gets created, it initialize its controls. This is done before the form
can display to the screen. At this time the OnCreate() event fires. This is a
TNotifyEvent event, which means that it does not take any argument other than the
Sender, which is a TObject type.

OnCreate() is the default event of a form. This means that if you double-click the form,
the code of this event would be created and made ready for you.

Practical Learning: Creating a Form
1. Create a new project with its default form
2. Save it in a new folder named FormEvents
3. Save the unit as Exercise and save the project as FormEvents
4. To use the OnCreate() event , double-click the middle of the form
5. Implement the OnCreate event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
ShowMessage("The form has been created");
}
//---------------------------------------------------------------------------

6. Test the application. Close it and return to Bcb

Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

378 Copyright 2003 FunctionX, Inc.

15.3.2 Form Showing
After the form has been created, it must display to the user. If the application contains
more than one form, you can display it. To display a form, just like any control, we saw
that the TControl::Show() method must be called. If the application is being launched, it
would call this method to display the main form of the application. If you have more than
one form and you want to display the other form, you can call the TControl::Show()
method.

When the TControl::Show() method is called, the OnShow() event is fired. This allows
you to perform any last minute processing before the form can display.

Practical Learning: Showing a Form
11. On the Events tab of the Object Inspector, double-click the right field of OnShow
and implement the OnShow() event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
ShowMessage("The form is showing");
}
//---------------------------------------------------------------------------
12. Test the application. Then close it and return to Bcb

15.3.3 Form Activation and Deactivation
When two or more forms are running on the computer, only one can receive input from
the user, that is, only one can actually be directly used at one particular time. Such a
window has a title bar with the color identified in Control Panel as Active Window. The
other window(s), if any, display(s) its/their title bar with a color called Inactive Window:



Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 379

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.

Practical Learning: Activating a Form
1. 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. Execute the application to display the form. Then close it
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. Execute the application to display the form
5. While the form is displaying, open Notepad to deactivate the form
6. From the taskbar, click the button of our form to activate it
7. After experimenting with the form, close it and return to Bcb

15.3.4 Window Painting
Whether a form has just been created or it needs to be shown, the operating system must
display it on the screen. To do this, the form colors and other visual aspects must be
retrieved and restored. This is done by painting it (the form). If the window was hidden
somewhere such as behind another window or was minimized, when it comes up, the
operating system needs to paint it.

When a form gets painted, it fires the OnPaint() event. This event also is a
TNotifyEvent type.

Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

380 Copyright 2003 FunctionX, Inc.

Practical Learning: Painting the Form
1. 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. Test the application and return to Bcb

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.

Practical Learning: Resizing a Form
1. 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. Execute the application and return to Bcb

15.3.6 Form Closure
As mentioned above, the user can close the form after using it. When the form is being
closed, it fires the OnClose() event. This gives you the opportunity to perform any last
minute processing such as finding out some information about the application or the form
itself before the form is actually closed. The OnClose() event is a TCloseEvent function
pointer whose syntax is:

void __fastcall OnClose(TObject *Sender, TCloseAction &Action)

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 Description
Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 381

caNone The form must not be closed at all
caHide The form must be hidden but not closed
caFree The form must be closed and its memory freed
caMinimize The form must be minimized and not closed

Practical Learning: Resizing a Form
1. 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. Execute the application and return to Bcb

15.3.7 Form Destruction
Once the form has been closed, the operating system must destroy it and reclaim the
memory space it was using. This causes the OnDestroy() event to fire. OnDestroy() is a
TNotifyEvent. If you had dynamically created some controls using the forms
OnCreate() event, use the OnDestroy() event to destroy them.

15.4 Application of Forms

15.4.1Multiple Forms
When you start Borland C++ Builder, it creates a starting form for you. If one form is
not enough for your application, you can add as many as necessary. To add (or to create)
a (new) form:
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.

Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

382 Copyright 2003 FunctionX, Inc.

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.

Practical Learning: Using the Forms Events
1. Create a new project with its default form
2. On the Object Inspector, click Caption and type Rapid Application Development
3. Click Name and type frmMain
4. To add another form to your application, on the View toolbar, click the New Form
button
5. Click the Name field and change it to frmSecond
6. As the new form is still selected, double-click in its middle to initiate its OnCreate
event.
7. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmSecond::FormCreate(TObject *Sender)
{
Width = 405;
Height = 350;
Caption = "I know you called me!";
}
//---------------------------------------------------------------------------
8. To display the main form, on the main menu, click View -> Forms

9. On the View Form dialog box, click frmMain and click OK
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
Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 383

13. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::FormDblClick(TObject *Sender)
{
frmSecond->ShowModal();
}
//---------------------------------------------------------------------------
14. To test the program, press F9.
15. Move the form from its current position by dragging its title bar.
16. To call the second form, double-click in the middle of the form.
17. After viewing the form, close it. Also close the main form.
18. To add another form to your application, on the main menu, click File -> New ->
Form.
19. Change the name of the new form to frmFloater
20. On the Object Inspector, click the OnCreate name and double-click the event field.
21. Implement the event as follows:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit3.h"
// Include the dependent header file
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmFloating *frmFloating;
//---------------------------------------------------------------------------
__fastcall TfrmFloating::TfrmFloating(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfrmFloating::FormCreate(TObject *Sender)
{
// The appearance should be that of a floating window
BorderStyle = bsToolWindow;
// Keep this floating always on top
FormStyle = fsStayOnTop;
// Change the form's background color
Color = TColor(RGB(202, 238, 255));
// Make sure this window aligns with the top-left
// corner of the calling form
Top = frmSecond->Top;
Left = frmSecond->Left;
// Set the dimensions of the window
Height = 325;
Width = 124;
}
//---------------------------------------------------------------------------
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
Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

384 Copyright 2003 FunctionX, Inc.


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;
}
//---------------------------------------------------------------------------
29. To test the program, press F9
30. To display the second form, double-click the main form.
31. To display the floating form, click the second form. Notice that the floating window
appears.
32. Move the second form and try to position it on top of the floating window. Notice
that the floating window is always on top of the second window.
33. Click the second form again. Notice that the floating form reappears.
34. Click the second form again to display the floating window. Make sure the floating
window displays.
35. Close the second form and close the first form also
36. Display the second form.
37. On the Object Inspector, double-click the event field of OnClose.
38. Implement it as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmSecond::FormClose(TObject *Sender, TCloseAction &Action)
{
if( frmFloater->Visible )
frmFloater->Close();
}
//---------------------------------------------------------------------------
39. Press F9 to test the program.
40. Display the second form.
41. Click the second form to display the floating window.
Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 385

42. Close the second form. Notice that the floating window has been closed.
43. Close the main form

15.4.2 Dynamic Forms
Besides designing a form visually, sometimes this will not be possible. You may have to
create the form dynamically. To create a form dynamically, declare a pointer to a TForm
class. If you want to quickly use a form in a function or an event, you can create it easily.
There are two options you have.

If you want to create a duplicate of the form you already have, you can use it as the base
class and simply create an instance of the form using the new operator. When
dynamically creating a form, you must specify the object or form that "owns" the form. If
you are creating a duplicate of the form, set the owner as the current form or the this
pointer. The owner is provided to the constructor. For example, to create a duplicate form
using a forms OnDblClick event, you can implement the event as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormDblClick(TObject *Sender)
{
TForm1* Former = new TForm1(this);
}
//---------------------------------------------------------------------------

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
Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

386 Copyright 2003 FunctionX, Inc.

TForm* Comet; // A dynamically created form
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

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);
}
//---------------------------------------------------------------------------

15.4.3 Customizing Dynamic Forms
Whatever technique you use to create your form, you can set its properties using code.
The most basic thing you should do is to display it. This is done using the
TForm::Show() method. If the form was created locally, you must display it in the
function or event where it was created:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormDblClick(TObject *Sender)
{
TForm* Former = new TForm(this);
Former->Show();
}
//---------------------------------------------------------------------------

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)
{
Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 387

TForm1 *Cosmos = new TForm1(this);
Cosmos->ShowModal();

delete Cosmos;
Cosmos = NULL;
}
//---------------------------------------------------------------------------

Imagine you dynamically declare an object, such as a form, as follows:

//---------------------------------------------------------------------------
class TForm1 : public TForm
{
...
private: // User declarations
TForm* Fake;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------

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)
{
Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

388 Copyright 2003 FunctionX, Inc.

if( Fake )
{
delete Fake;
Fake = NULL;
}
}
//---------------------------------------------------------------------------

15.5 The Multiple Document Interface (MDI)

15.5.1Introduction to MDI-Based Applications
A multiple document interface, abbreviated MDI, is an application whose main form
directly "owns" other forms. The main form is also considered the parent. The forms that
the parent form owns can display only within the rectangular borders of the parent form.
The main idea behind an MDI is to allow the user to open different documents and work
on them without having to launch another instance of the application:

Corel Draw is an example of
an MDI. The user can create
one document, open another
without closing the previous
one, and create or open as
many documents as the
computer memory allows it



As opposed to an MDI, a Single Document Interface (SDI) allows only one document at
a time in the application.

Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 389


WordPad is an example of an SDI. The
user can open only one document at a
time. If he wants another WordPad
document, he must open an additional
instance of WordPad.


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.

15.5.2 MDI Creation
Creating an MDI appication is not the most difficult assignment you will perform in C++
Builder. Any regular form can be made into an MDI. This is simply taken care of by
setting its FormStyle property to fsMDIForm and that's it. A form whose style is set as
MDI has a sunken client area that indicates that it can host other form:


Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

390 Copyright 2003 FunctionX, Inc.


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.

15.6 Dialog Boxes

15.6.1Introduction
A dialog box is a form with particular properties. Like a form, a dialog box is referred to
as a container. It is the primary interface of user interaction with the computer. By itself,
a dialog box means nothing. The controls it hosts accomplish the role of dialog between
the user and the machine. Here is an example of a dialog box:


Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 391


A dialog box has the following characteristics:
It is equipped with the system Close button . As the only system button, this
button allows the user to dismiss the dialog and ignore whatever the user would
have done on the dialog box.
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.

15.6.2 Dialog Box Creation
There are two main actions you can perform on a form to qualify it as a dialog box; but
normally, these are only suggestions, not rules. Based on the Microsoft Windows design
and standards, to create a dialog box:
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 and click the dialog box.
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.

Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

392 Copyright 2003 FunctionX, Inc.

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.

15.6.3 Modal Dialog Boxes
There are two types of dialog boxes: modal and modeless. A Modal dialog box is one that
the user must first close in order to have access to any other framed window or dialog
box of the same application.

One of the scenarios in which you use a dialog box is to create an application that is
centered around one. In this case, if either there is no other form or dialog box in your
application or all the other forms or dialog boxes depend on this central dialog box, it
must be created as modal. Such an application is referred to as dialog-based.

Some applications require various dialog boxes to complete their functionality. When in
case, you may need to call one dialog box from another and display it as modal. Here is
an example:


The Paragraph dialog box of
WordPad is a modal dialog box:
when it is displaying, the user
cannot use any other part of
WordPad unless he or she
closes this object first


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.

15.6.4 Modeless Dialog Boxes
A dialog box is referred to as modeless if the user does not have to close it in order to
continue using the application that owns the dialog box:

Borland C++ Builder Programming Chapter 15: Forms and Dialog Boxes

Copyright 2003 FunctionX, Inc. 393

The Find (and the Replace)
dialog box of WordPad (also
the Find and the Replace
dialog boxes of most
applications) is an example of
a modeless dialog box. If it is
opened, the user does not have
to close it in order to use the
application or the document in
the background.



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

15.6.5 C++ Builder Template Dialog Boxes
To make your development experience a little faster, C++ Builder ships with a lot of
dialog boxes ready for use. These dialog boxes are created as modal and are equipped
with an OK and a Cancel buttons. These dialogs are available on the Dialogs property
page of the New Items dialog box.

To use a C++ Builder dialog template, on the Standard toolbar, you can click the New
button . In the New Items dialog box, click the Dialogs tab, select the desired
template and click OK. Most dialog boxes are equipped with a bevel for aesthetic
purposes and two or three buttons. The OK buttons are configured with the Default
Chapter 15: Forms and Dialog Boxes Borland C++ Builder Programming

394 Copyright 2003 FunctionX, Inc.

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.

Practical Learning: Using a Template Dialog Box
1. Start a new project with the default form
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. After using the application, close it and return to Bcb

Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 395


Chapter 16: Controls Containers

16.1 The Form
The form, implying the dialog box, is the primary object used to host, hold, or carry other
controls of the application. Since we have had a thorough review of forms and dialog
boxes already, we will not spend any more time on them.
16.2 The Frame

16.2.1Introduction
A frame is a type of control container that resembles a form. Like a form, when you
create a frame, it possesses its own unit where its children can be programmatically
managed. Unlike a form, and like all the other containers we will review after this one
except the data module, a frame should be embedded on a form that would act as its
ultimate parent. Unlike most other containers except for the data module, users do not see
a frame and are not aware of its presence. It is used only by the programmer.

A frame is used for better management of controls because, like a form, a frame is
created as a separate entity with a body independent of a form.

Practical Learning: Introducing Frames
1. Start a new project with its default form
2. Save it in a new folder named InterestAndDiscount1
3. Save the unit as Exercise and save the project as InterestDiscount
4. Set its properties as follows
Caption: Interest and Discount
BorderStyle: bsDialog
Name: frmMain
ShowHint: true
5. Save All

16.2.2 Frame Creation
There are two general steps to making a frame available to your application
1. 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
Chapter 16: Controls Containers Borland C++ Builder Programming

396 Copyright 2003 FunctionX, Inc.

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;
}
//---------------------------------------------------------------------------

A frame control is based on the TFrame class which is in turn based on
TCustomtFrame.

Practical Learning: Using Frames
1. On the main menu, click File -> New -> Frame
2. Change the Name of the frame to fraSimpleInterest
3. Design the frame as follows:
Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 397



Control Caption or Text Name
Label Principal lblPrincipal
Edit 0 edtPrincipal
Label Interest Rate lblInterestRate
Edit 0 edtInterestRate
Label Months lblMonths
Edit 0 edtMonths
Button C&alculate btnCalculate
Label Interest Earned lblInterestEarned
Edit 0 edtInterestEarned
Label Amount Earned lblAmountEarned
Edit 0 edtAmountEarned

4. On the Standard toolbar, click the New button and, on the New Items dialog
box, double-click Frame
5. Change its Name to fraDeptStore and design it as follows:


Control Caption or Text Name
Label Marked Price lblMarkedPrice
Edit 0 edtMarkedPrice
Label Tax Rate lblTaxRate
Edit 0 edtTaxRate
Button C&alculate btnCalculate
Chapter 16: Controls Containers Borland C++ Builder Programming

398 Copyright 2003 FunctionX, Inc.

Label Tax Amount lblTaxAmount
Edit 0 edtTaxAmount
Label Net Price lblNetPrice
Edit 0 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. Click on the top-left section of the form
9. In the Select Frame To Insert dialog box, click fraSimpleInterest

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
Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 399


14. Test the application. Then close it and return to Bcb
15. Save All

16.3 The Data Module

16.3.1Introduction
Like the form and the frame controls, a data module is a control container that is created
independent of a form. Like the form and the frame, once created, a data module has its
own unit that can be used to support its child controls. Unlike all other containers we will
review here, a data module can receive or host only non-visual controls; that is, controls
that the user does not see.
16.3.2 Data Module Creation
A data module is particularly easy to create and manage since there is no true graphical
design to perform on its children. This simply means that the controls placed on a data
module do not have a location or dimensions. They can be placed anywhere in its
window: the user will see neither the data module nor its children.

To create a data module, on the main menu, you can click File -> New -> Data Module.
Alternatively, you can open the New Items dialog box from where you would select Data
Module. Any of these two actions would place a rectangular window with a white
background on your screen.

To use a data module, you can position only non-visual controls to it. In fact, when the
data module window is selected or has focus and you click a tab of the Component
Palette, only non-visual controls would be available. For example, here is the Standard
property page of the Component Palette when a data module is the top window:



Chapter 16: Controls Containers Borland C++ Builder Programming

400 Copyright 2003 FunctionX, Inc.

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;
}
//---------------------------------------------------------------------------

16.4 The Panel Control

16.4.1Introduction
A panel is a visible rectangular object that can provide two valuable services for
application design. A panel allows you to design good-looking forms by adjusting colors
and other properties such as Align and Style. A panel is also a regularly used control
container because it holds and carries controls placed on it. When a panel moves, it does
so with the controls placed on it. Panels are not transparent. Therefore, their color can be
Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 401

changed to control their background display. A panel is a complete control with
properties, methods, and events.

16.4.2 Characteristics of a Panel
Unlike the form, the frame, and the data module, during design, a panel must primarily be
positioned on another container, which would be a form, a frame, or another panel. At
run time, you can detach a panel from its parent, which is not possible at design time.
To add a panel to a container, you can click the Panel control from the Standard tab
of the Component Palette. Then click in the desired location on an existing container.

After adding the panel to a container, you may want to glue it to one border or corner
of its host, as you would do with the Anchors property. Unlike anchoring a control, the
Align property allows you to fix a control to one border of its host.

Some text-based controls that use a caption allow you to control how such text would be
aligned as a paragraph. The Alignment property is a TAlignment enumerator that lets
you align text to the left, the center, or the right. If the control, such as a panel would not
display a caption, you can leave this property as is. To change the Alignment property,
click it on the Object Inspector to reveal its combo box. Click the arrow and select the
desired value: taLeft, taCenter, or taRight.

A panel object is drawn with two borders: an inner and an outer borders. The
characteristic of a border is referred to as a bevel effect, which controls the border
appearance of a panel. The effects of this characteristic are managed through the
BevelInner and the BevelOuter properties. Using their combination, you can produce
special effects as follows:


Figure 27: Bevels Effects on a Panel

The other property to take into consideration is the BevelWidth value. This is an integer
that controls the border of a panel by setting the distance between the inner and outer
bevels. Here is the effect of a 2 value on the above pre-selections:

Chapter 16: Controls Containers Borland C++ Builder Programming

402 Copyright 2003 FunctionX, Inc.


Figure 28: Effect of the Bevel Width on a Panel

The BorderStyle property controls the line used to draw the border of the panel. It can
have one of two values. The bsNone value, which is its default, indicates that there will
not be a line drawn on the border. When the BorderStyle property is set to a bsSingle
value, a line of 1 pixel is drawn around the panel. The above panels were drawn with a
bsNone value for the BorderStyle. Here is the effect produced on panels that have a
BorderWidth value of 2 and the BorderStyle set to bsSingle:



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.

Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 403

Practical Learning: Using Panel Controls

1. Start a new application with its default form
2. In the Object Inspector, click Caption and type Dockable Windows
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. Delete the Caption value.
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 . Set its Align property to alClient
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);
}
//---------------------------------------------------------------------------
10. To test the program again, press F9
11. Press F12 to display the form
Chapter 16: Controls Containers Borland C++ Builder Programming

404 Copyright 2003 FunctionX, Inc.

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;
}
//---------------------------------------------------------------------------
15. Test the program

16.5 Property Sheets and Property Pages

16.5.1Overview
As your application becomes crowded with various controls, you may find yourself
running out of space. To solve such a problem, you can create many controls on a form or
container and display some of them only in response to some action from the user. The
alternative is to group controls in various containers and specify when the controls hosted
by a particular container must be displayed. This is the idea behind the concept of
property pages.

A property page is a control container that appears as a form or a frame. A property page
can appear by itself, as one. Here is an example:

Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 405



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.

Chapter 16: Controls Containers Borland C++ Builder Programming

406 Copyright 2003 FunctionX, Inc.

16.5.2 Property Sheet Creation
Property pages of a property sheet are also referred to as tab controls. In the VCL, a
property sheet is created using the TPageControl class. The control from this class, the
PageControl control, serves as the property sheet. To implement a property sheet in your
application, from the Win32 tab of the Component Palette, you can click PageControl
and click in the form that would be used as the property sheet platform. To create, add, or
remove the actual property pages of the property sheet, you can right-click the
PageControl control:



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
While one tab is selected, click another 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.

Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 407

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
Chapter 16: Controls Containers Borland C++ Builder Programming

408 Copyright 2003 FunctionX, Inc.


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:



I
I
f

y
o
u

s
e
l
e
c
t

Style: tsButtons Style: tsFlatButtons

Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 409

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).

16.5.3 Property Pages Creation
We saw that, to add property pages to a PageControl object, you can right-click it and
click New Page. You can also add pages programmatically, of course. To make use of a
property page, for example to add controls to it, you must first select the desired property
page. A page is selected when its tab is in front. If there are other property pages, their
tabs would be in the back. The page whose tab is in front or selected is also referred to as
the Active Page. To select a property page, you have three main options:

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
Chapter 16: Controls Containers Borland C++ Builder Programming

410 Copyright 2003 FunctionX, Inc.

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.

Practical Learning: Creating a Property Sheet
1. Start a new project with its default form
2. Change the Caption of the form to Algebra and Geometry
3. Set its BorderStyle property to bsDialog
4. Set the dimensions as Height = 402 and Width = 460
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. Double-click each button, press Tab and type Close();
8. To save the project, on the Standard toolbar, click the Save All button
9. Create a folder called AlgebraGeometry
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
Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 411

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 3-
Dimensional 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:



Chapter 16: Controls Containers Borland C++ Builder Programming

412 Copyright 2003 FunctionX, Inc.






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
Borland C++ Builder Programming Chapter 16: Controls Containers

Copyright 2003 FunctionX, Inc. 413


16.6 Wizard Pages

16.6.1Overview


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.

16.6.2 Wizard Creation
Although wizards are (still) popular, the VCL does not offer strong support for them.
That is because in the VCL, like many scenario implementations, wizards are not
particularly difficult. Everything depends on what you want to do with a wizard.




Chapter 17: Aesthetic and Graphics Controls Borland C++ Builder Programming

414 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 17: Aesthetic and Graphics Controls

Copyright 2003 FunctionX, Inc. 415


Chapter 17: Aesthetic and Graphics
Controls


17.1 Bevels

17.1.1Overview
To make your development more striking, the VCL provides objects that can be used
uniquely for their aesthetic appearance. Such is the case for the Bevel, the Image, and the
Shape controls, etc. Some other controls can be used as intermediate or carriers of
aesthetic objects that other controls may need but cannot carry on their own.

A bevel is a VCL control used to enhance the display of a form by adding a box, a frame
or a line. A bevel shares a lot of the other controls properties, this means that you can
modify them at design and/or run times.

To add a bevel object to your form, click the Bevel button on the Additional tab of
the Component Palette and click on a container.
17.1.2 Characteristics of Bevel
In its uniqueness, although the bevel does not have much functionality, some of its
properties make it a valuable accessory for your forms look. By default, a bevel does not
have a particular alignment on the form; it completely depends on the developer. Unlike
the form and panel controls, the bevel control is not a container. Therefore, although you
can place controls inside a bevel, the bevel does not control their position or alignment.
In other words, the bevel does not own them.

The two most important properties of a bevel are its shape and its style. The Shape
property is a TBevelShape enumerator that controls how the bevel appears. You can set
the bevel to appear as a line, to show borders or to be empty. To set the shape of the
bevel, click it on the form to select it. On the Object Inspector, click Shape to reveal its
combo box and select from the list. The values and their effects are as follows:

Chapter 17: Aesthetic and Graphics Controls Borland C++ Builder Programming

416 Copyright 2003 FunctionX, Inc.





To set the bevels shape programmatically, use code such as this:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Bevel1->Shape = bsFrame;
}
//---------------------------------------------------------------------------

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:

Borland C++ Builder Programming Chapter 17: Aesthetic and Graphics Controls

Copyright 2003 FunctionX, Inc. 417



Practical Learning: Using a Bevel Control


1. Create a new application with the starting form.
Chapter 17: Aesthetic and Graphics Controls Borland C++ Builder Programming

418 Copyright 2003 FunctionX, Inc.

2. Set the dimensions of the form as follows:
Height: 456
Width: 504
BorderStyle: bsDialog
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. Set the bevel properties as follows:
Align: alNone
Height: 18
Left: 0
Shape: bsBottomLine
Style: bsLowered
Top: 184
Width: 490
6. On the Component Palette, click the Standard tab. Double-click the Panel control

7. Change the panels properties as follows:
Caption: &Details >>
Cursor: crVSplit
Height: 25
Left: 408
Top: 160
Width: 75
Also add a few visual controls on the top and the bottom sections of the form
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 >>";
Borland C++ Builder Programming Chapter 17: Aesthetic and Graphics Controls

Copyright 2003 FunctionX, Inc. 419

}
}
//---------------------------------------------------------------------------
10. To test the application, on the Debug toolbar, click the Run button.

17.1.3 Bevel Methods
The Bevel control has no other methods than the constructor and the destructor. All of its
methods are derived from its parent and ancestor classes. The bevel control is based on
the TBevel class.

If you want to dynamically create a bevel, declare a pointer to TBevel. Using the new
operator, assign it the bevel constructor specifying the control component as the form on
which the bevel will be positioned. You must also specify the parent of the bevel. You
can dynamically create a bevel inside of a function or another controls event. After
creating the control, you can programmatically set its properties. If the control was
created locally, set these properties in the function or event:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TBevel* Bvl = new TBevel(Form1);
Bvl->Parent = this;
Bvl->Height = 115;
Bvl->Left = 8;
Bvl->Shape = bsFrame;
Bvl->Style = bsRaised;
Bvl->Top = 8;
Bvl->Width = 210;
}
//---------------------------------------------------------------------------

If you had created the control in the header file, you can use a function or event to
initialize the control.

17.2 The Image Control

17.2.1Introduction
A picture is an object you display in your application for any desired purpose. In order to
display a picture, you must have it somewhere so your application can either locate or
import it. To support the display of a picture, the VCL provides the TImage class.

17.2.2 Image Control Fundamentals
TImage is a class that carries as much information as necessary to display a picture on a
form. The picture can be in any of the popular formats (jpeg, bmp, ico, etc). The easiest
way to add a picture to a form is to add an Image control to a form after clicking the
Image button from the Additional property page of the Component Palette. When
Chapter 17: Aesthetic and Graphics Controls Borland C++ Builder Programming

420 Copyright 2003 FunctionX, Inc.

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
Borland C++ Builder Programming Chapter 17: Aesthetic and Graphics Controls

Copyright 2003 FunctionX, Inc. 421

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
Chapter 17: Aesthetic and Graphics Controls Borland C++ Builder Programming

422 Copyright 2003 FunctionX, Inc.

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.

17.3 The Paint Box Control

17.3.1Introduction
As its name suggests, a paint box is a control whose main purpose is to provide a
platform for painting. The user never sees a paint box as it does not have borders and it is
accessible only to the programmer. Like many other controls, the paint box provides a
Canvas object that receives and manages the painting assignments performed on this
control. In fact, the paint box has its own OnPaint() event for convenience.

To provide a paint box to your application, on the System tab of the Component Palette,
click the PaintBox button and click on the container that will own it.

17.3.2 Characteristics of a Paint Box
Because a paint box main purpose is to provide an area for painting, the only inherent
characteristic it has is a rectangular area which makes available a Canvas property. This
Canvas member gives access to all necessary tools used to paint, including pens, brushes,
colors, fonts, access to bitmaps, etc.

If you plan to use the whole area of the owner of the paint box, make sure you use the
Align property to specify this.



Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 423


Chapter 18: Command Controls


18.1 Command Buttons

18.1.1Overview
A button is a Windows control used to initiate an action. From the users standpoint, a
button is useful when clicked, in which case the user positions the mouse on it and
presses one of the mouses buttons. There are various kinds of buttons. The most
common and most regularly used is a rectangular object. In some programming
environments, the classic button is called a Command Button. There are other controls
that can serve as click controls and initiate the same behavior as if a button were clicked.
Such controls include a label, a static control, or a panel.

For a programmer, a button needs a host or container. The container could be a form, a
toolbar, etc. Once you have decided where the button will reside, you can use the drag n
drop process of Borland C++ Builder to add it to your program.

The most popular button used in Windows applications is a rectangular control that
displays a word or a short sentence that directs the user to access, dismiss, or initiate an
action or a suite of actions. In C++ Builder, this control is implemented using the Button
control from the Standard tab of the Component Palette. Therefore, to add a button
to a container, click the Button control and click on the container, which is usually
a form. Once you have added the control to your application, you can set its properties
using the Object Inspector.
18.1.2 Button Properties
For a user, the only things important about a button are the message it displays and the
action it performs. The default property of a button is the Caption: this is the word or
group of words that display(s) on top of the control, showing the message that would
direct the user as to what the button is used for. By default, after adding a button to a
form, the Caption property of the Object Inspector has focus; this allows you to just type
a caption.


When adding a button to a form, the Caption field would have focus only if the last
action you performed on the Object Inspector was for a control that does not have
Caption. If the last control you were working on has a Caption property but you were
setting another property, when you add a button, the Caption field would not have
focus.

The most popular captions are OK and Cancel. The OK caption is set for a form or a
dialog box that informs the user of an error, an intermediary situation, or an
acknowledgement of an action that was performed on the dialog that hosts the button.
The Cancel caption is useful on a button whose main parent (the form) would ask a
Chapter 18: Command Controls Borland C++ Builder Programming

424 Copyright 2003 FunctionX, Inc.

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!";
}
//---------------------------------------------------------------------------


When a form is hosting many controls, including buttons, it is a good
idea to set the control that has focus when the form opens. This is done
using the forms ActiveControl property. By default, as we know about
the Tab Order, the order of controls on a container is directed by the
order of adding them to the container. Even if a control was added last,
and regardless of the Tab Order, you can set a certain control to have
focus when the form launches.

To set the ActiveControl at design time, give focus to the form. On the
Object Inspector, click the ActiveControl field to reveal its combo box;
click its combo box and select a control from the list.

To set the active control programmatically, call the TForm::ActiveControl property
(actually, the ActiveControl property belongs to the TCustomForm class). Here is an
example:

//---------------------------------------------------------------------------
void __fastcall TForm1::SetTheActiveControl()
{
ActiveControl = Button3;
}
//---------------------------------------------------------------------------

A very important characteristic of a button is the Default property, which is a Boolean
type. If you set its value to true, this button would have a thick border. If the user presses
Enter any time, the action associated with the button would be executed. This is usually
set on a dialog box with a button whose caption displays OK.

Another useful property is Cancel. Using a Boolean variable, the Cancel property allows
the user to press Esc to perform the same action that would be used to dismiss a dialog
box. This is important on a dialog box if the buttons caption is Cancel or Close.

Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 425

As mentioned already, a dialog box can be created with one or more
buttons. If you create a modal dialog box and equip it with more than
one button, when the user closes it, you must know what button the
user had clicked. To support this, the VCL provides the
ModalResult property. This property autonomously can control the
behavior of the closing dialog box depending on the button that was
clicked.

The ModalResult property is an integer defined as TModalResult
and provides various values that each can be used on a button of a
dialog box. To set the desired value, after adding a button to a dialog
box and while the button is selected, on the Object Inspector, access
its ModalResult property and select the desired value.

The possible values of the ModalResult property are:

Constant Value Description
mrNone 0 No special role
mrOk idOK The user clicked OK or pressed Enter (assuming the
button has the Default property set to true) to close
mrCancel idCancel The used clicked Cancel or pressed Esc (assuming
the button has the Cancel property set to true) to
close
mrAbort idAbort The user clicked Abort to close
mrRetry idRetry The user clicked Retry to close
mrIgnore idIgnore The user clicked Ignore to close
mrYes idYes The user clicked Yes to close
mrNo idNo The user clicked No to close
mrAll mrNo + 1 The user clicked All to close
mrNoToAll mrAll + 1 The user clicked No To All to close
mrYesToAll mrNoToAll + 1 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)
{
Chapter 18: Command Controls Borland C++ Builder Programming

426 Copyright 2003 FunctionX, Inc.

if( Form2->ShowModal() == mrOk )
Caption = "I am OK now";
else if( Form2->ShowModal() == mrCancel )
Caption = "Soldier, dismiss!!!";
else
{
Form2->Close();
Caption = "What happened? Where are you?";
}
}
//---------------------------------------------------------------------------

Practical Learning: Using Buttons

1. Create a new project with the starting form
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. On the form, click Button1 to select it.
8. On the Object Inspector, click Caption and type &Close
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.
Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 427


18.1.3 Button Events
The most regular action the user performs on a button is to click it. Based on this, the
default event on a button is OnClick. To initiate the OnClick event on a button, you can
double-click it.

One of the most regular actions you will assign to a button is to close its parent form
when the user clicks the button. There are different reasons you would want to close a
form. If the form that hosts the button displays an intermediary message to the user, you
should provide all the necessary code to follow up after the user has clicked the button;
this is the case of property sheets or wizard pages, among others.

There are various ways you can close a dialog box from a button. The simplest way is to
call the TCustomForm::Close() method. This dismisses the form. To close a form, you
can also use the Win32 APIs PostQuitMessage() function. This function takes one
argument that is an integer. The argument could be set to almost any integer value
although it should be WM_QUIT.

Practical Learning: Using Button Events
1. On the form, double-click the Close button. Notice that the Code Editor opens.
2. Press Tab and type:
Close();
3. Click the arrow of the combo box on the top section of the Object Inspector and
select btnQuit
4. Click the Events tab.
5. Double-click the empty box on the right side of OnClick.
6. Notice the cursor on the Code Editor.
7. Press Tab and type:
PostQuitMessage(WM_QUIT);
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.
10. Click the Run button again to execute the program.
11. Press Esc. Notice that the form closes also and this takes you back to Bcb.
12. Change the PostQuitMessage as follows:
PostQuitMessage(0);
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.
Chapter 18: Command Controls Borland C++ Builder Programming

428 Copyright 2003 FunctionX, Inc.

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 to dismiss the Button control
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


26. On the Code Editor, click Unit1.cpp
27. To display the first form, press F12.
28. On the main menu, click File -> Include Unit HdrFrom the Use Unit dialog box,
make sure Form2 is selected and click OKTo add another button, from the Standard
tab of the component Palette, click the Button control.
Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 429

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?";
}
//---------------------------------------------------------------------------
36. To test the project, click the Run button
37. After testing the application, close it
38. To create a new project, on the main menu, click File -> NewMake sure
Application is selected and click OK. When asked to save the project, click No
39. On the Object Inspector, click Caption and type Form and Buttons Interaction
40. Click Height and type 280
41. Click Width and type 380
Chapter 18: Command Controls Borland C++ Builder Programming

430 Copyright 2003 FunctionX, Inc.

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 .
54. Change it as follows:
Caption = &Turtle
Left = 288
Name = btnAnimal
Top = 16
55. On the Component Palette, double-click Button .
56. Set its properties as follows:
Caption = &Hamburger
Left = 16
Name = btnFood
Top = 216
57. On the Component Palette, double-click Button
58. Change its properties as follows:
Cancel = true
Caption = Close
Name = btnClose
Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 431



59. On the form, double-click the Close button
60. Press Tab and type:
Close();
61. Press F12 to display the form
62. Double-click Juventus, press Tab and type:
BtnClose->Enabled = False;
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;
68. Press F12 to display the form
69. Double-click the Turtle button and implement its OnClick event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::btnAnimalClick(TObject *Sender)
{
if( btnTeam->Caption == "&J uventus" )
btnTeam->Caption = "&Naples";
else if( btnTeam->Caption == "&Naples" )
btnTeam->Caption = "&J uventus";
}
//---------------------------------------------------------------------------
70. To test the form, press F12 and press F9.
71. Click the Turtle button a few times and notice that the caption of the team button
changes. Click the team (Juventus or Naples) button. Notice that the Close button is
disabled and you cannot click it. Click the team button again to enable the Close
button.
Chapter 18: Command Controls Borland C++ Builder Programming

432 Copyright 2003 FunctionX, Inc.

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.

18.1.4 Button Methods
The Button doest not have many methods to customize its behavior. This is because a
button does not need more than what you would expect from it: point and click. Probably
the only method you would use from a button is the constructor used to dynamically
create a button. Most of the other methods used on a button are derived from member
variables of the TWinControl class.

18.2 Bitmap Buttons

18.2.1Introduction
A bitmap button is a command button that displays a bitmap and possibly a caption on
top. The main difference between a bitmap button and a command button is that, by
design, the former displays a bitmap.

A bitmap button is based on the TBitBtn class. To create a bitmap button, you can use
the BitBtn button from the Additional tab of the Component Palette.

18.2.2 Bitmap Button Characteristics
Because a bitmap button is usually meant to display a bitmap, you may want to provide a
bitmap for it. If you want the button to also display a caption, you can proceed as the
regular button and use the Caption property on the Object Inspector.

There are two primary ways you can provide or add a bitmap to the button. C++ Builder
ships with combinations of bitmaps and captions for a bitmap button. To use one of these,
after adding a BitBtn control to a container, on the Object Inspector, click Kind and
select one of the types of buttons. Some of the buttons you create using one of the Kind
types have already been configured to properly respond to the dialog box or form on
Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 433

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 Picture ModalResult Observation
bkCustom See Below mrNone No special role
bkOK

mrOk 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
bkCancel

mrCancel 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
bkAbort

mrAbort Used to display Abort and return the mrAbort
value
bkRetry

mrRetry Used to display Retry and return the mrRetry value
bkIgnore

mrIgnore Used to display Ignore and return the mrIgnore
value
bkYes

mrYes 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.
bkNo

mrNo Used to display No and return the mrAbort value
bkHelp

Used to display Help
bkClose

mrNoToAll Used to display No to All and return the
mrNoToAll value
bkAll

mrYesToAll Used to display Yes to All and return the
mrYesToAll value

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
Chapter 18: Command Controls Borland C++ Builder Programming

434 Copyright 2003 FunctionX, Inc.

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:

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
Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 435

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.

Practical Learning: Using Speed Buttons
1. Start a new application with its default form
2. Start saving it and create a new folder named BitmapButtons1
3. Save the unit as Main and save the project as BitmapButtons
4. Open Image Editor and create a new 16 x 16 pixels bitmap
5. Design it as follows:

6. Save it as Library in the BitmapButtons folder of the current project
7. Create a new 48 x 16 pixels bitmap and design it as follows:

8. Save it as Attached and return to Bcb
9. While the form is displaying, on the Object Inspector, Set the following properties:
Caption = Bitmap Buttons
Chapter 18: Command Controls Borland C++ Builder Programming

436 Copyright 2003 FunctionX, Inc.

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 under the first one
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

Click Open and click OK
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
17. Add another BitBtn control under the second one. While the new bitmap
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
Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 437

20. Change its following properties:
Name: btnAllowIt
Caption: Allow It
Hint: Allow item if ready
21. On the form, double-click the last button to access its OnClick event and implement
it as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::btnAllowItClick(TObject *Sender)
{
btnAttachment->Enabled = !btnAttachment->Enabled;
}
//---------------------------------------------------------------------------
22. Save your project and test it:
23. After using it, close it and return to Bcb

18.3 Speed Buttons

18.3.1Introduction
The speed button control provides the ability to create command buttons that behave
either like optional, mutual-exclusive items or like checked objects. A speed button can
be configured so that, when it has been clicked, it would stay down until it is clicked
again (this behavior is the same as a check box). You can also create a group of speed
buttons so that, out of the group, the selected one would display and keep a clicked or
down state (like radio buttons) while the other button(s) of the same group is (are) down.
This allows only one of the items in the group to appear clicked when chosen.

Over all, a speed buttons behavior is configured as that of a regular button. The design of
the speed button provides the most different aspect of the control.

18.3.2 Speed Buttons Characteristics
To create a speed button, use the SpeedButton control from the Additional tab of
the Component Palette to add it to the desired container. Unlike most other actions, when
Chapter 18: Command Controls Borland C++ Builder Programming

438 Copyright 2003 FunctionX, Inc.

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:
If the AllowUp property is set to false:
o 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
o If the user clicks a button that is already down in the group, nothing would
change with regards to their appearances
If the AllowUp property is set to true:
Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 439

o 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
o 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

18.4 Property Sheet and Wizards Buttons

18.4.1Buttons on a Property Sheet
A priori, a property page is just a control container and does not need buttons to function.
Here is an example of a property sheet without communication buttons:

Chapter 18: Command Controls Borland C++ Builder Programming

440 Copyright 2003 FunctionX, Inc.



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.

18.4.2 Property Sheet Buttons Implementation
You add the buttons on a property sheet the same way you would for a dialog box.
Simply click the Button control from the Standard tab or the BitBtn bitmap button from
the Additional tab of the Component Palette and click the area of the property page where
you want to place the button(s).

Normally, in the strict sense, the buttons that perform the exchange of information with
other parts of the application are positioned on the property sheet and not on the property
page(s). That is, the buttons are positioned outside of the property page. The buttons can
be positioned on the right side of the property sheet. That is the case for the Colors dialog
box of Microsoft Word:



Most of the time, the buttons are positioned on the bottom section of the property sheet.

Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 441

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:

Chapter 18: Command Controls Borland C++ Builder Programming

442 Copyright 2003 FunctionX, Inc.



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

Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 443



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.

18.4.3 Wizards Buttons
As seen when reviewing controls containers, a wizard is a series of dialog boxes that
assist the user with performing a task that requires intermediary steps. To implement its
functionality, a wizard displays some button in the lower section of the dialog box.

Depending on the programmer, different wizards use a few or more buttons. Usually a
wizard has buttons such as Back, Next, and Cancel. Here is an example:

Chapter 18: Command Controls Borland C++ Builder Programming

444 Copyright 2003 FunctionX, Inc.




Practical Learning: Creating a Wizard
1. Create a new project with its default form
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. Click the panel on the form to make sure it is selected.
6. On the main menu, click Edit -> Copy.
7. Click an empty area on the form to deselect the panel and select the form.
8. On the main menu, click Edit -> Paste.
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
Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 445

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 .
17. Click an unoccupied area in the lower section of the form.
18. Change the properties of the Bevel control as follows:
Height: 25
Left: 0
Shape: bsTopLine
Top: 272
Width: 430
19. On the Component Palette, click the Standard tab.
20. Click the Button control.
21. Click in the lower section of the form
22. Change the properties of the button as follows:
Caption: &Back
Left: 112
Name: btnBack
Top: 280
23. Add another button on the right side of the Back button and change its name to
btnNext
24. Add another button of the right side of the Next button and change its name to
btnFinish
25. Add one more button on the right side of the finish button. Set its Cancel property to
true and its name to btnCancel

18.4.4 Wizard Implementation
When a wizard starts, the user is presented with the first page. After using it, he or she
can click Next to move to the subsequent page. The user can continue clicking Next to
complete the necessary task(s) on each page. At any time and except on the first page, the
user can click the Back button to move backward.

Most wizards have a Cancel button that would allow the user to dismiss the dialog box.
In this case, whatever change the user would have performed on the controls hosted by
the wizard would be discarded.

Many wizards are also equipped with a Finish button. There are two scenarios in which
the Finish button can be used:

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:

Chapter 18: Command Controls Borland C++ Builder Programming

446 Copyright 2003 FunctionX, Inc.



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.

Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 447


Practical Learning: Allowing Setup of a Print
1. Double-click the Cancel button to access its OnClick() event
2. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::btnCancelClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
3. To bring back the form, press F12
4. Double-click an empty area on the form to access the form's OnCreate() event
5. Implement the FormCreate() event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// We are in the first page now
pnlFirstPage->Visible = True;
pnlSecondPage->Visible = False;
pnlThirdPage->Visible = False;
btnBack->Enabled = False;
}
//---------------------------------------------------------------------------
6. Press F12 to display the form
7. Double-click the Back button to access its OnClick() event
8. Implement it as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::btnBackClick(TObject *Sender)
{
if( pnlSecondPage->Visible == True )
{
pnlFirstPage->Visible = True;
Chapter 18: Command Controls Borland C++ Builder Programming

448 Copyright 2003 FunctionX, Inc.

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. Press F12 to bring back the form
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;
}
}
//---------------------------------------------------------------------------
11. Press F12 to bring back the form
12. Double-click the Finish button and implement its OnClick() event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::btnFinishClick(TObject *Sender)
{
pnlFirstPage->Visible = False;
pnlSecondPage->Visible = False;
Borland C++ Builder Programming Chapter 18: Command Controls

Copyright 2003 FunctionX, Inc. 449

pnlThirdPage->Visible = True;
btnBack->Enabled = True;
btnNext->Enabled = False;
}
//---------------------------------------------------------------------------
13. To test the application, press F9.



Chapter 19: Collections-Based Controls Borland C++ Builder Programming

450 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 451


Chapter 19: Collections-Based Controls


19.1 The Main Menu

19.1.1Overview
A menu of a computer program is a list of actions that can be performed on that
application. To be aware of these actions, the list must be presented to the user upon
request.

A menu is considered a main menu, when it carries most of the actions the user can
perform on a particular application. Such a menu is positioned on the top section of the
form in which it is used. By design, although a main menu is positioned on a form, it
actually belongs to the application.

A main menu is divided in categories of items and each category is represented by a
word. Here is an example:



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:

Chapter 19: Collections-Based Controls Borland C++ Builder Programming

452 Copyright 2003 FunctionX, Inc.



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.

19.1.2 Main Menu Creation
To create a main menu in the VCL, you can use the TMainMenu class. In C++ Builder,
the main menu is created using the MainMenu control . To get it, on the Standard
tab of the Component Palette, click the MainMenu button and click on the form. It
does not matter where you drop the MainMenu icon because it will not be seen what the
program runs.

To create the list of items that belong to the menu object, open the Items property. This
would display a window specially equipped for creating a main menu.

C++ Builder greatly simplifies the creation of a menu by providing menu templates. A
menu template is an already created menu that you can simply select and add to your
application. To use one of these, right-click the Menu Designer window and click Insert
From Template:

Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 453


Figure 29: Menu Designer

C++ Builder provides already made menus for file, edit, and help processing:


Figure 30: Insert Template

You can still create a menu manually either without using the template or to complete the
template. To create a menu category, on the Menu Designer window, click the top line
and, on the Object Inspector, click Caption, type a word or a group of words and press
Enter or click somewhere else.

On the menu items, a letter on each menu is underlined. This letter allows the user to
access the menu using a keyboard. For example, if the letter e is underline in a File menu
as in File, the user can access the File by pressing the Alt then the E keys.

A shortcut is a key or a combination of keys that the user can press to perform an action
that would also be performed using a menu item. When creating a menu using the Menu
Designer window, on the Object Inspector, you can specify a shortcut using the Shortcut
property.

Chapter 19: Collections-Based Controls Borland C++ Builder Programming

454 Copyright 2003 FunctionX, Inc.

Practical Learning: Creating a Main Menu
1. Start a new project with the default form.
2. On the Component Palette, click the Standard property sheet. Click the MainMenu
button and click on the form
3. On the form, double-click the MainMenu1 icon
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. Type &New Hire
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. On the Object Inspector, click Caption and type &Records
10. Using the Object Inspector, set the ShortCut to Ctrl + R
11. On the Menu Designer window, click the item under Records and on the Object
Inspector, click Caption.
12. Type Time &Sheet... and on the Object Inspector, specify the ShortCut as Ctrl + M
13. On the Menu Designer window, click Records and press Insert
14. On the Object Inspector, click Caption and type Searc&h...
15. To add a separator, click the first empty line under Time Sheet. On the Object
Inspector, click Caption, type - and press Enter
16. Click the empty item under the previously added separator line, type E&xit and
press Enter
17. To move the Search item, on the Menu Designer window, click and drag Search
down:

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
Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 455

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:

Figure 31: A Menu Designed
30. Click the box on the right side of Books and set its caption to &View
31. Click View and click the empty box under it
32. On the Object Inspector, click Caption, type &Toolbar and press Enter
33. Click the box under Toolbar, type &Status Bar and press Enter.
34. On the Menu Designer window, click Toolbar to select it
35. To put a check mark on a menu item, set the Checked property to true.
36. Also, for the Status Bar item, set the Checked property to true.
37. To move the whole View menu category, click and drag View to the left:
Chapter 19: Collections-Based Controls Borland C++ Builder Programming

456 Copyright 2003 FunctionX, Inc.


Figure 32: Moving a Menu Category
38. When Books is highlighted, release the mouse.
39. Close the Menu Designer Window. Notice that a menu for the form has been created.

19.1.3 Coding a Main Menu Item
Throughout this book, you will find out that there are other ways or intermediary ways to
create a menu, such as using the ActionList. Consequently, there are also other ways to
write code for a menu.

If you create a menu as we have just done, to write code for one of the menu items, from
the Menu Designer window, you can double-click the menu item. This would open the
OnClick event of the menu item in the Code Editor and you can start writing the desired
code for that item.

If you have already created the menu and you have closed the Menu Designer window,
on the form, you can open the menu category and click the menu item as if you were
regularly trying to use the menu. This would open the Code Editor with the OnClick
event of the menu item

Practical Learning: Coding a Main Menu Item
1. On the form, click Staff and click Exit
2. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::Exit1Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
3. Test the form. To close it, click Staff -> Exit

Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 457

19.1.4 Popup and Context-Sensitive Menus
A menu is considered or qualifies as popup if or because it can appear anywhere on the
form as the programmer wishes. Such a menu is also referred to as context-sensitive
because its appearance and behavior depends on where it displays on the form or on a
particular control.

The first difference between a main menu and a popup menu is that a popup menu
appears as one category or one list of items and not like a group of categories of menus
like a main menu. Secondly, while a main menu by default is positioned on the top
section of a form, a popup menu does not have a specific location on the form.

To use a popup menu, usually the user can right-click the section of the form or the object
on which the menu is configured to appear.

A popup menu is based on the TPopupMenu VCL class. To visually create a popup
menu, on the Standard tab of the Component Palette, click the PopupMenu button
and click on the form. Once you have a PopupMenu object, you can double-click it,
which would open the Menu Designer.

In the Menu Designer window, to create a menu, click an empty item and, on the Object
Inspector, type a text on the Caption field. Everything on an item of a popup menu can
be configured like an item of a main menu. Unlike a main menu, a popup menu provides
a single list of items. If you want different popup menus for your form, you have two
options. You can create various popup menus or programmatically change your single
popup menu in response to something or some action on your form.

There is nothing particularly specific with writing code for a popup menu item. You
approach it exactly as if you were dealing with a menu item of a main menu. You can
write code for an item of a popup menu independent of any other item of a main menu. If
you want an item of a popup menu to respond to the same request as an item of a main
menu, you can write code for one of the menu items (either the item on the main menu or
the item on the popup menu) and simply call its OnClick() event in the event of the other
menu item


Practical Learning: Creating a Popup Menu
1. On the Standard tab of the Component Palette, click the PopupMenu button
and click on the form.
2. On the form, double-click the PopupMenu button.
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. Set the caption of the last item to Finis&h
8. While still in the Menu Designer window, click Finish.
Chapter 19: Collections-Based Controls Borland C++ Builder Programming

458 Copyright 2003 FunctionX, Inc.

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.1Introduction
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.

Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 459

Practical Learning: Creating a Toolbar
1. To create a new popup menu, on the Standard tab of the Component Palette, double-
click 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. Then close the Menu Designer window.
5. To create a new popup menu, on the Standard tab of the Component Palette, double-
click PopupMenu
6. Change its name to mnuViewPopup and double-click it
7. Create two menu items as &Toolbar and &Status Bar
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.
10. Click the ToolBar button and click on the form.
11. On the Object Inspector, set the Flat property to true.
12. Set the Height to 25
13. Set the Images to imgMainMenu
14. Change the name of the toolbar to tbrStandard
15. To add a new button, right-click the toolbar and click New Button
16. Right-click the toolbar again and click New Button
17. Right-click the toolbar again and click New Button
18. While the new button is still selected, on the Object Inspector, change its
ImageIndex to 3 instead of 2
19. Right-click the toolbar again and, this time, click New Separator
20. Right-click the toolbar again and click New Button
Chapter 19: Collections-Based Controls Borland C++ Builder Programming

460 Copyright 2003 FunctionX, Inc.

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


19.2.2 Toolbar Programming
The controls on a toolbar are programmed with regards to their own kind. This means
that the coding of a control of a toolbar is not directly related to the toolbar: the toolbar is
simply a host to the controls positioned on it.

If the control whose code you want to write is a button, you can simply proceed with the
OnClick event of the button. If the button should perform the same action as a menu
item, and if you have already written code for the menu item, you can simply select its
Click event in the OnClick event field of the toolbar button. Otherwise, you can write
completely independent code for a button on a toolbar.

Practical Learning: Coding a Toolbar
1. On the main menu of the form, click View -> Toolbar
2. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::Toolbar1Click(TObject *Sender)
{
tbrStandard->Visible = !tbrStandard->Visible;
Toolbar1->Checked = !Toolbar1->Checked;
}
//---------------------------------------------------------------------------
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
Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 461

5. Test the form. Right-click the toolbar and click Standard. On the main menu of the
form, click View -> Standard

6. After viewing the form, close it.

19.3 Status Bars

19.3.1Introduction
A status bar is a Windows control that usually displays on the bottom section of a form or
the container that is hosting it. It is used to display short pieces of information to the user.
A status bar can be a simple long bar that covers the whole bottom section of a form. As
such, a status bar can be considered one long panel. A status bar in based on the
TStatusBar VCL class.

To create a status bar, on the Win32 tab of the Component Palette, click the StatusBar
button and click on the form. As soon as you click the form, the control would be
positioned on the bottom section of the host control. Most of your status bars will be
created on, or hosted by, a form but you can add a status bar to any control that can serve
as a parent for other controls.

19.3.2 Characteristics of a Status Bar
A newly added status bar is made of a long bar that assumes an alBottom value for the
Align property. By default, a new status bar is made of a single bar, called a simple
panel. On the status bar's panel, you can display various types of text such as short
messages informing the user about some things that are going on. If you plan to use a
single panel on your status bar, on the Object Inspector, you should set the SimplePanel
property to true.

If the SimplePanel of a StatusBar control has a value of true, you can use the
SimpleText property to display a string on the status bar. You can specify such text at
design time by typing a string in the value section of the SimpleText property. When the
form runs, such a string would permanently display on the status bar:

Chapter 19: Collections-Based Controls Borland C++ Builder Programming

462 Copyright 2003 FunctionX, Inc.


Figure 33: Status Bar

You can also programmatically display or change the string that a status bar displays. To
do this, simply assign the desired string to the SimpleText property of the StatusBar
control. In the following example, a different string displays on the status bar depending
on if the user double-clicked the form or right-clicked it:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormDblClick(TObject *Sender)
{
StatusBar1->SimpleText = "The form is already active";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if( Button == mbRight )
StatusBar1->SimpleText = "There is no popup menu for this form";
}
//---------------------------------------------------------------------------

Practical Learning: Creating a Status Bar
1. Click an empty area on the form to select it
2. On the Component Palette, click the Win32 property sheet
3. Double-click the StatusBar button
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. On the form, click the newly added status bar. On the Object Inspector, set its
SimplePanel property to true.
Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 463

7. Find and double-click the mnuViewPopup
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. Click an empty area on the form to select the form
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

Figure 34: Add Method - ShowToolTips

18. Click OK and implement the function as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::ShowToolTips(TObject* Sender)
{
DefaultStatusBar->SimpleText = Application->Hint;
}
//---------------------------------------------------------------------------
Chapter 19: Collections-Based Controls Borland C++ Builder Programming

464 Copyright 2003 FunctionX, Inc.

19. Press F12 to display the form
20. Double-click in the middle of the form and implement its OnCreate event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Application->OnHint = ShowToolTips;
}
//---------------------------------------------------------------------------
21. Press F12 to display the form
22. On the form, double-click the MainMenu1 icon
23. In the Menu Designer window, click New Hire
24. On the Object Inspector, click Hint and type Used when hiring a new employee
25. In the same way, click each menu item and set their Hint as follows:
Menu I tem Hint
New Hire Used when hiring a new employee
Records Display the records of all employees
Search Search an employee in our records
Time Sheet Fill out a time sheet
Exit Quit the application
Toolbar Toggles the toolbar
Status Bar Toggles the status bar
Show All Titles Display information on all books
Title Register a new book
Author Register a new author
Category Create a new category

26. Close the Menu Designer window
27. On the toolbar, click the first (left) button.
28. On the Object Inspector, click Hint, type
New Employee | Used when hiring a new employee and press Enter
29. Click the second button on the toolbar and set its Hint to
Records|Display the records of all employees
30. Set the Hint of the third button to Category | Create a new category
31. Set the Hint of the fourth button to New Book|Create a record for a new book
32. Press F9 to test your form

19.3.3 Status Bar Panels
Instead of a long unified bar, a status bar can be divided in sections each serving a
specific purpose. A section of a toolbar is called a panel. To use a more elaborate status
bar, you must set its SimplePanel property to false.

To create different panels for a status bar, on the form, you can double-click the
StatusBar control. Alternatively, after selecting the control on the form, on the Object
Inspector, you can click the ellipsis button of the Panels field. Once in the Panels
window, you can click the Add New button. You can also right-click in the Panels
Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 465

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.

19.4 Action Lists

19.4.1Introduction
An action list is a collection of actions that can be performed on an application. It is a
convenient means of configuring actions that can occur throughout the life of an
Chapter 19: Collections-Based Controls Borland C++ Builder Programming

466 Copyright 2003 FunctionX, Inc.

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.

Practical Learning: Introducing Action Lists
1. Start a new project with its default form
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. Save the unit as Main and save the project as Notice
5. Set the Caption of the form to Notice
6. Change the Name of the form to frmMain
7. Set the ShowHint property of the form to true
8. On the Win32 tab of the Component Palette, double-click ImageList
9. On the form, double-click ImageList1
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

19.4.2 The List of Actions
To create a list of actions, you can use the TActionList class. It is represented on the
Standard tab of the Component Palette by an ActionList button . To use it, click
ActionList from the Component Palette and click on the form. To create the actual list,
double-click the ActionList button on the form, which would open the Action List Editor
window.

To simplify the creation of a list of actions, C++ Builder provides a collection of pre-
configured actions. These items can appropriately respond to certain controls on your
form without much code, provided the control is the type of action for which the Action
item was created. To use one of the action templates, right-click anywhere in the Action
List Editor and click New Standard Action. On the Standard Action Classes window,
select the desired template and click OK.

Practical Learning: Creating a Memo-Based Application
1. On the Standard tab of the Component Palette, click ActionList and click on
the form
2. While ActionList1 is still selected, on the Object Inspector, click Images and select
ImageList1
3. On the form, double-click ActionList1 to open the Action List Editor
Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 467

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. Click Caption and type &New
7. Click Category and type File
8. Click Hint and type New|Creates a new document
9. Click Shortcut, then click the arrow of its combo box and select Ctrl + N
10. In the ImageIndex combo box, select 0
11. Right-click the left frame of the Action List Editor and click New Action
12. While the new Action1 item is still selected, on the Object Inspector, click Caption
and type &Save
13. In the Category combo box, select File
14. Click Name and type FileSave
15. Click Hint and type Save|Saves the current document
16. Set its ImageIndex to 1
17. Click Shortcut, then click the arrow of its combo box and select Ctrl + S
18. On the toolbar of the Action List Editor, click the arrow of the New Action button

Figure 35: Action List Editor

19. Click New Standard Action
20. In the Standard Action Classes dialog box, scroll down to the Internet node.
21. Double-click SendMail
22. Right-click the right frame of the editor and click New Standard Action
23. In the Standard Action Classes dialog box, scroll down to the File node. Click
TFileOpen. Press and hold Ctrl. Then click TFileSaveAs, TFilePrintSetup, and
TFileExit. Release Ctrl
Chapter 19: Collections-Based Controls Borland C++ Builder Programming

468 Copyright 2003 FunctionX, Inc.


Figure 36: Standard Action Classes

24. Click OK
25. On the left frame of the Action List Editor, click File to display its list of related
actions
26. On the right frame, click FileSaveAs1 to select it. Using the Object Inspector, set its
Shortcut to F12
27. While the File node is still selected in the left frame, in the right frame, click
FileOpen1 to select it and, on the toolbar of the editor, click the Move Up button
. Click File again to see the result
Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 469


Figure 37: Action List Editor With New Items

28. Right-click one of the frames of the editor and click New Standard Action
29. Click TEditCut. Press and hold Shift. Then click TEditDelete and release Shift
30. Click OK
31. Move the EditUndo1 item to be on top of the list
32. Move the EditDelete1 item and place it under EditPaste1

Figure 38: Action List Editor - Items Configuration

33. Close the Action List Editor window and save All
Chapter 19: Collections-Based Controls Borland C++ Builder Programming

470 Copyright 2003 FunctionX, Inc.

13. From the Standard tab of the Component Palette, click MainMenu and click
the form
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 Action
Open FileOpen1
Save FileSave
Save As FileSaveAs1
Print Setup PrintSetup1
Exit FileExit1
Undo EditUndo1
Cut EditCut1
Copy EditCopy1
Paste 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 and click an
empty area on the form
27. Using the Object Inspector, set the Height value to 40
28. Change the Name of the toolbar to tbrStandard
29. Set the ShowCaptions property to true
30. Set its Image property to ImageList1
31. Right-click the toolbar and click New Button. On the Object Inspector, set its
MenuItem property to FileNew
32. Complete the buttons as follows:
Borland C++ Builder Programming Chapter 19: Collections-Based Controls

Copyright 2003 FunctionX, Inc. 471


Figure 39: Toolbar Buttons With Captions
33. Press F12 to display the Code Editor. In Class Explorer, expand the Classes node and
TfrmMain. Right-click TfrmMain and click New Method...
34. Set the Method Name to ShowHints. Specify its Argument as TObject *Sender and
the Function Result to void and click OK.
35. Implement the method as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::ShowHints(TObject * Sender)
{
StatusBar->SimpleText = Application->Hint;
}
//---------------------------------------------------------------------------
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;
}
//---------------------------------------------------------------------------
37. Press F12 to display the form and Save All

19.4.3 Action Lists Messages and Events
As their name implies, the actions of a list are simply meant to perform central actions
that other objects may need. To manage their behavior, they are usually implemented
through a function pointer called TActionEvent. This function pointer is defined as
follows:

typedef void __fastcall (__closure *TActionEvent)(Classes::TBasicAction* Action,
bool &Handled);

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.
Chapter 19: Collections-Based Controls Borland C++ Builder Programming

472 Copyright 2003 FunctionX, Inc.




Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 473


Chapter 20: Text-Based Controls


20.1 Labels

20.1.1Introduction
Borland C++ Builder ships with many string-oriented classes for most of the controls
used to perform any necessary string manipulation. AnsiString is the main string classes
used by those controls as it provides the caption property of all controls that need to set or
control their caption. The label is one of those controls.

A label is a control that serves as a guide to the user. It provides static text that the user
cannot change but can read to get information on the form. The programmer can also use
it to display simple information to the user. Most controls on the form are not explicit at
first glance and the user would not know what they are. Therefore, you can assign a label
to a control as a help to the user.

To add a label to a container, click the Label button from the Standard tab of the
Component Palette and click on the object that would host it. You can also dynamically
create a label.

A label is based on the TLabel class that is a child of the TCustomLabel class which
itself is derived from the TGraphicControl class.

20.1.2 Label Characteristics
The most important characteristic of a label control is the text it displays. This is what the
user would read. The text of a label is its Caption property and is its default. To set a
labels caption, after adding the control to a container, click Caption in the Object
Inspector and type the desired value. As we mentioned when studying controls
characteristics, at design time, the text you type in the Caption is considered as is. If
you want to create a more elaborate and formatted string, you would have to do it
programmatically.

When you type the caption of a label, it is continually resized to accommodate its string.
If you edit the label, as you delete or add characters, the label resizes itself. If you want to
fix the size of the label regardless of its caption, set the Boolean property AutoResize to
false. By default, this property is set to false on most controls that use it; but on a label, it
is set to true. Once you have set AutoResize to true, if you change the text of the label,
only the portion that fits in the allocated space would be seen. Of course, you can resize it
manually.

Before or after typing the caption of a label, you can resize its allocated space to your
liking. This is because a string occupies a rectangular area. Here is an example:

Chapter 20: Text-Based Controls Borland C++ Builder Programming

474 Copyright 2003 FunctionX, Inc.



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:

taLeftJ ustify taCenter taRightJ ustify


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 taLeftJ ustify Alignment taLeftJ ustify Alignment taLeftJ ustify
Layout tlTop Layout tlCenter Layout tlBottom

Alignment taCenter Alignment taCenter Alignment taCenter
Layout tlTop Layout tlCenter Layout tlBottom

Alignment taRightJ ustify Alignment taRightJ ustify Alignment taRightJ ustify
Layout tlTop Layout tlCenter Layout tlBottom


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
Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 475

to perform any needed drawing in the allocated rectangle of the label. Here is an
example:



//---------------------------------------------------------------------------
void __fastcall TForm1::FormDblClick(TObject *Sender)
{
Label2->Canvas->Pen->Color = clBlue;
Label2->Canvas->Brush->Color = clSkyBlue;
Label2->Canvas->Brush->Style = bsDiagCross;
ClientToScreen(Point(Label2->Left, Label2->Top));
Label2->Canvas->Rectangle(Label2->ClientRect);
}
//---------------------------------------------------------------------------


Figure 40: Using the Canvas of a Label

Figure 41: Painting the Canvas of a Label


Practical Learning: Labels Design
1. 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. Click the Create New Folder button to create a folder
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. Type Employment as the name of the project and press Enter
Chapter 20: Text-Based Controls Borland C++ Builder Programming

476 Copyright 2003 FunctionX, Inc.

7. From the Standard tab of the Component Palette, click the Label control
8. Click the form
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:

Figure 42: The Employment Application With Labels
15. To add another label, click the Label control and click on the top section of the form.
16. On the Object Inspector, click the Caption field, type Employment Application
17. Click the gray box on the right side of the Font field
18. Click the ellipsis button to open the Font dialog
19. Change the font to Times New Roman
20. In the Font Style combo box, click Bold.
21. In the Font Size combo box, select 24
22. Click the arrow of the Color combo box and select Blue
Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 477


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, right-
click again and click Bring To Back until the blue label is on top of the white label:
Chapter 20: Text-Based Controls Borland C++ Builder Programming

478 Copyright 2003 FunctionX, Inc.


Figure 43: A Label With Shadow

39. To add another label on the form, on the Component Palette, double-click the Label
control .
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 Size Left Top
lblCompAdr 4412 Conic Rd Suite 204D MS Serif 7 414 24
lblCompCity Silver Spring MD 20910 MS Serif 7 420 34
lblCompPhone (301) 309-0330 MS Serif 7 440 46

50. Click an unoccupied area on the form
51. To add a horizontal line on the form, add a label and for its caption, type ____ (Shift
+ -) continuously until a horizontal line spans the form. Position that line under the
big blue label.
52. To test the project, press F9:
Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 479


Figure 44: The Employment Application With Labels

53. To save the project, on the Standard toolbar, click Save All.

20.1.3 Label Methods
The Label is based on the TLabel class and does not have any methods per se, only a
constructor and a destructor. You should never directly call either the TLabel constructor
or the ~TLabel destructor. These are only used if you want to dynamically create a label.

To dynamically create a label locally, in a function or an event, declare a pointer to
TLabel object and use the new operator, specify the component that owns the instance of
the control. To initiate the control, you must specify its container or parent. Here is an
example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TLabel* Lbl = new TLabel(Form1);
Lbl->Parent = Form1;
Lbl->Caption = "LaFace, Inc.";
Lbl->Left = 75;
Lbl->Top = 32;
}
//---------------------------------------------------------------------------


20.1.4 Label Messages and Events
The Label control has various events that are part of its repertoire, although they are
hardly used. You can configure a label to behave like a (web) link using its OnClick
event. For example the Win32 ShellExecute() function can be used to perform an
operation on a file. The following OnClick event on a label would launch WordPad:

//---------------------------------------------------------------------------
void __fastcall TForm1::lblWordPadClick(TObject *Sender)
Chapter 20: Text-Based Controls Borland C++ Builder Programming

480 Copyright 2003 FunctionX, Inc.

{
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.

Practical Learning: Coding a Label Control
1. 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. Test the program
Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 481

6. Close it and return to Bcb

20.2 The Static Text Control

20.2.1Introduction
The label control we used earlier is a completely native VCL object and not particularly
related to Win32. The Win32 library provides its own support to labelled text through a
window class named STATIC. To support the Win32 STATIC control, the VCL
provides the StaticText control. Over all, the VCLs Label and StaticText controls are
very similar. The biggest difference is that StaticText is a Windows control, like the
STATIC class of the Win32 API while the VCLs Label control is not.

To create a static text, at design time, from the Additional tab of the Component Palette,
you can click the StaticText button and click the container that would host it.
20.2.2 Characteristics of a Static Text
As stated already, the static text provides the same functionality as the label control. Its
text is aligned using the Alignment property. It also has the following properties similar
to those of the label: AutoSize, FocusControl, and ShowAccelChar.

Among the differences between both controls, the StaticText does not have a Canvas
property because you cannot draw into it. The StaticText control has a physical and
identifiable border, unlike the label. This characteristic is controlled by the BorderStyle
property which is an enumerator called TStaticBorderStyle and defined as follows:

enum TStaticBorderStyle { sbsNone, sbsSingle, sbsSunken };

Its effects are as follows:

sbsNone 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);
Chapter 20: Text-Based Controls Borland C++ Builder Programming

482 Copyright 2003 FunctionX, Inc.

}
//---------------------------------------------------------------------------



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.

20.3 Edit Boxes

20.3.1Introduction
An edit box is a Windows control used to get or display text for the users interaction. At
its most regular use, an edit box serves as a place to fill out and provide information.
Such a use is common on employment applications, login dialog boxes, etc.

Like most other controls, the role of an edit box is not obvious at first glance; that is why
it should be accompanied by a label that defines its purpose. From the users standpoint,
an edit box is named after the label closest to it. Such a label is usually positioned to the
left or the top side of the edit box. From the programmers point of view, an edit box is a
placeholder used for various things. For example, you can show or hide it as you see fit.
You can also use it only to display text without allowing the user to change it.

To create an edit box, once again, Bcb provides a lot of choices. The most fundamental
edit box is designed with the Edit control of the Standard tab of the component
palette.
20.3.2 Edit Box Characteristics
The most important aspect of an edit box is its text, whether it is displaying or requesting
it. This is the Text property. When you add an edit control, C++ Builder initializes it with
the name of the control; this would be Edit1 for the first edit box, Edit2 for the second,
etc. If you want the control to display some text when the form launches, type the text in
the Text property field in the Object Inspector. Otherwise, if you want the edit box to be
empty when it comes up for the first time, delete the content of the Text field.

Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 483

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 };

By default, it is set to ecNormal, which respects whatever character case is typed in an
edit box. Otherwise, you can set it to ecUpperCase or ecLowerCase to set the edit box
characters to uppercase or lowercase respectively.

Text typed in an edit box appears with its corresponding characters unless you changed
the effect of the CharCase property from ecNormal. This allows the user to see and be
able to read the characters of an edit. If you prefer to make them un-readable, you can use
the PasswordChar property. Although this property is a char type of data, changing it
actually accomplishes two things. Its default value is #0, which means that the characters
typed in it will be rendered in their alphabetic corresponding and readable characters. If
you change it, for example to *, any character typed in it would be un-readable and be
replaced by the value of this property. You can use any alphabetic character or digit to
represent the characters that would be typed but you must provide only one character.
Alternatively, you can specify an ASCII character instead. To do this, type # followed by
the number representing the ASCII character. For example, #98 would display b for each
character.

When using a form, the user can press Tab to move from one control to another. By
default, when such a control receives focus from the user pressing Tab, the whole text in
an edit control is selected. This is controlled by the AutoSelect property. If you do not
want that, you can set the AutoSelect Boolean property to false.

One of the actions the user may be asked to perform on an edit box is to type text in it. If
the control already contains text, the user can read it. Another common action the user
Chapter 20: Text-Based Controls Borland C++ Builder Programming

484 Copyright 2003 FunctionX, Inc.

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.

Practical Learning: Designing Edit Boxes
1. The Employment application should still be opened
To add an Edit box, on the Standard tab of the Component Palette, click the Edit
control
2. Click on the form
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:

Figure 45: The Employment Application With Edit Controls

7. Set the FocusControl property of each label to Edit control on its right.
8. To save the project, on the Standard toolbar, click Save All.
9. To test the form, press F9.
10. Save the project.

Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 485

20.3.3 The Edit Control and its Functionality
The edit control is based on the TEdit class whose immediate parent is TCustomEdit.
Like every VCL class, it has a constructor and a destructor. The constructor can be used
to dynamically create an edit control. The TEdit class provides other methods derived
from the controls parent or from ancestor classes.

We saw earlier the properties related to the selection of text from an edit control
performed either by you or by a user. Alternatively, the user may want to select the whole
content of the control. To programmatically select the whole text of an edit control, call
the SelectAll() method. Its syntax is:

void __fastcall SelectAll();

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();
}
//---------------------------------------------------------------------------

20.3.4 Edit Control Events
The Edit control is equipped with many events. Some of these events are from its parent
class the TCustomEdit class and some others are from ancestor classes.

The OnEnter() event occurs when a text-based control receives focus. This happens
when the user clicks the edit box, after previously pressing Tab, to give focus to the
control.

The OnChange() event occurs as the user is typing text in the control. This happens as
the user is changing the content of an edit control; this sends a message that the content
of the edit box has changed or has been updated. You can use this event to check, live,
what the user is doing in the edit box. For example, if you create a dialog with a first and
last names edit boxes, you can use another edit box to display the full name. The controls
could be drawn as follows:

Chapter 20: Text-Based Controls Borland C++ Builder Programming

486 Copyright 2003 FunctionX, Inc.


Figure 46: Personal Information

You can implement the OnChange event of the first name edit box as follows:

//---------------------------------------------------------------------------
void __fastcall TfrmPersInfo::edtFirstNameChange(TObject *Sender)
{
edtFullName->Text = edtFirstName->Text + " " + edtLastName->Text;
}
//---------------------------------------------------------------------------
When the second edit box is being edited, you can implement its OnChange event as
follows:
//---------------------------------------------------------------------------
void __fastcall TfrmPersInfo::edtLastNameChange(TObject *Sender)
{
edtFullName->Text = edtFirstName->Text + " " + edtLastName->Text;
}
//---------------------------------------------------------------------------

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.

20.4 The MaskEdit Control

20.4.1Introduction
The MaskEdit is a special Edit object that provides more control over text entered or
displaying in an edit box. The biggest difference between the Edit and the MaskEdit
controls is the masking possibilities available on the latter.

To add a MaskEdit control to your form, from the Additional tab of the Component
Palette, click the MaskEdit button and click on the form.

20.4.2 MaskEdit Characteristics
The most important property of a MaskEdit control, which sets it apart from the
(traditional) Edit control, is its ability to control what the user can and cannot enter in the
text side. To configure this text, on the Object Inspector, click the EditMask field to
reveal its ellipsis button. You have two options:

Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 487

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


Figure 47: The Input Mask Editor Dialog Box

Once there, you have various alternatives. The easiest way is to select one of the
available formats in the Sample Masks list. Once you select a sample, its formatted
mask displays in the Input Mask edit box. If the format is satisfying, you can click
OK. Otherwise, you can add or delete symbols in the Input Mask edit box as you see
fit.
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:


Figure 48: The Open Mask File Dialog Box

Click one file with a .dem extension and click OK. With the new mask in the Input
Mask Editor, examine the samples in the Sample Masks list and select one. You can
still customize any of the available masks.
Chapter 20: Text-Based Controls Borland C++ Builder Programming

488 Copyright 2003 FunctionX, Inc.


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:


Figure 49: The Open Dialog Box

Click Open:


Figure 50: The Mask Edit File

Create a new file following the example. Each mask is typed on its own line and press
Enter at the end of each mask. To save the file, locate the C:\Program
Files\Borland\Cbuilder5\Bin folder. In the Save dialog box, change the Save As Type to
All Files. Type a name in one-word followed by an extension .dem extension. To use
your list of masks, invoke the Input Mask Editor, click Masks Locate the C:\Program
Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 489

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.

Practical Learning: Using the MaskEdit Control
1. Open the employment application we were working on
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. On the Object Inspector, click the Name and type edtDateHired
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:
Chapter 20: Text-Based Controls Borland C++ Builder Programming

490 Copyright 2003 FunctionX, Inc.


Figure 51: The Input Mask Editor Dialog Box

7. 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:

Figure 52: The Masked Text Editor Dialog Box

9. 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;_
Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 491


Figure 53: The Employment Application With Mask Controls

16. Complete the form as follows:

Figure 54: The Employment Application Improved

17. The external bevel around the edit boxes has a Lowered Style while the external one
has a Raised Style. The Close button resides on a Panel control. The OnClick event
of the Close button is implement with the Close(); line.
18. To test the application, on the Debug toolbar, click the Run button.
19. After viewing the form, close it.

20.4.3 MaskEdit Methods
On its own, the MaskEdit control has only two methods: the constructor and the
destructor. The TMaskEdit constructor is used to dynamically create a MaskEdit object.
This requires an instance of a TMaskEdit class. Using the new operator, specify the
owner of the control. You must also specify the parent of the variable. Here is an
example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
Chapter 20: Text-Based Controls Borland C++ Builder Programming

492 Copyright 2003 FunctionX, Inc.

{
TMaskEdit *Mascot = new TMaskEdit(Form1);
Mascot->Parent = Form1;
}
//---------------------------------------------------------------------------

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.

20.4.4 MaskEdit Events
The main event of the MaskEdit control occurs when the content of the control has been
altered. The compiler takes care of validating the characters and symbols while the user is
editing the edit box. When the user has finished and presses Enter or Tab to possibly shift
the focus to another control, a notification is sent to the operating system. If the value
entered in the control does not conform to the EditMask property, an error is thrown and
the application may stop responding. For this reason, you should use the MaskEdit
control only when appropriately needed; or you should write an event handler or function
that would deal with errors of this control.

20.5 The IP Address Control

20.5.1Introduction
The IP Address control is an object that allows the user to enter an IP address or you to
retrieve one. The control appears like an edit box or a masked edit box divided in four
(semi-independent) sections:



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.

20.5.2 Operations on an IP Address Control
To create an IP Address control, you can either use the traditional Win32 approach or
create your own component that would implement the behavior set by the Win32 API.
Because in this book we will not create custom components, we will use the Win32
technique. Therefore, to create an IP Address control, call the CreateWindow() or the
CreateWindowEx() function and specify the class name as WC_IPADDRESS. Here is
an example:
Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 493


//---------------------------------------------------------------------------

#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 hWndI PAddress; // 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);
}
//---------------------------------------------------------------------------


Figure 55: The IP Address Control

Some of the jobs you will perform on an IP Address control consist of checking whether
the control contains an address, setting an IP address in its fields, retrieving the current
address from its fields, or deleting an address from its fields.

Before doing anything on an IP Address control, you may want to check whether its
fields are filled with some entries. To do this, you can send an IPM_ISBLANK message
using the SendMessage() function. Because you are only checking the contents of the
Chapter 20: Text-Based Controls Borland C++ Builder Programming

494 Copyright 2003 FunctionX, Inc.

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 = MAKEI PADDRESS(212, 56, 198, 92);

hWndIPAddress = CreateWindowEx(0,
WC_IPADDRESS,
NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
90, 14, 120, 20,
Handle,
NULL,
HInstance,
Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 495

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 FIRST_IPADDRESS(LPARAM lParam);
BYTE SECOND_IPADDRESS(LPARAM lParam);
BYTE THIRD_IPADDRESS(LPARAM lParam);
BYTE 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, I PM_GETADDRESS, 0,
(LPARAM)(LPDWORD)&CurAddress);

BYTE IPPart1 = FIRST_IPADDRESS((LPARAM)CurAddress);
BYTE IPPart2 = SECOND_I PADDRESS((LPARAM)CurAddress);
BYTE IPPart3 = THIRD_IPADDRESS((LPARAM)CurAddress);
BYTE IPPart4 = FOURTH_IPADDRESS((LPARAM)CurAddress);

ShowMessage("First:\t\t" + AnsiString(IPPart1) + "\n" +
"Second:\t\t" + AnsiString(IPPart2) + "\n" +
"Third:\t\t" + AnsiString(IPPart3) + "\n" +
"Fourth:\t\t" + AnsiString(IPPart4) + "\n" +
"Non-Blank Fields: " + AnsiString(SM));
}
//---------------------------------------------------------------------------

Chapter 20: Text-Based Controls Borland C++ Builder Programming

496 Copyright 2003 FunctionX, Inc.


Figure 56: Decoding an IP Address

When the fields of an IP Address control are empty, the user can fill them up with some
vales. To use it, the user must give it focus and start typing. Once he has provided the
three digits of a field, the caret moves to the next field. This is automatically done. If you
want to programmatically set the focus to a particular field in response to some users
action, you can send an IPM_SETFOCUS message to the control. The fields of the
control are in a zero-based array where the most left field has an index of 0 and the most
right field has an index of 3. To set focus to a field, pass its index as the wParam
parameter. The lParam parameter is not used.

By default, each field of an IP address has a range of values from a minimum whose
default value is 0 and a maximum whose default value is 255. The control is so much
aware of these values that, if the user tries entering a value that is lower than the set
range, the control would not respond. For example if the user tries typing for a negative
value, it would not be accepted. On the other hand, if the user tries to enter a value that is
higher than the range, the control would reset the field to the set maximum. If you want
to control the range of values allowed in a particular field, create it using the
MAKEIPRANGE macro. Its syntax is:

LPARAM MAKEIPRANGE(BYTE low, BYTE high);

The MAKEIPRANGE macro is only used to create a range of values. In order to
actually assign this range to an IP Address control, send an IPM_SETRANGE message
to the control using the SendMessage() function. The wParam parameter is used to
specify the index of the field whose range you want to set. The lParam parameter
contains the new range set by the return value of the MAKEIPRANGE macro. Here is
an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
LPARAM wRangeForField3 = MAKEI PRANGE(52, 212);

hWndIPAddress = CreateWindowEx(0,
WC_I PADDRESS,
NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
90, 14, 120, 20,
Borland C++ Builder Programming Chapter 20: Text-Based Controls

Copyright 2003 FunctionX, Inc. 497

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.

20.5.3 IP Address Control Events
Although the IP Address control is not an edit box, both share a few characteristics such
as their appearance. Like every visual and non-static control, in order to use an IP
Address control, the user must give it focus. This can be done by clicking a field in the
control. When the IP Address control receives focus, it sends an EN_SETFOCUS
message, which produces an event similar to the Edit controls OnEnter() event.

Like the edit control, when the user starts typing a new value in the IP Address control, it
sends an EN_CHANGE message which produces an OnChange() event (similar to that
of the Edit control). If the user starts changing the value of a field, the control sends the
IPN_FIELDCHANGED notification message.

If the user clicks away, which causes the control to lose focus, it sends an
EN_KILLFOCUS message. This produces an event similar to the OnExit() event of the
VCLs Edit control.


Chapter 21: Text-Based Applications Borland C++ Builder Programming

498 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 499


Chapter 21: Text-Based Applications


21.1 The Memo Control

21.1.1Overview
Like the Edit control, the Memo control is used to display or receive text. Unlike the Edit
control, the memo can be used to display multiple lines of text. Like the Edit control, the
Memo control is based on the TCustomEdit class. But there is an intermediary class
between the TCustomEdit and the TMemo class. It is the TCustomMemo class.

To add a memo to a form or another container, from the Standard tab of the Component
Palette, click the Memo button and click on the desired position on the form.

Practical Learning: Creating a Memo Object
1. Start a new a new project with its default form
2. Save it in a new folder called Notice3
3. Save the unit as Exercise and save the project as Notice
4. Change the Caption of the form to Notice and change its Name to frmMain
5. From the Win32 tab of the Component Palette, add an ImageList control to the
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. From the Standard tab of the Component Palette, add a MainMenu control to
the form. Set its Images property to ImageList1 and double-click it to open the menu
designer
7. Right-click the Menu Designer window and click Insert From Template. Double-
click 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:
Chapter 21: Text-Based Applications Borland C++ Builder Programming

500 Copyright 2003 FunctionX, Inc.




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:

Figure 57: Toolbar With Buttons and Caption

12. To add a memo, on the Standard tab of the Component Palette, click the Memo
button and click in the middle of the form

21.1.2 Characteristics of a Memo Control
By default, the Memo control is positioned where you drop it at design time. The Align
property specifies how the memo will be aligned with regard to its container. If the memo
is used as the main area of a text editor application, you should set its Align property to
alClient.

The user mostly reads and/or enters text in the memo when interacting with the control.
At design time, you can set the text that would display when the memo comes up. To
enter this text, on the Object Inspector, click the Lines field to reveal its ellipsis button
that allows you to open the String List Editor dialog box. If you want the control to be
empty at startup, delete the content of the String List Editor and click OK. Otherwise,
type the desired text and click OK.

The user, on the other hand has the ability to type text to alter the content of the memo
box. This is possible only if the ReadOnly property is set to true, which is the default. If
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 501

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.
Chapter 21: Text-Based Applications Borland C++ Builder Programming

502 Copyright 2003 FunctionX, Inc.


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 { taLeftJ ustify, taRightJ ustify, taCenter };

This property works exactly as we reviewed it for the Edit control.

As the user enters text in a memo box, the compiler considers that a paragraph starts from
the user typing a character until he or she presses Enter. Therefore, a paragraph could be
an empty space, a character, a word, a line of text, a whole page or an entire book.
Depending on the width of the Memo control, the text is incrementally added to the right
side of each previous character. If the caret gets to the right border of the control, the text
automatically continues to the next line, although it is still considered as one paragraph.
To start a new paragraph, the user has to press Enter. The ability for the text to continue
on the next line when the caret encounters the right border of the memo is controlled by
the WordWrap property whose default Boolean value is set to true. If you do not want
text to wrap to the subsequent line, set the WordWrap property to false. You can also set
it programmatically as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->WordWrap = False;
}
//---------------------------------------------------------------------------

Practical Learning: Configuring a Memo Control
1. 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. Delete the Lines line and click OK
4. Change its Name to mmoNotice
5. Make sure the WantTabs property is set to true and set the WantTabs property to
true

21.1.3 Memo Methods
The Memo control is based on the TMemo class. Like all VCL controls, TMemo has a
constructor and a destructor. The constructor is mostly used to dynamically create a
memo box. To do this, declare a pointer to TMemo and use the new operator to specify
its owner. You must also specify the parent of the variable.Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnNotesClick(TObject *Sender)
{
TMemo *Notes = new TMemo(Form1);
Notes->Parent = Form1;
Notes->Left = 350;
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 503

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().

Practical Learning: Configuring a Memo control
1. 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. Execute the program. Test cutting, copying, and pasting text
Chapter 21: Text-Based Applications Borland C++ Builder Programming

504 Copyright 2003 FunctionX, Inc.

7. Close it and save all

21.1.4 Memo Events
Like the edit control, the only event that the Memo control can claim on its owns is the
OnChange() event which is a TNotifyEvent type. This occurs when the user changes the
content of the control. This event is useful if you want to display a notification that the
text is not as it was when the form opened.

When the application focus moves from one to another control or the application itself to
a control, the OnEnter event is fired. When the control looses focus, an OnExit() event is
fired.

21.2 The Rich Text

21.2.1Introduction
Text is considered rich if it can afford a series of colorful and multi-style features that
make it more attractive that a regular ASCII text. Such a text can have some of its
sections in different colors. Its paragraphs can have customized attributes or arranged
independent of each other. Although you can create a complete rich but static text, the
common use of a rich text is to let the users process most of the formatting they need.

In VCL applications, a rich text is based on the TRichEdit class. To add rich text to your
application, you can use the RichEdit button from the Win32 tab of the Component
Palette. After adding a RichEdit control to a form, you can use the Object Inspector to
customize it, using its Properties.

Practical Learning: Introducing the RichEdit Control
1. Start a new application with its default form
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. Save the unit as Main and save the project as Editor
5. Change the Caption of the form to Editor Untitled and change its Name to
frmMain
6. Set the ShowHint property to true
7. In the header file, declare a private AnsiString variable named CurrentFileName
//---------------------------------------------------------------------------
#ifndef MainH
#define MainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TfrmMain : public TForm
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 505

{
__published: // IDE-managed Components
private:
AnsiString CurrentFileName; // User declarations
public: // User declarations
__fastcall TfrmMain(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TfrmMain *frmMain;
//---------------------------------------------------------------------------
#endif
8. In the constructor of the form, initialize the variable as:
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmMain *frmMain;
//---------------------------------------------------------------------------
__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
// The following string is not allowed for a Windows file
// This will allow us to know when the name of the file has been changed
CurrentFileName = "<Not Allowed>";
}
//---------------------------------------------------------------------------
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;
}
}
//---------------------------------------------------------------------------
11. On the Win32 tab of the Component Palette, click RichEdit and click in the
body of the form. Change its Name to rchEditor
12. On the Win32 tab of the Component Palette, double-click ImageList and, on
the form, double-click ImageList1
Chapter 21: Text-Based Applications Borland C++ Builder Programming

506 Copyright 2003 FunctionX, Inc.

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
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 507

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
Chapter 21: Text-Based Applications Borland C++ Builder Programming

508 Copyright 2003 FunctionX, Inc.

33. Click Filter and click its ellipsis button
34. Complete the Filter Editor dialog box as follows:


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;
}
//---------------------------------------------------------------------------
39. Press F12 to display the form. Save the project.
40. To proceed with the traditional Save As implementation, on the Component Palette,
click Dialogs. Click the SaveDialog button and click on the form. While the
new SaveDialog1 control is still selected, on the Object Inspector, click the
Properties tab and click DefaultExt. Type rtf
41. Click Filter and click its ellipsis button . Complete the Filter Editor dialog box as
follows:
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 509


Figure 58: The Filter Editor Dialog Box

42. Click OK
43. Click Title, type Save File As and press Enter.
44. Double-click ActionList1 to return to the ActionList Editor window. On the left
frame, click File. On the right frame, click FileSave1 and, in the Object Inspector,
click the Events tab. Double-click the empty box on the right side of OnExecute and
implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::FileSaveExecute(TObject *Sender)
{
// Find out if the current document has never been saved
// but is not empty
if( rchEditor->Modified && CurrentFileName == "<Not Allowed>" )
{
// Since the document is dirty, display the Save As dialog box
if( SaveDialog1->Execute() )
{
// Retrieve the new name file and display
// the file in the rich edit control
rchEditor->Lines->SaveToFile(SaveDialog1->FileName);
// Change/Update the global and complete name of the file,
// including its path
CurrentFileName = SaveDialog1->FileName;
// Extract the name of the file
AnsiString FName = ExtractFileName(CurrentFileName);
// Display the name of the current file on the title bar
Caption = "Editor - " + FName;
}
}
else
{
// It appears that this document already had a name
// but the document was previously modified
// Therefore, simply save it internally
rchEditor->Lines->SaveToFile(CurrentFileName);
}
}
Chapter 21: Text-Based Applications Borland C++ Builder Programming

510 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------
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"
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 511

"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 . Complete the Filter Editor dialog box as
follows:
Chapter 21: Text-Based Applications Borland C++ Builder Programming

512 Copyright 2003 FunctionX, Inc.


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
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 513

AnsiString FName = ExtractFileName(CurrentFileName);
// Display the file name on the title bar
Caption = "Editor - " + FName;
// Reset the Modified flag of the rich edit control
rchEditor->Modified = False;
}
}
// 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 button
and click the form.
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
Chapter 21: Text-Based Applications Borland C++ Builder Programming

514 Copyright 2003 FunctionX, Inc.


Figure 59: The Menu Designer

64. Click the box on the right side of File. Right-click an empty area in the Menu Editor
and click Insert From Template.
65. In the Insert Template dialog box, double-click Edit Menu.
66. Delete the Paste Special, Go To, Links, and Object menu items
67. In the Edit menu of the Menu Designer, Set the Actions as follows: Undo =
EditUndo, Repeat <command> = EditRedo, Cut = EditCut1, Copy = EditCopy1,
Paste = EditPaste1, Find = SearchFind1, Replace = SearchReplace1
68. Using the empty boxes under the Replace menu item, Add the following actions:
SearchFindFirst1, SearchFirstNext1, EditDelete1, EditSelectAll1. Arrange the menu
item to have the following order:
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 515


Figure 60: Menu Designer

69. Click the box right to Edit. In the Object Inspector, click Caption and type &View
and press Enter
70. Click View and click the box under View. Using the Object Inspector, set its Action
to ViewStandard. Set the Action of the next empty box to ViewFormatting. Set the
action of the next empty box to ViewStatusBar
71. Close the Menu Editor
72. From the Standard tab of the Component Palette, click PopupMenu and click
on the form
73. On the form, right-click PopupMenu1 and click Menu Designer
74. As the first item is selected, on the Object Inspector, set the Action to EditCut1. Set
the Action of the second menu item to EditCopy1. Set the Action of the third menu
item to EditPaste1.
75. Set the Caption of the 4th menu item to - to add a separator.
76. Set the 5th menu item to ColorSelect1. Set the new menu items Action to FontEdit1.
Set the Action of the next item to RichEditBullets1
77. Close the Menu Designer
78. From the Win32 property page of the Component Palette, click ToolBar and
click an empty area on the form.
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.
Chapter 21: Text-Based Applications Borland C++ Builder Programming

516 Copyright 2003 FunctionX, Inc.

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:

Figure 61: The Editor Applicaton Toolbar

84. Add the buttons and separators to the bottom toolbar and set their Action values to
the RichEditBold1, RichEditItalic1, RichEditUnderline1, RichEditStrikeOut1, a
separator, RichEditBullets1, FormatNbr, a separator, RichEditAlignLeft1,
RichEditAlignCenter1, and RichEditAlignRight1, a separator
85. Right-click the bottom toolbar again and click New Button. Set the ImageIndex to 5
(FirstIndex) and change its Name to btnFirstIndent
86. Add a New Button to the bottom toolbar. Set the ImageIndex to 6 and change its
Name to btnIndentLeft
87. Add one more button for the ImageIndex 7 and named btnIndentRight

Figure 62: The Editor Application Toolbar Expanded

88. From the Win32 tab of the Component Palette, click StatusBar and click an
empty area on the form
89. Using the Object Inspector, change its Name to StatusBar
90. On the form, double-click the status bar
91. In the Panels Editor, right-click and click Add and, on the Object Inspector, set the
Width to 320
92. Right-click in the Panels Editor again and click Add. Change the Width to 100
93. Right-click the Panels Editor again and click Add.
94. Close the Panels Editor and save the project
95. In the Class Explorer, right-click TfrmMain and click New Method...
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 517


Figure 63: The Add Method Dialog Box - ShowHint

96. Set the Method Name to ShowHints. Specify its Argument as TObject *Sender
and the Function Result to void. Click the __fastcall check box and click OK.
97. Implement the method as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::ShowHints(TObject * Sender)
{
StatusBar->Panels->Items[0]->Text = Application->Hint;
}
//---------------------------------------------------------------------------
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;
Chapter 21: Text-Based Applications Borland C++ Builder Programming

518 Copyright 2003 FunctionX, Inc.

}
//---------------------------------------------------------------------------
99. Press F12 to display the form
100. On the form, double-click ActionList1 to open the Action List Editor. In the left
frame, click View. In the right frame, click ViewStandard. In the Object Inspector,
click the Events tab. Double-click the event side of the OnExecute field and
implement its event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::ViewStandardBarExecute(TObject *Sender)
{
tbrStandard->Visible = !tbrStandard->Visible;
ViewStandard->Checked = !ViewStandard->Checked;
}
//---------------------------------------------------------------------------
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;
}
//---------------------------------------------------------------------------
103. Save your project
104. On the form, click the rich edit control to select it. In the Properties tab of the Object
Inspector, click the Font field. Then click the ellipsis button
105. On the Font dialog box, scroll down in the Font combo box and select Times New
Roman. In the Font Style combo box, select Regular. In the Size combo box, click
10. Make sure no style is selected in the Effects section and make sure the Color
combo box displays Black:
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 519


Figure 64: The Font Dialog Box

106. Click OK
107. Save All

21.2.2 Rich Text Implementation
The most fundamental property on any text-based control is the text it holds, for a
RichEdit, the text is stored in objects called lines and represented by the Lines property.
For a RichEdit control, a Lines item is in fact an individual object based on the TStrings
class. We will learn many techniques of manipulating a TStrings object when we study
lists.

For a text-based control, a paragraph is a series of words that start with a letter or empty
space until the flow of text is interrupted, which is usually considered a carriage return, or
simply the end of the document. The management of such a paragraph is performed for a
TRichEdit object using the Paragraph property. The paragraph is actually controlled by a
class called TParaAttributes. By itself, this paragraph controls the alignment of the
block, whether the block is part of a bulleted list, and such details as Tab measurements
or indentation. To set or change the properties of a paragraph, you must first select it. To
select a paragraph, you do not need to formally select it or any portion of its text. As long
as the cursor is positioned inside of the paragraph, any paragraph attribute you set or
change would apply to the whole paragraph. To manipulate more than one paragraph at
the same time, you or your user must select them. The paragraphs do not need to be
wholly selected. As long as a section is selected on each, the paragraphs are considered
selected.

The most common property of a paragraph is its alignment, which states whether the
paragraph is positioned to the left, the center, or the right. This capability is controlled by
the Alignment property. Its three possible values are taLeftJustify, taCenter, and
taRightJustify. To align text at design time, select the desired value using the Alignment
property on the Object Inspector. Unfortunately, this would apply to the whole contents
of the RichEdit control. Most of the time, you will want to let users change a paragraph's
alignment while they are using your application. To change the alignment at run time,
Chapter 21: Text-Based Applications Borland C++ Builder Programming

520 Copyright 2003 FunctionX, Inc.

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 = taLeftJ ustify;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnCenterClick(TObject *Sender)
{
rchEditor->Paragraph->Alignment = taCenter;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnAlignRightClick(TObject *Sender)
{
rchEditor->Paragraph->Alignment = taRightJ ustify;
}
//---------------------------------------------------------------------------

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)
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 521

{
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.

Practical Learning: Creating a RichEdit-Based Application
1. On the form, click rchEditor to select the RichEdit control
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. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::FontEdit1BeforeExecute(TObject *Sender)
{
// Get the characteristics of the select text
// Apply them to the Font dialog box
FontEdit1->Dialog->Font->Name = rchEditor->SelAttributes->Name;
FontEdit1->Dialog->Font->Style = rchEditor->SelAttributes->Style;
FontEdit1->Dialog->Font->Size = rchEditor->SelAttributes->Size;
FontEdit1->Dialog->Font->Color = rchEditor->SelAttributes->Color;
}
//---------------------------------------------------------------------------
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.
Chapter 21: Text-Based Applications Borland C++ Builder Programming

522 Copyright 2003 FunctionX, Inc.

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. Close the Menu Designer.
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;
}
//---------------------------------------------------------------------------
12. Save All

21.2.3 Rich Text Management
Because it can be asked to perform various and sometimes complex assignments, a rich
edit control can process two categories of messages: those originally built-in the control
and those that have been added over the years. This is why you may have to first load a
rich edit DLL from the operating system because the control has mostly been updated
since you installed your programming environment. All of the basic functionality of a
text-based control is natively supported in the rich edit control as we have seen or used it
so far. Common operations include cutting or copying from another document, pasting
text to the current document, formatting characters and paragraphs. The new release of
the control may have added functionality that the version in your VCL implementation
does not have.

To allow users to print its content, the RichEdit control is equipped with the Print()
method. This method only requires the name of the document that is being printed.

One of the regular actions users perform on a document is to do, undo, or redo something
on the application. The ability to undo an action is already implemented in the original
version of the rich edit control. To all a user to redo an action, you can send an
EM_REDO message to the control using the SendMessage() function. The wParam and
the lParam parameters are not used and must be passed as 0.

Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 523

Practical Learning: Implementing Rich Text Messages
1. 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. Test the application and return to Bcb

21.2.4 Text Formatting
Advanced text formatting on a Microsoft rich edit control is performed using the
CHARFORMAT or the CHARFORMAT2 structures. Because we are interested in the
latest features, we will use CHARFORMAT2. It is defined as follows:

typedef struct _charformat2 {
UINT cbSize;
DWORD dwMask;
DWORD dwEffects;
LONG yHeight;
LONG yOffset;
COLORREF crTextColor;
BYTE bCharSet;
BYTE bPitchAndFamily;
TCHAR szFaceName[LF_FACESIZE];
WORD wWeight;
SHORT sSpacing;
COLORREF crBackColor;
LCID lcid;
DWORD dwReserved;
SHORT sStyle;
WORD wKerning;
BYTE bUnderlineType;
BYTE bAnimation;
BYTE bRevAuthor;
Chapter 21: Text-Based Applications Borland C++ Builder Programming

524 Copyright 2003 FunctionX, Inc.

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).

Practical Learning: Formatting Text
1. Display the main form. Double-click ImageList1. From the resources that
accompany this book, add the AllCaps,
2. Right-click the bottom toolbar and click Separator
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. Test the application and return to Bcb

21.2.5 Paragraph Formatting
Besides the regular techniques provided by the TRichEdit class, paragraph formatting on
a rich edit control can be performed using the PARAFORMAT or the
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 525

PARAFORMAT2 structures. Because everything available in the first is implemented in
the second, we will use the PARAFORMAT2 structure. It is defined as follows:

typedef struct _paraformat {
UINT cbSize;
DWORD dwMask;
WORD wNumbering;
WORD wEffects;
LONG dxStartIndent;
LONG dxRightIndent;
LONG dxOffset;
WORD wAlignment;
SHORT cTabCount;
LONG rgxTabs[MAX_TAB_STOPS];
LONG dySpaceBefore;
LONG dySpaceAfter;
LONG dyLineSpacing;
SHORT sStyle;
BYTE bLineSpacingRule;
BYTE bOutlineLevel;
WORD wShadingWeight;
WORD wShadingStyle;
WORD wNumberingStart;
WORD wNumberingStyle;
WORD wNumberingTab;
WORD wBorderSpace;
WORD wBorderWidth;
WORD wBorders;
} PARAFORMAT2;
#define wEffects wReserved

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.

Practical Learning: Formatting Paragraphs
1. 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:
Chapter 21: Text-Based Applications Borland C++ Builder Programming

526 Copyright 2003 FunctionX, Inc.


Figure 65: The Editor Application - Paragraph

9. 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:

Figure 66: The Edit Tab Order Dialog Box

10. While the dlgParagraph form is still displaying, on the main menu, click File ->
Include Unit Hdr... In the Use Unit dialog box, make sure Main is selected and click
OK
11. Click the Events tab of the Object Inspector and double-click the empty field of
OnActivate
12. Implement the event as follows:
//---------------------------------------------------------------------
void __fastcall TdlgParagraph::FormActivate(TObject *Sender)
{
// Make sure the values of the edit boxes
// reflect the indentation of the paragraph in the background
edtLeft->Text = frmMain->rchEditor->Paragraph->LeftIndent;
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 527

edtRight->Text = frmMain->rchEditor->Paragraph->RightIndent;
edtFirstLine->Text = frmMain->rchEditor->Paragraph->FirstIndent;
}
//---------------------------------------------------------------------
13. On the View toolbar, click the View Form button
14. On the View Form dialog box, click frmMain and click OK
15. On the main menu, click File -> Include Unit Hdr...
16. In the Use Unit dialog box, make sure Paragraph is selected and click OK
17. On the main menu of the form, click Format -> Paragraph... and implement its
OnClick event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::Paragraph1Click(TObject *Sender)
{
dlgParagraph->ShowModal();
}
//---------------------------------------------------------------------------
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));
}
//---------------------------------------------------------------------------
19. Test your application and return to Bcb
20. Save your project.

21.3 The Find Dialog Box

21.3.1Introduction
When facing large text, users sometimes need to find a word, a group of words, or a
section inside of the displayed text. Microsoft Windows provides a dialog box that can
help with such a task. It is the Find dialog box:

Chapter 21: Text-Based Applications Borland C++ Builder Programming

528 Copyright 2003 FunctionX, Inc.


Figure 67: The Find Dialog Box

21.3.2 Searching for Text
To search for a word or a group of words in a text, the user must first have a searchable
document. To proceed, the user would call the Find dialog which can be available from a
toolbar button or a menu item. The Find dialog box is equipped with a text box called
Find What. In this text box, the user can type a word or an expression. She can specify
whether the search should look for the whole word or not. This is set using the Match
Whole Word Only check box. If she types a word or an expression and is sensitive to the
case of characters, she can make sure that the dialog box would need to match only the
word or expression with letter exactly as typed, in uppercase and lowercase. After
specifying the options, the user can click the Find Next button. If a match is found, the
found match should be selected and highlighted in the text in the background. The user
can continue searching down the text by clicking Find Next continuously. Once all
matches have been found, or if no match was found, a message box should let the user
know.

By default, the search proceeds from the top section of the document to the bottom. The
user can reverse this direction by clicking the Up radio button in the Direction section.

The Find dialog box is modeless, which allows the user to work on the background text,
such as the found match, without closing the dialog box. After performing a search, the
user can click Cancel.

21.3.3 Word and Expression Search
The Find dialog box is represented in the VCL by the TFindDialog class. To make this
dialog box available at design time, from the Dialogs property page of the Component
Palette, you can click FindDialog and click on the Form.

The word or expression to find is a string known as FindText. If you have a default text
you want to display in the Find What text box when the dialog box comes up, you can
provide it in the FindText property of the Object Inspector. In the same way, when the
user is performing a search, the text specified in the Find What text box is represented as
the FindText value.

Because the Find dialog box is modeless, the user can decide to keep it open while she is
working in the background text. Sometimes, the dialog box can be obstructing.
Fortunately, with code, you can control the location of this dialog box to make it less
annoying. The location of the dialog box is set or controlled using the Position property.
The Position is a TPoint object that specifies the vertical measurement from the top of
the document to the top border of the dialog box, and the horizontal measurement from
the left border of the document to the left border of the Find dialog box. If you want to
control only the vertical distance of the dialog box from the top border of the document,
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 529

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.

Practical Learning: Allowing Text Search
1. 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. Save the project. Test the application and return to Bcb.

21.4 The Replace Dialog Box

21.4.1Overview
Another regular operation users perform on text is to find a word or an expression and
replace it with another word or an expression. This is possible through the use of the
Replace dialog box:

Chapter 21: Text-Based Applications Borland C++ Builder Programming

530 Copyright 2003 FunctionX, Inc.


Figure 68: The Replace Dialog Box


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.
21.4.2 Making Text Replacement Possible
To make it possible for users to find and replace text in a document, the VCL provides
the TReplaceDialog class, which is represented by the ReplaceDialog object from the
Component Palette. Therefore, to make replacement of text available, from the Dialogs
tab, double-click ReplaceDialog.

The ReplaceDialog object uses the same properties as the FindDialog object and adds to
the FindDialog options. Because of its functionality, the FindReplace control adds the
ReplaceText property. This carries the string that would replace the possible found text.

Practical Learning: Allowing Text Replacement
1. Display the ActionList Editor. In the right frame, click SearchReplace1
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. Implement the event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::SearchReplace1ReplaceDialogFind(TObject *Sender)
{
int MatchPos, StartPos, EndPos;
Borland C++ Builder Programming Chapter 21: Text-Based Applications

Copyright 2003 FunctionX, Inc. 531


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");
}
Chapter 21: Text-Based Applications Borland C++ Builder Programming

532 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------
6. Save the project. Test the application and return to Bcb.


Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 533


Chapter 22: Track-Based Controls


22.1 The UpDown Control

22.1.1Overview
A spin button, also called UpDown, is a Windows control equipped with two opposite
arrow buttons or . The user clicks one of the arrow buttons at one time to
increase or decrease the current value of the control. The value held by the control is also
called its position.

The values of a spin button range from a minimum to a maximum. When the up or right
arrow is clicked, the value of the control increases. If the user clicks and holds the mouse
on the up or right pointing pointing arrow button, the value of the control keeps
increasing until it reaches its maximum and then stops. The opposite behavior applies
when the user clicks or holds the mouse on the down or left-pointing arrow button.

Because a spin button is only equipped with arrows, it does not inherently show its value.
Therefore, this control is usually accompanied by another, text-based, control, usually an
edit box, that indicates its position .

Practical Learning: Introducing UpDown Buttons
1. Start a new project with its default form
2. Save it in a new folder called ColorPreview1
3. Save the unit as Exercise and save the project as ColorPreview
4. Change the Caption of the form to Color Previewer and change its name to
frmMain
5. Set the BorderStyle of the form to bsDialog
6. Open Image Editor. Create a new 32 x 32 icon and design it as follows:
Chapter 22: Track-Based Controls Borland C++ Builder Programming

534 Copyright 2003 FunctionX, Inc.


7. Design its equivalent 16 x 16 icon as follows:

8. Save the icon as previewer in the folder of the current project
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
Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 535

12. From the Standard tab of the Component Palette, click Panel and draw a wide
panel in the top section of the form. Delete its caption
13. Change its Name to pnlPreview
14. Using its Color property in the Object Inspector, change its color to clSilver
15. Add a BitBtn to the form and set its Kind to bkClose

16. Save All

22.1.2 Characteristics of an UpDown Control
To create an UpDown button, you can use the UpDown control from the Win32 tab
of the Component Palette. Although not required, this control should be accompanied by
another object that would display the current value of the updown control. The most
commonly used accompanying object is an edit control but you can use any control you
judge appropriate.

After adding the UpDown control on a container such as a form, use the Object Inspector
to control its properties. The most usual way to add the Edit control is to position it on the
left side of the UpDown button If using an edit box or a label, the accompanying control
is usually positioned to the left of the updown object:



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
Chapter 22: Track-Based Controls Borland C++ Builder Programming

536 Copyright 2003 FunctionX, Inc.

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:
Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 537

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.

Practical Learning: Designing an UpDown control
1. From the Standard tab of the Component Palette, click the Edit control and
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. On the Win32 tab, click the UpDown control and click on the right side of the
edit box on the form
4. On the Object Inspector, change its name to updRed
5. Make sure the AlignButton field displays udRight.
6. Click Associate and select edtRed
7. Make sure the Increment field display 1. Change the Max value to 255 and accept
the Min value as 0
8. Change the Position value to 192
Chapter 22: Track-Based Controls Borland C++ Builder Programming

538 Copyright 2003 FunctionX, Inc.

9. On the form, select both the edit and the updown controls. Press Ctrl + C to top.
Then press Ctrl + V to paste
10. Change the name of the new edit control to edtGreen
11. Change the name of the new updown control to updGreen and set its Associate
property to edtGreen (it should be set already but check it)
12. Paste again.
13. Change the name of the new edit control to edtBlue
14. Change the name of the new updown control to updBlue and make sure its Associate
property is set to edtBlue

Figure 69: The Color Previewer Application

15. To test the new controls, press F9
16. Close it and return to Bcb

22.1.3 The UpDown Control Methods
The UpDown control is based on the TUpDown class. This class has only two methods:
a constructor and a destructor. The TUpDown constructor is used to dynamically create
an UpDown button. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
TUpDown *Counter = new TUpDown(this);
Counter->Parent = this;
TEdit *Displayer = new TEdit(this);
Displayer->Parent = this;
Displayer->Left = 16;
Displayer->Top = 16;
Displayer->Width = 32;
Counter->Left = Displayer->Left + Displayer->Width;
Counter->Top = Displayer->Top;
Counter->Min = 12;
Counter->Max = 248;
Counter->Increment = 2;
Counter->Position = 36;
Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 539

Counter->Associate = Displayer;
}
//---------------------------------------------------------------------------

22.1.4 The UpDown Control Events
The main event of the UpDown control occurs when the user clicks one of the arrows.
Whether the user clicks the up pointing arrow to increase or the down pointing arrow to
decrease the value or position of the control, the OnClick() event is fired. The pointing
arrows are represented using the TUDBtnType enumerator that has two values. The up
or right pointing arrow is recognized as btNext while the down or left pointing arrow is
referred to as btPrev.

When the user clicks one of the arrows, you can write code to perform an action
depending on the button that was clicked. Using this OnClick() event, you do not have to
associate the UpDown control with an edit box to display integers; you can use the event
to format the value or configure any other behavior you see fit. For example, instead of
displaying an integer, you can display a floating number, a string, anything, that is
traditionally not allowed.

When the user clicks one of the arrows of the UpDown control, the operating system is
notified just before this action occurs. This notification is done through the
OnChanging() event. This allows you to perform a last minute configuration before the
value or position of the control changes. You can also use this event to deny changing the
value of the control.

The OnChangingEx() event also fires just before the value of the UpDown control
changes. This time, you can set a new value for the control if the change is successful.

It is important and professionally convenient to make sure that the user can use the up
and down arrow keys of the keyboard to increase or decrease the value of the UpDown
control. If the user presses and holds the up arrow key, the UpDown control would be
incrementing its value until either the user releases the key or the control reaches its
maximum limit. Here is an example of how to track the OnMouseUp mouse event of the
UpDown control:

//---------------------------------------------------------------------------
void __fastcall TForm1::UpDown1MouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
ShowMessage("You stopped spinning at " + AnsiString(edtSpin->Text));
}
//---------------------------------------------------------------------------

Practical Learning: Configuring an UpDown control
1. On the form, double-click the most left updown control to access its OnClick event
2. On the form again, double-click the middle updown control
3. Once again, on the form, double-click the right updown control
4. Implement their OnClick events as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::updRedClick(TObject *Sender, TUDBtnType Button)
Chapter 22: Track-Based Controls Borland C++ Builder Programming

540 Copyright 2003 FunctionX, Inc.

{
TColor CurrentColor = TColor(RGB(updRed->Position,
updGreen->Position,
updBlue->Position));

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. Test the application

Figure 70: The Color Previewer Application Completed

6. Close it and return to Bcb

22.2 The Spin Button

Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 541

22.2.1Characteristics of a Spin Button
The Spin button is a Windows control used to increase and decrease values by clicking an
up or a down buttons. Visually the spin button resembles the UpDown control. There
have some differences with regard to their respective configuration.

To add a spin button to your application, use the CSpinButton from the Samples tab of
the Component Palette. You can place the control on a form or any other container.

Two of the properties that differentiate the CSpinButton from the UpDown controls is
that the CSpinButton control allows you to add your own button indicators as bitmaps.
To specify which bitmap must point up on the control, click the ellipsis button on the
UpGlyph field of the Object Inspector. You can also specify a bitmap that would point
down using the DownGlyph property:



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:
$20/CD if the customer is ordering less than 20 units
$15/CD if the customer is ordering up to 50 units
$12/CD if the customer is ordering up to 100 units
$8/CD if the customer is ordering up to 500 units
$5/CD for any order over 500 units

Practical Learning: Introducing the Spin Button
1. Start a new project with irs default form
2. Save it in a new folder named CDPublisher1
3. Save the unit as Exercise and save the project as CDPublisher
4. Change the Caption of the form Compact Disc Publisher
5. Change its Name to frmMain and set its BorderStyle to bsDialog
6. Design the form by adding the following controls:

Figure 71: The Compact Disc Publisher Application
Chapter 22: Track-Based Controls Borland C++ Builder Programming

542 Copyright 2003 FunctionX, Inc.



Control Caption or Text Name
Bevel
Label Number of Items:
Edit 0 edtQuantity
Label Unit Price
Edit 20.00 EdtUnitPrice
Label Total Price:
Edit 0.00 EdtTotal
Button Kind = bkClose

7. Save all

22.2.2 The Spin Button Methods
The spin button is equipped with a constructor and a destructor. The TCSpinButton
class constructor is typically used to dynamically create a spin button. To do that,
declare a pointer to a TCSpinButton class, specifying the owner of the control and its
parent. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TCSpinButton *LetMe = new TCSpinButton(Form1);
LetMe->Parent = Form1;
}
//---------------------------------------------------------------------------

22.2.3 The Spin Button Events
The biggest difference between an UpDown control and a spin button is the way each
handles the incrementing and decrementing of its values. Simply put, the spin button does
not have a value to the sense of a progress control; you must set, assign, and configure
the value or position of the spin button on your own. While the main event of an
UpDown control occurs when the user clicks one of its arrows, to apply a specific
behavior, you can either consider the whole event or find out what button was clicked.
The spin button uses two different events for each button. Clicking the up pointing arrow
fires the OnUpClick() event while the OnDownClick event fires when the user clicks
the down pointing arrow.

Practical Learning: Using the Spin Button
1. On the Component Palette, click the Samples tab
2. Click CspinButton and flick on right side of the Number of Items edit box on
the form
3. On the Object Inspector, change its name to spnQuantity
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:
Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 543

private:
int Value;
void __fastcall EvaluatePrice(); // User declarations
5. 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--;
Chapter 22: Track-Based Controls Borland C++ Builder Programming

544 Copyright 2003 FunctionX, Inc.

else
edtQuantity->Text = 0;

EvaluatePrice();
}
//---------------------------------------------------------------------------
9. To test the form, on the main menu, click Run -> Run

Figure 72: The Compact Disc Publisher Application Completed

10. After experimenting with the spin button, close the form

22.3 The Spin Edit Control

22.3.1Introduction
Without a doubt, the SpinEdit control is the easiest spin control of the three sets of
UpDown controls. There is hardly any configuration to do.

To add a SpinEdit to your application, from the Samples tab of the Component Palette,
click the CSpinEdit button and click in the desired location on the form.

22.3.2 Characteristics of the SpinEdit Control
The CSpinEdit control is used to display integer values in the edit box section of the
control. The displaying value is controlled by the MinValue and the MaxValue
properties that can be set in the Object Inspector. You can also specify their values
programmatically as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
CSpinEdit1->MinValue = -12;
CSpinEdit1->MaxValue = 228;
}
//---------------------------------------------------------------------------

By default, the values of a SpinEdit control increase by 1 and decrease by 1. If that
value is not appropriate for your application, you can change it using the Increment
property in the Object inspector.When a SpinEdit control appears at startup, the control
displays the value 0 even if its MinValue is set to a value other than 0. To set the default
Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 545

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.

22.3.3 The Spin Edit Methods
Like all VCL controls, the CSpinEdit object has a constructor and a destructor, based on
the TCSpinEdit class. The constructor is typically used to dynamically create the control
at runtime. To do this, declare a pointer to a TCSpinEdit class. You must specify the
owner and the parent of the control. You can create such a control in an event or a
function. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TCSpinEdit* Staple = new TCSpinEdit(this);
Staple->Parent = this;
}
//---------------------------------------------------------------------------

You must include the CSPIN.h header file to the form or unit that would be using the
Spin Edit control.

22.4 Track Bars

22.4.1Introduction
A track bar is a Windows control used to slide a small bar or pointer, also called a thumb,
along a continuous line. To use the trackbar, the user can drag the thumb in one of two
directions. This changes the position of the thumb. The user can also click a position
along the control line to place the thumb at a desired location. Alternatively, when the
trackbar has focus, the user can use the arrow keys to move the thumb.

As far as positions are concerned, there are two types of track bars, depending on te
orientation: horizontal or vertical:

Chapter 22: Track-Based Controls Borland C++ Builder Programming

546 Copyright 2003 FunctionX, Inc.


Figure 73: Track Bars Orientations

A trackbar is configured with a set of values from a minimum to a maximum. Therefore,
the user can make a selection included in that range. Optionally, a trackbar can be
equipped with small marks called ticks. These can visually guide the user for the
available positions of the thumb. A trackbar can be used as a progress control to help the
user monitor an activity. A trackbar also allows the user to specify a value that conforms
to a range. When equipped with small indicators, also called ticks, a trackbar can be used
to control exact values that the user can select in a range, preventing the user from setting
just any value.

Practical Learning: Introducing Track Bars
1. Start a new project with its default form
2. Save it in a new folder named CarInventory1
3. Save the unit as Exercise and save the project as CarInv
4. Open Image Editor and design 32 x 32 pixels icon and its 16 x 16 associated icon as
follows:


5. Save the icon as CarInvent
6. Using the Project Options dialog box, set the Title to Car Inventory and set the icon
as the above
Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 547

7. Change the name of the form to frmMain and change its caption to Car Inventory
8. Also change its BorderStyle to bsDialog
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
11. In the constructor of the form, initialize the array as follows:
//---------------------------------------------------------------------------
__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
Car[0].Make = "Honda"; Car[0].Model = "Civic";
Car[0].CarYear = 1998; Car[0].Doors = 4;
Car[0].CarPicture = new Graphics::TBitmap;
Car[0].CarPicture->LoadFromFile("Civic.bmp");

Car[1].Make = "Nissan"; Car[1].Model = "Sentra";
Car[1].CarYear = 1997; Car[1].Doors = 4;
Car[1].CarPicture = new Graphics::TBitmap;
Car[1].CarPicture->LoadFromFile("Sentra.bmp");

Car[2].Make = "Ford"; Car[2].Model = "Focus";
Car[2].CarYear = 2002; Car[2].Doors = 4;
Car[2].CarPicture = new Graphics::TBitmap;
Car[2].CarPicture->LoadFromFile("Focus.bmp");

Car[3].Make = "Mercury"; Car[3].Model = "Grand Marquis";
Car[3].CarYear = 2000; Car[3].Doors = 4;
Car[3].CarPicture = new Graphics::TBitmap;
Car[3].CarPicture->LoadFromFile("GdMarquis.bmp");

Car[4].Make = "Kia"; Car[4].Model = "Rio";
Chapter 22: Track-Based Controls Borland C++ Builder Programming

548 Copyright 2003 FunctionX, Inc.

Car[4].CarYear = 1997; Car[4].Doors = 4;
Car[4].CarPicture = new Graphics::TBitmap;
Car[4].CarPicture->LoadFromFile("Rio.bmp");

Car[5].Make = "Ford"; Car[5].Model = "E350";
Car[5].CarYear = 2000; Car[5].Doors = 5;
Car[5].CarPicture = new Graphics::TBitmap;
Car[5].CarPicture->LoadFromFile("E350.bmp");

Car[6].Make = "Ford"; Car[6].Model = "Escort";
Car[6].CarYear = 2002; Car[6].Doors = 4;
Car[6].CarPicture = new Graphics::TBitmap;
Car[6].CarPicture->LoadFromFile("Escort.bmp");

Car[7].Make = "Hyundai"; Car[7].Model = "Elantra";
Car[7].CarYear = 1999; Car[7].Doors = 4;
Car[7].CarPicture = new Graphics::TBitmap;
Car[7].CarPicture->LoadFromFile("Elantra.bmp");

Car[8].Make = "Ford"; Car[8].Model = "Escape";
Car[8].CarYear = 2003; Car[8].Doors = 4;
Car[8].CarPicture = new Graphics::TBitmap;
Car[8].CarPicture->LoadFromFile("Escape.bmp");

Car[9].Make = "Lincoln"; Car[9].Model = "Navigator";
Car[9].CarYear = 2000; Car[9].Doors = 5;
Car[9].CarPicture = new Graphics::TBitmap;
Car[9].CarPicture->LoadFromFile("Navigator.bmp");
}
//---------------------------------------------------------------------------
12. Design the form by adding the following controls
Refer to the text in the edit controls for their name
Add an Image control. Set its Center property to true. Set its Transparent property to
true
Add a BitBtn control and set its Kind to bkClose

Figure 74: The Car Inventory Application

Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 549

13. Save All

22.4.2 Characteristics of a Track Bar
To add a trackbar to a form, from the Win32 tab of the Component Palette, double-click
the TrackBar button .

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;
}
Chapter 22: Track-Based Controls Borland C++ Builder Programming

550 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------

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.

Practical Learning: Implementing a Track Bar
1. On the Win32 tab of the Component Palette, click the TrackBar button and
click on the lower-left section of the form
2. Set its properties as follows:
Height: 40
ThumbLength: 16
TickMarks: tmBoth
Borland C++ Builder Programming Chapter 22: Track-Based Controls

Copyright 2003 FunctionX, Inc. 551


3. Save all

22.4.3 Track Bar Events
A track bar is important when you can get its value and use it in a transaction. The most
fundamental operation you can do is to display its value in an edit or a label controls.
When the value of the track bar changes, we will use its OnChange() event to track its
position and display its value on a label.

Practical Learning: Using Track Bar Events
1. On the form, double-click the trackbar to access its OnChange event
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);
Chapter 22: Track-Based Controls Borland C++ Builder Programming

552 Copyright 2003 FunctionX, Inc.


Image1->Picture->Bitmap = Car[0].CarPicture;
}
//---------------------------------------------------------------------------
4. Test the application

Figure 75: The Car Inventory Application Completed

5. Close it and return to Bcb
6. Save All


Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 553


Chapter 23: Progress-Based Controls


23.1 Timers

23.1.1Introduction
A timer is a non-spatial object that uses recurring lapses of time in a computer or in your
application. To work, every lapse of period, the control sends a message to the operating
system. The message is something to the effect of "I have counted the number of lapses
you asked me to count".

As opposed to the time that controls your computer, a timer is partly but greatly under
your control. Users do not see nor do they use a timer as a control. As a programmer, you
decide if, why, when, and how to use this control.

Practical Learning: Introducing the Timer Control
1. Start a new application with the default form
2. Save it in a new folder named TrafficLight1
3. Save the unit as Exercise and save the project as TrafficLight
4. Change the Caption of the form to Traffic Light
5. Change its BorderStyle property to bsDialog
6. Change its dimensions to Height = 256 and Width = 112
7. From the Statndard tab of the Component Palette, double-click the Panel control

8. Change the properties of the panel as follows:
BevelInner = bvNone
BevelOuter = bvLowered
BevelWidth = 5
Caption = Empty
Color = clBlack
Height = 209
Left = 17
Top = 8
Width = 81
9. While the panel is still selected on the form, from the Additional tab of the
Component Palette, double-click the Shape control
10. Change the following properties for the new Shape:
Brush: Color = clGray
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

554 Copyright 2003 FunctionX, Inc.

Brush: Style = bsSolid
Height = 65
Left = 8
Top = 8
Width = 65
11. On the panel, click the newly added Shape to make sure it is selected. Press Ctrl + C
to copy it to the clipboard
12. Click anywhere on the panel to select it.
13. Press Ctrl + V to paste the Shape
14. While the new Shape is still selected, change its Left value to 8 and its Top value to
72
15. Click an empty area on the panel to select it and press Ctrl + V.
16. While the new shape is still selected, change its Left value to 8 and its Top value to
136
17. Click the panel on the form and ress Ctrl + V again
18. Change the properties of the new shape as follows:
Brush: Color = clRed
Height = 57
Left = 8
Name = shpRed
Shape = stCircle
Top = 12
19. On the form, click the red circle and press Ctrl + C. Then press Ctrl + V to add a new
Shape
20. Change its properties as follows:
Brush: Color = clSilver
Height = 57
Left = 8
Name = shpYellow
Shape = stCircle
Top = 76
21. On the form, click the Panel and press Ctrl + V
22. Change the properties of the new Shape to:
Brush: Color = clSilver
Height = 57
Left = 8
Name = shpGreen
Shape = stCircle
Top = 140
23. Save All

23.1.2 Characteristics of a Timer
The timer in VCL applications is made available through the TTimer class. To add it to
your application at design time, from the System property page of the Component Palette,
click Timer and click on the form.

Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 555

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.

Practical Learning: Using Timer Controls
1. From the System tab of the Component Palette, double-click Timer
2. On the Object Inspector, click the Events tab
3. Although the dialog box will be equipped with the system Close button, we should
provide our own mean of closing the application.
4. On the form, click the red circle.
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. Change the event as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::shpGreenMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
Close();
}
//---------------------------------------------------------------------------
8. Press F12 to display the form
9. Double-click the Timer on the form to access its OnTimer event
10. Change its code as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
// If the current color is red
if( shpRed->Brush->Color == clRed )
{
// Change the color to green
Timer1->Interval = 3500;
shpRed->Brush->Color = clSilver;
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

556 Copyright 2003 FunctionX, Inc.

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;
}
}
//---------------------------------------------------------------------------
11. Test your program

12. Close it and return to Bcb
13. Save All

23.1.3 The Tick Counter
The Win32 API provides a special function used to count a specific number of lapses that
have occurred since you started your computer. This information or counter is available
through the GetTickCount() function. Its syntax is:

DWORD GetTickCount(VOID);

This function takes no argument. If it succeeds in performing its operation, which it
usually does, it provides the number of milliseconds that have elapsed since you started
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 557

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);
}
//---------------------------------------------------------------------------

Practical Learning: Counting the Computer's Ticks
1. Start a new application with the default form
2. Save it in a new folder named CompTicks1
3. Save the unit as Exercise and save the project CompTicks

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. Add a GroupBox control and set its Caption to Elapsed Time
6. Add a Timer control from the System tab of the Component Palette. Set its
Interval to 20
7. Add an Edit control and change its Name to edtComputerTime
8. Add another Edit control and change its Name to edtApplicationTime
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);
};
//---------------------------------------------------------------------------
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

558 Copyright 2003 FunctionX, Inc.

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();
}
//---------------------------------------------------------------------------
11. Press F9 to test the application

12. After testing the application, close it and return to Bcb
13. To make the values easier to read, change the form as follows. Delete both Edit
boxes and replace them with Label controls named lblComputerTime and
lblApplicationTime respectively:
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 559


14. Change the code of the OnTimer event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::Timer1Timer(TObject *Sender)
{
unsigned long CurrentTickValue = GetTickCount();
unsigned int Difference = CurrentTickValue - TimeTheComputerStarted;

unsigned int ComputerHours, ComputerMinutes, ComputerSeconds;
unsigned int ApplicationHours, ApplicationMinutes, ApplicationSeconds;

ComputerHours = (CurrentTickValue / (3600 * 999)) % 24;
ComputerMinutes = (CurrentTickValue / (60 * 999)) % 60;
ComputerSeconds = (CurrentTickValue / 999) % 60;
ApplicationHours = (Difference / (3600 * 999)) % 24;
ApplicationMinutes = (Difference / (60 * 999)) % 60;
ApplicationSeconds = (Difference / 999) % 60;

AnsiString ComputerTime, ApplicationTime;

ComputerTime = IntToStr(ComputerHours) + " hours, " +
IntToStr(ComputerMinutes) + " minutes " +
IntToStr(ComputerSeconds) + " seconds";
ApplicationTime = IntToStr(ApplicationHours) + " hours " +
IntToStr(ApplicationMinutes) + " minutes " +
IntToStr(ApplicationSeconds) + " seconds";

lblComputerTime->Caption = ComputerTime;
lblApplicationTime->Caption = ApplicationTime;
}
//---------------------------------------------------------------------------
15. Test the application

16. After testing the application, close it
17. Save All
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

560 Copyright 2003 FunctionX, Inc.


23.2 Progress Bars

23.2.1Overview
A progress bar is a control that displays (small) rectangles that are each filled with a
color. These (small) rectangles are separate but adjacent each other so that, as they
display, they produce a bar. To have the effect of a progress bar, not all these rectangles
display at the same time. Instead, a numeric value specifies how many of these (small)
rectangles can display at one time.

There are two types of progress bars and various characteristics they can have. For
example, although most progress bars are horizontal, the control can assume a vertical
orientation



As mentioned already, a progress bar is made of small colored rectangles. These
rectangles can display distinctively from each other although they are always adjacent.
The programmer can also specify what color would fill the small rectangles. To make it
less confusing, all of the small rectangles display in the same color. The small rectangles
can be "glued" to produce a smooth effect, in which case they would not appear distinct.

To create a progress bar, use the ProgressBar control from the Win32 tab.

Practical Learning: Introducting Progress Bars
1. Start a new project with its default form
2. Save it in a new folder named BodyMonitor1
3. Save the unit as Exercise and save the project as BodyMonitor
4. Open Image Editor. Design a 32 x 32 icon and its associated 16 x 16 icon as follows:
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 561



5. Save it as BMon
6. Open the Project Options dialog. In the Application property page, set the title to
Body Monitor Simulation
7. Change the icon to the above
8. Change the forms properties as follows:
Name: frmMain
Caption: Body Monitoring
ShowHint: true
Position: poScreenCenter
9. Save All

23.2.2 Progress Bar Properties
By default, a newly added progress bar assumes a horizontal position. This aspect is
controlled by the Orientation property which is a TProgressBarOrientation
enumerator defined as follows:

enum TProgressBarOrientation { pbHorizontal, pbVertical };

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);
}
//---------------------------------------------------------------------------

As mentioned already, a progress bar appears as a series of small adjacent rectangles. By
default, these rectangles dispplay distinctively. If on the other hand you want to glue
them and produce a smooth bar, use the Smooth Boolean property whose default value is
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

562 Copyright 2003 FunctionX, Inc.

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
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 563

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.

Practical Learning: Creating Progress Bars
1. On the Win32 tab of the Component Palette, click the ProgressBar button and
click on the form
2. Change its properties as follows:
Height: 225
Left: 16
Max: 250
Name: pgrBlood
Orientation: pbVertical
Position: 128
Smooth: true
Top: 24
Width: 18
3. In the same way, add other progress bars and design the form as follows:


Control Name Caption Additional Proeprties
Label lblBlood 000
Label lblHeart 000
Label lblKidney 000
Label lblBrain 000
Label lblLLung 000
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

564 Copyright 2003 FunctionX, Inc.

Label lblRLung 000
Label lblPancreas 000
Label lblLiver 000
Label lblBladder 000
Label lblStomach 000
ProgressBar pgrBlood
Max: 650
Position: 288
ProgressBar pgrHeart
Max: 240
Position: 204
ProgressBar pgrKidney
Max: 450
Position: 120
ProgressBar pgrBrain
Max: 1000
Position: 760
ProgressBar pgrLLung
Max: 750
Position: 428
ProgressBar pgrRLung
Max: 750
Position: 320
ProgressBar pgrPancreas
Max: 800
Position: 224
ProgressBar pgrLiver
Max: 1200
Position: 240
ProgressBar pgrBladder
Max: 550
Position: 350
ProgressBar pgrStomach
Max: 1250
Position: 650
Shape shpBlood
Brush: Color: clGray
Shape: stCircle
Hint: Start Blood Monitoring
Shape shpHeart
Brush: Color: clGray
Shape: stCircle
Hint: Start Heart Monitoring
Shape shpKidney
Brush: Color: clGray
Shape: stCircle
Hint: Start Kidney Monitoring
Shape shpBrain
Brush: Color: clGray
Shape: stCircle
Hint: Start Brain Monitoring
Shape shpLLung
Brush: Color: clGray
Shape: stCircle
Hint: Start Left Lung
Shape shpRLung
Brush: Color: clGray
Shape: stCircle
Hint: Start Right Lung
Shape shpPancreas
Brush: Color: clGray
Shape: stCircle
Hint: Start Pancreas Monitoring
Shape shpLiver
Brush: Color: clGray
Shape: stCircle
Hint: Start Liver Monitoring
Shape shpBladder
Brush: Color: clGray
Shape: stCircle
Shape shpStomach
Brush: Color: clGray
Shape: stCircle
Hint: Start Stomach Monitoring
Bevel
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 565

Label Blood
Label Heart
Label Kidney
Label Brain
Label Lungs
Label Left
Label Right
Label Pancreas
Label Liver
Label Bladder
Label Stomach
Panel pnlClose Close

4. Add 10 timers to the form and configure them as follows:


Control Name Enabled Interval
Timer tmrBlood False 650
Timer tmrHeart False 240
Timer tmrKidney False 450
Timer tmrBrain False 1000
Timer tmrLLung False 750
Timer tmrRLung False 750
Timer tmrPancreas False 800
Timer tmrLiver False 1200
Timer tmrBladder False 550
Timer tmrStomach False 1250
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

566 Copyright 2003 FunctionX, Inc.


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

23.2.3 Progress Bar Methods and Messages
The ProgressBar control is based on the TProgressBar class. Like every VCL class, it is
equipped with a constructor that can be used to dynamically create the control.

We have seen that a progress bar implements its behavior by drawing small adjacent
rectangles. This control does not know and does not decide when to draw these
indicators. Therefore, after creating a progress bar, you must provide a means of
changing its value, that is, a way to increment its position. Although it is usually used to
show the evolution of a task, it does not actually have an internal mechanism to monitor
such an activity. Another control is usually used to trigger this. Nevertheless, when the
value of a progress bar changes, the control refers to the Step property to increment its
Position. Based on this Step value, when it is time to increment, the progress bar calls its
StepIt() method. Its syntax is:

void __fastcall StepIt(void);

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);

Pass the desired incremental value as the Delta argument.

Practical Learning: Implementing Progress Bars
1. 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();
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 567

}
//---------------------------------------------------------------------------
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)
{
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

568 Copyright 2003 FunctionX, Inc.

int BrainLevel = random(1000);

pgrBrain->Position = BrainLevel;

if( BrainLevel > 550 )
shpBrain->Brush->Color = clRed;
else
shpBrain->Brush->Color = clGreen;

lblBrain->Caption = lblBrain->Caption.sprintf("<%d>", BrainLevel-450);
}
//---------------------------------------------------------------------------
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);

Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 569

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 )
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

570 Copyright 2003 FunctionX, Inc.

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)
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 571

{
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:
//---------------------------------------------------------------------------
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

572 Copyright 2003 FunctionX, Inc.

void __fastcall TfrmMain::shpRLungMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( tmrRLung->Enabled )
{
tmrRLung->Enabled = False;
shpRLung->Brush->Color = clGray;
shpRLung->Hint = "Start Right Lung Monitoring";
}
else
{
tmrRLung->Enabled = True;
shpRLung->Hint = "Stop Right Lung Monitoring";
}
}
//---------------------------------------------------------------------------
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:
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 573

//---------------------------------------------------------------------------
void __fastcall TfrmMain::shpBladderMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( tmrBladder->Enabled )
{
tmrBladder->Enabled = False;
shpBladder->Brush->Color = clGray;
shpBladder->Hint = "Start Bladder Monitoring";
}
else
{
tmrBladder->Enabled = True;
shpBladder->Hint = "Stop Bladder Monitoring";
}
}
//---------------------------------------------------------------------------
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();
}
//---------------------------------------------------------------------------
22. Execute the application
23. To test a progress bar, click the shape button under it. To stop it, click its shape
button again
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

574 Copyright 2003 FunctionX, Inc.


24. After using the form, close it and return to Bcb
25. Save All

23.3 Scroll Bars

23.3.1Introduction
A scrollbar is a control that allows the user to navigate a document in two directions by
clicking a button that displays an arrow. The control is equipped with one button at each
of its ends. Between the buttons, there is a (long) bar and on the bar, there is sliding
object called a thumb:

Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 575


Figure 76: Scroll Bars Example

To use a scroll bar, the user can click one of the arrows. This causes the thumb to move
towards the button that was clicked. The user can also click and hold the mouse on a
button. This causes the thumb to move continuously, as long as the button is held down,
towards the button, until the thumb cannot move any farther. The user can also drag the
thumb in one direction to move it or she can click between a button and the thumb. This
causes the thumb to move faster that clicking a button. The thumb of a scroll bar can be
positioned only along the scroll bar, between the scroll bars button.

Based on their orientation, there are two types of scroll bars: horizontal and vertical. The
horizontal scroll bar allows the user to navigate a document left and right. The vertical
scroll bar allows navigating up and down.

Based on their relationship with the parent control or owner, there are two types of scroll
bars: those that are (automatically) associated with their parent or owner and scroll bar
controls that are manually added by the programmer.

23.3.2 Automatically-Added Scroll Bars
To effectively implement their functionality, some controls must be equipped with one or
two scroll bars. As we will see with list-based controls such as list boxes, combo boxes,
tree views, list views, etc, when the items of their list exceed the allocated client area of
the control, the list should display a scroll bar to give access to the hidden part of their
list. This type of automatically added scroll bar is usually positioned on the right side of
the control for most Latin-based languages including US English.

Chapter 23: Progress-Based Controls Borland C++ Builder Programming

576 Copyright 2003 FunctionX, Inc.

The Palette property page of Borland
C++ Builders Environment Options
dialog box shows the Pages list box and
the Components list view. Because
each control has a long list that it
cannot show completely, it is equipped
with a vertical scroll bar. This allows
the user to display the hidden list when
needed.

Figure 77: Dialog Boxes - Environment Options

These types of scroll bars are automatically added by the operating system to the control
that needs it, unless the programmer explicitly prevented their showing.

Some controls are ready to display a scroll bar upon request. Such controls include the
form, the Memo, the RichEdit, the ScrollBox, etc. When designing one of these controls,
you can ask to display or hide either or both scroll bars as you see fit. This type of scroll
bar is implemented through the TScrollingWinControl class.

23.3.3 Text-Based Applications and Scroll Bars
Because they are always likely to display a long text, the Memo and the RichEdit controls
of the VCL are natively ready to display scroll bars, either or both. This is easily done
using the ScrollBars property. It provides four options as follows:

Value Comments
ssNone No scroll bar will be displayed
This is the default value
ssHorizontal A horizontal scroll bar will display at
the bottom of the control or document
ssVertical A vertical scroll bar will display on the
right side of the control or document
ssBoth 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

Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 577

Thanks to rapid application development (RAD) and object-oriented programming
(OOP), you do not have to permanently set the scroll bar(s). You can act in response to
the user doing something and decide when to display or hide either or both scroll bars.

Practical Learning: Using Scroll Bars on a Text-Based Application
1. 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. Test the application. Close it and return to Bcb

23.4 The Scroll Bar Control

23.4.1Introduction
Microsoft Windows provides another type of scroll bar, considered a complete control in
its own right. Like all other controls, this one must be explicity created and is not added
automatically but it provides most of the same basic functionality as the operating system
automatically added scroll bars.

To create a scroll bar control, on the Standard tab of the Component Palette, click the
ScrollBar button and click a container. The scroll bar is one of the earliest controls
of the Microsoft Windows operating system. To create it using the Win32 approach call
the CreateWindow() or the CreateWindowEx() functions and specify the class name as
SCROLLBAR. If you decide to create it in Win32, you would need to configure all of its
functionality. In the VCL, all operations that are needed on a scroll bar control are ready
to be used.

Chapter 23: Progress-Based Controls Borland C++ Builder Programming

578 Copyright 2003 FunctionX, Inc.

Practical Learning: Using Scroll Bars
1. Start a new project with the default form
2. Save it in a new folder named BodyTag1
3. Save the unit as Main and save the project as BodyTag
4. Change the following properties for the form:
BorderStyle = bsDialog
Caption = Body Tag Formatter
Height = 316
Name = frmMain
Position: poScreenCenter
ShowHint = true
Width = 350
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 16 x 16


6. Save the icon as BodyTag in the folder of the current project
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. Design the form as follows:
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 579



Control Name Caption or
Text
Other Properties
Label Preview
Bevel Shape: bsBottomLine
Panel pnlPreview Color: clWhite
Hint: Current Color
BitBtn Kind: bkClose
GroupBox Hexadecimal
Label Red
Edit edtHexaRed FF
Label Green
Edit edtHexaGreen FF
Label Blue
Edit edtHexaBlue FF
GroupBox Numeric
Label Red
Edit edtNumRed 255
Label Green
Edit edtNumGreen 255
Label Blue
Edit edtNumBlue 255
Label Color
Edit edtBody Hint: Hexadecimal formula of
current color

9. Save All

23.4.2 Characteristics of the Scroll Bar Control
By default, when you add a scroll bar to a form, the control assumes the horizontal
position. This position is controlled by the Kind property whose default value is
sbHorizontal. To change the direction of the control to vertical, set this property to
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

580 Copyright 2003 FunctionX, Inc.

sbVertical. The Kind property is controlled by the TScrollBarKind enumerator defined
as follows:

enum TScrollBarKind { sbHorizontal, sbVertical };

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;
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 581


Figure 78: The Position of a Scroll Bar

When the user clicks an arrow of a scrollbar, the bar slides one unit. This unit is called
SmallChange and is set to 1 by default. If you want the bar to slide more than one unit,
change the SmallChange property to an integer value between Min and Max. The higher
the value, the faster the sliding would occur because the bar would jump by
SmallChange units.

There are two main ways the user can scroll faster using scrollbars: by pressing either
page buttons or by clicking the scrolling region. The amount covered using this technique
is controlled by the LargeChange property. Once again, the user can scroll only between
Min and Max. This means that you can set this value only to an integer from Min to
Max. To find the scrolling amount, the compiler would divide the actual scrolling range
(the difference between the Max and Min) by the LargeChange value. When the user
clicks in the scrolling region or presses the Page Up or Page Down keys, the bar would
jump by LargeChange up to the scrolling amount value. You can change the
LargeChange property programmatically as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ScrollBar1->Kind = sbVertical;
ScrollBar1->Min = -122;
ScrollBar1->Max = 240;
ScrollBar1->Position = 38;
ScrollBar1->SmallChange = 4;
ScrollBar1->LargeChange = 20;
}
//---------------------------------------------------------------------------

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:


Chapter 23: Progress-Based Controls Borland C++ Builder Programming

582 Copyright 2003 FunctionX, Inc.

Practical Learning: Using Scroll Bars
1. 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. On the Object Inspector, change its properties as follows:
Kind = sbVertical
Max = 255
Min = 0
Name = scrRed
Position = 255
3. On the form, click the scrollbar control to select it
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:

Figure 79: The Body Tag Formatter Application

8. Save All

23.4.3 Methods to Manage a Scroll Bar
The ScrollBar control is equipped with a constructor, TScrollBar, that is typically used
to dynamically create a scrollbar. To do this, declare a pointer to a TScrollBar object.
Specify the owner of the control, which is usually the form on which it is positioned.
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 583

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);
}
//---------------------------------------------------------------------------

23.4.4 Scroll Bar Events
The most fundament event of a scrollbar occurs when the bar slides. This happens when:

The user clicks one of the arrows.
The user presses one of the arrow keys.
The user presses Page Up or Page Down

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;
}
//---------------------------------------------------------------------------

Chapter 23: Progress-Based Controls Borland C++ Builder Programming

584 Copyright 2003 FunctionX, Inc.

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};

The syntax of the OnScroll event is:

void __fastcall (__closure *TScrollEvent)(System::TObject* Sender,
TScrollCode ScrollCode,
int &ScrollPos);

Practical Learning: Using the Scroll Bar Events
1. 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);

Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 585

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);

}
//---------------------------------------------------------------------------
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. Test the application
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);
}
//---------------------------------------------------------------------------
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

586 Copyright 2003 FunctionX, Inc.

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 = I ntToStr(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:
Borland C++ Builder Programming Chapter 23: Progress-Based Controls

Copyright 2003 FunctionX, Inc. 587

//---------------------------------------------------------------------------
void __fastcall TfrmMain::edtBlueKeyPress(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';

scrBlue->Position = 255 - edtBlue->Text.ToInt();
scrBlueChange(Sender);
}
//---------------------------------------------------------------------------
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);
}
//---------------------------------------------------------------------------
12. Test the form
Chapter 23: Progress-Based Controls Borland C++ Builder Programming

588 Copyright 2003 FunctionX, Inc.


Figure 80: The BodyTag1 Application

13. After using it, close it and save the project



Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 589


Chapter 24: Selection-Based Controls


24.1 Radio Buttons

24.1.1Introduction
A radio button is a control that appears as a round box . Normally, a radio button is
accompanied by one or more other radio buttons that appear and behave as a group. The
user decides what button is valid by clicking one of them. When a button has been
clicked, its round box fills with a (big) dot: . When one button is selected, the other
round buttons of the (same) group are empty. The user can select another by clicking a
new choice, which empties the previous selection. This technique of selecting is referred
to as mutually-exclusive.

To indicate what a particular radio button is used for, it is usually accompanied by a
label. This static text informs the user as to what the control is used for.

Because they come in a group, radio buttons should be organized in a specific container.
To implement their mutual exclusiveness, radio buttons should be positioned in either a
RadioGroup, GroupBox, or a Panel controls. If you add the radio buttons on this type of
container, by default, they will be treated as a group. You should refrain from positioning
radio buttons directly on a form or a frame.

At design time, to create a group of radio buttons, first position a container, such as a
panel or a group box, on the form. If you want each radio button to behave like a full
control, first position a GroupBox or a panel controls on the form. To add each radio
button, from the Standard tab of the Component Palette, click the RadioButton and click
inside of the container.

24.1.2 Characteristics of Radio Buttons
From the programmers standpoint, the most important property of any control is its
Name. Therefore, after adding the radio buttons to a container, you can change their
names using the Object Inspector.

Two properties are of particular importance to both you and the user: the label and the
state of the control. The label is text that specifies what a particular radio button is used
for. To set the label of a radio button, on the Object Inspector, click Caption and type the
desired label. Repeat this for each radio button. If you want to change a radio buttons
caption at runtime, you can do so programmatically as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
rdoGender1->Caption = "&Man";
}
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

590 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------

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.

Practical Learning: Creating Radio Buttons
1. Start a new project with the default form
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. Click Unit1 to select it. Type Main and press Enter
5. Type Operations as the name of the project and press Enter
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. On the Component Palette, double-click the RadioButton control
10. On the Object Inspector, click Caption and type &Addition
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 591

11. Click Name and type rdoAddition
12. Move the new radio button to the top section of and inside the panel.
13. On the Component Palette, click the Radio Button
14. Click inside the panel
15. Click Name and type rdoSubtraction
16. Click Caption and type &Subtraction
17. In the same way, add another RadioButton control to the panel. Set its Caption to
&Multiplication and its Name to rdoMultiplication
18. Add one more RadioButton to the panel. Set its Name to rdoDivision and its
Caption to &Division

19. Move the panel to the right section of the form.
20. Notice that it moves with its children. You will redesign the form as follows:

21. Add another panel with four labels captioned Number &1:, Number &2:, ------
------------, and Result:
22. Add three Edit controls inside the panel and on the right side of the
corresponding labels. These edit boxes will be named edtNumber1, edtNumber2,
and edtResult
23. Add another panel to an empty area of the form
24. While the new and last panel is selected, click the Align field. Click its arrow and
select alBottom

Chapter 24: Selection-Based Controls Borland C++ Builder Programming

592 Copyright 2003 FunctionX, Inc.

24.1.3 Radio Buttons Methods
The primary method you will use with a radio button is the TRadioButton constructor.
This member function is mostly used to dynamically create a radio button. Since all
objects in C++ Builder must be called using pointers, to create a radio button, declare a
pointer to TRadioButton and use the new operator to specify the control that owns the
radio button. A bad idea would be to assign it to the form. As we have reviewed, you
should never (or hardly) position a radio button or a group of radio buttons on a form.
Therefore, before creating a radio button, you should have a container on your form. You
can add such a host at design time or at run time.

If you want to programmatically create radio buttons, you must know that each button
will be created as its own object, besides their container.

24.1.4 Radio Button Events
A radio button is just a special form of button. Its most used event is OnClick. After
creating each radio button as a control, you can use its OnClick event to configure its
functionality.

Practical Learning: Implementing Radio Button Events
1. On the form double-click the Addition radio button to access its OnClick event
2. Implement it as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmOperations::rdoAdditionClick(TObject *Sender)
{
double Num1;
double Num2;
double Result;

if( edtNumber1->Text == "" )
Num1 = 0;
else
Num1 = StrToFloat(edtNumber1->Text);

if( edtNumber2->Text == "" )
Num2 = 0;
else
Num2 = StrToFloat(edtNumber2->Text);

Result = Num1 + Num2;
edtResult->Text = Result;
}
//---------------------------------------------------------------------------
3. Click the arrow on the top section of the Object Inspector and select rdoSubtraction
4. Click the Events tab
5. Double-click the empty box on the right side of OnClick
6. Implement the function just like the Click event of the addition but change the
operation as follows:
Result = Num1 - Num2;
7. On the top combo box of the Object Inspector, select rdoMultiplication
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 593

8. Double-click the box of the OnClick field
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;
}
}
//---------------------------------------------------------------------------
11. To display the form, press F12
12. Double-click the panel on the bottom section of the form to access its OnClick event
and implement it as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmOperations::Panel3Click(Tobject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
13. To test the form, press F9.
14. After using it, close the form and save your project

24.2 The Radio Group Control

Chapter 24: Selection-Based Controls Borland C++ Builder Programming

594 Copyright 2003 FunctionX, Inc.

24.2.1Introduction
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.


Practical Learning: Using a Radio Group Control
1. 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
<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" alink="#008000" vlink="#FF0000">
3. Enlarge the form and select all controls (Ctrl + A). Then move all controls to the
right side as follows:

4. Save All

24.2.2 Characteristics of the RadioGroup Control
The RadioGroup control has its own mechanism of creating and managing its radio
buttons. The radio buttons are created as strings. Therefore, after placing and drawing a
RadioGroup control on a form or a frame, you can open its String List Editor from its
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 595

Items property. You can then type a label for each radio button on its own line. Here is an
example:


Figure 81: The String List Editor

Once you click OK, the radio buttons would be created and proportionately added to the
RadioGroup. Although the strings appear as radio buttons, they only act like them with
full functionality but they are not controls to their full extent. This means that these radio
buttons do not have a name or any Windows control property, method, of events on their
own.

Once the list of strings has been created, the container will take care of positioning the
radio buttons so they can fit inside the host. If you type too many or too long strings for
the host, resize the container.

The list of radio buttons of a RagioGroup control is a TStrings type represented on the
TRadioGroup class as the property. Therefore, you can also programmatically create the
list of radio buttons using the TStrings::Add() method as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
grpEmplStatus->Items->Add("Part-Time");
grpEmplStatus->Items->Add("Full-Time");
grpEmplStatus->Items->Add("Contractor");
grpEmplStatus->Items->Add("Consultant");
}
//---------------------------------------------------------------------------

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
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

596 Copyright 2003 FunctionX, Inc.

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;
}
//---------------------------------------------------------------------------


Practical Learning: Implementing a Radio Group Control
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. On the Object Inspector, double-click (TStrings) from the Items field
3. Type Background and press Enter. Complete the list with Text, Link, Active Link,
and Visited Link
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 597


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:
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

598 Copyright 2003 FunctionX, Inc.


8. From the Standard tab of the Component Palette, click the Memo control and
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. From the Standard tab of the Component Palette, click Edit and click on the
existing Memo control. Change the properties of the new Edit control as follows:
Name = edtPreviewText
ReadOnly = true
Text = Body tag formatter and colorizer
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
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 599

Text = This link has already been visited

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()
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

600 Copyright 2003 FunctionX, Inc.

{
//TODO: Add your source code here
// Retrieve the current hexadecimal colors from their Edit controls
HexBG = edtBackground->Text;
HexText = edtText->Text;
HexLink = edtLink->Text;
HexALink = edtALink->Text;
HexVLink = edtVLink->Text;

// Get the integral position of each ScrollBar control
int Red = 255 - scrRed->Position;
int Green = 255 - scrGreen->Position;
int Blue = 255 - scrBlue->Position;

// Display the position of each ScrollBar
// in its corresponding Edit control
edtNumRed->Text = Red;
edtNumGreen->Text = Green;
edtNumBlue->Text = Blue;

// Get the hexadecimal equivalent of each ScrollBar control
AnsiString HexRed = IntToHex(Red, 2);
AnsiString HexGreen = IntToHex(Green, 2);
AnsiString HexBlue = IntToHex(Blue, 2);

// Display the hexadecimal value in the appropriate Edit controls
edtHexaRed->Text = HexRed;
edtHexaGreen->Text = HexGreen;
edtHexaBlue->Text = HexBlue;

// Change the color of the Preview panel
// according to the values of the ScrollBar positions
pnlPreview->Color = TColor( RGB(Red, Green, Blue) );

// Get the position of each ScrollBar control
// Create a hexadecimal color starting with #
// And display the color in the appropriate Edit control
switch( grpBodyAttributes->ItemIndex )
{
case 0:
edtBackground->Text = "#" +
IntToHex(255 - scrRed->Position, 2) +
IntToHex(255 - scrGreen->Position, 2) +
IntToHex(255 - scrBlue->Position, 2);
mmoPreview->Color = pnlPreview->Color;
edtPreviewText->Color = pnlPreview->Color;
edtPreviewLink->Color = pnlPreview->Color;
edtPreviewALink->Color = pnlPreview->Color;
edtPreviewVLink->Color = pnlPreview->Color;
HexBG = edtBackground->Text;
break;
case 1:
edtText->Text = "#" +
IntToHex(255 - scrRed->Position, 2) +
IntToHex(255 - scrGreen->Position, 2) +
IntToHex(255 - scrBlue->Position, 2);
edtPreviewText->Font->Color = TColor( RGB(Red, Green, Blue) );
HexText = edtText->Text;
break;
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 601


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();
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

602 Copyright 2003 FunctionX, Inc.

}
//---------------------------------------------------------------------------
21. Test the application
22. After using the form, close it and save your project
23. When the user clicks a button from the Body Attributes RadioGroup control, we
need to display its color on the Preview panel. To do this, we first need to find out
what radio button was clicked. Once we know this button, we will retrieve the color
of its font from the Preview Memo, translate that color into red, green, and blue
values, and then use those values to automatically update the scroll bars and the edit
boxes. While we are at it, we also need to update the corresponding Edit control in
the RadioGroup box. Since this functionality will be used by all radio buttons in the
group, we will use a global function to which we can pass two TEdit objects. One of
the TEdit arguments will represent the Edit control from the Preview Memo whose
radio button was clicked. The other TEdit argument will be the result produced after
translating the color into integers
In the private section of the TForm1 class, declare a function as follows:
void __fastcall ClickOption(TColor Clr, AnsiString Result);
24. Implement the function as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::ClickOption(TColor Clr, AnsiString Result)
{
long lngColor;
int Red, Green, Blue;

pnlPreview->Color = Clr;
lngColor = Clr;

Red = lngColor % 256;
Green = (lngColor / 256) % 256;
Blue = lngColor / 65536;

scrRed->Position = Red;
scrGreen->Position = Green;
scrBlue->Position = Blue;

edtRed->Text = Red;
edtGreen->Text = Green;
edtBlue->Text = Blue;

edtHexaRed->Text = IntToHex(Red, 2);
edtHexaGreen->Text = IntToHex(Green, 2);
edtHexaBlue->Text = IntToHex(Blue, 2);

Result = "#" + IntToHex(Red, 2) + IntToHex(Green, 2) + IntToHex(Blue, 2);
}
//---------------------------------------------------------------------------
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
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 603

// 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;
}
}
//---------------------------------------------------------------------------
26. Test your application

27. After using it, close and return to Bcb
28. Save All
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

604 Copyright 2003 FunctionX, Inc.


24.2.3 RadioGroup Methods
The fastest and most convenient way to dynamically create a group of radio buttons
consists of using a RadioGroup control. If you do not have this control already, either by
adding it at design time or by creating anyhow, you can use the new operator to assign an
instance of the TRadioGroup to the owner of this control. The compiler needs to know
the owner that would have the responsibility of cleaning the RadioGroup once it is not
needed anymore. This time, the owner should be the form, unless the RadioGroup control
will be hosted by another container. Also, specify the parent of the control. If you will
need the radio buttons in just one function or event, you can create the RadioGroup
control in that function. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnCreateGroupClick(TObject *Sender)
{
TRadioGroup* Group = new TRadioGroup(Form1);
Group->Parent = Form1;
}
//---------------------------------------------------------------------------

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)
{
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 605

grpMaritalStatus = new TRadioGroup(Form1);
grpMaritalStatus->Parent = Form1;

grpMaritalStatus->Items->Add("Single");
grpMaritalStatus->Items->Add("Married");
grpMaritalStatus->Items->Add("Divorced");
grpMaritalStatus->Items->Add("Widow");
grpMaritalStatus->Left = 220;
grpMaritalStatus->Top = 20;
grpMaritalStatus->Height = 100;
grpMaritalStatus->Width = 124;
grpMaritalStatus->Caption = "Marital Status";
}
//---------------------------------------------------------------------------

24.2.4 RadioGroup Messages and Events
Like most other visual controls, the RadioGroup fires the OnClick event when it is
clicked. This event makes all radio buttons of the group to be considered as one.
Therefore, when the user clicks this control, you can simply access the ItemIndex
property to find out what button is checked.


24.3 Check Boxes

24.3.1Introduction
A check box is a Windows control that allows the user to set or change the value of an
item as true or false. Although it can appear by itself, a check box sometimes comes in a
group with others, allowing the user to select as many choices as are available.
Depending on the application, a little square appears. The user makes a selection by
clicking in the square which toggles a check mark . Toggling means that if the square
were empty , after clicking it, a check mark would appear in it . Otherwise, the check
mark would be removed. Like a radio button, a check box is usually accompanied by a
label to indicate what the check control is used for.

From the users standpoint, a check box is selected when its check mark is set; and the
item is not selected when its square is empty. From the developer standpoint, a check
mark has two (Boolean) values: true or false (or TRUE or FALSE, or True or False).
When a check mark is selected, its value is true. Otherwise, its value is false.

A check mark is a special button and can be programmed as a regular control. Depending
on the applications needs, you can display or hide it, enable or disable it as necessary.
You can adjust the controls behavior depending on other controls on the same form, the
same application, or external factors.

Check boxes provide non-exclusive choice, which means that each check box can
behave as independent as needed with regards to the other check boxes of the same
container. If you are creating just one check box, you can place it where you want on the
form. If you are creating more than one check box that are addressing the same issue, you
should include them in a rectangular container so their belonging to the same group
would be obvious to the user. The group can be hosted by a GroupBox, a bevel, a
RadioGroup, or a Panel controls. If you place the controls in a Bevel or a RadioGroup,
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

606 Copyright 2003 FunctionX, Inc.

since these two are true containers for other controls, you will not be able to move all
controls as one object during design.

Practical Learning: Introducing Check Boxes
1. Start a new project with its default form
2. Save it in a new folder named FastFood1
3. Save the unit as Exercise and save the project as FastFood
4. Open Image Editor. Design a 32 x 32 and 16 x 16 icon as follows:



5. Save it as FastFood in the folder of the current project
6. Create a new 16 x 16 size bitmap and design it as follows:


7. Save it as Ingredients in the folder of the current project
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. Change the forms following properties:
BorderStyle: bsDialog
Caption: Fast Food Restaurant Customer Menu
Name: frmMain
Position: poScreenCenter
10. Design the dialog box as follows:
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 607


Control Name Caption Other Properties
Group Box Dont Care Bread
Radio Button rdoBun B&un Alignment: taLeftJustify
Radio Button rdoRoll &Roll Alignment: taLeftJustify
Checked: true
Group Box Dont Care Meat
Radio Button rdoBeefPatty Bee&f Patty Alignment: taLeftJustify
Checked: true
Radio Button rdoGrilledChicken &Grilled
Chicken
Alignment: taLeftJustify
Radio Button rdoChickedBreast C&hicken Breast Alignment: taLeftJustify
Group Box Dont Care Ingredients

11. Save All

24.3.2 Characteristics of Check Boxes
If you are planning to have just one check box, from the Standard tab of the Component
Palette, click the CheckBox control and click on the desired section of the form or
container. If you want to use more than one check box, you should first place a group
control on your form, then add the desired CheckBox controls.

The most obvious property of the check box is its state as being checked or not. By
default, a check box is not checked (it is empty). At design time, you can make sure that a
check box appears checked or not by changing the Boolean value of the Checked
property in the Object Inspector. When you set this property, it would be updated
automatically on the form. You or the user can also control this property at runtime. To
change the Checked property programmatically, simply assign a true or false value to the
Checked proeprty. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
CheckBox1->Checked = True;
}
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

608 Copyright 2003 FunctionX, Inc.

//---------------------------------------------------------------------------

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:


Figure 82: Right Aligned

Figure 83: Left Aligned


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 full-
time 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:

Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 609



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.


Practical Learning: Creating Check Boxes
1. On the Standard toolbar, click the New button . In the New Items dialog box,
click Dialogs and double-click Standard Dialog (Vertical)
2. Save it as Ingredients
3. Change its Name to dlgIngredients. Change its Caption to Ingredients Selection
Here is how you will design it:

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
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

610 Copyright 2003 FunctionX, Inc.

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
10. Set its properties as follows:
Alignment: taLeftJustify
AllowGrayed: true
Caption: &Regulars
Name: chkRegulars

11. Complete the design of the dialog box as follows:

Control Name Caption or Text Other Property
CheckBox chkSweetener &Sweetener Alignment: taLeftJustify
Checked: true
CheckBox chkCheese Ch&eese Alignment: taLeftJustify
CheckBox chkBacon B&acon Alignment: taLeftJustify
GroupBox Dont Care Options
BitBtn btnIngredients &Ingredients Glyph: Ingredients
RadioButton rdoMayonnaise &Mayonnaise Alignment: taLeftJustify
Checked: true
RadioButton rdoKetchup &Ketchup Alignment: taLeftJustify
RadioButton chkMustard Mus&tard Alignment: taLeftJustify
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 611

Bevel Shape: bsFrame
BitBtn Kind: bkClose
Label Total Price:
Edit edtTotalPrice $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();
}
//---------------------------------------------------------------------------
13. Test the application. Close it and return to Bcb
14. Save All

24.3.3 Check Box Methods
The check box control has only its constructor and its destructor as methods. The
constructor allows you to dynamically create the control. To do this, use the new operator
to assign a TCheckBox object to the named instance of the control. You must also
specify what control owns the check box. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TCheckBox* Checker = new TCheckBox(Form1);
Checker->Parent = Form1;
}
//---------------------------------------------------------------------------

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);
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

612 Copyright 2003 FunctionX, Inc.

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 = taRightJ ustify;
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.

24.3.4 Check Box Events
As far as Microsoft Windows is concerned, a check box is just a modified button. This
allows it to use the common characteristics of command buttons and radio controls.
Based on this, when the user clicks a check box, an OnClick() event fires. All the other
events derive from its ancestors the TControl and the TWinControl classes.

Practical Learning: Implementing Check Boxes
1. 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
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 613

check button and then implement a behavior accordingly.
Display the main form and double-click the Sweetener check control.
2. Implement its OnClick event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::chkSweetenerClick(TObject *Sender)
{
// If the sweetener check box is not checked
if( chkSweetener->Checked == False )
{
// Disable the Options radio buttons
rdoMayonnaise->Enabled = False;
rdoKetchup->Enabled = False;
rdoMustard->Enabled = False;
}
else
{
// Otherwise, enable the Options radio buttons
rdoMayonnaise->Enabled = True;
rdoKetchup->Enabled = True;
rdoMustard->Enabled = True;
}
}
//---------------------------------------------------------------------------
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;
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

614 Copyright 2003 FunctionX, Inc.

}
// 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) )
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 615

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. Now we can calculate the price of the sandwich.
In the header file of the main form, declare a private member function of type void
__fastcall named EvaluatePrice

Figure 84: Add a Method - EvaluatePrice

8. Implement the method as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::EvaluatePrice()
{
//TODO: Add your source code here
double PriceBread, PriceMeat,
PriceCheese, PriceBacon, TotalPrice;

// The price of bread is $0.85
PriceBread = 0.85;

// To get the price of the meat, find out what button
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

616 Copyright 2003 FunctionX, Inc.

// is selected in the Meat group box
if( rdoBeefPatty->Checked == True )
// The beef patty is $1.50
PriceMeat = 1.50;
else if( rdoGrilledChicken->Checked == True ||
rdoChickenBreast->Checked == True )
// Cheicken breast and grilled chicken are $1.80 each
PriceMeat = 1.80;
else // J ust in case
PriceMeat = 0.00;

// There is no extra cost for the Regular ingredients
// and nothing to add for the sweetener

// On the other hand,
// if the customer wants cheese, $0.30 is added to the price
if( chkCheese->Checked == True )
PriceCheese = 0.30;
else // otherwise, the customer don't want no cheese
PriceCheese = 0.00;

// If the customer wants bacon, $0.45 is added to the price
if( chkBacon->Checked == True )
PriceBacon = 0.45;
else
PriceBacon = 0.00;

// Now, we can calculte the total price
TotalPrice = PriceBread + PriceMeat + PriceCheese + PriceBacon;
edtTotalPrice->Text = FloatToStrF(TotalPrice, ffCurrency, 8, 2);
}
//---------------------------------------------------------------------------
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();
}
//---------------------------------------------------------------------------
Borland C++ Builder Programming Chapter 24: Selection-Based Controls

Copyright 2003 FunctionX, Inc. 617

void __fastcall TfrmMain::chkBaconClick(TObject *Sender)
{
EvaluatePrice();
}
//---------------------------------------------------------------------------
11. Test the application:

Figure 85: The Fast Food Restaurant Application

12. Close it and return to Bcb
13. Heighten the form and add the following controls to the top section

Figure 86: The Fast Food Application Improvement

Control Name Caption or Text Other Properties
Label Processed By:
Edit edtClerk
Label Order Date:
MaskEdit edtOrderDate EditMask: !99/99/0000;1;_
Chapter 24: Selection-Based Controls Borland C++ Builder Programming

618 Copyright 2003 FunctionX, Inc.


14. Save All


Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 619


Chapter 25: List-Based Controls


25.1 List Boxes

25.1.1Overview

A list box presents a list of items to choose from.
Each item displays on a line. The user makes a
selection by clicking in the list. Once clicked, the
item or line on which the mouse landed becomes
highlighted, indicating that it is the current choice.
After an item has been selected, to make a different
selection, the user would click another. She can
also press the up and down arrow keys to navigate
through the list and make a selection.

A list box can also be configured to allow multiple
selections.

Figure 87: The Date and Time Dialog Box

One of the main reasons for using a list box is to display a list of items to the user.
Sometimes the list would be very large. If the list is longer than the available space on the
control, the control would be equipped with a scroll bar that allows the user to navigate
up and down to access all items of the list. You will have the option of deciding how
many items to display on the list.

25.1.2 List Box Creation
A list box is immediately derived from the TCustomListBox class and that is where its
properties are set. There are three main ways you add a list box to your form.

To create a list box, you can click the ListBox control from the Standard tab of the
Component Palette and click on the form. Once the list box is positioned on a container,
you can move it by clicking and dragging the control. You can also resize it using any of
the techniques we learned to add, position, move, and resize controls on a component. If
the list will cover many items, design it so its height can display 8 items at a time.
Otherwise, for a list of 8 or less items, use only the necessary height that would
accommodate all of the items.
Chapter 25: List-Based Controls Borland C++ Builder Programming

620 Copyright 2003 FunctionX, Inc.

25.1.3 List Box Properties
The most fundamental and the most obvious aspect of a list box is the list of items it
displays. The list of items of this control is a TStrings object. If you know the list of
items for the control, there are two easy ways you can create it. At design time, on the
Object Inspector, you can click the Items property to reveal its ellipsis button on the right
field . To create the list, click this button to call the String List Editor, type each item
desired, on its own line by pressing Enter:


Figure 88: The String List Editor Dialog Box

To programmatically add the items to a list box, use the TStrings::Add() method. If the
list were empty, the new items would be added to the list. If the list already contained one
or more items, the new items would be added, by default, to the end of the existing
one(s). Here is an example of creating a list of items or adding new ones to a list:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
ListBox1->Items->Add("Chad");
ListBox1->Items->Add("Equatorial Guinea");
ListBox1->Items->Add("Egypt");
ListBox1->Items->Add("Madagascar");
}
//---------------------------------------------------------------------------

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"));
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 621

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
Chapter 25: List-Based Controls Borland C++ Builder Programming

622 Copyright 2003 FunctionX, Inc.

TCustomListBox::ItemIndex has a value of 1.During introduction, we saw that if a List
Box includes more items than the vertical size, the Height, allows displaying, the control
would be equipped with a vertical scroll that would allow the user to move up and down
and access the whole list. If at least one of the items is wider than the control can display,
this might not be obvious to the user. You would have to provide the control with a
horizontal scroll bar. The Win32 provides a SendMessage() function that can take care of
this. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
SendMessage (ListBox1->Handle, LB_SETHORIZONTALEXTENT, 200, 0);
}
//---------------------------------------------------------------------------

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.
25.1.4 List Box Methods
The only methods a list box has on its own are its constructor and its destructor. The
TListBox is typically used to dynamically create a list box. To create a list box inside of
an event or a function, use the new operator. To do this, declare a pointer to a TListBox
class. When declaring this object, the constructor of the TListBox needs to know the
component that owns the control; this is usually the host or container of the list box. If the
List Box will be placed on a form called Form1, you can create the List Box as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::CreateTheList()
{
TListBox* Liste = new TListBox(this);
Liste->Parent = Form1;
}
//---------------------------------------------------------------------------

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>
Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 623

#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
Chapter 25: List-Based Controls Borland C++ Builder Programming

624 Copyright 2003 FunctionX, Inc.

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("J Builder");
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:

Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 625

//---------------------------------------------------------------------------
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if( Key == 13 ) // If the user presses the Enter key
{
if(Edit1->Text != "") // If Edit1 contains something
{
lstCountries->Items->Add(Edit1->Text);
// Add the content of Edit1 to ListBox1
Edit1->Text = ""; // Reset Edit1 and make it empty
}
else // Since Edit1 is empty, display a message
Panel1->Caption = "There is nothing to add";
}
}
//---------------------------------------------------------------------------

The TCustomListBox::Clear() method is used to clear the control of its whole content:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
lstCountries->Clear();
}
//---------------------------------------------------------------------------

25.1.5 Operations on List Boxes
The most regular operation users perform on a list box is to select items. This selection is
performed by the user clicking one of the items. Using code, you can also select an item
in the list. To select the 6th item of the list, you could write the code as:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
lstCountries->ItemIndex = 6;
}
//---------------------------------------------------------------------------

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:
Chapter 25: List-Based Controls Borland C++ Builder Programming

626 Copyright 2003 FunctionX, Inc.


//---------------------------------------------------------------------------
void __fastcall TForm1::Button6Click(TObject *Sender)
{
// First find out whether an item is selected
if(lstCountries->ItemIndex != -1) // Put the selected item in the edit
Edit1->Text = lstCountries->Items->Strings[lstCountries->ItemIndex];
}
//---------------------------------------------------------------------------

inserting a new string above the selected item:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button6Click(TObject *Sender)
{
lstCountries->Items->Insert(lstCountries->ItemIndex, Edit1->Text);
}
//---------------------------------------------------------------------------
or under the selected string:
//---------------------------------------------------------------------------
void __fastcall TForm1::Button6Click(TObject *Sender)
{
lstCountries->Items->Insert(lstCountries->ItemIndex + 1, Edit1->Text);
}
//---------------------------------------------------------------------------

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.
Practical Learning: Configuring List Boxes
1. To start a new project, on the Standard toolbar, click the New button
2. Make sure the Application icon is selected and click OK
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. Click the Dual List Box icon:
5. Click OK.
6. To remove the starting form, press F12 to display the Code Editor.
7. Click the Unit1.cpp to select it.
8. Right-click the Unit1.cpp tab and click Close Page
9. When asked whether you want to save Unit1, click No.
10. To test the application, press F9.
Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 627

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");
}
//---------------------------------------------------------------------------
19. Display the form.
20. Click the Cancel button and press Delete
21. Also delete the Help button
22. Double-click the OK button and implement its OnClick event as follows:
//---------------------------------------------------------------------------
void __fastcall TDualListDlg::OKBtnClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
23. To test the dialog box, press F9
24. After using the list boxes, click OK to close the form.
25. Press F12 to display the form
26. Click the left list to select it
27. To make sure that double-clicking an item in the left list has the same effect as
clicking the Include button, click the Events tab
28. Double-click the event of the OnDblClick field and implement it as follows:
void __fastcall TDualListDlg::SrcListDblClick(TObject *Sender)
Chapter 25: List-Based Controls Borland C++ Builder Programming

628 Copyright 2003 FunctionX, Inc.

{
IncludeBtnClick(Sender);
}
//---------------------------------------------------------------------------
29. Display the form and click the right list
30. On the Object Inspector, double-click the event of the OnDblClick field and
implement it as follows:
//---------------------------------------------------------------------------
void __fastcall TDualListDlg::DstListDblClick(TObject *Sender)
{
ExcludeBtnClick(Sender);
}
//---------------------------------------------------------------------------
31. To test the application, press F9
32. After using the form, close it
33. Open the Editor1 application
Here is the dialog box we are going to design

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:
Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 629

//---------------------------------------------------------------------
void __fastcall TdlgDateAndTime::FormActivate(TObject *Sender)
{
lstDateAndTime->Clear();

TDateTime TodayDate = Date();
TDateTime RightNow = Time();

lstDateAndTime->Items->Add(TodayDate);
ShortDateFormat = "m/d/yy";
lstDateAndTime->Items->Add(TodayDate);
ShortDateFormat = "mm/dd/yy";
lstDateAndTime->Items->Add(TodayDate);
ShortDateFormat = "yy/m/dd";
lstDateAndTime->Items->Add(TodayDate);
ShortDateFormat = "yyyy/m/dd";
lstDateAndTime->Items->Add(TodayDate);
DateSeparator = '-';
ShortDateFormat = "yyyy/m/dd";
lstDateAndTime->Items->Add(TodayDate);
ShortDateFormat = "dd/mmm/yy";
lstDateAndTime->Items->Add(TodayDate);
LongDateFormat = "dddd";
DateSeparator = ',';
ShortDateFormat = "dddd/ mmmm dd/ yyyy";
lstDateAndTime->Items->Add(TodayDate);
ShortDateFormat = "mmmm dd/ yyyy";
lstDateAndTime->Items->Add(TodayDate);
ShortDateFormat = "dddd/ dd mmmm/ yyyy";
lstDateAndTime->Items->Add(TodayDate);
ShortDateFormat = "dd mmmm/ yyyy";
lstDateAndTime->Items->Add(TodayDate);
ShortTimeFormat = "h:n:s";
lstDateAndTime->Items->Add(RightNow);
ShortTimeFormat = "hh:nn:ss";
lstDateAndTime->Items->Add(RightNow);
LongTimeFormat = "HH:nn:ss";
lstDateAndTime->Items->Add(RightNow);

// To eliminate or reduce the possiblity of an error
// make sure a format is always selected
lstDateAndTime->ItemIndex = 0;
}
//---------------------------------------------------------------------------
46. Save your project
47. To display the main form, on the main menu, click View -> Forms... On the View
Form dialog box, double-click frmMain
48. On the form, double-click MainMenu1
49. In the Menu Editor, click the box on the right side of Format
50. In the Object Inspector, click Properties and click Caption
51. Type &Insert and press Enter
52. On the Menu Designer, click Insert and click the box under it. Type &Date and
Time... and press Enter
53. Set the Hint value to Inserts today's date or the current time
54. Drag the Insert menu and position it between View and Format
Chapter 25: List-Based Controls Borland C++ Builder Programming

630 Copyright 2003 FunctionX, Inc.

55. Close the Menu Designer
56. On the main menu of C++ Builder, click File -> Include Unit Hdr...
57. In the Use Unit dialog box, make sure DateAndTime is selected and click OK
58. On the main menu of Editor, click Insert -> Date and Time... and implement its event
as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::DateandTime1Click(TObject *Sender)
{
dlgDateAndTime->ShowModal();

if( dlgDateAndTime->ModalResult == mrOk )
{
TListBox *Lst = dlgDateAndTime->lstDateAndTime;
rchEditor->SelText = Lst->Items->Strings[Lst->ItemIndex];
}
}
//---------------------------------------------------------------------------
59. Test your project
60. After using the application, close it and return to Bcb
61. Save All

25.2 Check List Boxes

25.2.1Overview
A Check List Box is a List Box whose items are each equipped with a check box:

Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 631


Figure 89: The Project Options Dialog Box

A Check List Box combines the functionalities of the List Box and the Check Box
controls. As a list box, it displays each of its items on a line. If there are too many items
than the control can display, it would be equipped with a vertical scroll bar. As described
for the list box, if at least of of the items is wider than the control's width, you can make
the list box display a horizontal scroll bar.

To select an item in the list, the user can click the desired string. Unlike the regular list
box, the user cannot select more than one string in the list. For this reason, if you desire a
normal list of objects, use the regular List Box control. The most important and obvious
characteristic of the Check List Box is that each item displays a check box on its left.
This box allows the user to select or deselect each item. To select an item, the user must
click its box and not its string, to indicate an explicit selection. This draws a check mark
in the box. As described with the Check Box control, the user can deselect a string by
removing the check mark. The check mark indicates that an item is selected and the
absence of the check mark indicates the contrary. Like the Check Box control, you can
allow the user to indicate a "half-checked" item. In this case, a check box can appear
unchecked, checked, or grayed.
Practical Learning: Creating a Pizza Application
1. Start a new project with its default form
2. Save the project in a new folder named Pizza1
3. Save the unit as Main and the project as Pizza
4. Design the form as follows:
Chapter 25: List-Based Controls Borland C++ Builder Programming

632 Copyright 2003 FunctionX, Inc.



Control Name Caption Other Properties
Form BorderStyle: bsDialog
ShowHint: true
GroupBox Caption: Pizza Size
RadioButton rdoSmall Small Alignment: taLeftJustify
RadioButton rdoMedium Meium Alignment: taLeftJustify
RadioButton rdoLarge Large Alignment: taLeftJustify

5. Save All

25.2.2 Characteristics of a Checked List Box
To provide a Check List Box, from the Additional tab of the Component Palette, click the
CheckListBox button 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 3-
D check box, you can change the Boolean Flat property from its true default to a false
value:

Flat = true Flat = false
Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 633




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
Chapter 25: List-Based Controls Borland C++ Builder Programming

634 Copyright 2003 FunctionX, Inc.

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};

If an item is checked, its State has a value of
cbUnchecked
If an item is clearly checked, its State has a value
of cbChecked
If an item is undecided, that is, if it is grayed, its

Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 635

State has a value of cbGrayed

The check mark appearance of the items of a Check List Box control are stored in an
array called State. To find out the check mark or lack of it of an item, call the State
property and specify the index of the desired item. The following checks whether a check
mark appears on the 5th item of the list:

//---------------------------------------------------------------------------
void __fastcall TForm1::CheckListBox1Click(TObject *Sender)
{
if( CheckListBox1->State[4] == cbChecked )
ShowMessage("The Bay Bridge is closed.\nYou can't get to Annapolis!!!");
}
//---------------------------------------------------------------------------

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;
}
//---------------------------------------------------------------------------



Practical Learning: Creating a Check List Box Application
1. On the Component Palette, click the Additional tab and click the CheckListBox
button
2. Click an empty area on the form
3. While the CheckListBox control is still selected, on the Object Inspector, click the
ellipsis button of the Items
4. Create the list of items as follows:
Chapter 25: List-Based Controls Borland C++ Builder Programming

636 Copyright 2003 FunctionX, Inc.


Figure 90: The String List Editor for a Check List Box

5. Click OK
6. Change the Name of the control to lstToppings
7. Design the rest of the form as follows:

Figure 91: The Pizza Application in Design

Control Name Caption or Text
Other
Properties
Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 637

Label Toppings Selection
CheckListBox
GroupBox Topping Distribution
RadioButton rdoMixToppings Mix Toppings
RadioButton rdoHalfAndHalf Half and Half
RadioButton rdoThirdAnd2Thirds 1/3 and 2/3
RadioButton rdoThirdEach 1/3 Each
Label Pizza Price
Edit edtPizzaPrice 9.25
Label Price Toppings
Edit edtPriceToppings 0.00
Label Total Price
Edit edtTotalPrice 0.00
BitBtn Kind: bkClose

8. Save All

25.2.3 Methods to Manage a Check List Box
A Check List Box control is based on the TCheckListBox class. Therefore, to
programmatically create this control, you can use its constructor. Specify the control
owner and its parent. All of the functionality of the Check List Box control is provided
though its properties.

When the user clicks the check box of an item, the Check List Box fires an
OnClickCheck event. You can use this event to take some action. The OnClickCheck
event of a TNotifyEvent type and therefore specifies only the Sender of the event.

Practical Learning: Using Check List Box Events
1. 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
Chapter 25: List-Based Controls Borland C++ Builder Programming

638 Copyright 2003 FunctionX, Inc.

// If an item is not checked, count it as 0
if( lstToppings->Checked[0] == True )
Pepperoni = 1;
else
Pepperoni = 0;

if( lstToppings->Checked[1] == True )
Sausage = 1;
else
Sausage = 0;

if( lstToppings->Checked[2] == True )
GroundBeef = 1;
else
GroundBeef = 0;

if( lstToppings->Checked[3] == True )
Olives = 1;
else
Olives = 0;

if( lstToppings->Checked[4] == True )
Pineapple = 1;
else
Pineapple = 0;

if( lstToppings->Checked[5] == True )
ExtraCheese = 1;
else
ExtraCheese = 0;

if( lstToppings->Checked[6] == True )
Onions = 1;
else
Onions = 0;

if( lstToppings->Checked[7] == True )
GreenPepper = 1;
else
GreenPepper = 0;

// Calculate the total number of items that have a check mark
NumberOfToppings = Pepperoni + Sausage + GroundBeef + Olives +
Pineapple + ExtraCheese + Onions + GreenPepper;
// Calculate the total price of toppings
PriceToppings = NumberOfToppings * PriceEachTopping;
// Calculate the total price of the order
PriceTotal = PricePizza + PriceToppings;

// If only one topping is selected, there is no need to specify
// how the toppings would be distributed
if( NumberOfToppings == 1 )
{
rdoMixToppings->Enabled = False;
rdoHalfAndHalf->Enabled = False;
rdoThirdAnd2Thirds->Enabled = False;
edtThirdEach->Enabled = False;
}
// If two toppings are selected, give the customer the option of
Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 639

// 1) mixing both toppings on the pizza
// 2) putting each topping on one 1/2 of the pizza
// 3) putting one topping on 1/3 of the pizza and the other topping on 2/3
else if( NumberOfToppings == 2 )
{
rdoMixToppings->Checked = True;
rdoMixToppings->Enabled = True;
rdoHalfAndHalf->Enabled = True;
//rdoThirdAnd2Thirds->Checked = False;
rdoThirdAnd2Thirds->Enabled = True;
edtThirdEach->Checked = False;
edtThirdEach->Enabled = False;
}
// If three toppings are selected, let the user decides
// 1) to mix toppings on the pizza
// 2) to lay each topping on 1/3 of the pizza area
else if( NumberOfToppings == 3 )
{
rdoMixToppings->Checked = True;
rdoMixToppings->Enabled = True;
rdoHalfAndHalf->Enabled = False;
rdoThirdAnd2Thirds->Enabled = False;
edtThirdEach->Enabled = True;
}
// If more than three toppings are selected, the only solution is
// to distribute them on the whole pizza area
else if( NumberOfToppings > 3 )
{
rdoMixToppings->Checked = True;
rdoMixToppings->Enabled = True;
rdoHalfAndHalf->Enabled = False;
rdoThirdAnd2Thirds->Enabled = False;
edtThirdEach->Enabled = False;
}

edtPizzaPrice->Text = FloatToStrF(PricePizza, ffFixed, 6, 2);
edtPriceToppings->Text = FloatToStrF(PriceToppings, ffFixed, 6, 2);
edtTotalPrice->Text = FloatToStrF(PriceTotal, ffFixed, 6, 2);
}
//---------------------------------------------------------------------------
3. On the form, double-click the Small, the Medium, and the Large radio buttons
4. Implement them as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::rdoSmallClick(TObject *Sender)
{
// Let the OnClickCheck of the Check List Box perform the calculations
lstToppingsClickCheck(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::rdoMediumClick(TObject *Sender)
{
// Let the OnClickCheck of the Check List Box perform the calculations
lstToppingsClickCheck(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::rdoLargeClick(TObject *Sender)
{
Chapter 25: List-Based Controls Borland C++ Builder Programming

640 Copyright 2003 FunctionX, Inc.

// Let the OnClickCheck of the Check List Box perform the calculations
lstToppingsClickCheck(Sender);
}
//---------------------------------------------------------------------------
5. Test the application:

Figure 92: The Pizza Application - Result
6. After using the form, close it and return to Bcb
7. Save All

25.3 Combo Boxes

25.3.1Introduction
Like a radio button, a combo box allows the user to select one item from a group. Unlike
radio buttons, a combo box saves space by using just as much room as an edit control.
Like a list box, a combo box displays a list of items to the user. Unlike a list box, there is
a version of a combo box that retracts once the user has made his selection; this is useful
when space saving is particularly important.

To add a combo box to your application, from the Standard tab of the Component Palette,
click the ComboBox button and click on the form. You can then resize or
reposition the control to the desired location.


Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 641

25.3.2 Characteristics of a Combo Box
Just like every control of your application, the name is the most important property of the
control, for you and the compiler. This name allows you and the compiler to refer to the
control. By default, the first combo box you add to your form at design time is called
ComBox1, the second would be ComboBox2, etc. To change the name of the control,
click the Name field, type a new name and press Enter (or click somewhere else).

Probably the first thing the user sees on a combo box is the text it displays. Although the
text of an item is an AnsiString, the items are composed from the TStrings class. To
create this list, when the control is selected on the form, on the Object Inspector, click the
Items field to reveal an ellipsis button. Click the ellipsis button to display the String
List Editor. Type each string and press Enter:

Once you click OK, the control would be filled with the new items. You can also fill out
the control using the methods of the TStrings class. For example, to create a list of items,
you could write:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnFillTheListClick(TObject *Sender)
{
cbxColors->Items->Add("Old Blue");
cbxColors->Items->Add("Light Blue");
cbxColors->Items->Add("Salmon");
cbxColors->Items->Add("Dark Violet");
}
//---------------------------------------------------------------------------

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.

Chapter 25: List-Based Controls Borland C++ Builder Programming

642 Copyright 2003 FunctionX, Inc.

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.

25.3.3 Methods of Combo Box Management
The TComboBox as a class has only its constructor and its destructor as methods. Its
other methods are derived from the the parent TCustomComboBox class. The
TComboBox constructor is used to dynamically create an instance of the object. If you
cannot add the control at design time, declare a pointer to TComboBox and use the new
operator to assign the controls owner for cleaning purposes:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnCreateComboBoxClick(TObject *Sender)
{
TComboBox *CarMake = new TComboBox(this);
CarMake->Parent = this;
}
//---------------------------------------------------------------------------

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;
Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 643

CarMake->Items->Add("Ford");
CarMake->Items->Add("Renault");
CarMake->Items->Add("Fiat");
CarMake->Items->Add("Honda");
}
//---------------------------------------------------------------------------

25.3.4 Operations on Combo Box Using Events
There are two mains operations a user will perform on a ComboBox control: selecting an
item from the list or changing the content of the list. Many of the events that occur during
the use of a combo box are from other controls or actions that are not programmatically
directly related to a combo box. It is usually possible to fill out the list of a combo box at
design time; that is, if you know the list of items that will be used. Otherwise, you will
use another event or function to provide the items of the list. For example, the OnCreate
event of a form is a common place to fill out a list, especially if you want the list to owns
its items the first time it appears to the user. If you create a combo box named cbxSports
but do not provide its items at design time, you can use the OnCreate event of the
hosting form to fill it up as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
cbxMajor->Items->Add("Accounting");
cbxMajor->Items->Add("Medical Assistant");
cbxMajor->Items->Add("Fire Science");
cbxMajor->Items->Add("Computer Sciences");
cbxMajor->Items->Add("Business Administration");
cbxMajor->Items->Add("Hospitality Management");
cbxMajor->Items->Add("Criminal J ustice");
cbxMajor->Items->Add("Computer Technician");
cbxMajor->Items->Add("Cartography");
cbxMajor->Items->Add("Music");
cbxMajor->ItemIndex = 3;
}
//---------------------------------------------------------------------------

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
Chapter 25: List-Based Controls Borland C++ Builder Programming

644 Copyright 2003 FunctionX, Inc.

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);
}
//---------------------------------------------------------------------------

Practical Learning: Configuring List Boxes
1. 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. On the form, click the bottom (Formatting) toolbar
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
6. Double-click (TStrings) from the Items property
7. Type 8 and press Enter
8. Complete the list with 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 32, 36, 48, 54, 72

Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 645

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);
Chapter 25: List-Based Controls Borland C++ Builder Programming

646 Copyright 2003 FunctionX, Inc.

// 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;
}

for(i = 0; i < Screen->Fonts->Count; i++)
cboFonts->Items->Add(Screen->Fonts->Strings[i]);
}
//---------------------------------------------------------------------------
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.
Borland C++ Builder Programming Chapter 25: List-Based Controls

Copyright 2003 FunctionX, Inc. 647



Chapter 26: Grid-Based Controls Borland C++ Builder Programming

648 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 649


Chapter 26: Grid-Based Controls


26.1 The Win32 Calendar

26.1.1Introduction
The Win32 API provides a control used to select dates on a colorful calendar. The dates
used and the way they display are based on the Regional Settings of the Control Panel. It
may also depend on the operating system



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
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

650 Copyright 2003 FunctionX, Inc.

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.

Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 651

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).

Practical Learning: Creating a Calendar-Based Application
1. Start Borland C++ Builder or a new project with its default form
2. Save the project in a new folder called Payroll1
3. Save the unit as Main and the project as Payroll
4. Change the Caption of the form to Employees Payroll
5. Change the name of the form to frmMain
6. Design the form as follows:


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
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

652 Copyright 2003 FunctionX, Inc.

Glyph: C:\Programs Files\Common Files\Borland
Shared\Images\Buttons\calendar.bmp

7. 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

26.1.2 Calendar Properties
To create a calendar, add the MonthCalendar button to a form or container. The
MonthCalendar object is created from the TMonthCalendar class which is indirectly
derived from TWinControl. The MonthCalendar control is a rectangular object without a
border. After placing it on the form, it displays the current month and only one month.
This is because, by default, its width and height are set enough to accommodate only one
month. To display more than one month, change the width of the control to provide
enough space:



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:

Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 653



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.

Practical Learning: Adding a Calendar Control
1. Click an empty area on the form, under the panel
2. On the Component Palette, click the Win32 tab. Double-click the MonthCalendar
button
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

654 Copyright 2003 FunctionX, Inc.

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. Position both calendars as follows:

5. Change the Name of the left Calendar control to calStartPeriod and change the
Name of the right Calendar to calEndPeriod
6. Change the FirstDayOfWeek of both controls to dowMonday
7. Set the Visible property of both calendars to false
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;
}
//---------------------------------------------------------------------------

10. Test the application and return to Bcb
11. Save All

26.1.3 Calendar Methods and Events
The TMonthCalendar class provides a constructor that can be used to programmatically
create a calendar and place it on a form or container. To do this, declare a pointer to
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 655

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.

Practical Learning: Adding a Calendar Control
1. 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)
{
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

656 Copyright 2003 FunctionX, Inc.

// Get the date that the user has just selected
TDateTime DateSelected = calStartPeriod->Date;
// Find out the numeric "name" of the weekday
Word DOTW = DayOfTheWeek(DateSelected);

// Make sure the user selected a Monday
if( DOTW == 1 )
{
// Since the user selected a Monday date,
// display it in the Ending Period edit box
edtStartPeriod->Text =
calStartPeriod->Date;//.FormatString("dddd, M MMM yyyy");
}
// Make sure the user can select only a Monday date
else if( DOTW != 1 )
{
ShowMessage("The selected date is invalid\n"
"A time sheet period starts on Monday");
return;
}
}
//---------------------------------------------------------------------------
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");
}
}
//---------------------------------------------------------------------------
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 657

4. Test the application



5. Return to Bcb and Save All
26.2 The Date and Time Picker

26.2.1Overview
The Date and Time Picker is a control that allows the user to select either a date or a time
value. This control provides two objects in one:





Chapter 26: Grid-Based Controls Borland C++ Builder Programming

658 Copyright 2003 FunctionX, Inc.



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 to a form
or other container.

Practical Learning: Creating a Date Time Picker Application
1. Create a new project with its default form
2. Save the project in a new folder called Payroll2
3. Save the unit as Main and the project as Payroll
4. Design the form as follows:

Form
BorderStyle: dsDialog Caption: Employees Payroll
Name: frmMain ShowHint: true
Panel
Caption: None
5. Save All

26.2.2 The Time Picker
After adding a DateTimePicker control to a container, to make it a Timer Picker control,
change its Kind property to a dtkTime value. This changes the control into a spin
control:



Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 659

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 Description
h Hour for 12-hour basis
Used to display the hour with one digit if the value
is less than 10
hh Hour for 12-hour basis
Used to display the hour with a leading 0 if the
value is less than 10
H Hour for 24-hour basis
Used to display the hour with one digit if the value
is less than 10
HH Hour for 24-hour basis
Used to display the hour with a leading 0 if the
value is less than 10
m Minute
Used to display the minute with one digit if the
value is less than 10
mm Minute
Used to display the minute with a leading 0 if the
value is less than 10
t AM/PM Displays the letter A or P for the AM or PM section
tt AM/PM 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.

26.2.3 The Date Picker
After adding the DateTimePicker control to the form, to allow the user to set the dates
and not the times on the control, set its Kind property to dtkDate. This would change the
control into a combo box. If you do not like the combo box, you can display a spin button
instead. This ability is controlled by the DateMode property. The dmComboBox value is
used to display a combo box while the dmUpDown value displays a spin button.

Chapter 26: Grid-Based Controls Borland C++ Builder Programming

660 Copyright 2003 FunctionX, Inc.

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:

Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 661

Format Used For Description
d Days Displays the day as a number from 1 to 31
dd Days
Displays the day as a number with a leading 0 if the number
is less than 10
ddd Weekdays Displays a weekday name with 3 letters as Mon, Tue, etc
dddd Weekdays Displays the complete name of a week day as Monday, etc
M Months Displays the numeric month from 1 to 12
MM Months
Displays the numeric month with a leading 0 if the number is
less than 10
MMM Months Displays the short name of the month as Jan, Feb, Mar, etc
MMMM Months Displays the complete name of the month as January, etc
yy Years Displays two digits for the year as 00 for 2000 or 03 for 2003
yyyy Years 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.

Practical Learning: Adding Date Picker Controls
1. Add two labels and two DateTimePicker controls to the label as follows:

Chapter 26: Grid-Based Controls Borland C++ Builder Programming

662 Copyright 2003 FunctionX, Inc.

2. Change the Name of the left control to dteStartPeriod
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. Test the application
6. Close the form and return to Bcb

26.2.4 Date Time Picker Events
The Date Time Picker control enjoys some events that the Calendar object does not have.
Whenever the user changes the date or time value of the control, an OnChange event
fires. You can use this event to take some action such as indicating to the user that the
new date is invalid.

If you had allowed the user to enter almost any type of value in the control by setting the
ParseInput Boolean property to true, and if he decides to edit the value of the control,
while the user is doing this, the OnUserInput event fires. This can be a good place to
check what the user is doing and take some action. The syntax of the OnUserInput event
is:

void __fastcall DateTimePickerUserInput(TObject *Sender, const AnsiString UserString,
TDateTime &DateAndTime, bool &AllowChange)

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,
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 663

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.

Practical Learning: Using Date Time Picker Events
1. 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
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

664 Copyright 2003 FunctionX, Inc.

5. After using the form, close it and return to Bcb

26.3 The String Grid Control

26.3.1Overview of Grids
A grid is a technique of using columns and rows to represent data in a visual format.
There is no strict rule as to what a grid control is used for. It can be used to simply
display a series of values by categories. It can also be used as a time sheet, which will be
the basis of the next exercise. Sometimes it is used as a calendar, similar to the
MonthCalendar control and as we will see later on.

To organize its content, a grid is made of vertical and horizontal lines. These lines are
used as separators. They create vertical entities called columns and horizontal sections
called rows:



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.

Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 665

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.

26.3.2 String Grid Properties
To create a grid of data, the Visual Component Library (VCL) provides various controls.
One of these controls is called StringGrid and is implemented by the TStringGrid class.

To create a grid, you can add a StringGrid button . Therefore, you can click this
button and position it on a form or another control container:



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
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

666 Copyright 2003 FunctionX, Inc.

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

26.3.3 Cells Properties
The intersection of a column and a row is called a cell. To distinguish cells that hold
indicative values and those that hold usable or modifiable values, cells are divided in two
categories distinguished by two colors. The cells on the top section and those on the left,
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 667

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:



You can also change these colors programmatically. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
TStringGrid *StatRates = new TStringGrid(this);
StatRates->Parent = this;

StatRates->Color = TColor(RGB(255, 230, 204));
StatRates->FixedColor = TColor(RGB(255, 128, 0));
}
//---------------------------------------------------------------------------

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.

Chapter 26: Grid-Based Controls Borland C++ Builder Programming

668 Copyright 2003 FunctionX, Inc.

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;
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 669



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.

Practical Learning: Controlling a StringGrid Properties
1. Open the Payroll1 application you started previously
2. Continue designing the form as follows:


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
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

670 Copyright 2003 FunctionX, Inc.

Options GoEditing: true GoTabs: true
Panel
BitBtn
Glyph C:\Program Files\Common Files\Borland
Shared\Images\Buttons\calculat.bmp
Caption: &Process It Default: true Name: btnProcessIt
StringGrid
ColCount: 3 Height: 78 Name: grdEarnings
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

3. Save All
4. Double-click an empty area on the form to access its OnCreate() event
5. To customize the StringGrid's cells, add the following code:
//---------------------------------------------------------------------------
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";

grdTimeSheet->Cells[0][0] = "Monday";
grdTimeSheet->Cells[1][0] = "Tuesday";
grdTimeSheet->Cells[2][0] = "Wednesday";
grdTimeSheet->Cells[3][0] = "Thursday";
grdTimeSheet->Cells[4][0] = "Friday";
grdTimeSheet->Cells[5][0] = "Saturday";
grdTimeSheet->Cells[6][0] = "Sunday";

grdTimeSheet->RowHeights[0] = 18;

grdTimeSheet->Cells[0][1] = "0.00";
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 671

grdTimeSheet->Cells[1][1] = "0.00";
grdTimeSheet->Cells[2][1] = "0.00";
grdTimeSheet->Cells[3][1] = "0.00";
grdTimeSheet->Cells[4][1] = "0.00";
grdTimeSheet->Cells[5][1] = "0.00";
grdTimeSheet->Cells[6][1] = "0.00";

grdTimeSheet->RowHeights[1] = 18;

grdTimeSheet->Cells[0][2] = "0.00";
grdTimeSheet->Cells[1][2] = "0.00";
grdTimeSheet->Cells[2][2] = "0.00";
grdTimeSheet->Cells[3][2] = "0.00";
grdTimeSheet->Cells[4][2] = "0.00";
grdTimeSheet->Cells[5][2] = "0.00";
grdTimeSheet->Cells[6][2] = "0.00";

grdTimeSheet->RowHeights[2] = 18;

grdEarnings->Cells[0][1] = "Regular";
grdEarnings->Cells[0][2] = "Overtime";
grdEarnings->Cells[1][0] = "Hours";
grdEarnings->Cells[2][0] = "Amount";
}
//---------------------------------------------------------------------------
6. Save All and Test the application:

7. Close the form and return to Bcb

26.3.4 StringGrid Methods
To programmatically create a StringGrid control, declare a pointer to TStringGrid class
using the new operator. Use its default constructor to specify the container of the control
as parent. Here is an example:

//---------------------------------------------------------------------------
#include <vcl.h>
#include <Grids.hpp>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

672 Copyright 2003 FunctionX, Inc.

#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
TStringGrid *StatRates = new TStringGrid(this);
StatRates->Parent = this;
}
//---------------------------------------------------------------------------

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]= 15;
StringGrid1->RowHeights[1]= 22;
StringGrid1->RowHeights[2]= 10;
StringGrid1->RowHeights[3]= 35;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::StringGrid1Click(TObject *Sender)
{
TRect Recto = StringGrid1->CellRect(StringGrid1->Col, StringGrid1->Row);
int Area = Recto.Width() * Recto.Height();
Label1->Caption = Area;
}
//---------------------------------------------------------------------------


Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 673




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);

This method is usually used on a Mouse event such as OnMouseDown(),
OnMouseMove(), and OnMouseUp() as these events provide the mouse coordinates.
The MouseToCell() method retrieves the horizontal and vertical coordinates of the
mouse, translates that position to the column and row indexes of the cell under the mouse
and return those values. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::StringGrid1MouseMove(TObject *Sender,
TShiftState Shift, int X, int Y)
{
int x, y;

StringGrid1->MouseToCell(X, Y, x, y);
Label1->Caption = "Col: " + AnsiString(x) + " Row: " + AnsiString(y);
}
//---------------------------------------------------------------------------





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;
};
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

674 Copyright 2003 FunctionX, Inc.


TGridCoord __fastcall MouseCoord(int X, int Y);

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);
}
//---------------------------------------------------------------------------

26.3.5 StringGrid Events
As a descendant of TWinControl, the TStringGrid class inherits all the usual events
that are common to Windows control. It fires the OnClick event when the user clicks
anywhere on the control. It sends an OnDblClick event when the user double-click any
cell. It uses all mouse events (OnMouseDown, OnMouseMove, OnMouseUp,
OnMouseWheelDown, and OnMouseWheelUp) as well as keyboard events
(OnKeyDown, OnKeyPress, and OnKeyUp). Besides the regular control events, the
StringGrid control fires events that are proper to its functionality.

Just before the user selects the content of a cell, the control fires the OnSelectCell()
event. Its syntax is:

void __fastcall OnSelectCell(TObject *Sender, int ACol, int ARow, bool &CanSelect)

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;
}
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 675



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";
}
//---------------------------------------------------------------------------

Chapter 26: Grid-Based Controls Borland C++ Builder Programming

676 Copyright 2003 FunctionX, Inc.



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 };
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 677

typedef Set<Grids_3, gdSelected, gdFixed> TGridDrawState;

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] = 15;
StringGrid1->ColWidths[1] = 75;
StringGrid1->ColWidths[2] = 75;
StringGrid1->ColWidths[3] = 90;
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

678 Copyright 2003 FunctionX, Inc.

StringGrid1->ColWidths[4] = 120;

StringGrid1->RowHeights[0] = 16;
StringGrid1->RowHeights[1] = 16;
StringGrid1->RowHeights[2] = 16;
StringGrid1->RowHeights[3] = 16;
StringGrid1->RowHeights[4] = 16;

AnsiString text = StringGrid1->Cells[ACol][ARow];
StringGrid1->Canvas->TextRect(Rect, Rect.Left, Rect.Top, text);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
StringGrid1->Cells[0][1] = "1";
StringGrid1->Cells[0][2] = "2";
StringGrid1->Cells[0][3] = "3";
StringGrid1->Cells[0][4] = "4";

StringGrid1->Cells[1][0] = "First Name";
StringGrid1->Cells[2][0] = "Last Name";
StringGrid1->Cells[3][0] = "Phone Number";
StringGrid1->Cells[4][0] = "Email Address";

StringGrid1->Cells[1][1] = "Alex";
StringGrid1->Cells[2][1] = "Walters";
StringGrid1->Cells[3][1] = "(202) 133-7402";
StringGrid1->Cells[4][1] = "waltersa88@yahoo.com";
StringGrid1->Cells[1][2] = "Bertrand";
StringGrid1->Cells[2][2] = "Kumar";
StringGrid1->Cells[4][2] = "kumarb@mailman.com";
StringGrid1->Cells[3][3] = "Hermine";
}
//---------------------------------------------------------------------------



Practical Learning: Using String Grid Events
1. 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>
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 679

#include <DateUtils.hpp>
#include <StrUtils.hpp>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
3. Implement the above method as follows:
//---------------------------------------------------------------------------
double __fastcall TfrmMain::EvaluateTime(AnsiString StrTime)
{
//TODO: Add your source code here
double dValue;
AnsiString StrValue = AnsiReplaceStr(StrTime, " ", "");

if( StrValue.I sEmpty() )
return 0.00;
else
return StrToFloat(StrValue);
}
//---------------------------------------------------------------------------
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:
//---------------------------------------------------------------------------
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

680 Copyright 2003 FunctionX, Inc.

void __fastcall TfrmMain::btnProcessItClick(TObject *Sender)
{
double RegHours1, RegHours2, OvtHours1, OvtHours2;
double RegAmount1, RegAmount2, OvtAmount1, OvtAmount2;
double RegularHours, OvertimeHours;
double RegularAmount, OvertimeAmount, TotalEarnings;

double HourlySalary = StrToFloat(edtHourlySalary->Text);
double OvtSalary = HourlySalary * 1.5;

if( HoursWeek1 < 40 )
{
RegHours1 = HoursWeek1;
RegAmount1 = HourlySalary * RegHours1;
OvtHours1 = 0.00;
OvtAmount1 = 0.00;
}
else if( HoursWeek1 >= 40 )
{
RegHours1 = 40;
RegAmount1 = HourlySalary * 40;
OvtHours1 = HoursWeek1 - 40;
OvtAmount1 = OvtHours1 * OvtSalary;
}

if( HoursWeek2 < 40 )
{
RegHours2 = HoursWeek2;
RegAmount2 = HourlySalary * RegHours2;
OvtHours2 = 0.00;
OvtAmount2 = 0.00;
}
else if( HoursWeek2 >= 40 )
{
RegHours2 = 40;
RegAmount2 = HourlySalary * 40;
OvtHours2 = HoursWeek2 - 40;
OvtAmount2 = OvtHours2 * OvtSalary;
}

RegularHours = RegHours1 + RegHours2;
OvertimeHours = OvtHours1 + OvtHours2;
RegularAmount = RegAmount1 + RegAmount2;
OvertimeAmount = OvtAmount1 + OvtAmount2;
TotalEarnings = RegularAmount + OvertimeAmount;

grdEarnings->Cells[1][1] = FloatToStrF(RegularHours, ffFixed, 6, 2);
grdEarnings->Cells[1][2] = FloatToStrF(OvertimeHours, ffFixed, 6, 2);
grdEarnings->Cells[2][1] = FloatToStrF(RegularAmount, ffFixed, 6, 2);
grdEarnings->Cells[2][2] = FloatToStrF(OvertimeAmount, ffFixed, 6, 2);
edtTotalEarnings->Text = FloatToStrF(TotalEarnings, ffFixed, 6, 2);
}
//---------------------------------------------------------------------------
8. Save All and test the application. Here is an example:
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 681



9. Close the form and return to Bcb
26.4 The CCalendar Control

26.4.1Introduction
Borland C++ Builder ships with a custom or sample control called CCalendar



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.

26.4.2 Using a CCalendar
To add a CCalendar control to an application, click its button from the Samples tab
of the Component Palette and click a form or other container.

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
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

682 Copyright 2003 FunctionX, Inc.

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.

26.5 The Color Grid Control

26.5.1Overview



Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 683

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.

26.5.2 Using a Color Grid Control
To use a Color Grid in your application, click a ColorGrid button from the Samples
tab of the Component Palette and click on the form or container.

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
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

684 Copyright 2003 FunctionX, Inc.

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.


26.6 The Value List Editor

26.6.1Introduction
The value list editor is a grid-based control that is used to create an ini list. An ini list is a
group of strings in the form Name=Value. Although you can create an ini list manually,
the VCL provides a visual mechanism of creating and managing such a list.

26.6.2 The Visual List Editor Control
To create a list of items of the form Name=Value, you can use a special control,
VisualListEditor , which is available from the Additional tab of the Component
Palette.

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");
}
//---------------------------------------------------------------------------

26.6.3 Combo Boxes on a Value List Editor
The reason for using the Value List Editor is to allow the user to create or edit a list of
items. The user does this by typing the desired strings in the Key and the Value columns.
Alternatively, instead of typing the values, you can let the user select the desired value.
To do this, you can provide either a combo box or an external dialog box.

Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 685

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;
}
//---------------------------------------------------------------------------

Chapter 26: Grid-Based Controls Borland C++ Builder Programming

686 Copyright 2003 FunctionX, Inc.


Figure 93: The Value List Editor With a Combo Box

Using this approach, you can create different lists or different combo boxes for various
Value cells. To do this, you should use the OnGetPickList event. In fact, instead of
creating the list of items of a combo box in the OnCreate event of the form, which
makes you specify what cell would have the combo box, if you want all Value cells to
have a combo box, you can create the list in the OnGetPickList 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");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ValueListEditor1GetPickList(TObject *Sender,
const AnsiString KeyName, TStrings *Values)
{
Values->Add("SD");
Values->Add("ND");
Values->Add("NV");
Values->Add("VA");
Values->Add("WV");
Values->Add("WA");
Values->Add("SC");
Values->Add("NC");
Values->Add("PA");
Values->Add("IL");
Values->Add("LA");
Values->Add("TX");
Values->Add("MD");
Values->Add("DC");
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 687

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("J amaica");
}
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");
}
}
//---------------------------------------------------------------------------

26.6.4 Ellipsis Buttons on a Value List Editor
To create an ellipsis button, you assign the appropriate style to the EditStyle property of
the ItemProps member variable. 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");
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

688 Copyright 2003 FunctionX, Inc.

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;
}
//---------------------------------------------------------------------------


Figure 94: The Value List Editor With a Button


Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 689


Chapter 27: Lists View Controls


27.1 Tree Views

27.1.1Overview
A TreeView consists of displaying a hierarchical view of items arranged in a parent-child
format. It is typically used to show items that belong to interrelated categories, such as
parent to child and child to grandchild, etc; or folder to subfolder to file.

The starting item of the tree is called the Root and illustrates the beginning of the tree.
Each item, including the root, that belongs to the tree is referred to as a node.

In the following charts, the down arrow means, "has the following child or children"



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.

Chapter 26: Grid-Based Controls Borland C++ Builder Programming

690 Copyright 2003 FunctionX, Inc.

Consider the following tree:




Practical Learning: Preparing a Tree View
1. Create a new project with its starting form.
2. To save the project, on the Standard toolbar, click Save All
3. Locate the folder where the exercises are selected.
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. Change the Caption of the form to Countries Statistics
7. From the Standard tab of the Component Palette, double-click the Panel control
.
8. Set the following properties:
Align = alTop
Alignment = taLeftJustify
BevelInner = bvRaised
bvOuter = bvLowered
Height = 28
9. To type the caption, click the Caption field, press Delete, press Space, and type
Countries
10. Click an unoccupied area of the form to deselect the panel
11. From the Standard tab, double-click the Panel control again.
12. Set its Align property to alBottom, its BevelOuter to bvLowered, delete the value
of its Caption, and set its Height to 20
27.1.2 Tree View Design
C++ Builder provides an exceptional way of creating a tree view at design time; in other
words, you can create a complete tree view without writing a single line of code.

A member of a TreeView is called a node. Visually, a node on a TreeView displays its
text in one of two states: selected or not selected. On another visual front, a node has a
child or it is standing alone. When a node has a child, it displays a + (collapsed) or
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 691

(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.

Practical Learning: Designing a Tree View
1. Click in the middle of the form. On the Win32 tab of the Component Palette double-
click 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. Click the Items field and click its ellipsis button
4. On the TreeView Items Editor dialog box, click the New Item button
5. Type World and press Enter
6. Type Universe
7. On the dialog box, click Universe and click Delete
8. Click World and click the New Subitem button.
9. Type America and press Enter
10. Type Africana and press Enter
11. Type Europe
12. Click Africana and using the Text edit box, edit it to display Africa and press Enter
13. Type Asia
14. Click America and click New Subitem
15. Type Chile and press Enter.
16. Type Canada
17. Click Asia and click New Subitem
18. Type Thailand and press Enter
19. Type Afghanistan


Chapter 26: Grid-Based Controls Borland C++ Builder Programming

692 Copyright 2003 FunctionX, Inc.

20. Click OK.
21. To test the TreeView, press F9
22. Notice the + button on the World item.
23. Click + and notice the button
24. Click the other + button to expand the tree items and click the signs

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
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 693



33. Click OK.
34. On the form, click TreeView1 to select it.
35. On the Object Inspector, set the Images property to ImageList1.
36. While the tree view control is still selected, click the Items field and click its ellipsis
button.
37. On the TreeView Items Editor dialog box, click World to select it. Set its Image
Index to 0 and its Selected Index to 1
38. Click the + button on World to expand it.
39. Click America to select it.
40. Set its Image Index to 2 and its Selected Index to 3
41. Set the other images according to the following table:
Item Text Image I ndex Selected Index
World 0 1
America / Africa/ Europe / Asia 2 3
Chile / Canada / Thailand / Afghanistan 4 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
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

694 Copyright 2003 FunctionX, Inc.


45. After using the form, close it and save the application
27.1.3 Dynamic Tree Views
C++ Builder provides three classes equipped with various properties and functions to
deal with a tree view. The basic class that manages the TreeView control is the
TTreeView. This class controls the visual settings such as background color, borders,
dimensions, etc. Each node is managed by the TTreeNode class. The class is used as a
descriptor of the control. For example, it can be used to find out what node is selected,
whether the node is expanded or collapsed, whether a node has children. The
TTreeNodes class is used to manage a TreeView; it combines various operations such as
creating the treeview, adding, inserting or deleting nodes, or updating the list of items.

To create a tree view, position a TreeView control on the form (although you can still
create it completely with code). Once the control is placed, you can customize its window
settings derived from the TControl class. The nodes are usually created from the
OnCreate event of the host form.

To display check boxes on the items of a tree view, call the SetWindowLong() function
and specify that you want to add the TVS_CHECKBOXES style. This can be done as
follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
LONG GWLTreeView = GetWindowLong(TreeView1->Handle, GWL_STYLE);
SetWindowLong(TreeView1->Handle, GWL_STYLE,
GWLTreeView | TVS_CHECKBOXES);
}
//---------------------------------------------------------------------------


Practical Learning: Programmatically Creating a Tree View
1. Create a new project with its starting form
2. Save it in a new folder named Countries2
3. Save the unit as Main and save the project as Countries2
4. On the Win32 tab of the Component Palette double-click the TreeView control
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 695

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], "Europe");
TreeView1->Items->AddChild(TreeView1->Items->Item[4], "Belgium");
TreeView1->Items->AddChild(TreeView1->Items->Item[4], "Denmark");
TreeView1->Items->AddChild(TreeView1->Items->Item[4], "Poland");

// Third child and its children
TreeView1->Items->AddChild(TreeView1->Items->Item[0], "America");
TreeView1->Items->AddChild(TreeView1->Items->Item[8], "Panama");
TreeView1->Items->AddChild(TreeView1->Items->Item[8], "Colombia");

// Fourth child and its children
TreeView1->Items->AddChild(TreeView1->Items->Item[0], "Asia");
TreeView1->Items->AddChild(TreeView1->Items->Item[11], "Bangladesh");
}
//---------------------------------------------------------------------------
7. To test the treeview, press F9
8. Expand the various nodes to display their children
9. Close the form
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");
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

696 Copyright 2003 FunctionX, Inc.

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");
}
//---------------------------------------------------------------------------
11. To display the form, press F12.

27.1.4 Adding Images to Nodes
We have seen than a tree view's nodes are created from a combination of the TTreeNode
and the TTreeNodes classes. The images associated with nodes are handled by the
Images property from the TCustomTreeView class.

To use images on a treeview, first include an ImageList control to the form. Using the
Form ImageList dialog box, add the desired images to the list of images. Note the
position of each image in the list because you will need to know the position of each
image.

To associate an image to a node, first declare a TTreeNode object and set or assign it a
position. The TTreeNode object needs to know the position of the node for almost any
operation that node will be involved in. The most important property related to an image
is the ImageIndex; this is the position of the image on the list created previously. The
other image you might be interested in is the Selected index: this informs the TreeNode
object about the position of the image the node will display when selected.

Practical Learning: Adding Pictures to a Tree View
1. To add some pictures to the treeview, from the Win32 tab, double-click the
ImageList to add it to the form
2. On the form, double-click the ImageList icon
3. Click Add.
4. From the Icons folder, include the following icons: Options, World, Target, Dans,
Botswana, Belgium, Denmark, and Columbia.
5. Click OK
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 697

6. On the form, click the TreeView control.
7. On the Object Inspector, set the Images field to ImageList1
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. To test the treeview, press F9
10. After viewing and using the form, close it.

27.2 List Views

27.2.1Introduction
A list view consists of using one of four views to display a list of items. The list is
typically equipped with icons that allow the user to verify what view is displaying. There
are four views used to display items

Large Icons: The view displays a list of items using icons with a 32x32 pixels size of
icons. This is the preferred view when the main idea consists of giving an overview of the
items

Small Icons: Like the other next two views, it uses 16x16 pixel icons to display a
simplified list of the items. Once more, no detail is provided about the items of this list.
The list is organized in disparate columns with some on top of others. If the list is
supposed to be sorted, the alphabetical arrangement is organized from left to right.

List: This list, using small icons, is also organized in columns; this time, the columns are
arranged so that the first column gets filled before starting the second. If the list is sorted,
the sorting is arranged in a top-down manner.

Details: This view displays arranged columns of items and provides as much detail as the
list developer had arranged it.

27.2.2 List View Design
C++ Builder provides a means of creating a list view without any coding, as long as you
need just one view to display the list.

Chapter 26: Grid-Based Controls Borland C++ Builder Programming

698 Copyright 2003 FunctionX, Inc.

To create a list view, use the ListView control from the Win32 tab. If you plan to
display a list without columns, you can create the list in a major one-step process. If the
list needs a column, its creation would involve a major two-step process. Once the
ListView control is placed on the form, create its items using the Items field. The
columns are created using the Columns field.
Practical Learning: Designing a List View
1. Create a new project with its starting form
2. Save it in a new folder named Statistics1
3. Save the unit as Main and save the project as Statistics1
4. Change the Caption of the form to Statistics
5. Change its dimensions to Height = 195 and Width = 445
6. To create our listview, on the Component Palette, click the Win32 tab.
7. Click the ListView control
8. Click on the form.
9. Change the position and the dimensions
Height = 114
Left = 16
Top = 16
Width = 410
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


18. Click OK.
19. To test the list view, press F9.
20. After viewing the listview, close the form
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 699

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


29. Close the Editing ListView1->Columns window.
30. On the Object Inspector, change the ViewStyle property to vsReport
31. To add the images to the listview, on the Win32 tab, double-click the ImageList
control
32. With the ImageList1 selected, change its Name to imgLarge
33. On the form, double-click the imgLarge control to add the images
34. Click Add
35. Locate the folder where the exercises are located. Access the Flags folder.
36. Click Yemen and click Open. If you receive a message warning that the bitmap is
too large, click No to All
37. Add the other following flags: Botswana, Belgium, Colombia, Denmark, and Benin:
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

700 Copyright 2003 FunctionX, Inc.


38. Click the list as follows:
Item Image Index New SubItem
Yemen 0 527,970
17,479,206
17B
Sanaa
Botswana 1 600,370
1,576,470
1.6B
Gaborone
Belgium 2 30,510

10,241,506
116.5B
Brussels
Colombia 3 1,138,910
39,685,655
22B
Bogota
Danemark 4 43,094
5,336,394
59.7B
Copenhagen
Benin 5 112,620
6,395,919
299M
Cotonou

Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 701



39. Click OK.
40. On the Win32 tab, double-click the ImageList control again.
41. Change its Name to imgSmall
42. Double-click the imgSmall icon
43. Using the Add button, add the following bitmaps: smYemen, smBotswana,
smBelgium, smColombia, smDanemark, and smBenin
44. Click OK
45. On the form, click the ListView.
46. On the Object Inspector, set the SmallImages property to imgSmall
47. Change the ViewStyle to vsReport.
48. To test the listview, press F9.
49. After viewing the listview, close the form.
50. On the Component Palette, click the Standard tab.
51. Add four buttons to the form positioned under the list box.
52. Change their captions to &Large Icons, &Small Icons, &List, and &Details
respectively:
53. Double-click the Large Icons button to access its OnClick event.
54. Double-click the other buttons and implement their events as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ListView1->ViewStyle = vsI con;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
ListView1->ViewStyle = vsSmallIcon;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
ListView1->ViewStyle = vsList;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

702 Copyright 2003 FunctionX, Inc.

ListView1->ViewStyle = vsReport;
}
//---------------------------------------------------------------------------
55. To test the ListView, press F9.
56. After viewing the ListView, close the form.

27.2.3 Dynamic List Views
Creating a ListView from code provides the same flexibility as a ListView object created
at design-time, minus the ability to visualize live the list being created. To create a list
view, place a ListView control on the form.

The columns and the items of the list are created separately. A column is created using
the TListColumn class. This allows you to add and format columns. An item of the list is
created using the TListItem class.

To display check boxes on the list view, send a
LVM_SETEXTENDEDLISTVIEWSTYLE message to the control. Here is an
example:

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
SendMessage((HWND)ListView1->Handle,
(UINT)LVM_SETEXTENDEDLISTVIEWSTYLE,
(WPARAM)(DWORD) LVS_EX_CHECKBOXES,
(LPARAM)LVS_EX_CHECKBOXES);
}
//---------------------------------------------------------------------------



Practical Learning: Programmatically Creating a List View
1. Start a new application with its default form
2. Save it in a new folder named Countries3
3. Save the unit as Main and save the project as Countries3
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
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 703

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 = taRightJ ustify;

ListCol = ListView1->Columns->Add();
ListCol->Caption = "Population (M)";
ListCol->Width = 90;
ListCol->Alignment = taRightJ ustify;

ListCol = ListView1->Columns->Add();
ListCol->Caption = "Budget";
ListCol->Width = 50;
ListCol->Alignment = taRightJ ustify;

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");
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

704 Copyright 2003 FunctionX, Inc.

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. To test the form, press F9


9. After viewing the form, close it
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
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 705

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;
19. To test the form, press F9


20. After using the form, close it.
21. On the main menu, click File -> Reopen -> ..\Countries\Project1.bpr. If the project is
not on the list, open it.
22. On the form, double-click the TreeView1 control.
23. In the TreeView Item Editor, click each country and click Delete to delete all
countries except the continents:
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

706 Copyright 2003 FunctionX, Inc.



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);
39. In the source file, implement the function as follows:
//---------------------------------------------------------------------------
void __fastcall TForm1::DisplayColumns(TTreeNode *NodeSelected)
{
TListColumn *Col;

// Erase the previous columns so they would not be added to the new ones
ListView1->Columns->Clear();

// Find out what node was selected, the root or a continent
if( NodeSelected == TreeView1->Items->Item[0] )
{
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 707

Col = ListView1->Columns->Add();
Col->Caption = "Name";
Col->Width = 100;

Col = ListView1->Columns->Add();
Col->Caption = "Area (sq km)";
Col->Width = 120;
Col->Alignment = taRightJ ustify;

Col = ListView1->Columns->Add();
Col->Caption = "Population";
Col->Width = 120;
Col->Alignment = taRightJ ustify;
}
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 = taRightJ ustify;

Col = ListView1->Columns->Add();
Col->Caption = "Population";
Col->Width = 100;
Col->Alignment = taRightJ ustify;

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 two-
dimensional 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" },
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

708 Copyright 2003 FunctionX, Inc.

{ "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" },
{ "J amaica", "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]); // Area
lstItem->SubItems->Add(CountryStats[0][2]); // Population
lstItem->SubItems->Add(CountryStats[0][3]); // Capital
lstItem->SubItems->Add(CountryStats[0][4]); // Internet Code
lstItem->ImageIndex = 1;

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]);
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 709

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];
Chapter 26: Grid-Based Controls Borland C++ Builder Programming

710 Copyright 2003 FunctionX, Inc.

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;
}
}
//---------------------------------------------------------------------------
43. Test the application
44. After using the form, close it and save the project.

27.3 Splitter Bars

27.3.1Introduction
A splitter is a bar that divides a window area in two sections. These sections are referred
to as panes or frames.. The splitter bar is used to create independent sections holding
various controls. The splitter can divide the sections horizontally or vertically, depending
on how you create it. Among other things, the design and implementation allows
displaying related data.
27.3.2 Creating a Splitter
To create a splitter bar, click the Splitter control from the Additional tab of the
Component Palette. The Splitter must be added to a control that has the Align property
set to a value other than alNone. If the splitter is embedded to a control that has the
alLeft or alRight Align property, it will create two vertical sections. If the Align
property of a control is set to alTop or alBottom when receiving the splitter, this creates
two horizontal sections. By using a combination of panels and other controls, you can
divide the window in as many sections as necessary and in any way.

Practical Learning: Adding a Splitter Bar
1. 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
Borland C++ Builder Programming Chapter 26: Grid-Based Controls

Copyright 2003 FunctionX, Inc. 711

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. Thats it. To test the form, press F9:

5. Drag the splitting line in the middle of the form to resize the sections
6. After using the form, close it.
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

712 Copyright 2003 FunctionX, Inc.

Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 713


Chapter 28: Creating and Using Lists


28.1 The TList Class

28.1.1Introduction
We saw how to use the TStrings and its child the TStringList classes to create a list of
strings. The role of TStrings is to create a list of single items, such as a list of names or a
list of numbers. Sometimes you will want a list that itself is made of objects, an object in
this sense being made of its own variables that characterize the object.

The Visual Component Library provides various classes adapted for creating a list of
objects, such as a list of cars, a list of houses, or a list of countries. As you know, such
objects are made of internal parts; for example a car is made of doors, tires, and options
(air condition, power steering, cruise control, etc) while a house is made of rooms,
furniture, books, electronic devices, etc.

Practical Learning: Creating an Application
1. Start a new application with its default form.
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. Click Unit1 to select it, type Main and press Enter.
6. Type AddressBook1 and press Enter
7. Change the Caption of the form to Address Book - Contacts List and change its
Name to frmMain

28.1.2 List Preparation
The TList class is used to create a list of objects, any type of object. In fact, the TList
considers that an object is defined as

void *ObjectOfTheList[Index];

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.
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

714 Copyright 2003 FunctionX, Inc.


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.

Practical Learning: Creating an Object
1. Press F12 to display the Code Editor. In the public section of the TForm1 class,
declare a pointer to TList as follows:



//---------------------------------------------------------------------------
class TfrmMain : public TForm
{
__published: // IDE-managed Components
private: // User declarations
public:
TList * ListOfContacts; // User declarations
__fastcall TfrmMain(TComponent* Owner);
};
//---------------------------------------------------------------------------
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;
}
//---------------------------------------------------------------------------
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 715

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. Save your project

28.1.3 Preparing an Object for a List
Before creating the actual list of objects, you should first define the kind of object will
make up the list. One way to do this is to create a class that holds the members or
characteristics of the object. For example, for a list of countries, each object can include
the country name, its area, its population, its capital, its internet code, etc. The object can
be created as follows:

class TCountry
{
public:
AnsiString CountryName;
Double Area;
Extended Population;
AnsiString Capital;
AnsiString InternetCode;
};

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;
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

716 Copyright 2003 FunctionX, Inc.

TCountry& __fastcall operator=(const TCountry &Ctry);
};

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 access each member and assign it an appropriate value
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.

Practical Learning: Creating an Object
1. To create an object, on the Standard toolbar, click the New button .
2. In the New property sheet of the New Items dialog box, click Unit and click OK
3. Save the unit as Contacts
4. In the Contacts.h file, type the following:
//---------------------------------------------------------------------------
#ifndef ContactsH
#define ContactsH
#include <vcl.h>
//---------------------------------------------------------------------------
struct TContact
{
// The items that make up a (valid) contact
AnsiString FirstName;
AnsiString LastName;
AnsiString Address;
AnsiString City;
AnsiString State;
AnsiString ZIPCode;
AnsiString Country;
AnsiString EmailAddress;
AnsiString HomePhone;
// Default constructor for an empty contact
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 717

__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("J ohn"), LastName("Doe"),
Address("123 Main Street"), City("Great City"),
State("Big State"), ZI PCode("01234"), Country("USA"),
EmailAddress("johndoe@inc.com"), 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)
{
}
//---------------------------------------------------------------------------
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

718 Copyright 2003 FunctionX, Inc.

__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. Save your project

28.1.4 List Build-Up
Once you have an object whose list you want to create, you can create each instance of
the object and add the instance to the list. Because the objects are stored in an array, you
would repeat this process for each object you want to include in the list. The TList class
provides all the necessary operations you would need to perform on a list.

Before adding an object to a list, you must "fill" it up first. This can be done by providing
a value for each member of the object. Because you are responsible for creating the
object, you are also responsible for providing the right value for each member of the
object. If you provide a wrong or invalid value, TList does not have any mechanism of
checking the internal values of the object. Consequently, you can build the simplest
object that contains only one or two members, or you can build the most complex object
that resembles a chemical reaction. The choice is yours.

Once the object is complete or at least acceptable as far as its structure is defined, you can
add it to the list. This can be taken care of by using the TList::Add() method. Its syntax
is:

int __fastcall Add(void * Item);

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 Count-
1. 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
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 719

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.

Practical Learning: Creating a List
1. In the public section of the TfrmMain class, declare a pointer to TContact as
follows:
//---------------------------------------------------------------------------
#ifndef MainH
#define MainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "Contacts.h"
//---------------------------------------------------------------------------
class TfrmMain : public TForm
{
__published: // IDE-managed Components
void __fastcall FormDestroy(TObject *Sender);
private: // User declarations
public:
TList * ListOfContacts;
TContact * Contact; // User declarations
__fastcall TfrmMain(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TfrmMain *frmMain;
//---------------------------------------------------------------------------
#endif
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 = "toussainth@mcps.mc.md.us";
Contact->HomePhone = "(301) 805-5008";
ListOfContacts->Add(Contact);

Contact = new TContact("Bertrand", "Yamaguchi", "7215 16th Street #D14",
"Washington", "DC", "20002", "USA",
"yamaguchib@russelinc.net", "(202) 661-5000");
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

720 Copyright 2003 FunctionX, Inc.

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 = "arronsl12@fortsullivan.ddm.mil";
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 = "singhc@csamd.net";
Contact->HomePhone = "(301) 667-1437";
ListOfContacts->Add(Contact);
}
//---------------------------------------------------------------------------
3. Save your project

28.1.5 List Navigation
List navigation consists of moving back and forth among items or locating an item on a
list. Once you have added some objects, each one is stored in an array of Items[] defined
as follows:

void *Items[int Index];

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);
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 721


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.

Practical Learning: Navigating a List
1. Display the form and design it as follows:


2. The form contains three GroupBox controls Named, from top to bottom,
grpPersonalInformation, grpResidence, and grpCommunication respectively.
3. The Name to give to each Edit control is displayed on the above form. The
bottom Edit control is named edtRecordNumber.
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. The bottom buttons , from left to right, are Named btnFirst, btnPrevious,
btnNext, btnLast. They use glyphs from the Bitmaps folder. The bitmaps from left
to right are First, Previous, Next, and Last
6. The label with a Caption of 000 is Named lblCount
7. Save the project.
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:
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

722 Copyright 2003 FunctionX, Inc.

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();
12. Implement the function as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::UpdateButtons()
{
if( CurrentRecord <= 1 )
{
btnFirst->Enabled = False;
btnPrevious->Enabled = False;
btnNext->Enabled = True;
btnLast->Enabled = True;
}
else if( CurrentRecord < ListOfContacts->Count )
{
btnFirst->Enabled = True;
btnPrevious->Enabled = True;
btnNext->Enabled = True;
btnLast->Enabled = True;
}
else if( CurrentRecord == ListOfContacts->Count )
{
btnFirst->Enabled = True;
btnPrevious->Enabled = True;
btnNext->Enabled = False;
btnLast->Enabled = False;
}
}
//---------------------------------------------------------------------------
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
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 723

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);
14. Implement the function as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::DisplayRecord(const Contact * Contact)
{
// Display or update each Edit control
edtFirstName->Text = Contact->FirstName;
edtLastName->Text = Contact->LastName;
edtAddress->Text = Contact->Address;
edtCity->Text = Contact->City;
edtState->Text = Contact->State;
edtZIPCode->Text = Contact->ZIPCode;
edtCountry->Text = Contact->Country;
edtEmailAddress->Text = Contact->EmailAddress;
edtHomePhone->Text = Contact->HomePhone;
}
//---------------------------------------------------------------------------
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[CurrentRecord-
1]);
DisplayRecord(Contact);

edtRecordNumber->Text = IntToStr(CurrentRecord);
UpdateButtons();
}
//---------------------------------------------------------------------------
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

724 Copyright 2003 FunctionX, Inc.

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 )
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 725

{
// If the Edit control is empty
if( edtRecordNumber->Text.IsEmpty() )
return; // Do nothing
else if( (edtRecordNumber->Text.ToInt() > 0) &&
(edtRecordNumber->Text.ToInt() <= ListOfContacts->Count) )
{
// Since the Edit control contains
CurrentRecord = edtRecordNumber->Text.ToInt();
Contact = reinterpret_cast<TContact *>(ListOfContacts-
>Items[CurrentRecord-1]);
DisplayRecord(Contact);
UpdateButtons();
}
else if( (edtRecordNumber->Text.ToInt() < 0) ||
(edtRecordNumber->Text.ToInt() > ListOfContacts->Count) )
{
ShowMessage("The record number is not valid");
}
}
}
//---------------------------------------------------------------------------
23. Test the application

Chapter 28: Creating and Using Lists Borland C++ Builder Programming

726 Copyright 2003 FunctionX, Inc.



24. After using the form, close it and save your project

28.1.6 Operations on a List
Operations on items of a list consist of adding, inserting, deleting, or moving items to or
from a list. All these operations are already provided by the TList class. The most you
have to do is to adapt the class to your goal. Although the operations on a list can be
performed in the form layout we have used so far, they can better be viewed and
appreciated in a view that displays all records at the same time. That is why sometimes
spreadsheet views are used in databases. For this exercise, we will use, we will use a List
View control.

Practical Learning: Using a List View
1. Create a new application with its default form
2. Save the project in a new folder named AddressBook2
3. Save Unit1 as Main and save the project as AddressBook2 and
4. Change its properties of the form as follows:
Caption = Address Book - Contacts List
Height = 340
Name = frmMain
ShowHint = true
Width = 456
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 727

5. From the Win32 tab of the Component Palette, double-click ImageList and. on
the form, double-click ImageList1
6. From the Bitmaps folder, and using the Add button, select the Exit bitmap
7. From the Standard tab of the Component Palette, double-click MainMenu
8. While the MainMenu1 icon is still selected, set its Images property to ImageList1
9. Double-click MainMenu1
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 and click an
unoccupied area on the form
15. Set the properties of the toolbar as follows:
EdgeBorders = [ebTop,ebBottom]
Flat = true
Height = 24
Images = ImageList1
16. From the Win32 tab of the Component Palette, add a List View control under
the toolbar on the form
17. On the Object Inspector, change the properties as follows:
GridLines = true
Height = 248
Left = 0
Name = lvwContacts
RowSelect = true
Top = 26
ViewStyle = vsReport
Width = 448
18. On the form, double-click the ListView1 control
19. Using the Add New button, create the columns as follows:
Caption Alignment Width
First Name taLeftJustify 80
MI taLeftJustify 24
Last Name taLeftJustify 80
Email Address taLeftJustify 160
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

728 Copyright 2003 FunctionX, Inc.

Home Phone taCenter 100

20. Add a Panel to the form with the following properties:
Align = alBottom
BevelOuter = bvLowered
Caption = none
Height = 20

21. Save your project
22. Create a new Unit and save it as Contacts
23. In the Contacts.h file, create a TContact class as follows:
//---------------------------------------------------------------------------
#ifndef ContactsH
#define ContactsH
#include <vcl.h>
//---------------------------------------------------------------------------
class TContact
{
public:
AnsiString FirstName;
AnsiString MI;
AnsiString LastName;
AnsiString Address;
AnsiString City;
AnsiString State;
AnsiString ZIPCode;
AnsiString Country;
AnsiString EmailAddress;
AnsiString HomePhone;
__fastcall TContact(AnsiString FN="J ohn", AnsiString M="T",
AnsiString LN="Doe",
AnsiString Adr="123 Main Street",
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 729

AnsiString CT="USA",
AnsiString St="ZZ", AnsiString ZIP="01234",
AnsiString Ctry="USA",
AnsiString Email="doej@mailme.com",
AnsiString HP="(123) 456-7890");
__fastcall TContact(const TContact &Cont);
virtual __fastcall ~TContact();
TContact& operator=(const TContact& Cont);
};
//---------------------------------------------------------------------------
#endif
24. In the Contacts.cpp file, define the object as follows:
//---------------------------------------------------------------------------

#pragma hdrstop

#include "Contacts.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)

//---------------------------------------------------------------------------
__fastcall TContact::TContact(AnsiString FN, AnsiString M,
AnsiString LN, AnsiString Adr,
AnsiString CT, AnsiString St, AnsiString ZIP,
AnsiString Ctry, AnsiString Email, AnsiString HP)
: FirstName(FN), MI(M), 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), MI(Cont.MI), LastName(Cont.LastName),
Address(Cont.Address), City(Cont.City),
State(Cont.State), ZIPCode(Cont.ZIPCode), Country(Cont.Country),
EmailAddress(Cont.EmailAddress), HomePhone(Cont.HomePhone)
{
}
//---------------------------------------------------------------------------
__fastcall TContact::~TContact()
{
}
//---------------------------------------------------------------------------
TContact& TContact::operator=(const TContact& Cont)
{
FirstName = Cont.FirstName;
MI - Cont.MI;
LastName = Cont.LastName;
Address = Cont.Address;
City = Cont.City;
State = Cont.State;
ZIPCode = Cont.ZIPCode;
Country = Cont.Country;
EmailAddress = Cont.EmailAddress;
HomePhone = Cont.HomePhone;

Chapter 28: Creating and Using Lists Borland C++ Builder Programming

730 Copyright 2003 FunctionX, Inc.

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)
{
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 731

delete ListOfContacts;
ListOfContacts = NULL;
}
//---------------------------------------------------------------------------
28. Save your project

28.1.7 A List and its New Items
In the previous section of this lesson, we learned how to statically create a list. That is,
we learned how you, the programmer, can create a list for the user. If you want users to
have more control on the list, you should give them the ability to add new items to a list
or insert items in the order of her choice.

We have already learned that, to add an item to the end of a list, you can use the
TList::Add() method. To add a new record, we will provide the user with a dialog box
that contains all the fields that compose a complete record. The user can provide the piece
of information that are available. Upon clicking OK, we will retrieve the record and add
it to the list.

Once a few records exist in the list, it is time for maintenance. The ListView we have
created will display only a few pieces of information, due to lack of space. That is why
we will display only the name, the email address and the telephone number of a contact.
If the user wants to see more details about a contact, we will allow him to select a record,
either double-click it or click a button. A dialog box will display the whole record. To do
this, when the user double-clicks a record, we will retrieve its index and display its details
in the same dialog box used to create a new record. Upon closing the dialog box, if the
user clicks OK after changing any piece of information on the contact, we will found out
if he wants to update the change.

Practical Learning: Adding and Inserting Items to a List
1. On the View toolbar, click the New Form button
2. Change its properties as follows:
BorderStyle = bsDialog
Caption = Address Book - Record Details
Height = 360
Name = dlgRecord
ShowHint = true
Width = 490
3. Save the form as Record and design it as follows:
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

732 Copyright 2003 FunctionX, Inc.



4. The form contains three GroupBox controls Named, from top to bottom,
grpPersonalInformation, grpResidence, and grpCommunication respectively.
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. The right buttons are BitBtn type with Kinds mrOk and mrCancel
7. Save the project.
8. Display the other form (View -> Forms, frmMain) and double-click ImageList1
9. Using the Add button, add the NewRecord button
10. On the form, double-click MainMenu1.
11. Click the blue box under the File menu and click Caption. Type &New Record...
and click Name. Type mnuNewRecord
12. Set the ImageIndex value of the New Record menu item to 1 and set its ShortCut to
Ctrl+N. Move the new menu item on top of Exit
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 733



13. Close the Menu Designer
14. On the main menu of C++ Builder, click File -> Include Unit Hdr...
15. Record should be selected. Therefore, click OK.
16. Whenever anything changes about the list of records, for example when the user
adds or deletes a record, the list of records should be updated. Instead of performing
this operation every time, we can use a central function that displays records. When
the records need to be displayed or updated, we can just call it.
In the private section of the header file of the TfrmMain class, declare a function as
follows:
private:
TContact *Contact;
TList *ListOfContacts;
void __fastcall ShowContacts(); // User declarations
public: // User declarations
__fastcall TfrmMain(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TfrmMain *frmMain;
//---------------------------------------------------------------------------
#endif
17. In the source file, implement the function as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::ShowContacts()
{
// If the list view had some items, dismiss them
lvwContacts->Items->Clear();

// For each record, display its values in the list view
for( int i = 0; i < ListOfContacts->Count; i++ )
{
TListItem * lstItem;

lstItem = lvwContacts->Items->Add();
lstItem->Caption = reinterpret_cast<TContact *>(ListOfContacts->Items[i])->FirstName;
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

734 Copyright 2003 FunctionX, Inc.

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
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 735

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...


Chapter 28: Creating and Using Lists Borland C++ Builder Programming

736 Copyright 2003 FunctionX, Inc.



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->edtZI PCode->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) ||
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 737

(dlgRecord->edtAddress->Text != Contact->Address) ||
(dlgRecord->edtCity->Text != Contact->City) ||
(dlgRecord->edtState->Text != Contact->State) ||
(dlgRecord->edtZI PCode->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

28.1.8 List Item Insertion
Besides the TList::Add() method used to add an item to a list, the TList class also
allows you to insert an item in any order inside the list. This operation is handled by the
TList::Insert() method. Its syntax is:

void __fastcall Insert(int Index, void *Item);
Chapter 28: Creating and Using Lists Borland C++ Builder Programming

738 Copyright 2003 FunctionX, Inc.


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.

Practical Learning: Adding and Inserting Items to a List
1. On the form, double-click ImageList1 and, using the Add button, add the Insert
bitmap
2. On the form, right-click the toolbar and click New Button
3. Make sure that the new bitmap is assigned to the new button and change its Name to
btnInsertRecord
4. Change its Hint to Insert Record
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->edtZI PCode->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;
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 739

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;

// Insert the new record to the list
ListOfContacts->Insert(ItemSelected, Contact);
}
}

// 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. Double-clicks the event on the right side of OnKeyDown and implement it as
follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::lvwContactsKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if( Key == VK_INSERT )
btnInsertRecordClick(Sender);
}
//---------------------------------------------------------------------------
9. Test the application
10. After using it, close the form and save the project

28.1.9 Item Removal From a List
If you find out that your list includes an item you don't need, you can remove such an
item from your list. The deletion of an item from a TList list can be handled by the
TList::Delete() method. Its syntax is:

void __fastcall Delete(int Index);

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.

Chapter 28: Creating and Using Lists Borland C++ Builder Programming

740 Copyright 2003 FunctionX, Inc.

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.

Practical Learning: Deleting Records
1. 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. Change its Hint to Delete Record
4. Double-click the new button and implement its event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::btnDeleteRecordClick(TObject *Sender)
{
// Find out if an item is selected
int SelectedItem = lvwContacts->ItemIndex;

if( SelectedI tem >= 0 )
{
int Response =
Application->MessageBox("Are you sure you want to delete this record?",
"Address Book",
MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION);
if( Response == IDYES )
{
// If an item is selected, delete it
ListOfContacts->Delete(SelectedItem);
// Update and show the list of records
ShowContacts();
}
}
}
//---------------------------------------------------------------------------
5. If the user presses Delete, we can perform the same action
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 )
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 741

btnDeleteRecordClick(Sender);
}
//---------------------------------------------------------------------------
8. Test the application
9. After using it, close the form and save the project

28.1.10 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.
Practical Learning: Clearing a List
1. 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. Double-click the new button and implement its event as follows:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::btnClearClick(TObject *Sender)
{
int Warning1 =
Application->MessageBox("This action will erase all of your contacts"
"\nAre you sure you want to erase all contacts?",
"Address Book",
MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION);
if( Warning1 == IDYES )
{
int Warning2 =
Application->MessageBox(
"Are you absolutely sure that you want to erase all contacts?",
"Address Book",
MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION);
if( Warning2 == IDYES )
{
ListOfContacts->Clear();
lvwContacts->Items->Clear();
}
}
}
//---------------------------------------------------------------------------
4. Test the application
5. After using it, close the form and save the project

Chapter 28: Creating and Using Lists Borland C++ Builder Programming

742 Copyright 2003 FunctionX, Inc.

28.2 The VCL's Collection of List

28.2.1Introduction
Besides the TList class that we have studied in this lesson, the VCL provides a wide
range of classes that can be used to create lists or maintain them. This means that,
although you can create your lists from scratch using C++, there are already classes that
can help you accomplish your scenario.

28.2.2 The TOrderedList Class
Like TList, the TOrderedList class is derived straight from TObject. The purpose of
TOrderedList is to lay a foundation of creating a list that can be ordered. This class
itself does not do anything. It gives birth to other classes that take care of the necessary
function definitions. This is because C++ Builder implements ordered lists differently.

28.2.3 The TQueue Class
A queue is a technique of arranging objects in an order so that items are aligned, like
people at the bank: the first person on the line is also the first person to be served and to
get out of the bank. This technique is referred to as First-In First-Out (FIFO). To create
such a list in a VCL application, you can use the TQueue class. It is derived from the
TOrderedList class.

28.2.4 The TStack Class
A stack is a technique of arranging objects like putting books in a box. If you decide to
remove them, you would first remove the last book you had put in the box. This
arrangement is referred to as Last-In First-Out (LIFO). To create such a list, you can use
the TStack class which is derived from the TOrderedList. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnStackClick(TObject *Sender)
{
class TContractor
{
public:
AnsiString FirstName;
AnsiString LastName;
};

TContractor *Std = new TContractor;
TStack *ListOfNumbers = new TStack;

Std->FirstName = "J ames";
Std->LastName = "Waters";
ListOfNumbers->Push(Std);
Std = reinterpret_cast<TContractor *>(ListOfNumbers->Peek());
edtFirstName1->Text = Std->FirstName;
edtLastName1->Text = Std->LastName;

Std->FirstName = "Peter";
Std->LastName = "Moon";
Borland C++ Builder Programming Chapter 28: Creating and Using Lists

Copyright 2003 FunctionX, Inc. 743

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.



Appendix Borland C++ Builder Programming

744 Copyright 2003 FunctionX, Inc.















Appendices


Computers: An Overview

Creating a Program

Data Output: An Introduction

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 745


A/ Date and Time Functions

Dates
The date and time are at the core of computer use. The date is a unit that measures the
number of years, months, or days elapsed in a specific period. To represent the dates,
computers, applications, and compilers are configured with appropriates techniques. The
C++ Builder compiler counts dates from 12/30/1899 at midnight. With this compiler,
dates and related operations are dealt with using a class called TDateTime. Since this
class is not a control, you can use it with any control that displays text.

The dates used in your applications will mostly be based on the TDateTime class. To
perform most operations in your programs, you will need to know when, how, and where
to use this class. As an object, you will need to declare it.

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");

This could also be written as:

AnsiString S = "05/04/1988";
TDateTime Mine = S;

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;

The Date() Function
The simplest way to get the date in your application consists of calling the Date()
function. For example, you can use it to display the current date:
In an Edit control: edtToday->Text = Date();
Appendix Borland C++ Builder Programming

746 Copyright 2003 FunctionX, Inc.

On a label: Label1->Caption = Date();
In a forms title bar: Form1->Caption = Date();

Date, String, and Numeric Conversions

Converting a String to Date
Unless you are using a special control that recognizes date values, most of the controls
will receive and display text. For this reason, as far as the compiler is concerned, the user
or the client of a control would be supplying text derived from an AnsiString class.
Therefore, before performing any operation on such a text, you will have to convert the
content of the control to a valid date. In the same way, if you want the compiler to
consider a date value as a normal string, you would convert the date value into a string.

If the user types a valid date into an edit box, such as 10/4/1988, this is still considered a
string and you would not be able to use it in an operation. To convert a string to a date,
you can use the StrToDate() function. The syntax is:

TDateTime __fastcall StrToDate(const AnsiString S);

This function takes an argument as the string that needs to be converted. The string must
be provided in a recognizable format, following the Regional Settings of the Windows
Control Panel. For example, in the United States, the components of a date are separated
with a forward slash /. The typical formats of a date 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 would return a valid date.
Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm2::btnConvertClick(TObject *Sender)
{
TDateTime Value = StrToDate(edtSource->Text);
edtTarget->Text = Value;
}
//---------------------------------------------------------------------------


Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 747


Converting a Date to a String
The TDateTime and the AnsiString classes are configured to work in harmony and allow
great flexibility. This allows a TDateTime date value to be easily recognized by, and
converted to, an AnsiString string. Unless you are performing detailed formatting, a date
value can be transparenty identified as a string. For this reason, you can display a date
value as a string without any conversion. As an example, when the user clicks a button
named Button1 on a form, the date value of the AfewMonthsAgo variable would display
in an Edit control named Edit1:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime AFewMonthsAgo("04/ 28/ 1998");

Edit1->Text = AFewMonthsAgo;
}
//---------------------------------------------------------------------------

An AnsiString string is able to recognize a TDateTime value because the AnsiString is
overloaded in the TDateTime class. Its syntax is;

__fastcall operator AnsiString() const;

The operator AnsiString() function allows any valid TDateTime value to be automatically
converted to a string when needed. You can also explicitly use the function if necessary.
Here is an example:




//---------------------------------------------------------------------------
void __fastcall TForm1::btnDisplayClick(TObject *Sender)
{
TDateTime AFewMonthsAgo("04/28/1998");
AnsiString Converted = AFewMonthsAgo.operator AnsiString();
edtResult->Text = Converted;
}
//---------------------------------------------------------------------------

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);

Appendix Borland C++ Builder Programming

748 Copyright 2003 FunctionX, Inc.

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;
}
//---------------------------------------------------------------------------

Converting a Date to a Double-Precision Number
A TDateTime value can be configured to display as a decimal number. The syntax used
is:

__fastcall operator double() const;

The double data type was overloaded in the TDateTime class to allow converting a
TDateTime variable to a fractional equivalent. The number can be made of two parts. If
the number is round, that is, without a decimal value, it represents the number of days
elapsed since 12/30/1899. If the number has a decimal part, the decimal portion
represents a fraction of the day over a 24-hour period.

The operator double() function can be used to get the number of days and the amount of
time elapsed since 12/30/1899 at midnight and the time of the current date. In the
following example, the difference between the current date an time and 12/30/1899 at
midnight is displayed as a double-precision number in an Edit control of a form when the
form comes up:

//---------------------------------------------------------------------------
void __fastcall TForm2::FormCreate(TObject *Sender)
{
TDateTime RightNow = Now();

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 749

double Qty = RightNow.operator double();
Edit1->Text = Qty;
}
//---------------------------------------------------------------------------



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;
}
//---------------------------------------------------------------------------

Converting a Date to an Integer
Like the double data type, the int data type was overloaded in the TDateTime class to
convert a TDateTime value to an integer. The syntax of the function is:

__fastcall operator int() const;

To get the equivalent number of days of a TDateTime value, declare an integer and
assign it the desired TDateTime value by calling the operator int() function. If the
operator is applied on a variable that holds a particular date, the operator int() function
would produce the number of days elapsed since 12/30/1988. In the following example,
the number of days since 12/30/1988 to the current date is displayed in an Edit control
when the user clicks the button:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnShowDaysClick(TObject *Sender)
{
TDateTime Today = Date();
int Days = Today.operator int();

edtDifference->Text = Days;
}
//---------------------------------------------------------------------------


Appendix Borland C++ Builder Programming

750 Copyright 2003 FunctionX, Inc.

The Computers System of Displaying Dates
Your computer and your customers uses a set of attributes to display date values. These
are provided or configured in the Regional Settings. To access the Regional Settings, in
Microsoft Windows XP, click Start Control Panel. From the Control Panel, click Date,
Time, Language, and Regional Options:





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:

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 751



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.
The Short Date Format
The Short Date Format is special string that all applications of your computer refer to in
order to display a date using the numeric version of a date.

The months hold the values 1=January, 2=February, 3=March, 4=April, 5=May, 6=June,
7=July, 8=August, 9=September, 10=October, 11=November, 12=December. Instead on
numbers, a month is represented with the letter m; the computer has the responsibility of
finding the right month.

The computer refers to the following characters and it depends on how these characters
are retrieved:

Character Meaning Why use it?
m or M The month displays as a single digit if the
numeric month is less than 10.
Can be used to display May
as 5 instead of 05
mm or MM The month displays a leading 0 if the
numeric month is less than 10.
Can be used to display May
as 05 instead of 5
d or D The day displays as a single digit if the day
of the month is less than 10.
Can be used to display the 8
th

of the month as 8 and not 08
dd or DD The day displays a leading 0 if the day of
the month is less than 10.
Can be used to display 8
th
day
of the month as 08 and not 8
y, yy, Y, or YY The year displays with two digits like 88 for
1988
Used when two digits suffice
to display the year
yyy, YYY, yyyy, YYYY The year displays with 4 digits such as 1988
and not 88
Used to display all digits for a
year

Appendix Borland C++ Builder Programming

752 Copyright 2003 FunctionX, Inc.

To display a date value, use the syntax:

ShortDateFormat = Format;

To create a format, assign an appropriate string to the ShortDateFormat variable. After
creating such a format, you can display a date value in the desirede control the same way
you would proceed. Before displaying the date, the compiler would scan the event and
find out what format to apply. Here are examples of displaying individual portions of a
date value:







//---------------------------------------------------------------------------
void __fastcall TfrmDates::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);

ShortDateFormat = "d";
edtd->Text = DateValue;
ShortDateFormat = "dd";
edtdd->Text = DateValue;
ShortDateFormat = "M";
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 753

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:




//---------------------------------------------------------------------------
void __fastcall TfrmMain::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
Appendix Borland C++ Builder Programming

754 Copyright 2003 FunctionX, Inc.


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;
}
//---------------------------------------------------------------------------

In the same way, you can create your own formats.
The Long Date Format
The operating system provides a more explicit system of displaying date values. This is
called the Long Date Format and it uses a global variable called LongDateFormat; this
variable is defined in the Regional Options of the Control Panel. When using the
LongDateFormat, there are various types of combinations applied to the characters,
depending on what you are trying to display.

To display the date using the Long Date Format formats, the computer uses the following
combinations:

d: The single d is used to display the numeric day of a month. Each month starts at 1,
then 2, and so on. The month of February can have 28 or 29 days depending on whether
the year is a leap year. The other months have 30 or 31 days. If the day occurs before the
10
th
, the number would display without the leading 0.
dd: The double d as dd displays the numeric day of the month, from 1 tom 31 depending
on the month and whether it is a leap year for February. If the month occurs before
October, it would display with the leading 0.

M: The single M is used to display the numeric position of the month. January is 1,
February is 2 and December is 12. If the month occurs before October, it would display
without the leading 0.
MM: The double M as MM displays the numeric position of a month from 1 to 12. If the
number is less than 10, it would display with the leading 0.
MMM: The triple M as MMM displays the name of the month using three letters. It uses
a global variable called ShortMonthNames. This variable is defined by the operating
system. By default, the names of the month are Jan, Feb, Mar, Apr, May, Jun, Jul, Aug,
Sep, Oct, Nov, and Dec.
MMMM: The quadruple M as MMMM displays the complete name of a month as
defined as the operating system. The variable used is called LongMonthNames. The
names of the months are January, February, March, April, May, June, July, August,
September, October, November, and December.

Y or YY: The single y or double yy is used to display the numeric year with the last two
digits.
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 755

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;
}
//---------------------------------------------------------------------------

Appendix Borland C++ Builder Programming

756 Copyright 2003 FunctionX, Inc.

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 )
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 757

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:




//---------------------------------------------------------------------------
void __fastcall TForm1::btnDecodeClick(TObject *Sender)
{
TDateTime Typed = StrToDate(edtDate->Text);
Word Year, Month, Day;

DecodeDate(Typed, Year, Month, Day);

edtDay->Text = Day;
edtMonth->Text = Month;
edtYear->Text = Year;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnCloseClick(TObject *Sender)
{
Close();
Appendix Borland C++ Builder Programming

758 Copyright 2003 FunctionX, Inc.

}
//---------------------------------------------------------------------------

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;
AnsiString Months[] = { "",
"J anuary", "February", "March", "April",
"May", "J une", "J uly", "August",
"September", "October", "November", "December" };

DecodeDate(Typed, Year, Month, Day);

edtDay->Text = Day;
edtMonth->Text = Months[Month];
edtYear->Text = Year;
}
//---------------------------------------------------------------------------


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 year: valid values range from 0 to 9999;
the month: valid values range from 1 to 12; January is 1, February is 2, etc;
the day: this could be 28, 29, 30, or 31 depending on the month and whether the
year is a leap year, which controls the number of days for the month of
February.

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
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 759

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:



//---------------------------------------------------------------------------
void __fastcall TForm1::btnEncodeClick(TObject *Sender)
{
Word Day, Month, Year;

Day = edtDay->Text.ToInt();
Month = edtMonth->Text.ToInt();
Year = edtYear->Text.ToInt();

TDateTime Value = EncodeDate(Year, Month, Day);
edtDate->Text = Value;
}
//---------------------------------------------------------------------------

Finding Out the Leap Year
One of the arduous operations performed on dates is to find out out whether the year
value of a date is a leap year. Luckily, the IsLeapYear() function can perform it. The
syntax of this function is:

bool __fastcall IsLeapYear(Word Year);

This function takes an unsigned short integer argument variable and examines it. If the
argument, which must be a valid year number, is a leap year, the function returns true;
otherwise, it would return false. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if( IsLeapYear(Edit1->Text.ToInt()) )
Edit2->Text = "Leap Year";
else
Edit2->Text = "Not";
}
//---------------------------------------------------------------------------
Appendix Borland C++ Builder Programming

760 Copyright 2003 FunctionX, Inc.



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 =
I sLeapYear(Year) ? " is a leap year" : " is not a leap year";

ShowMessage("The date you typed was " + Value + "\n" +
AnsiString(Year) + LeapYear);
}
//---------------------------------------------------------------------------

The Day of the Week
The VCL is equipped with a special function that can be used to retrieve the day of the
week of a given date. The function used is the DayOfWeek() and its syntax is:

int __fastcall DayOfWeek(System::TDateTime Date);

To use this function, pass a TDateTime value or variable to the DayOfWeek() function.
The TDateTime object passed as argument must hold a valid date value. After execution,
the function returns an integer between 1 (included) and 7 (included) that represents the
position of the day. Sunday is referred to as the first day of the week and has a value of 1;
Monday is 2, etc.

Here is an example:


//---------------------------------------------------------------------------
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 761

void __fastcall TForm1::btnGetItClick(TObject *Sender)
{
TDateTime Value = StrToDate(edtDate->Text);
int Day = DayOfWeek(Value);

edtDay->Text = Day;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnCloseClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------

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;
}
//---------------------------------------------------------------------------

Appendix Borland C++ Builder Programming

762 Copyright 2003 FunctionX, Inc.



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);
AnsiString DayName[] = { "", "Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday", "Friday", "Saturday" };

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;
}
//---------------------------------------------------------------------------

Increasing Months on a Date
The addition operator of the TDateTime class is used to add a number of days to a date
value. If you want to add months to a date value, you can use the IncMonth() function. Its
syntax is:

TDateTime __fastcall IncMonth(const TDateTime Source, int Months);

This function takes two arguments. The first is the date value or variable that serves as
the source or reference to work on. This argument must hold a valid TDateTime date
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 763

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);

Appendix Borland C++ Builder Programming

764 Copyright 2003 FunctionX, Inc.

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:




//---------------------------------------------------------------------------
void __fastcall TForm1::btnReplaceClick(TObject *Sender)
{
TDateTime Original = StrToDate(edtOriginal->Text);
TDateTime Replacement;

ReplaceDate(Replacement, Original);
edtReplacement->Text = DateToStr(Replacement);
}
//---------------------------------------------------------------------------

Comparison Operations on Dates
The TDateTime class was configured with many arithmetic and comparison operators.
These are used on date values allowing you to use TDateTime variables as if they were
regular variables.
The Comparison for Equality
To find out whether two data values are the same, simply apply the Equality operator on
their values. Once you have two TDateTime values or variables, the compiler is
configured to perform this comparison.

Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);

if(Start == End)
pnlComparison->Caption = "Both dates are the same";
else
pnlComparison->Caption = "Those are different dates!";
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 765



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!";
}
//---------------------------------------------------------------------------

The Comparison for Inequality
To find out whether two data values are not the same, simply apply the inequality
operator on their values. Once you have two TDateTime values or variables, the compiler
is configured to perform this comparison.

Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);

if( Start != End )
pnlComparison->Caption = "Those are different dates!";
else
pnlComparison->Caption = "Both dates are the same";
}
//---------------------------------------------------------------------------

Appendix Borland C++ Builder Programming

766 Copyright 2003 FunctionX, Inc.



This comparison is possible because the inequality operator != is overloaded in the
TDateTime class. Its syntax is:

bool __fastcall operator !=(const TDateTime& Target) const;

To compare two dates using the overloaded inequality 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 = "Those are different dates!";
else
pnlComparison->Caption = "Both dates are the same";
}
//---------------------------------------------------------------------------

The Comparison for Inferiority
To find if Source date occurs prior to a Target date, apply the less than comparison
operator. To do this, use the operator as if both date values were normal integral or
floating-point values. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);

if( Start < End )
pnlComparison->Caption = DateToStr(Start) + " occurs prior to " +
DateToStr(End);
else
pnlComparison->Caption = "I can't make up my mind";
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 767



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";
}
//---------------------------------------------------------------------------

The Comparison for Inferiority or Equality
Two TDateTime values can be compared to find out whether they are the same or if a
source date occurs prior to a target date. To perform this comparison, apply the less than
or equal operator as if the values were regular integer or floating-points.

Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);

if( Start <= End )
pnlComparison->Caption = "Your film will be ready after 5 O'Clock";
else
pnlComparison->Caption = "Wrong date sequence";
}
//---------------------------------------------------------------------------

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;
Appendix Borland C++ Builder Programming

768 Copyright 2003 FunctionX, Inc.


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";
}
//---------------------------------------------------------------------------
The Comparison for Superiorty
To find if a source date occurs after a target date, apply the greater than comparison
operator. This operator is used the same way you would for a regular integer or a
floating-point values. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);

if( Start > End )
pnlComparison->Caption = DateToStr(Start) + " occurs after " +
DateToStr(End);
else
pnlComparison->Caption = "I can't make up my mind";
}
//---------------------------------------------------------------------------



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)
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 769

{
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";
}
//---------------------------------------------------------------------------

The Comparison for Superiority or Equality
Two TDateTime values can be compared to find out whether they are the same or if a
source date occurs after a target date. To perform this comparison, apply the greater than
or equal operator as if the values were regular integer or floating-points.

Here is an example:

//---------------------------------------------------------------------------
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""\"";
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";
}
//---------------------------------------------------------------------------

Appendix Borland C++ Builder Programming

770 Copyright 2003 FunctionX, Inc.

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.
Assigning One Date to Another
To assign one date to another, simply use the assignment operator = as if the variables
were regular values. In the following example, the user enters a starting date in an Edit
control and finds out the number of days a customer of a car rental wants to keep the car.
The clerk enters this value in the # of Days edit box. When the clerk clicks somewhere
else, that is, when the edit box loses focus, the content of the edit box is checked. If the
number of days is 1 or less, which means the customer wants the car for only one day, the
first date is assigned to a second TDateTime variable and displays in the End Date edit
box:

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 771



//---------------------------------------------------------------------------
void __fastcall TForm1::edtNbrOfDaysExit(TObject *Sender)
{
TDateTime Start, End;
int NbrOfDays;

Start = StrToDate(edtStartDate->Text);
NbrOfDays = StrToInt(edtNbrOfDays->Text);

if(NbrOfDays <= 1)
{
End = Start;
edtEndDate->Text = End;
}
else
{
edtEndDate->Text = "";
edtEndDate->SetFocus();
}
}
//---------------------------------------------------------------------------

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;
Appendix Borland C++ Builder Programming

772 Copyright 2003 FunctionX, Inc.

}
else
{
edtEndDate->Text = "";
edtEndDate->SetFocus();
}
}
//---------------------------------------------------------------------------

Adding Values to a Date
To add a number of days to a TDateTime value, simply add an integer to the intended
date value. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnAdditionClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);

TDateTime Addition = Start + 5;

edtAddition->Text = Addition;
}
//---------------------------------------------------------------------------

You can also get the number of days from the user by using another control on the
application. Here is an example:




//---------------------------------------------------------------------------
void __fastcall TForm1::btnEstimateClick(TObject *Sender)
{
TDateTime DepositDate = edtDepositDate->Text;
int Days = edtDays->Text.ToInt();
TDateTime PickupDate = DepositDate + Days;

edtPickupDate->Text = PickupDate;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnCloseClick(TObject *Sender)
{
Close();
}
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 773

//---------------------------------------------------------------------------

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::btnAdditionClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtDate->Text);
int Months = StrToInt(edtValue->Text);
unsigned short Year, Month, Day;

Start.DecodeDate(&Year, &Month, &Day);
TDateTime End(Year, Month+Months, Day);
edtResult->Text = DateToStr(End);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnExitClick(TObject *Sender)
{
Close();
Appendix Borland C++ Builder Programming

774 Copyright 2003 FunctionX, Inc.

}
//---------------------------------------------------------------------------

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);
}
//---------------------------------------------------------------------------

Assigning an Added Date
You can add a value to a date variable and assign the new value to the original date. This
is the same as using the += operator. The syntaxes used for this operator 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 add an number of days to an existing date value, you can use the += operator as
follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnDisplayClick(TObject *Sender)
{
TDateTime OneDay = StrToDate(edtDate->Text);
TDateTime Added;
OneDay += 5;
edtResult->Text = DateToStr(OneDay);
}
//---------------------------------------------------------------------------



Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 775

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:



//---------------------------------------------------------------------------
void __fastcall TForm1::btnDifferenceClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);
int Years = End - Start;
edtYears->Text = (Years / 365);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnCloseClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------

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,
Appendix Borland C++ Builder Programming

776 Copyright 2003 FunctionX, Inc.

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;
}
//---------------------------------------------------------------------------

The subtraction operation is possible on TDateTime values because the subtraction
operator - is overloaded in the TDateTime class as follows:

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;

The subtraction operator - is overloaded in the TDateTime class to allow getting the
difference of days, that is, the elapsed number of days between two dates. The subtraction
is performed as Date2 Date1. In this case, if Date2 occurs after Date1, the result would
be a positive number; otherwise a negative value would indicate that Date2 occurs before
Date1.

You can also use the operator int() overloaded function to get the difference of days
between two dates. To do this, declare an integer that stores the subtracted number
between two TDateTime dates. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnDifferenceClick(TObject *Sender)
{
TDateTime Start = StrToDate(edtStart->Text);
TDateTime End = StrToDate(edtEnd->Text);

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 777

TDateTime Diff = End - Start;
int Days = Diff.operator int();

edtDifference->Text = Days;
}
//---------------------------------------------------------------------------

Assigning a Subtracted Date
The -= operator is used to subtract a number of days from a date and assign the new value
to the date (whose value has been subtracted). This operation is performed using the
overloaded -= operator that uses the following syntaxes:

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 subtract a number of days from a date, you can use the subtraction operator. To assign
the subtracted value to the original date, use the -= operator. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnDisplayClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
int IntValue = StrToInt(edtSubtract->Text);

DateValue -= IntValue;
edtResult->Text = DateToStr(DateValue);
}
//---------------------------------------------------------------------------



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--;
}
//---------------------------------------------------------------------------
Appendix Borland C++ Builder Programming

778 Copyright 2003 FunctionX, Inc.


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:

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 779

TDateTime& operator ++();
TDateTime operator ++(int);

To apply the pre-increment 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::btnIncrementClick(TObject *Sender)
{
static TDateTime Original = StrToDate(edtDate->Text);
edtIncremented->Text = Original.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). 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);
}
//---------------------------------------------------------------------------


This was the opening dialog box:


This was performed after the first click:




Formatting and Controlling the Display of Dates
The TDateTime class and the VCL provide special functions that can be used to control
how the date values display in your application. Calling one of these functions, a date can
use almost any format you see fit or any that makes your application user-friendly.

To control how a date value displays, the TDateTime class uses the FormatString()
method. Its syntax is:

AnsiString __fastcall FormatString(const AnsiString& format);

The TDateTime::FormatString() method takes a string argument that specifies what
format to apply. After formatting the date value, the method returns an AnsiString string.

Alternatively, the VCL has its own function that performs the same operation. Its syntax
is:

Appendix Borland C++ Builder Programming

780 Copyright 2003 FunctionX, Inc.

AnsiString __fastcall FormatDateTime(const AnsiString Format,
System::TDateTime DateValue);

The FormatDateTime() function takes two arguments. The Format argument is a string
that specifies how the date should be formatted. Since this function is not part of the
TDateTime class, it needs a valid TDateTime date value to work on; that is the role of the
DateValue argument.

The Default Display
The default format used by both the TDateTime::FormatString() and the
FormatDateTime() functions conforms to the ShortDateFormat of the Windows
Control Panel. For these functions, that format is represented by the c string. You can
still use it explicitly. For the TDateTime::FormatString() method, an example would
be:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime DateValue("10/22/2001");
Edit1->Text = DateValue.FormatString("c");
}
//---------------------------------------------------------------------------

A similar example applied on the FormatDateTime() function would be:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime DateValue("10/22/2001");
Edit1->Text = FormatDateTime("c", DateValue);
}
//---------------------------------------------------------------------------

Displaying the Numeric Day
The days of months are numbered from 1 to 31, depending on the month. The formatting
functions represent each of these days with the d format. Here is an example used for
the TDateTime::FormatString() method:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnFormatStringClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("d");
}
//---------------------------------------------------------------------------

This would produce:

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 781



The implementation of this format using the FormatDateTime() function is:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnFormatDateTimeClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatDateTime->Text = FormatDateTime("d", DateValue);
}
//---------------------------------------------------------------------------

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);
}
//---------------------------------------------------------------------------

Displaying Weekday Names
The names of the week use two formats: 3 letters or full name. To display a name with 3
letters, use the ddd format. The names will be Sun, Mon, Tue, Wed, Thu, Fri, or Sat.
Here is an example using the TDateTime::FormatString() method:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
Appendix Borland C++ Builder Programming

782 Copyright 2003 FunctionX, Inc.

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);
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 783

edtFormatString->Text = FormatDateTime("dddd dd", DateValue);
}
//---------------------------------------------------------------------------

Displaying Numeric Months
Months are displayed using two categories: a number or a name.

To display the numeric range of a month, use the m format. The months are numbered
as follows: 1=January, 2=February, 3=March, 4=April, 5=May, 6=June, 7=July,
8=August, 9=September, 10=October, 11=November, and 12=December. Here is an
example that displays the numeric month of a date, using the
TDateTime::FormatString() method:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("m");
}
//---------------------------------------------------------------------------

Following the rules we applied to display a combination of a weekday and a months
numeric day, you can display a month day and the months numeric value. This time,
instead of an empty space, you should use a character that would indicate that the date is
displaying a combination of month and day (or day and month). The best character to
use is the one that conforms to the regional settings of your computer. In the United
States English, this would be the forward slash /. Here is an example that uses the
FormatDateTime() function:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = FormatDateTime("m/ dd", DateValue);
}
//---------------------------------------------------------------------------

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);
}
//---------------------------------------------------------------------------

This would produce:

Appendix Borland C++ Builder Programming

784 Copyright 2003 FunctionX, Inc.




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");
}
//---------------------------------------------------------------------------

Displaying Months Names
You can display a month by its name using one of two formats: short or long name. The
short names of months are: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, and
Dec. To display a month with one of these names, use the mmm format. The following
example uses the TDateTime::FormatString() method:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("mmm");
}
//---------------------------------------------------------------------------

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)
{
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 785

TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("ddd dd mmm");
}
//---------------------------------------------------------------------------



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);
Appendix Borland C++ Builder Programming

786 Copyright 2003 FunctionX, Inc.

}
//---------------------------------------------------------------------------

Displaying the Year
A year value can be displayed using 2 or 4 digits. To display a year value as a number
between 00 and 99, use the y or the yy formats as follows (this event uses the
TDateTime::FormatString() method):

//---------------------------------------------------------------------------
void __fastcall TForm1::btnFormatClick(TObject *Sender)
{
TDateTime DateValue = StrToDate(edtDate->Text);
edtFormatString->Text = DateValue.FormatString("y");
}
//---------------------------------------------------------------------------

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
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 787

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.
Appendix Borland C++ Builder Programming

788 Copyright 2003 FunctionX, Inc.

Declaring Time Variables
The time portion of a TDateTime object can be declared and manipulated as a value. To
declare time variables, you will use one of the constructors of the TDateTime class. If
you declare a variable using the default constructor, as TDateTime TimeValue, the time
will be initialized to midnight or 12:00:00 AM.

The time in regular value is a floating number that ranges from 0 included to 1 excluded.
More precisely, the time is a value defined as follows:

0 >= Time >= 0.99999

The 0 constant represents midnight while the 0.99999 double-precision number
represents 12:59:59 PM. In between, 0.50 represents 12:00:00 PM (noon time), 0.325
represents 7:48:00 AM, and 0.738 represents 5:42:43 PM.

To declare a time variable, use the TDateTime class and specify the name for the
variable. Here is an example:

TDateTime Mine;

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;

Such an initialization is the same as:

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:
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 789


//---------------------------------------------------------------------------
void __fastcall TForm1::btnTimeClick(TObject *Sender)
{
double Number = edtNumber->Text.ToDouble();
TDateTime TimeValue = Number;

edtTime->Text = TimeValue;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnCloseClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------

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)
{
Appendix Borland C++ Builder Programming

790 Copyright 2003 FunctionX, Inc.

TDateTime TimeValue("10:22:52 AM");
Label3->Caption = TimeValue;
}
//---------------------------------------------------------------------------

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

The Time() Function
To get or display the time on your application, you can use the Time() function. You can
display it on:
An edit box: edtTime->Text = Time();
A label: lblCurrent->Caption = Now is the time: + Time();
A panel: pnlTime->Caption = At this time, we have + Time();

Converting a String to Time
To convert a string to a time value, use the StrToTime() function whose syntax is:

TDateTime __fastcall StrToTime(const AnsiString S);

This function takes a string as argument. The string must be in a in a valid time format,
following the Regional Rettings of the Control Panel. For example, in the United States,
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 791

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;
}
//---------------------------------------------------------------------------




Converting a Time Value to a String
A time value is, default, recongnized and treated as a string whenever necessary. This
flexibility allows you to perform transparent conversions from a time variable to a string
value. Therefore, it perfectly legimate to write the following assignment:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime TimeValue("22:32:58");
AnsiString Right = TimeValue;

Edit1->Text = Right;
}
//---------------------------------------------------------------------------

This conversion is possible because the AnsiString constructor is overloaded in the
TDateTime class. Its syntax is:

__fastcall operator AnsiString() const;

Therefore, to convert a time value to a string, you can simply assign the time to a string
variable. You can also explicity call the overloaded AnsiString as follows:

//---------------------------------------------------------------------------
Appendix Borland C++ Builder Programming

792 Copyright 2003 FunctionX, Inc.

void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime StartTime("9:05:00 AM");
AnsiString ToDisplay = StartTime.operator AnsiString();

Edit1->Text = ToDisplay;
}
//---------------------------------------------------------------------------

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;
}
//---------------------------------------------------------------------------

Converting a Time Value to a Double-Precision Number
A TDateTime time value is a number that represents a fraction of the day on a 24-hour
basis. This is quite helpful in algebraic operation related assignments.

To convert a time value to a double-precision number, simply cast the time value to a
double. In the following example, the current time is converted to double and displayed
in an edit box when the user clicks a button on a form:

//---------------------------------------------------------------------------
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 793

void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime TimeX = Time();
double Value = double(TimeX);
Edit1->Text = Value;
}
//---------------------------------------------------------------------------

The conversion of a time value to a double-precision number is possible because the
double data type was overloaded in the TDateTime to allow this conversion. The syntax
of the function used is:

__fastcall operator double() const;

You can use this function transparently as done in the previous example. You can also
call it explicitly call it as follows:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime TimeX("09:42:18 AM");
double Value = TimeX.operator double();
Edit1->Text = Value;
}
//---------------------------------------------------------------------------

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;

Appendix Borland C++ Builder Programming

794 Copyright 2003 FunctionX, Inc.

Each component is retrieved using a pointer to an unsigned short. The presence of
pointers allows you to pass empty variables whose values would be altered by the
function and returned with new values.

In the following example, the current time is stored in a TDateTime variable named
RightNow. A form is equipped with four Edit controls named edtTime edtHours,
edtMinutes, and edtSeconds. Although the mSec argument is required to decode a time
value, it was used only during the decoding operation. After calling the function, the
hour, the minute, and the second are retrieved and displayed in the corresponding edit
boxes:



//---------------------------------------------------------------------------
void __fastcall TForm1::btnDecodeClick(TObject *Sender)
{
TDateTime RightNow = Time();

unsigned short Hours, Minutes, Seconds, Milliseconds;
RightNow.DecodeTime(&Hours, &Minutes, &Seconds, &Milliseconds);

edtTime->Text = RightNow;
edtHours->Text = Hours;
edtMinutes->Text = Minutes;
edtSeconds->Text = Seconds;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnExitClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 795



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:

the hours: the values range from 0 to 23; midnight is 0 while 3 PM is 15
the minutres: the values range from 0 to 59;
the seconds: the values range from 0 to 59;
the milliseconds: the values range from 0 to 999.

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:

Appendix Borland C++ Builder Programming

796 Copyright 2003 FunctionX, Inc.



//---------------------------------------------------------------------------
void __fastcall TForm1::btnEncodeClick(TObject *Sender)
{
Word Hours, Minutes, Seconds, Milliseconds;

Hours = edtHours->Text.ToInt();
Minutes = edtMinutes->Text.ToInt();
Seconds = edtSeconds->Text.ToInt();
Milliseconds = edtMilliseconds->Text.ToInt();

TDateTime Value = EncodeTime(Hours, Minutes, Seconds, Milliseconds);
edtTime->Text = Value;
}
//---------------------------------------------------------------------------

Replacing a Time Value
To change the time of a TDateTime value, you can use the ReplaceTime() function. Its
syntax is:

void __fastcall ReplaceTime(TDateTime &TimeTarget, const TDateTime
TimeSource);

The ReplaceTime() function takes two arguments. The second argument is the original
time that is used as the time reference. The first argument is the time value that needs to
be changed or replaced. The TimeSource argument must be a valid time variable or in a
recognizable format; otherwise, the function would fail and throw an error. The
TimeTarget argument can be an existing time value of a TDateTime variable. After the
function has executed, if successful, the time portions of both arguments would be the
same.

Comparison Operations On Time Values
The TDateTime class provides various functions used to perform any type of
comparisons between time values. By default, these operatiorns can be applied the same
way as done on regular variables. They can also be customized for more detailed
comparisons.
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 797

The Comparison for Equality
To find out whether two values carry the same time formats, apply the equality
comparison, exactly the same way you would proceed for regular variables.

In the following example, two time values are retrieved from two edit boxes for
comparison. If both times are the same, a message displays accordingly on a panel:

//---------------------------------------------------------------------------
void __fastcall TForm1::Panel1Click(TObject *Sender)
{
TDateTime StartTime = StrToTime(edtStart->Text);
TDateTime EndTime = StrToTime(edtEnd->Text);

if( StartTime == EndTime )
Panel1->Caption = "Same Time";
else
Panel1->Caption = "";
}
//---------------------------------------------------------------------------

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 Comparison for Difference
As opposed to the equality operation, you can use the inequality operator to find out
whether two dates are different. To do this, simply apply the operator the same way you
would do on regular variables. Here is an example following the same logic as above:

//---------------------------------------------------------------------------
void __fastcall TForm1::Panel1Click(TObject *Sender)
{
TDateTime StartTime = StrToTime(edtStart->Text);
TDateTime EndTime = StrToTime(edtEnd->Text);

if( StartTime != EndTime )
Panel1->Caption = "Different Times";
else
Panel1->Caption = "";
}
//---------------------------------------------------------------------------

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;

Appendix Borland C++ Builder Programming

798 Copyright 2003 FunctionX, Inc.

The Comparison for Previous Occurrence
To find out whether a certain time occurs before another, use the less than operator <
the same way you would proceed for a regular variable. In the following example, when
the user click the Compare button on a form, the time values on two edit boxes and
retrieved and examined. If the time displayed in the Start Shift edit box occurs before the
time in the End Shift edit box, a dialog box displays a message accordingly:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnCompareClick(TObject *Sender)
{
TDateTime Start = StrToTime(edtStart->Text);
TDateTime End = StrToTime(edtEnd->Text);

if( End < Start )
ShowMessage("Invalid Time Sheet. Please verify accuracy.");
}
//---------------------------------------------------------------------------

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 Comparison for Previous or Equal Occurrence
The less than or equal to operator <= is used to check whether a time occurs previous
to, or in concordance with, another time. To use this operator on time values, proceed as
if you were dealing with regular variables. Here is an example based on the above event:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnCompareClick(TObject *Sender)
{
TDateTime Start = StrToTime(edtStart->Text);
TDateTime End = StrToTime(edtEnd->Text);

if( End <= Start )
ShowMessage("Make sure your time sheet is correct.\n"
"If you didn't work on this day, "
"you don't have to sign the time sheet."
"\n\t\t\t\t\t\tThanks");
}
//---------------------------------------------------------------------------

The operator that allows this comparison uses the following syntax:

bool __fastcall operator <=(const TDateTime& rhs) const;

The Comparison for Subsequent Occurrence
To find out whether one time occurs after another, use the greate than operator >.
This operator works the same way it would for a regular variable. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnCompareClick(TObject *Sender)
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 799

{
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;

The Comparison for Latter or Same Time
If a time occurs after or in concordance with another time, you can find this out using the
greater than or equal operator. This operator is applied on two valid time values the
same way it would be used on regular variables. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnCompareClick(TObject *Sender)
{
TDateTime Start = StrToTime(edtStart->Text);
TDateTime End = StrToTime(edtEnd->Text);

if( Start >= End )
{
lblMessage->Caption = "Your time sheet is not correct.";
edtStart->SetFocus();
}
}
//---------------------------------------------------------------------------

This comparison is possible thanks to the following overloaded operator in the
TDateTime class:

bool __fastcall operator >=(const TDateTime& rhs) const;

Controlling Time Display
The TDateTime class and the systdate.h file provide various techniques to control how
the time displays. Fundamentally the time displays according to the settings of the
Control Panel. There are two main functions for this purpose.

The TDateTime class is equipped with the FormatString() function whose syntax is:

AnsiString __fastcall FormatString(const AnsiString& Format);

Appendix Borland C++ Builder Programming

800 Copyright 2003 FunctionX, Inc.

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.
Displaying in Default Format
By default, the FormatDateTime() and the TDateTime::FormatString() functions
follow the format set by the computers Regional Settings of the Control Panel, which
usually ignores the 0 for hours less than 10.

To display the time using the default format, call the TDateTime::FormatString() method
and provide the Format argument as c. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime RightNow = Time();
RightNow.FormatString("c");

Edit1->Text = RightNow;
}
//---------------------------------------------------------------------------

Displaying the Leading Zero
When the hour portion of a time value is less than 10, you can control whether to display
the leading zero. This also applies to the minutes and the seconds. Each of these entities
provide two formats to take care of this. The syntax used to display the time is

hh:nn:ss AM/PM

The hh, nn, and ss portions are not case sensitive.

To ignore a leading zero when an hour less than 10 displays, use only h for the hour
portion. Here is an example:

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TDateTime RightNow = Time();

Edit1->Text = RightNow.FormatString("h:nn:ss AM/PM");
}
//---------------------------------------------------------------------------

Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 801



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
Appendix Borland C++ Builder Programming

802 Copyright 2003 FunctionX, Inc.

edtStart->Text = TimeX.FormatString("hh:n:ss am/pm");
// Displaying the AMPM portion in uppercase
edtEnd->Text = TimeX.FormatString("hh:nn:ss AM/PM");
}
//---------------------------------------------------------------------------

Combining Date and Time

Fundamental Functions: Now()
C++ Builder provides a function that combines date and time. This function, called
Now(), can be used to display its value on:
An edit box: edtNow->Text = Date and Time: + Now();
A label: lblNow->Caption = Now: + Now();
A forms caption: Caption = Now();

Converting a String to Date and Time
To convert a string to a valid date and time, use the StrToDateTime() function. This
function follows the same rules as the StrToDate() function. Its syntax is:

TDateTime __fastcall StrToDateTime(const AnsiString S);

This function takes a string as argument. The string must have a valid TDateTime format;
otherwise, the operation would faild. If everything works fine, after converting the string,
the function returns a valid TDateTime value.

In the following example, a form is created with a MaskEdit control named edtSource, an
Edit control named edtTarget, two Button controls named btnConvert and btnExit. The
EditMask of the MaskEdit control is configured as follows:

!99/99/0000 !90:00:00 >LL;1;_



Here is the OnClick event of the Convert button:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnConvertClick(TObject *Sender)
{
TDateTime Current = StrToDateTime(edtSource->Text);
edtTarget->Text = Current;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnExitClick(TObject *Sender)
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 803

{
Close();
}
//---------------------------------------------------------------------------




Converting a Date and Time to a String
To convert a date and time value to a string, use the DateTimeToStr(). Its syntax is:

AnsiString __fastcall DateTimeToStr(System::TDateTime Date);

This function takes, as argument, a valid TDateTime variable or value. If the argument is
not a valid date and time value, an error would result. After performing the conversion on
the date and time value, the function returns the value as a string.

In the following example, when the user clicks a button named btnConvert, the event uses
the Now() funtion to retrieve the current date and time then converts it to string and
displays the string in an Edit control named edtTarget:

//---------------------------------------------------------------------------
void __fastcall TForm1::btnConvertClick(TObject *Sender)
{
AnsiString Current = DateTimeToStr(Now());
edtTarget->Text = Current;
}
//---------------------------------------------------------------------------

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;
}
//---------------------------------------------------------------------------

Appendix Borland C++ Builder Programming

804 Copyright 2003 FunctionX, Inc.


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
arc .................................................................. 120
arithmetic ......................................................... 91
ArrowKeys..................................................... 537
ASCII ............................................................. 483
Assign().......................................................... 256
atan() .............................................................. 132
AutoResize..................................................... 473
AutoSelect...................................................... 483
B
Back ............................................................... 443
Background.................................................... 683
BackgroundEnabled....................................... 684
BALTIC_CHARSET..................................... 252
BEGIN_MESSAGE_MAP............................ 345
behavior ........................................................... 36
BevelInner...................................................... 553
BevelOuter ..................................................... 444
Bits................................................................. 235
bitwise............................................................ 253
bkAbort .......................................................... 433
bkAll .............................................................. 433
bkCancel ........................................................ 433
bkClose .................................................. 433, 548
bkCustom....................................................... 433
bkHelp............................................................ 433
bkIgnore ......................................................... 433
bkNo............................................................... 433
bkOK.............................................................. 433
bkRetry........................................................... 433
blGlyphBottom.............................................. 434
blGlyphLeft.................................................... 434
blGlyphRight.................................................. 434
blGlyphTop.................................................... 434
blinking cursor ............................................... 342
Blue................................................................ 235
BorderStyle ............................................ 481, 557
Borland C++ Builder........................................ 19
BoundsRect .................................................... 340
bsDialog......................................................... 557
bsNone ........................................................... 599
bsSolid ........................................................... 553
bsTopLine ...................................................... 445
btNext............................................................. 539
btPrev............................................................. 539
BUTTON....................................................... 325
Buttons
New Form.................................................... 34
Save All........................................................ 27
bvNone................................................... 444, 553
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 805

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
TParaAttributes .......................................... 519
TProgressBar.............................................. 566
TQueue....................................................... 742
TRadioGroup ............................................. 595
TReplaceDialog ......................................... 530
TScreen ........................................................ 32
TStack ........................................................ 742
TStringGrid........................................ 665, 669
TStrings...................................................... 519
TTextAttributes.......................................... 520
TTimer ....................................................... 554
TTreeNode ................................................. 694
TTreeNodes................................................ 694
TTreeView................................................. 694
TUpDown .................................................. 538
TValueListEditor ....................................... 685
TWinControl .............................................. 432
classes.hpp ..................................................... 346
clBackground ......................................... 239, 246
clBlack ................................................... 240, 553
clBlue............................................................. 240
clBtnFace ....................................................... 238
clBtnHighlight................................................ 239
clBtnShadow.................................................. 239
clBtnText ....................................................... 239
clCaptionText................................................. 239
clCream.......................................................... 240
clDefault......................................................... 240
clDkGray........................................................ 240
clFushsia ........................................................ 240
clGradientActiveCaption ............................... 239
clGradientInactiveCaption ............................. 239
clGray............................................. 240, 477, 553
clGreen........................................................... 240
clHighlightText .............................................. 239
clHotLight ...................................................... 239
Click() ............................................................ 355
ClientRect ...................................................... 362
clInactiveBorder............................................. 239
clInactiveCaption ........................................... 239
clInactiveCaptionText .................................... 239
clInfoBk ......................................................... 239
clInfoText....................................................... 239
CLIP_CHARACTER_PRECIS ..................... 253
CLIP_DEFAULT_PRECIS ........................... 253
CLIP_EMBEDED.......................................... 253
CLIP_LH_ANGLES...................................... 253
CLIP_MASK................................................. 253
CLIP_STROKE_PRECIS.............................. 253
CLIP_TT_ALWAYS..................................... 253
clLime ............................................................ 240
clLtGray......................................................... 240
clMaroon........................................................ 240
clMedGray ..................................................... 240
clMenu ........................................................... 239
clMenuBar ..................................................... 239
Appendix Borland C++ Builder Programming

806 Copyright 2003 FunctionX, Inc.

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_GRADIENTINACTIVECAPTION239
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
ColWidths ...................................................... 668
COMBOBOX ................................................ 325
Command Button........................................... 423
Common Controls............................................ 37
CompareStr().................................................... 67
CompareText()................................................. 66
Component Palette ........................................... 24
computer programs .......................................... 20
Constants
INF............................................................. 101
Pi 121
sLineBreak ................................................... 76
container........................................................... 37
Containers
Frame ......................................................... 395
Control Panel ................................................. 649
Controls
CheckListBox ............................................ 635
DateTimePicker ......................................... 661
Edit............................................................. 721
GroupBox................................................... 721
MonthCalendar .......................................... 653
RadioGroup................................................ 596
ScrollBar .................................................... 582
Shape.......................................................... 553
Timer.......................................................... 554
TrackBar .................................................... 548
TreeView.................................................... 691
Controls Styles
TVS_CHECKBOXES ............................... 694
CreateFont() ........................................... 251, 254
CreateFontIndirect()....................................... 254
CreateForm().................................................... 34
CreateMessageDialog().................................... 86
csDropDown .................................................. 641
csDropDownList ............................................ 641
csOwnerDrawFixed ....................................... 641
csOwnerDrawVariable................................... 641
CSpinButton................................................... 541
csSimple......................................................... 641
cycle............................................................... 121
CycleToRad()................................................. 121
D
DateFormat .................................................... 660
DateMode....................................................... 659
DateTimePicker
OnChange()................................................ 662
OnUserInput() ............................................ 662
DateUtils.hpp ................................................. 655
Default ................................................... 441, 501
DEFAULT_CHARSET ................................. 252
DEFAULT_PITCH........................................ 253
DEFAULT_QUALITY.................................. 253
DefaultColWidth............................................ 668
DefaultDrawing.............................................. 669
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 807

DefaultRowHeight ......................................... 668
Definitions
Action List ................................................. 465
Angle.......................................................... 120
arc .............................................................. 120
Button ........................................................ 423
CCalendar .................................................. 681
Check Box ................................................. 605
Check List Box .......................................... 630
Child .......................................................... 321
circle .......................................................... 120
circumference............................................. 120
Class Explorer.............................................. 26
Code Editor .................................................. 25
Color Grid.................................................. 683
Combo Box................................................ 640
Component Palette....................................... 24
CSpinEdit................................................... 544
Cycle .......................................................... 121
Date and Time Picker................................. 657
degree......................................................... 120
diameter ..................................................... 120
Edit Box..................................................... 482
Focus.......................................................... 342
Frame ......................................................... 395
Grid............................................................ 664
Hint ............................................................ 326
INI List....................................................... 684
Instance ........................................................ 32
Interest Rate ............................................... 110
IP Address.................................................. 492
Label .......................................................... 473
Library ......................................................... 31
List Box ..................................................... 619
list view...................................................... 697
Main Menu................................................. 451
MaskEdit .................................................... 486
Menu.......................................................... 451
Message ..................................................... 343
Method....................................................... 340
Month Calendar ......................................... 649
Node........................................................... 689
Object........................................................... 26
Object Inspector........................................... 27
Parent ......................................................... 321
Payment ..................................................... 110
Periods ....................................................... 110
Present Value ............................................. 109
Progress Bar............................................... 560
project .......................................................... 27
Property........................................................ 51
Radian........................................................ 120
Radio Button.............................................. 589
RadioGroup................................................ 594
Root............................................................ 689
Scroll Bar ................................................... 574
speed button ............................................... 437
Spin Button ................................................ 540
Splitter........................................................ 710
Status Bar ................................................... 461
StringGrid .................................................. 665
Timer.......................................................... 553
tool tip........................................................ 326
toolbar .......................................................... 22
Toolbar....................................................... 458
Toolbar Separator....................................... 458
Track Bar ................................................... 545
TreeView.................................................... 689
UpDown..................................................... 533
Win32........................................................... 31
Windows Control ......................................... 36
degree............................................................. 120
DegToRad() ................................................... 122
delete.............................................................. 255
Design Time..................................................... 36
DesktopHeight ............................................... 361
DesktopLeft ................................................... 361
DesktopRect ................................................... 361
DesktopTop.................................................... 361
DesktopWidth ................................................ 361
Details ............................................................ 697
Dialog Boxes
Add Method ............................................... 615
Align ............................................................ 46
Alignment .................................................... 46
Color .......................................................... 243
Colors......................................................... 440
Console Wizard............................................ 33
Edit Tab Order ........................................... 526
Find ............................................................ 527
Font ............................................................ 257
New Items .................................................... 33
Project Options........................................... 631
Replace....................................................... 529
Size............................................................... 46
String List Editor........................ 245, 595, 620
Tab Order ................................................... 331
diameter ......................................................... 120
dividend ......................................................... 108
Division.......................................................... 591
dkDock........................................................... 621
dmComboBox................................................ 659
dmUpDown.................................................... 659
DockSite......................................................... 621
DoubleDecliningBalance()............................. 106
dowMonday ................................................... 654
Down.............................................................. 438
DRAFT_QUALITY....................................... 253
DragKind ....................................................... 621
DragMode ...................................................... 621
DropDownCount............................................ 642
dtkDate........................................................... 659
Appendix Borland C++ Builder Programming

808 Copyright 2003 FunctionX, Inc.

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
Execute() ........................................................ 245
existence......................................................... 321
exp() ............................................................... 101
expl().............................................................. 102
exponent ........................................................... 98
ExtendedSelect............................................... 621
ExtTextOut() .................................................. 242
F
FF_DECORATIVE........................................ 253
FF_DONTCARE ........................................... 253
FF_MODERN................................................ 253
FF_ROMAN.................................................. 253
FF_SCRIPT ................................................... 253
FF_SWISS ..................................................... 253
FG.................................................................. 684
finance.............................................................. 91
FIXED_PITCH.............................................. 253
FixedCols ....................................................... 666
FixedRows ..................................................... 666
floor() ............................................................... 97
Floor() .............................................................. 98
focus............................................................... 342
Focus.............................................................. 546
FocusControl.................................................. 483
Foreground..................................................... 683
ForegroundEnabled........................................ 684
Format
DateTimePicker ......................................... 659
frame .............................................................. 321
frexp() .............................................................. 98
Frexp() ............................................................. 99
frexpl() ............................................................. 98
fsBold............................................................. 478
fsUnderline..................................................... 478
Functions
abs ................................................................ 95
AnsiCompareStr........................................... 67
AnsiExtractQuotedStr .................................. 72
AnsiLowerCase............................................ 63
AnsiQuotedStr.............................................. 71
AnsiSameCaption......................................... 68
AnsiSameStr ................................................ 68
AnsiSameText.............................................. 68
AnsiUpperCase ............................................ 62
atan............................................................. 132
ceil................................................................ 96
Ceil............................................................... 96
CompareStr .................................................. 67
CompareText................................................ 66
CreateFont.......................................... 251, 254
CreateFontIndirect ..................................... 254
CreateMessageDialog .................................. 86
CreateWindow............................................. 50
CreateWindowEx......................................... 50
CycleToRad ............................................... 121
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 809

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
SLNDepreciation ....................................... 107
sqrt ............................................................. 105
sqrtl ............................................................ 105
StringReplace............................................... 71
strlen............................................................. 57
StrToFloat .................................................... 93
StrToInt ........................................................ 92
SYDDepreciation....................................... 108
SystemParametersInfo ............................... 357
UpperCase.................................................... 62
WinMain().................................................... 31
FutureValue()................................................. 110
FW_BLACK.................................................. 252
FW_BOLD..................................................... 252
FW_DEMIBOLD .......................................... 252
FW_DONTCARE.......................................... 252
FW_EXTRABOLD ....................................... 252
FW_EXTRALIGHT ...................................... 252
FW_HEAVY.................................................. 252
FW_LIGHT ................................................... 252
FW_MEDIUM............................................... 252
FW_REGULAR............................................. 252
FW_THIN...................................................... 252
FW_ULTRABOLD ....................................... 252
FW_ULTRALIGHT ...................................... 252
G
Garamond....................................................... 478
GB.................................................................. 684
GB2312_CHARSET...................................... 252
gdFixed .......................................................... 677
gdFocused ...................................................... 677
GdMarquis ..................................................... 546
gdSelected...................................................... 677
geometry .......................................................... 91
GetBkColor() ................................................. 241
GetBkMode() ................................................. 242
GetBValue()................................................... 237
GetDesktopWindow() .................................... 357
GetGValue()................................................... 237
GetParent()..................................................... 329
GetRValue()................................................... 237
GetSysColor() ................................................ 238
GetTickCount() .............................................. 556
GetWindowRect() .......................................... 340
Glyph ..................................................... 433, 439
go16x1 ........................................................... 683
go1x16 ........................................................... 683
go2x8 ............................................................. 683
go4x4 ............................................................. 683
go8x2 ............................................................. 683
goColMoving................................................. 669
goRowMoving ............................................... 669
graphical user interface (GUI) ......................... 37
GREEK_CHARSET...................................... 252
Green.............................................................. 235
Appendix Borland C++ Builder Programming

810 Copyright 2003 FunctionX, Inc.

GridCoord() ................................................... 673
GridLineWidth............................................... 667
GridOrdering.................................................. 683
GroupIndex.................................................... 438
H
Handle............................................................ 251
HANGUL_CHARSET .................................. 252
HBRUSH......................................................... 32
HCURSOR ...................................................... 32
Header............................................................ 633
HeaderBackgroundColor ............................... 634
HEBREW_CHARSET .................................. 252
Height .................................................... 333, 439
HFONT.......................................... 248, 251, 253
HH:MM:SS AM/PM...................................... 659
HICON............................................................. 32
Hide()............................................................. 342
hInstance ........................................................ 323
HInstance ......................................................... 32
HINSTANCE................................................... 32
hint ................................................................... 23
hMenu............................................................ 323
host................................................................. 395
HWND........................................................... 324
hWndParent ................................................... 323
I
idAbort ........................................................... 425
IDABORT........................................................ 81
idCancel ......................................................... 425
IDCANCEL..................................................... 81
IDIGNORE...................................................... 81
idNo ............................................................... 425
IDNO............................................................... 81
idOK .............................................................. 425
IDOK............................................................... 81
idRetry ........................................................... 425
IDRETRY........................................................ 81
idYes.............................................................. 425
IDYES.............................................................. 81
Increment ....................................................... 544
INF................................................................. 101
INI list ............................................................ 684
Initialize() ........................................................ 32
InputBox()........................................................ 88
InputQuery() .................................................... 89
instance ............................................................ 32
int ..................................................................... 32
INT_MAX..................................................... 549
Integrated Development Environment (IDE)... 20
InterestPayment()................................... 113, 115
InterestRate() ................................................. 116
InternalRateOfReturn() .................................. 117
Interval ........................................................... 555
IntPower() ...................................................... 100
IPM_GETADDRESS .................................... 495
IPM_ISBLANK............................................. 493
IPM_SETADDRESS..................................... 494
IPM_SETFOCUS .......................................... 496
IPM_SETRANGE.......................................... 496
IPN_FIELDCHANGED ................................ 497
IsMasked........................................................ 489
ItemIndex....................................................... 595
ItemProps....................................................... 685
Items
TList........................................................... 720
TListView.................................................. 698
TreeView.................................................... 691
K
Kind ............................................................... 579
DateTimePicker ................................. 658, 663
dtkTime...................................................... 658
L
labs() ................................................................ 95
lapse ............................................................... 553
Large Icons .................................................... 697
LargeChange.................................................. 581
Layout ............................................................ 439
LB_SETHORIZONTALEXTENT ........ 622, 633
lbOwnerDrawFixed........................................ 621
lbOwnerDrawVariable................................... 621
lbStandard ...................................................... 621
ldexp()............................................................ 102
Ldexp()........................................................... 102
ldexpl()........................................................... 102
Left......................................................... 332, 358
library............................................................... 31
Lines............................................................... 500
List ................................................................. 697
LISTBOX....................................................... 325
LnXP1() ......................................................... 103
Log10() .......................................................... 103
Log2............................................................... 104
LOGFONT..................................................... 254
LogN() ........................................................... 104
long .................................................................. 93
LongTimeFormat ........................................... 628
lowercase ......................................................... 61
LP_ADDSTRING.......................................... 620
lpClassName .................................................. 323
LPCTSTR ................................................ 32, 323
lpfnWndProc.................................................... 32
lpParam.......................................................... 323
lpszClassName ............................................... 325
lpWindowName ............................................. 323
M
MAC_CHARSET .......................................... 252
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 811

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
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
Appendix Borland C++ Builder Programming

812 Copyright 2003 FunctionX, Inc.

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
PARAFORMAT2 .......................................... 524
Parent ............................................................. 321
ParseInput ...................................................... 659
PasswordChar ................................................ 483
Payment()....................................................... 112
pbLowered ..................................................... 465
PBM_SETBARCOLOR ................................ 562
PBM_SETRANGE ........................................ 562
PBM_SETRANGE32 .................................... 562
pbNone........................................................... 465
pbRaised......................................................... 465
PBS_SMOOTH.............................................. 562
PBS_VERTICAL........................................... 561
Perform()........................................................ 353
Pi 121
pitch ............................................................... 253
POINT............................................................ 335
poScreenCenter .............................................. 561
Position .......................................... 362, 536, 580
PostQuitMessage() ......................................... 427
pow().............................................................. 100
Power()........................................................... 101
powl()............................................................. 100
PresentValue()................................................ 114
Print() ............................................................. 522
PrintDlg.......................................................... 523
printf() .............................................................. 93
procedure ....................................................... 343
programmer.................................................... 473
project .............................................................. 27
Projects
AddressBook1............................................ 713
AddressBook2............................................ 726
BitmapButtons ........................................... 435
BodyMonitor1............................................ 560
BodyTag1........................................... 578, 594
CarInventory1 ............................................ 546
CDPublisher1............................................. 541
ColorPreview............................................. 533
CompTicks1............................................... 557
Countries1.................................................. 690
Countries2.................................................. 694
Editor1 ............................................... 504, 628
Editor2 ....................................................... 644
Employment ............................................... 475
FastFood1................................................... 606
Notice......................................................... 466
Operations.................................................. 590
Payroll1.............................................. 651, 669
Payroll2...................................................... 658
Pizza1......................................................... 631
TrafficLight1.............................................. 553
PROOF_QUALITY....................................... 253
Properties
ActiveControl............................................. 424
Align .......................................................... 362
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 813

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
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
Appendix Borland C++ Builder Programming

814 Copyright 2003 FunctionX, Inc.

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
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
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 815

Increment ................................................... 544
MaxValue .................................................. 544
MinValue ................................................... 544
TCustomEdit .................................................. 485
TCustomForm
Close()........................................................ 427
TDateTimePicker
CalAlignment............................................. 660
Date............................................................ 660
DateFormat ................................................ 660
DateMode .................................................. 659
Format........................................................ 659
Kind ........................................................... 658
MaxDate .................................................... 660
MinDate ..................................................... 660
Name.......................................................... 662
OnDropDown() .......................................... 663
ParseInput .................................................. 659
Time........................................................... 659
TEdit .............................................................. 485
BorderStyle ................................................ 599
CharCase.................................................... 483
Clear() ........................................................ 485
ClearSelection() ......................................... 485
FocusControl.............................................. 483
OnChange()................................................ 485
OnEnter() ................................................... 485
PasswordChar ............................................ 483
ReadOnly ................................................... 721
SelLength................................................... 484
SelStart....................................................... 484
SelText ....................................................... 484
ShowAccelChar ......................................... 483
Text ............................................................ 482
TFileExit ........................................................ 507
TFileOpen...................................................... 507
TFilePrintSetup.............................................. 507
TFileSaveAs .................................................. 507
TFindDialog
FindText..................................................... 528
Position ...................................................... 528
Top............................................................. 529
TFont ..................................................... 248, 429
Assign()...................................................... 256
Color .......................................................... 251
Height ........................................................ 249
Name.......................................................... 249
Size .................................................... 250, 476
Style ........................................... 250, 258, 476
TFontDialog................................................... 257
TFontEdit ....................................................... 507
TFontStyles.................................................... 250
TForm
ActiveControl............................................. 424
BorderStyle ................................................ 557
Font ............................................................ 476
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
Appendix Borland C++ Builder Programming

816 Copyright 2003 FunctionX, Inc.

ViewStyle .................................................. 701
Width ......................................................... 702
tlTop............................................................... 474
TMainMenu
Caption....................................................... 453
Images................................................ 499, 727
Items .......................................................... 452
TMaskEdit
EditMask.................................................... 675
Text ............................................................ 492
tmBoth ........................................................... 550
tmBottomRight .............................................. 550
TMemo .......................................................... 499
Align .......................................................... 500
Lines .......................................................... 500
MaxLength................................................. 501
Modified .................................................... 501
ReadOnly ................................................... 500
TabOrder.................................................... 501
WantReturns .............................................. 501
WantTabs................................................... 501
WordWrap ................................................. 502
TMessage....................................................... 344
TModalResult ................................................ 425
TMonthCalendar
BoldDays()................................................. 655
CalColors ................................................... 652
Date............................................................ 653
FirstDayOfWeek........................................ 653
MultiSelect................................................. 653
OnClick() ................................................... 655
OnGetMonthInfo()..................................... 655
ShowToday................................................ 653
Visible........................................................ 654
TMouseButton ............................................... 351
TMouseEvent................................................. 351
TMouseMoveEvent ............................... 351, 352
TMsgDlgButtons ............................................. 84
tmTopLeft ...................................................... 550
TNotifyEvent ................. 256, 346, 471, 480, 637
OnChange .................................................. 471
TNotifyType .................................................. 480
TObject .................................................... 55, 347
toolbar ...................................................... 22, 321
ToolBar
Align .......................................................... 458
Buttons
DropDownMenu .................................... 460
OnClick() ............................................... 460
Style ....................................................... 460
tbsDropDown......................................... 460
Style ........................................................... 458
Top................................................................. 332
TParaAttributes
FirstIndent .................................................. 520
LeftIndent .................................................. 520
Numbering ................................................. 520
RightIndent ................................................ 520
TPoint............................................................. 335
TPopupMenu.................................................. 457
Caption....................................................... 457
OnClick() ................................................... 457
TPrintDlg ....................................................... 507
TProgressBar.................................................. 566
Max ............................................................ 562
Min............................................................. 562
Orientation ................................................. 561
Position ...................................................... 562
Smooth....................................................... 561
Step ............................................................ 563
StepBy() ..................................................... 566
StepIt() ....................................................... 566
TProgressBarOrientation
pbHorizontal .............................................. 561
pbVertical................................................... 561
TRadioButton................................................. 592
Alignment .................................................. 590
Caption....................................................... 589
Checked ..................................................... 590
Left............................................................. 590
Name.......................................................... 589
OnClick() ................................................... 592
Top............................................................. 590
TRadioGroup
Columns ..................................................... 596
ItemIndex ................................................... 595
Transparent .................................................... 474
TRect.............................................................. 338
TRichEdit
HideSelection............................................. 521
Lines........................................................... 519
Paragraph ................................................... 519
PopupMenu................................................ 521
Print() ......................................................... 522
SelAttributes .............................................. 521
TRichEditAlignCenter ................................... 507
trigonometry..................................................... 91
TScreen............................................................ 32
HintFont ..................................................... 327
TScrollBar...................................................... 582
Kind ........................................................... 579
LargeChange .............................................. 581
Max ............................................................ 580
Min............................................................. 580
OnChange()................................................ 583
OnScroll() .................................................. 584
PageSize..................................................... 581
Position ...................................................... 580
SetParams() ................................................ 583
SmallChange .............................................. 581
TScrollBarKind.............................................. 580
TScrollCode................................................... 584
Borland C++ Builder Programming Appendix

Copyright 2003 FunctionX, Inc. 817

TScrollEvent .................................................. 584
TScrollingWinControl ................................... 576
TSearchFind................................................... 507
TSearchFindFirst ........................................... 507
TSearchFindNext ........................................... 507
TSearchReplace ............................................. 507
TSize.............................................................. 336
TSpeedButton
AllowUp .................................................... 438
Caption....................................................... 439
Down.......................................................... 438
Flat ............................................................. 438
Glyph ......................................................... 439
GroupIndex................................................ 438
Height ........................................................ 439
Layout ........................................................ 439
Margin........................................................ 439
NumGlyphs................................................ 439
OnClick() ................................................... 654
Spacing ...................................................... 439
Width ......................................................... 439
TStaticBorderStyle......................................... 481
TStatusBar ..................................................... 461
Align .......................................................... 461
Panels......................................................... 464
Alignment .............................................. 465
Bevel ...................................................... 465
Style ....................................................... 465
psOwnerDraw.................................... 465
psText ................................................ 465
Text ........................................................ 465
Width ..................................................... 465
SimplePanel ............................................... 461
SimpleText................................................. 462
TStringGrid
CellRect()................................................... 672
Cells ........................................................... 669
ColCount .................................................... 665
ColHeights ................................................. 668
Color .......................................................... 667
Cols............................................................ 669
ColWidths .................................................. 668
DefaultDrawing ......................................... 669
DefaultRowHeight ..................................... 668
FixedCols................................................... 666
FixedRows ................................................. 666
GridCoord() ............................................... 673
GridLineWidth........................................... 667
MouseToCell()........................................... 673
OnColumnMoved().................................... 676
OnDrawCell() .................................... 676, 677
OnGetEditMask()....................................... 675
OnGetEdtText() ......................................... 675
OnRowMoved() ......................................... 676
OnSelectCell() ........................................... 674
OnSetEditText()................................. 675, 679
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
Appendix Borland C++ Builder Programming

818 Copyright 2003 FunctionX, Inc.

Strings ........................................................ 684
TVS_CHECKBOXES ................................... 694
TWinControl.......................................... 612, 652
Handle........................................................ 481
Parent ......................................................... 329
SetFocus() .................................................. 342
U
udRight........................................................... 537
UINT................................................................ 32
underflow....................................................... 101
uppercase ......................................................... 61
UpperCase() ..................................................... 62
V
VARIABLE_PITCH...................................... 253
VCL_MESSAGE_HANDLER...................... 345
visibility......................................................... 321
Visible............................................................ 330
Visual Component Library (VCL) ................... 31
W
WantReturns .................................................. 501
WantTabs....................................................... 501
WC_IPADDRESS ......................................... 492
Width ..................................................... 333, 439
Win32 .............................................................. 31
Win32 API ..................................................... 649
Win32 Functions
SetWindowLong() ..................................... 694
Win32 Structures
CHARFORMAT........................................ 523
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



Copyright 2003 FunctionX, Inc. 819

Anda mungkin juga menyukai