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