FunctionX Practical Learning Series
Table of Contents
2
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Table of Contents
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 Application’s 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
Copyright © 2003 FunctionX, Inc.
3
Table of Contents
Borland C++ Builder Programming
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 4
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Table of Contents
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 Copyright © 2003 FunctionX, Inc.
5
Table of Contents
Borland C++ Builder Programming
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
6
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Table of Contents
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 . Bézier 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 Copyright © 2003 FunctionX, Inc.
7
Table of Contents
Borland C++ Builder Programming 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
8
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Table of Contents
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 Control’s 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 . Window’s 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 Window’s Desktop .............................................................. 357 14.1.2 . Application’s Containers ............................................................ 357 14.1.3 . The Parent’s Location................................................................. 358 14.2 Control Alignment and Constraint..................................................... 360 14.2.1 . The Client Area .......................................................................... 360 14.2.2 . Control’s Alignment in the Client Area...................................... 362 14.2.3 . The Client Area’s Constraints .................................................... 365 14.2.4 . Control Anchoring...................................................................... 365 14.2.5 . Child Controls and Drag’n’Drop Operations.............................. 366 CHAPTER 15: FORMS AND DIALOG BOXES ....................................... 367 Copyright © 2003 FunctionX, Inc.
9
Table of Contents
Borland C++ Builder Programming
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 Form’s 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
10
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Table of Contents
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 Copyright © 2003 FunctionX, Inc.
11
Table of Contents
Borland C++ Builder Programming
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 12
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Table of Contents
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 Copyright © 2003 FunctionX, Inc.
13
Table of Contents
Borland C++ Builder Programming
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 14
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Table of Contents
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 Computer’s 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 Copyright © 2003 FunctionX, Inc.
15
Table of Contents
Borland C++ Builder Programming
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
16
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
17
Table of Contents
Borland C++ Builder Programming
PART I Introduction to Borland C++ Builder This section introduces the Borland C++ Builder programming environment with the necessary information you need in order to navigate around the screen, create applications and files, use the menus and other accessories that can ease your experience with this product.
18
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 1: The Borland C++ Builder IDE
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
Copyright © 2003 FunctionX, Inc.
19
Chapter 1: The Borland C++ Builder IDE
Borland C++ Builder Programming
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. Bcb’s 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
20
1.
Click the application’s 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 doubleclicked) 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
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 1: The Borland C++ Builder IDE
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
Copyright © 2003 FunctionX, Inc.
21
Chapter 1: The Borland C++ Builder IDE
Borland C++ Builder Programming
Edit -> Copy View -> Toolbars -> Custom
Click Edit then click Copy 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 T Alt, G Ctrl + H Ctrl + Shift + E
Means Press the t key Press and release Alt. Then press G Press and hold Ctrl. While you are still holding Ctrl, press H once. Then release Ctrl Press and hold Ctrl. Then press and hold Shift. Then press E once. Release Ctrl and Shift
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 22
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 1: The Borland C++ Builder IDE
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.
Borland’s 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
Copyright © 2003 FunctionX, Inc.
and see the hint 23
Chapter 1: The Borland C++ Builder IDE
Borland C++ Builder Programming
7.
Without clicking, move the mouse to another button and to other buttons. Notice that some button’s hints also display a shortcut that would lead to the same action:
8.
To use a toolbar’s 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.”
24
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
1.1.7
Chapter 1: The Borland C++ Builder IDE
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
Copyright © 2003 FunctionX, Inc.
25
Chapter 1: The Borland C++ Builder IDE
Borland C++ Builder Programming
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.
26
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 1: The Borland C++ Builder IDE
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.
Copyright © 2003 FunctionX, Inc.
.
27
Chapter 1: The Borland C++ Builder IDE
1.3
Borland C++ Builder Programming
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 company’s 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.
28
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 1: The Borland C++ Builder IDE
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
Copyright © 2003 FunctionX, Inc.
29
Chapter 2: Controls Fundamentals
30
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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
Copyright © 2003 FunctionX, Inc.
31
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
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 computer’s 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 Application’s 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. 32
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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
#include #pragma hdrstop
Copyright © 2003 FunctionX, Inc.
33
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
//--------------------------------------------------------------------------#pragma argsused WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { Application->Initialize(); return 0; } //---------------------------------------------------------------------------
7.
To save the application, on the Standard toolbar, click the Save All button
8.
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 Standard toolbar or by clicking File -> New -> Form on the main menu.
on the
After creating the form’s resource, you must let the application know that you have a have. To do this, you must call the CreateForm() method of the TApplication class. Its syntax is: void __fastcall CreateForm(System::TMetaClass* InstanceClass, void *Reference);
The name of the form is passed as pointer as the second argument. Because the form is being added to the application, giving the application access to it for further operations and processing, the CreateForm() method requires that the class that controls the form be specified. This is normally done by calling the __classid() operator. Once you have specified the form to the application, you must also specify the name of the source file that holds the implementation of the form. This is done by calling the USEFORM macro. Its syntax is: USEFORM(FileName, FormName)
This macro takes two arguments. The first is a string that specifies the source file. The second specifies the name of the form as it appears in the project. In future lessons, we will see that various object controls spend their time sending messages to the application. As the application receives them, it processes them and acts 34
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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 #include #pragma hdrstop USEFORM("Unit2.cpp", Form2); //--------------------------------------------------------------------------#pragma argsused WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { Application->Initialize(); Application->CreateForm(__classid(TForm2), &Form2); Application->Run(); return 0; } //---------------------------------------------------------------------------
5.
To test the application, press F9
Copyright © 2003 FunctionX, Inc.
35
Chapter 2: Controls Fundamentals
6.
Borland C++ Builder Programming
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 control’s 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 object’s 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. 36
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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.
Copyright © 2003 FunctionX, Inc.
37
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
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.
38
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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 user’s 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):
Copyright © 2003 FunctionX, Inc.
.
39
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
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.
40
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
15. To add another control, on the Standard tab, click the ActionList control
.
16. Click and drag on the form to draw a tall and wide rectangle. Then release the mouse. 17. Notice that the new control keeps a constant shape. 18. To add another form, on the View toolbar, click New Form
.
19. While Form2 has focus, on the Standard tab, double-click the Edit control to place it on Form2 20. On the Standard tab, click the Panel control
.
21. On the form, click in the lower right section of the form and drag to the upper-center section of the form to draw a rectangle that partially covers the edit box. Release the mouse:
22. Make sure the new panel is still selected. From the Standard tab, double-click the Edit control:
23. Notice that the new edit box is positioned in the center of the panel and not in the center of the form. This is because the Panel control is a “container” and can hold its own controls. 24. On the Standard tab, click the Edit control. Copyright © 2003 FunctionX, Inc.
41
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
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
42
1.
To display one of the forms, on the main menu, click View -> Forms…
2.
From the View Form dialog box, click Form1
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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. Copyright © 2003 FunctionX, Inc.
43
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
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…
44
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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:
Copyright © 2003 FunctionX, Inc.
45
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
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 46
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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 control’s 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.
Copyright © 2003 FunctionX, Inc.
47
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
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
4.
Add other edit controls as Edit2, Edit3, and Edit4.
5.
From the Standard tab of the Component Palette, click 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.
and click on the form to add Edit1
to add a button
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:
48
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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:
Copyright © 2003 FunctionX, Inc.
49
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
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
50
Windows Controls and Their Properties
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
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 property’s name and the property's value:
Copyright © 2003 FunctionX, Inc.
51
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
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 property’s 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:
There are fields that expect you to type Text Fields: 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. 52
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 2: Controls Fundamentals
Expandable Fields: Some fields have a + button. This indicates that the property has a set of sub-properties that actually belong to the same property and are set together. To expand such a field, click its + button and a – button will appear:
To collapse the field, click the – button. Some of the set properties are created from an enumerator. Since the field was created as a Pascal-style set, this allows the property to have more than one value. For example, the BorderIcons property can have the minimize and the maximum buttons. Some other fields that have a + button are actually complete classes or a combination of classes. Boolean Fields: Some fields can have only a true or false value. To change their setting, you can either select from the combo box or doubleclick the property to toggle to the other value. Action Fields: Some fields would require a list of items and need to be controlled by an intermediary action. Such fields display an ellipsis button . When you click the button, a dialog box would come up and you can set the value for the field. You can also doubleclick the field value to call the dialog. Selection Fields: To change the value of some of the fields, you would use their combo box to display the available values. After clicking the arrow, a list would display. There are various types of selection fields. Some of them display just two items. To change their value, you can just double-click the field. Some other fields have more than two values in the field. To change them, you can click their arrow and select from the list. You can also double-click a few times until the desired value is selected. On some other properties, you can double-click the field and a dialog box would come up.
Copyright © 2003 FunctionX, Inc.
53
Chapter 2: Controls Fundamentals
Borland C++ Builder Programming
PART II Applications Prerequisites The second part of this book was inserted after various hesitations. Logically, we would have started studying Windows controls that the VCL is rich of, but these controls use some characteristics, properties, techniques and methods that must be reviewed prior to actually studying the controls. Therefore, in this section, we will encounter such aspects as strings, files, message boxes, mathematic functions, primary dialog boxes, etc. Most controls featured in the VCL take so high advantage of these aspects that we judged it necessary to first show what their related properties are dealing with.
54
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 3: Strings
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");
Copyright © 2003 FunctionX, Inc.
55
Chapter 3: Strings
Borland C++ Builder Programming
Once you have defined the string you can use it as you see fit. You can use it to change a control’s 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:
56
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 3: Strings
//--------------------------------------------------------------------------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
Copyright © 2003 FunctionX, Inc.
57
Chapter 3: Strings
Borland C++ Builder Programming
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); } //---------------------------------------------------------------------------
58
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 3: Strings
To remove any space on the right side of a string, you can use the AnsiString::TrimRight() method. Its syntax is: AnsiString __fastcall TrimRight() const;
If the original string has space on its right, this function would remove it and return the same string without any trailing space. Otherwise, the original string would be returned: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Edit2->Text = Edit1->Text.TrimRight(); } //---------------------------------------------------------------------------
Another function used to perform the same operation is the TrimRight(). Its syntax is: AnsiString _fastcall TrimRight(const AnsiString S);
The (global) TrimRight() function requires one argument as the string that needs to be trimmed. The function returns the original string without the trailing space: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Edit2->Text = TrimRight(Edit1->Text); } //---------------------------------------------------------------------------
Other functions allow you to combine the last two operations into one. You can use the AnsiString::Trim() method to remove spaces on both sides of a string. Its syntax is: AnsiString __fastcall Trim() const;
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); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
59
Chapter 3: Strings
3.3
Borland C++ Builder Programming
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:
An integer: AnsiString Int = 120;
A long integer:
A floating-point value:
A double-precision number:
A string:
AnsiString Symbol = 'H';
AnsiString Longer = -73495745;
AnsiString WeeklyEarnings = 675.15;
AnsiString WeeklyEarnings = 675.15; AnsiString Silver = 2.15e28;
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:
60
A character:
An interger:
char Sign = 'q'; Edit1->Text = AnsiString(Sign);
Integer Number = 808; Caption->Text = AnsiString(Number);
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
A long integer:
A floating-point value:
A double-precision number:
A string:
Chapter 3: Strings
long Value = 497783L; Panel1->Caption = AnsiString(Value);
Float Distance = 1205.62; Label1->Caption = AnsiString(Distance);
Double YearlyIncome = 24588; Edit1->Text = AnsiString(YearlyIncome);
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 nullterminated 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;
Copyright © 2003 FunctionX, Inc.
61
Chapter 3: Strings
Borland C++ Builder Programming
This member function considers an AnsiString variable and examines each one of its characters. If a character is an alphabetic character in lowercase, it would be converted to uppercase. If the character is either an alphabetical character in uppercase or it is not an alphabetic character, it would be kept “as is”. This method also considers the Regional Settings of the computer being used, as set in Control Panel. If you want to convert a single character to uppercase, after initializing or getting, call this method. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { String S = 's'; Edit1->Text = S.UpperCase(); } //---------------------------------------------------------------------------
You can use this same method to convert a string to uppercase as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { String S1 = "James N. Fame!"; String S2 = S1.UpperCase(); Edit1->Text = S2; } //---------------------------------------------------------------------------
Besides the AnsiString method, you can use the UpperCase() function to convert a character or string to uppercase. Its syntax is: AnsiString __fastcall UpperCase(const AnsiString S);
This function uses the same algorithm as the AnsiString::UpperCase() method. Here is an example of using it: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { String S1 = "James N. Fame!"; String S2 = UpperCase(S1); Edit2->Text = S2; } //---------------------------------------------------------------------------
The AnsiUpperCase() function uses the same syntax as the UpperCase() function and applies the same algorithm as the AnsiString::UpperCase() method. Unlike the UpperCase() function, AnsiUpperCase() considers the Regional Settings of the computer being used. Look at how these two functions convert the same French string: Using UpperCase() //--------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
62
//--------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming {
Chapter 3: Strings {
String S1 = "La maison? Ça a été brûlée!"; String S2 = UpperCase(S1); Edit1->Text = S2;
} //---------------------------------------------------
String S1 = "La maison? Ça a été brûlée!"; 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 Copyright © 2003 FunctionX, Inc.
63
Chapter 3: Strings
Borland C++ Builder Programming
AnsiString::UpperCase() method but uses the same syntax as the UpperCase() function: //--------------------------------------------------------------------------void __fastcall TForm1::btnConvertClick(TObject *Sender) { String S1 = "La version Française de Borland C++ Builder est là. " "Elle est arrivée!"; 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; } //---------------------------------------------------------------------------
64
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
3.5
Chapter 3: Strings
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 casesensitivity. Some of the functions, when performing their comparison on Dates or currency values, would refer to the Regional Settings of your computer as set in the Control Panel.
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(); }
Copyright © 2003 FunctionX, Inc.
65
Chapter 3: Strings
Borland C++ Builder Programming
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:
3.5.3
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
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 user’s 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)
66
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 3: Strings
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
Copyright © 2003 FunctionX, Inc.
67
Chapter 3: Strings
Borland C++ Builder Programming
comparison renders a Boolean value of true or false. Both libraries can perform any sort of comparison. When you have two strings and would like to find out whether both are equal, you can use the (overloaded) == operator. If both strings are equal, the conditional comparison would return true. You can also use the AnsiSameStr() function. Its syntax is: bool __fastcall AnsiSameStr(const AnsiString First, const AnsiString Second);
The function takes the Windows Regional Settings into consideration when comparing the First and the Second strings with case-sensitivity. If both strings are the same, the function return true. If they are not the same, the result is false. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnSendClick(TObject *Sender) { String EMail1 = edtEMail1->Text; String EMail2 = edtEMail2->Text; if(AnsiSameStr(EMail1, EMail2)) { frmCongratulations->ShowModal(); Close(); }
} //---------------------------------------------------------------------------
Alternatively, to compare two strings, you can use the AnsiSameText() function. Both functions use the same syntax. Like the AnsiSameStr() function, the AnsiSameText() considers the Regional Settings. Unlike the AnsiSameStr() function, the AnsiSameText() function does not consider the case of the characters in the strings. If the strings you want to compare are captions, such as those you see on a form, it would be cumbersome to write a comparison function that would examine them. This is because the captions on a form usually have an ampersand used to underline one of their characters. Examples include First Name, Address, City, Full Name or Department. Fortunately, Borland provides the AnsiSameCaption() function. Its syntax is: bool __fastcall AnsiSameCaption(const AnsiString First, const AnsiString Second);
This function takes two captions and compares them considering the Regional Settings . Regardless of where the ampersand is positioned, the other characters of the captions would be examined. If both captions are the same, the function would return true. In the following example, two captions are set as First Name and First Name respectively. A regular comparison would find them different, but the AnsiSameCaption() function finds that both strings are the same: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { lblCaption1->Caption = "&First Name"; lblCaption2->Caption = "First &Name"; } //--------------------------------------------------------------------------void __fastcall TForm1::btnCompareCaptionsClick(TObject *Sender)
68
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
{
Chapter 3: Strings
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:
3.6
Equal: ==
Not Equal: !=
Less Than <
Less Than Or Equal To <=
Greater Than >
Greater Than Or Equal To >=
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: Copyright © 2003 FunctionX, Inc.
69
Chapter 3: Strings
Borland C++ Builder Programming
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;
70
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 3: Strings
In many cases, you can also use the AnsiString::AnsiPos() method). Its syntax is: extern PACKAGE int __fastcall AnsiPos(const AnsiString Substr, const AnsiString S);
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:
Copyright © 2003 FunctionX, Inc.
71
Chapter 3: Strings
Borland C++ Builder Programming
//--------------------------------------------------------------------------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 = '"';
72
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 3: Strings
AnsiString RemoveQuotes = AnsiExtractQuotedStr(BookTitle, Quote); edtBookTitle->Text = RemoveQuotes; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
73
Chapter 4: Message Boxes
74
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 4: Message Boxes
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 Borland’s 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);
Copyright © 2003 FunctionX, Inc.
75
Chapter 4: Message Boxes
Borland C++ Builder Programming
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); } //---------------------------------------------------------------------------
76
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 4: Message Boxes
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: Copyright © 2003 FunctionX, Inc.
77
Chapter 4: Message Boxes
Borland C++ Builder Programming
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: 78
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 4: Message Boxes
//--------------------------------------------------------------------------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
Copyright © 2003 FunctionX, Inc.
79
Chapter 4: Message Boxes
Borland C++ Builder Programming
MB_ICONQUESTION MB_ICONSTOP MB_ICONERROR MB_ICONHAND
Asking a question that expects a Yes or No, or a Yes, No, or Cancel answer 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 MB_DEFBUTTON1 MB_DEFBUTTON2 MB_DEFBUTTON3 MB_DEFBUTTON4
If the message box has more than one button, the default button would be The first button The second button The third button 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",
80
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 4: Message Boxes
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!"; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
81
Chapter 4: Message Boxes
Borland C++ Builder Programming
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 82
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 4: Message Boxes
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 Borland’s enhanced message box and it provides a good alternative to the Win32’s 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:
Copyright © 2003 FunctionX, Inc.
83
Chapter 4: Message Boxes
Borland C++ Builder Programming
Value
Icon
mtWarning mtError mtInformation mtConfirmation mtCustom
None
The Buttons argument is used to specify the type(s) of button(s) to display on the dialog box. The buttons are defined using the TMsgDlgButtons set as follows: Value
Button
Value
mbYes
mbRetry
mbNo
mbIgnore
mbOK
mbAll
mbCancel
mbNoToAll
mbAbort
mbYesToAll
Button
mbHelp The last argument is used if there is a help file available, in which case you would specify the particular index related to this message box. This message box uses the name of the project as its caption.
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) {
84
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 4: Message Boxes
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 << mbIgnore, 0, 20, 120); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
85
Chapter 4: Message Boxes
Borland C++ Builder Programming
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 #include #include #include #include //--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components TButton *Button1; TButton *Button2; TPanel *Panel1; void __fastcall Button1Click(TObject *Sender); void __fastcall Button2Click(TObject *Sender); void __fastcall FormCreate(TObject *Sender); void __fastcall Panel1Click(TObject *Sender); private: // User declarations TForm* Mine; public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------#endif
Next, use the new operator to specify the owner of the form: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
86
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 4: Message Boxes
: 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 #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) {
Copyright © 2003 FunctionX, Inc.
87
Chapter 4: Message Boxes
Borland C++ Builder Programming
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
88
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 4: Message Boxes
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.
Copyright © 2003 FunctionX, Inc.
89
Chapter 4: Message Boxes
Borland C++ Builder Programming
If the user clicks OK (or presses Enter), like the InputBox() function, whether the user had changed the value of the edit box or not, the content of the edit box, provided as the Value argument, would be returned. Because Value is passed by reference, the function can return two values. Unlike the InputBox() function, if the user clicks Cancel (or presses Esc) after dealing with the dialog box, the value of the Value argument would be ignored. You can validate the returned value of Value by writing a conditional statement that examines whether the user had clicked OK or Cancel. In the following example, when the user clicks a button on the form, the compiler finds out if the user had clicked OK; in which case it would display the returned value of the Value argument in an Edit control of the form: procedure TForm1.Button1Click(Sender: TObject); var Answer: string; begin if InputQuery('Exiting Application', 'Are you sure you want to exist (Y=Yes/Y=No)?', Answer) = True then Edit1.Text := Answer; end;
90
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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
Copyright © 2003 FunctionX, Inc.
91
Chapter 5: Math Functions
Borland C++ Builder Programming
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();
92
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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 C’s 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;
Copyright © 2003 FunctionX, Inc.
93
Chapter 5: Math Functions
Borland C++ Builder Programming
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 94
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
considered as neutral. In some operations, the number considered will need to be only positive even if it is provided in a negative format. The absolute value of a number x is x if the number is (already) positive. If the number is negative, its absolute value is its positive equivalent. For example, the absolute value of 12 is 12, while the absolute value of –12 is 12. To get the absolute value of a number, you can use one of the C/C++ abs() function. Its syntax is: int abs(int x); This function takes an integer as the argument and returns its absolute value equivalent. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnAbsoluteClick(TObject *Sender) { int Number = Edit1->Text.ToInt(); Edit2->Text = abs(Number); } //---------------------------------------------------------------------------
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. 13 12.155 12 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.
Copyright © 2003 FunctionX, Inc.
95
Chapter 5: Math Functions
Borland C++ Builder Programming
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 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 #include #pragma hdrstop
96
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
//--------------------------------------------------------------------------#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...
Copyright © 2003 FunctionX, Inc.
97
Chapter 5: Math Functions
Borland C++ Builder Programming
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 * 2Exp For the following example, a form is equipped with three Edit controls named edtNumber, edtMantissa, and edtExponent. It also has a Button control named btnCalculate with the Default property set to true. The user must type a number in the Number edit box and press Enter. Then the OnClick event of the button executes to perform the frexp() function which leads to displaying the results in the appropriate edit boxes : 98
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
//--------------------------------------------------------------------------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 VCL’s 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 * 2Exp //--------------------------------------------------------------------------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
Copyright © 2003 FunctionX, Inc.
99
Chapter 5: Math Functions
Borland C++ Builder Programming
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 = xy 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 #include #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 VCL’s 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 doubleprecision 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) {
100
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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 doubleprecision 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 #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; }
Copyright © 2003 FunctionX, Inc.
101
Chapter 5: Math Functions
Borland C++ Builder Programming
//---------------------------------------------------------------------------
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 * 2y 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 VCL’s 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 * 2P //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { float Source = 450.04;
102
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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 = log10x which is equivalent to x = 10y Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Extended X, Result; X = StrToFloat(Edit1->Text); Result = Log10(X); Edit2->Text = FloatToStr(Result);
} //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
103
Chapter 5: Math Functions
Borland C++ Builder Programming
Log2 The Log2() function is used to calculate the logarithm of a number on base 2. The syntax of the function is: Extended __fastcall Log2(Extended X); The variable whose logarithmic value will be calculated is passed as argument X to the function. The function uses the formula: Y = log2x. This is the same as x = 2y //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Extended X, Result; X = StrToFloat(Edit1->Text); Result = Log2(X); Edit2->Text = FloatToStr(Result);
} //---------------------------------------------------------------------------
LogN The LogN() function is used to calculate the logarithmic value of a number to the desired base. Its syntax is: Extended __fastcall LogN(Extended Base, Extended Number); This function takes two arguments. The second, Number, is the variable whose value will be evaluated. The first argument, Base, is used as the base of the logarithm. The formula used by this function is: y = logbx which is the same as x = by For the LogN() function, this formula would be: LogN(Base, Number) = NumberBase; //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Extended Base, Number, Result; Base = StrToFloat(Edit1->Text); Number = StrToFloat(Edit2->Text); Result = LogN(Base, Number); Edit3->Text = FloatToStr(Result); } //---------------------------------------------------------------------------
104
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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, Copyright © 2003 FunctionX, Inc.
105
Chapter 5: Math Functions
Borland C++ Builder Programming
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 #include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
106
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
} //--------------------------------------------------------------------------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;
Copyright © 2003 FunctionX, Inc.
107
Chapter 5: Math Functions
Borland C++ Builder Programming
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 SumOf-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 1 2 3 4 5
Fraction 5/15 4/15 3/15 2/15 1/15
* Amount * $18,000.00 * $18,000.00 * $18,000.00 * $18,000.00 * $18,000.00 Total Depreciation
= = = = = = =
Depreciation $6,000.00 $4,800.00 $3,600.00 $2,400.00 $1,200.00 $18,000.00
The VCL function used to calculate the depreciation of an asset using the sum of the years is called SYDDepreciation() and its syntax is: Extended __fastcall SYDDepreciation(constExtended Cost, const Extended Salvage, int Life, int Period); The Cost parameter is the original value of the item. In our example, this would be $18,000. The Salvage parameter is the value the asset would have (or has) at the end of its useful life. The Life is the number of years of the asset would have a useful life. The 108
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
Period is the particular period or rank of a Life portion; for example, if the Life of the depreciation is set to 5 (years), the Period could be any number between 1 and 5. If set to 1, the depreciation would be calculated for the first year. If the Period is set to 4, the depreciation would be calculated for the 4th year. You can also set the Period to a value higher than Life. For example, if Life is set to 5 but you pass 8 for the Period, the depreciation would be calculated for the 8th year. If the asset is worthless in the 8th year, the depreciation would be 0. Here is an example:
//--------------------------------------------------------------------------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. Copyright © 2003 FunctionX, Inc.
109
Chapter 5: Math Functions
Borland C++ Builder Programming
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:
110
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
//--------------------------------------------------------------------------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:
Copyright © 2003 FunctionX, Inc.
111
Chapter 5: Math Functions
Borland C++ Builder Programming
//--------------------------------------------------------------------------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:
112
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
//--------------------------------------------------------------------------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:
Copyright © 2003 FunctionX, Inc.
113
Chapter 5: Math Functions
Borland C++ Builder Programming
//--------------------------------------------------------------------------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:
114
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
//--------------------------------------------------------------------------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: Copyright © 2003 FunctionX, Inc.
115
Chapter 5: Math Functions
Borland C++ Builder Programming
//--------------------------------------------------------------------------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:
116
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
//--------------------------------------------------------------------------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: Copyright © 2003 FunctionX, Inc.
117
Chapter 5: Math Functions
Borland C++ Builder Programming
//--------------------------------------------------------------------------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); } //---------------------------------------------------------------------------
118
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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();
Copyright © 2003 FunctionX, Inc.
119
Chapter 5: Math Functions
Borland C++ Builder Programming
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 R C
Note Equidistant means “same distance”
B
A circle is a group or series of distinct points drawn at an exact same distance from another point referred to as the center. The distance from the center C to one of these equidistant points is called the radius, R. The line that connects all of the points that are equidistant to the center is called the circumference of the circle. The diameter is the distance between two points of the circumference to the center; in other words, a diameter is double the radius. To manage the measurements and other related operations, the circumference is divided into 360 portions. Each of these portions is called a degree. The unit used to represent the degree is the degree, written as ˚. Therefore, a circle contains 360 degrees, that is 360˚. The measurement of two points A and D of the circumference could have 15 portions of the circumference. In this case, this measurement would be represents as 15˚. The distance between two equidistant points A and B is a round shape geometrically defined as an arc. An angle, ө, is the ratio of the distance between two points A and B of the circumference divided by the radius R. This can be written as:
Therefore, an angle ө is the ratio of an arc over the radius. Because an angle is a ratio and not a “physical” measurement, which means an angle is not a dimension, it is independent of the size of a circle. Obviously this angle represents the number of portions included by the three points. A better unit used to measure an angle is the radian or rad.
120
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
A
ө B A cycle is a measurement of the rotation around the circle. Since the rotation is not necessarily complete, depending on the scenario, a measure is made based on the angle that was covered during the rotation. A cycle could cover part of the circle in which case the rotation would not have been completed. A cycle could also cover the whole 360˚ of the circle and continue there after. A cycle is equivalent to the radian divided by 2 * Pi. The VCL ships with functions used to perform conversions of values between different units. To use any of these functions, you must include the VCL math header file as: #include
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
#include #pragma hdrstop
Copyright © 2003 FunctionX, Inc.
121
Chapter 5: Math Functions
Borland C++ Builder Programming
#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); } //---------------------------------------------------------------------------
122
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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:
Copyright © 2003 FunctionX, Inc.
123
Chapter 5: Math Functions
Borland C++ Builder Programming
//--------------------------------------------------------------------------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); 124
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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 Copyright © 2003 FunctionX, Inc.
125
Chapter 5: Math Functions
Borland C++ Builder Programming
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 126
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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 #include #pragma hdrstop //--------------------------------------------------------------------------#pragma argsused int main(int argc, char* argv[]) { int Tables, Chairs, BookShelves, TrashCans, Desktops, Laptops, Printers, FaxMachines, Books, Pens, Pencils, Others; cout cout cout cout
<< << << <<
"Company Inventory\nType the number of items of each category\n"; "Desktops: "; cin >> Desktops; "Laptops: "; cin >> Laptops; "Printers: "; cin >> Printers;
Copyright © 2003 FunctionX, Inc.
127
Chapter 5: Math Functions
cout cout cout cout cout cout cout cout cout
<< << << << << << << << <<
Borland C++ Builder Programming
"Fax Machines: "; cin >> FaxMachines; "Chairs: "; cin >> Chairs; "Tables: "; cin >> Tables; "Book Shelves: "; cin >> BookShelves; "Books: "; cin >> Books; "Trash Cans: "; cin >> TrashCans; "Pens: "; cin >> Pens; "Pencils: "; cin >> Pencils; "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.
128
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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
Copyright © 2003 FunctionX, Inc.
129
Chapter 5: Math Functions
Borland C++ Builder Programming
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.
130
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 5: Math Functions
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)
Copyright © 2003 FunctionX, Inc.
131
Chapter 5: Math Functions
Borland C++ Builder Programming
{ 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.
132
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
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).
Copyright © 2003 FunctionX, Inc.
133
Chapter 6: Accessories for File Processing
Borland C++ Builder Programming
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 dialog’s 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 dialog’s 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
6.2
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
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:
134
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
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
The primary role of the Save As dialog box is to allow users to store a file on the hard drive of the computer, on a portable media such as a floppy disk, or on a network drive. To make this efficient and complete, the user must supply two valuable pieces of information: the location and the name of the file. The location of a file is also known as its path. The name of a file follows the directives of the operating system. On MS DOS and Windows 3.X, it had to be in an 8.3 format. The actual name had to have a maximum of 8 characters with restrictions on the characters that could be used. The user also had to specify three characters after a period. The three characters, known as the file extension, were used by the operating system to classify the file. That was all necessary for those 8bit and 16-bit operating systems. Various rules have changed. For example, the names of folders and files on Microsoft Windows >= 95 can have up to 255 characters. The extension of the file is mostly left to the judgment of the programmer but the files are still using extensions. Applications can also be configured to save different types of files; that is, files with different extensions. To use the Save As dialog box, users usually click an item under the File menu. Here is how it works for most regular applications. The user creates a new file. If the user wants to save the file, she can click File -> Save. If the file was not previously saved, the application would call the Save As dialog box. If a file is displaying, whether it was saved previously or not, the user can also click File -> Save As... which also would call the Save As dialog box. Two objects are particularly important on the Save As dialog box: The Save In combo box and the File Name edit box or combo box (the File Name box is made of a combo box to make it user-friendly but over all, users hardly use the list side of this combo box). Since Windows 95, the user does not have to specify an extension if the programmer makes it easy. To help with this, the Save As dialog box is equipped with a Save As Type combo box. This combo box allows the user to select one of the extensions. The available extensions have to be created by the programmer so the user can select from this preset list. If the programmer neglects this, the user would have no extension to select from. Although the file can still be saved, the operating system would not associate it with a known type of file. Therefore, if you specify a series of extensions, the user can select one of these and, in the File Name box, she can simply type a name for the file. If the user does not specify an extension, the operating system would allocate the extension of the Save As Type combo box. Users of regular commercial applications, such as word processors, spreadsheet programs, or databases, etc, are usually trained not to care about
Copyright © 2003 FunctionX, Inc.
135
Chapter 6: Accessories for File Processing
Borland C++ Builder Programming
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; } //---------------------------------------------------------------------------
136
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
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|"
Copyright © 2003 FunctionX, Inc.
137
Chapter 6: Accessories for File Processing
Borland C++ Builder Programming
"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:
138
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
HRESULT SHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath );
Here is an example: //--------------------------------------------------------------------------#include #include #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.
Copyright © 2003 FunctionX, Inc.
139
Chapter 6: Accessories for File Processing
Borland C++ Builder Programming
Practical Learning: Using the Save As Dialog Box 1.
On the Dialogs tab of the Component Palette, click the SaveDialog button click the form
and
2.
While the Save Dialog1 button is still selected on the dialog, in the Object Inspector, click the DefaultExt field and type rtf
3.
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
140
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
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.
Copyright © 2003 FunctionX, Inc.
141
Chapter 6: Accessories for File Processing
Borland C++ Builder Programming
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
142
1.
On the Dialogs tab of the Component, click the OpenDialog button the form
and click
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:
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
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:
Copyright © 2003 FunctionX, Inc.
143
Chapter 6: Accessories for File Processing
Borland C++ Builder Programming
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:
144
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
The VCL provides a convenient dialog box used to browse the drives of the user’s 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;
} //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
145
Chapter 6: Accessories for File Processing
6.5
Borland C++ Builder Programming
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:
146
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
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:
Copyright © 2003 FunctionX, Inc.
147
Chapter 6: Accessories for File Processing
Borland C++ Builder Programming
//--------------------------------------------------------------------------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 148
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
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. Copyright © 2003 FunctionX, Inc.
149
Chapter 6: Accessories for File Processing
Borland C++ Builder Programming
If you want to programmatically provide a Print dialog box, you can declare a pointer to TPrintDialog class. The compiler will need to know "who" owns the printer. This can be done as follows: // Adding a Print dialog box at run time TPrintDialog *dlgPrint = new TPrintDialog(Form1);
Using a menu item or a toolbar button, you can call the Execute() method of the TPrintDialog class. As a Boolean function, you should first make sure that the user was able to open the Print dialog box, then you can execute printing. Suppose you have a RichEdit control whose content you want to print and suppose you have added a menu item called mnuPrint and a PrintDialog control named PrintDialog1, you can perform printing with the following code: //--------------------------------------------------------------------------void __fastcall TForm1::mnuPrintClick(TObject *Sender) { if( PrintDialog1->Execute() ) RichEdit1->Print(""); } //---------------------------------------------------------------------------
The TPrintDialog class provides all the options to customize the behavior of the Print dialog box. One of the first choices people make when printing is whether to print the whole document or just sections of it. By default, the Print dialog box always allows users to print the document completely. This is represented by the All radio button. Most other characteristics of the Print dialog box are not singly set. A particular characteristic usually works in connection with another effect of the same Print dialog box. When customizing the Print dialog box, you will mostly set its options using the Options property with one or more other related properties. The Options property is a Set; this means that it allows you to combine the options you want. If you want users to be able to select a portion of text and print only the selected portion, you must make sure the Selection radio button is available. This is done by setting the poSelection option to true. If you want the dialog box to appear with the Selection radio button selected, you can set the PrintRange property to prSelection. Usually this would not be a standard good idea. If you want this radio button to be selected when the dialog box comes up, you should first make sure that a portion of the document to be printed has been selected. For example, if you are (or the user is) trying to print from a RichEdit control, you can check whether there is already selected text before displaying the dialog box as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnPrinterClick(TObject *Sender) { if( RichEdit1->SelLength ) PrintDialog1->PrintRange = prSelection; else PrintDialog1->PrintRange = prAllPages; if( PrintDialog1->Execute() ) RichEdit1->Print("");
} //---------------------------------------------------------------------------
150
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 6: Accessories for File Processing
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:
Copyright © 2003 FunctionX, Inc.
151
Chapter 6: Accessories for File Processing
Borland C++ Builder Programming
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.
152
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
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
Copyright © 2003 FunctionX, Inc.
153
Chapter 7: File Processing
Borland C++ Builder Programming
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
r
Opens an existing file for reading only
w
Saves a new file
a
Opens an existing file, saves new file, or saves a existing file that has been modified Opens an existing file
it would be opened and can be read. After the file is opened, the user cannot add data to it the file's contents would be deleted and replaced by the new content 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 the file is opened and its existing data can be modified or updated he file is opened, its contents would be deleted and replaced with the new contents it is opened and its contents can be updated. New information written to the file would be added to the end of the file
r+ w+
Creates new file or saves an existing one
a+
Creates a new file or modifies an existing one
If the file does not exist the operation would fail
a new file is created and can be written to a new file is created and can be written to
the operation would fail a new file is created and can be written to 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);
154
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
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 #include 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() )
Copyright © 2003 FunctionX, Inc.
155
Chapter 7: File Processing
{
Borland C++ Builder Programming
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:
156
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
Character
Used for
c d e f g h i o s u x
A single character An integer A floating-point number A floating-point number A floating-point number A short integer A decimal, a hexadecimal, or an octal integer An octal integer A string followed by a white space character An unsigned decimal integer A hexadecimal integer
After specifying the format, you can type the name of the variable that is being saved. You can repeatedly use the fprintf() function for each variable you want to save. If you have opened a file and want to retrieve data stored from it, you can use the fscanf() or the fwscanf() function. Their syntaxes are: int fscanf(FILE *stream, const char *format[, address, ...]); int fwscanf(FILE *stream, const wchar_t *format[, address, ...]);
The first argument, stream, must be a valid instance of a FILE structure. The second argument, format, follows the same rules as for the fprintf() and the fwprintf() functions. After typing the format, type the name of the variable that is being retrieved.
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, fscanf(FOpen, fscanf(FOpen, fscanf(FOpen,
Copyright © 2003 FunctionX, Inc.
"%s", FirstName); "%s", LastName); "%s", DOB); "%d", &Gender);
157
Chapter 7: File Processing
Borland C++ Builder Programming
fscanf(FOpen, fscanf(FOpen, fscanf(FOpen, fscanf(FOpen, fscanf(FOpen, fscanf(FOpen,
}
"%s", "%s", "%s", "%s", "%s", "%s",
English); Language2); History); Geography); Sciences); Sports);
edtFirstName->Text = FirstName; edtLastName->Text = LastName; edtDOB->Text = DOB; cboGender->ItemIndex = Gender; edtEnglish->Text = English; edt2ndLanguage->Text = Language2; edtHistory->Text = History; edtGeography->Text = Geography; edtSciences->Text = Sciences; edtSports->Text = Sports;
fclose(FOpen); } //---------------------------------------------------------------------------
2.
In the Main.cpp file, change the OnClick event of the Save button as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnSaveClick(TObject *Sender) { FILE *FSave; char FirstName[30], LastName[30], DOB[40]; int Gender; double English, Language2, History, Geography, Sciences, Sports; strcpy(FirstName, edtFirstName->Text.c_str()); strcpy(LastName, edtLastName->Text.c_str()); strcpy(DOB, edtDOB->Text.c_str()); Gender = cboGender->ItemIndex; English = StrToFloat(edtEnglish->Text); Language2 = StrToFloat(edt2ndLanguage->Text); History = StrToFloat(edtHistory->Text); Geography = StrToFloat(edtGeography->Text); Sciences = StrToFloat(edtSciences->Text); Sports = StrToFloat(edtSports->Text); if( SaveDialog1->Execute() ) { FSave = fopen(SaveDialog1->FileName.c_str(), "w"); if( FSave == NULL ) { ShowMessage("The file could not be opened"); return; } fprintf(FSave, fprintf(FSave, fprintf(FSave, fprintf(FSave,
158
"%s\n", FirstName); "%s\n", LastName); "%s\n", DOB); "%d\n", Gender);
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
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); } //---------------------------------------------------------------------------
7.2
3.
Save and test the application
4.
After using it, close it and return to Bcb
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: Copyright © 2003 FunctionX, Inc.
159
Chapter 7: File Processing
Borland C++ Builder Programming
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. 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. 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 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 If FileName already exists, its content is destroyed and the file becomes as new 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 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.
ios::ate
ios::in
ios::out
ios::trunc ios::nocreate
ios::noreplace
Image you have a form with three edit boxes whose job is to get the first name, the last name, and the age of a student, you can save its data using a SaveDialog as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnSaveClick(TObject *Sender) { char FirstName[30], LastName[30]; int Age; strcpy(FirstName, edtFirstName->Text.c_str()); strcpy(LastName, edtLastName->Text.c_str()); Age = edtAge->Text.ToInt(); if( SaveDialog1->Execute() ) {
160
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
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
Copyright © 2003 FunctionX, Inc.
161
Chapter 7: File Processing
2.
Borland C++ Builder Programming
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 button’s 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 #include 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);
162
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
BlueBG
Chapter 7: File Processing
= 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: Copyright © 2003 FunctionX, Inc.
163
Chapter 7: File Processing
Borland C++ Builder Programming
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
164
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
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
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 button’s 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
Copyright © 2003 FunctionX, Inc.
165
Chapter 7: File Processing
Borland C++ Builder Programming
>> 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 = IntToHex(__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
166
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
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. Copyright © 2003 FunctionX, Inc.
167
Chapter 7: File Processing
Borland C++ Builder Programming
When saving a file, the Mode argument of the constructor can be passed as one of the following constants: Write Mode fmCreate fmOpenWrite fmOpenReadWrite
Description If the user is saving a new file, this is used to save it as new This is used to save a file as new. If the file was previously saved and reopened, this mode would erase its previous contents and fill it with the new data If the file already existed, this can be used to save a file that has been modified. Otherwise it can be used to create a new file
When it is possible that other people or application would try accessing the same file at the same time, the following constants can be used to manage the sharing of files: Share Mode
Description
fmShareExclusive
Only the current application can access the file The file can be opened by other applications but they cannot modify its contents The file can be modified by other applications but they cannot open it There is no restriction on what the other applications can do with the current file
fmShareDenyWrite fmShareDenyRead fmShareDenyNone
To combine these two mode for the Mode argument, you use the bitwise OR operator | Imagine you create a form equipped with a Memo and an Edit controls:
Here is an example of saving the contents of both controls to a file: //--------------------------------------------------------------------------void __fastcall TForm1::btnSaveClick(TObject *Sender)
168
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
{
Chapter 7: File Processing
// 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. This allows opening an existing file, modifying, and saving it.
fmOpenReadWrite
You can combine this mode with one of the above share modes using the bitwise OR operator. Here is an example from the same above form design: //--------------------------------------------------------------------------void __fastcall TForm1::btnOpenClick(TObject *Sender) { TFileStream *FStream; if( OpenDialog1->Execute() ) { try { FStream = new TFileStream(OpenDialog1->FileName, fmOpenRead | fmShareExclusive); FStream->ReadComponent(Memo1); FStream->ReadComponent(Edit1);
Copyright © 2003 FunctionX, Inc.
169
Chapter 7: File Processing
Borland C++ Builder Programming
} __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;
170
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Integer
Chapter 7: File Processing
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);
Copyright © 2003 FunctionX, Inc.
171
Chapter 7: File Processing
Borland C++ Builder Programming
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:
172
•
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 Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
•
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 READ_CONTROL, WRITE_DAC, and WRITE_OWNER
•
STANDARD_RIGHTS_ALL: Combines DELETE, 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
DELETE,
READ_CONTROL,
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.
Copyright © 2003 FunctionX, Inc.
173
Chapter 7: File Processing
Borland C++ Builder Programming
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.
174
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
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 form’s 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);
Copyright © 2003 FunctionX, Inc.
175
Chapter 7: File Processing
Borland C++ Builder Programming
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; }
176
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
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
Copyright © 2003 FunctionX, Inc.
177
Chapter 7: File Processing
5.
Close the form and return to Bcb
6.
Save All
Borland C++ Builder Programming
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.
178
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 7: File Processing
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 )
Copyright © 2003 FunctionX, Inc.
179
Chapter 7: File Processing
Borland C++ Builder Programming
{ }
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
180
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
{ }
Chapter 7: File Processing
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
Copyright © 2003 FunctionX, Inc.
181
Chapter 8: Strings Lists
182
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
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:
Copyright © 2003 FunctionX, Inc.
183
Chapter 8: Strings Lists
Borland C++ Builder Programming
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.
184
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
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.
Copyright © 2003 FunctionX, Inc.
185
Chapter 8: Strings Lists
Borland C++ Builder Programming
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 186
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
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.
Copyright © 2003 FunctionX, Inc.
187
Chapter 8: Strings Lists
Borland C++ Builder Programming
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");
188
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
if( Found ) cbxNetwork->Items->Insert(Found, "Digital Camera");
} //---------------------------------------------------------------------------
You could also look for Source in a target list and delete it if it exists. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnDeleteClick(TObject *Sender) { int Found = cbxNetwork->Items->IndexOf("Printer"); if( Found ) cbxNetwork->Items->Delete(Found);
} //---------------------------------------------------------------------------
Another type of operation you will perform in a list is to retrieve the text of a particular string in the group; this usually happens when you want to transfer an item to another control or display a message about the item. The Strings property allows you to get an item based on its position in the list. This property is an array that represents the items of the list. To locate a string, use the square brackets to specify its position. Because the list is zero-based, the first item is 0 and would be represented by Strings[0], the second is 1 and would be called with Strings[1], etc. For the following example, when the user clicks the list box, regardless of the item selected, a label on the form would retrieve the third item of the list, provided the list has at least 3 items: //--------------------------------------------------------------------------void __fastcall TForm1::ListBox1Click(TObject *Sender) { Label1->Caption = ListBox1->Items->Strings[2]; } //---------------------------------------------------------------------------
One of the most effective ways to use the Strings property is to find out the item that the user would have selected in a list and act accordingly. In the following example, when the user selects a radio button from a RadioGroup control, the caption of the button selected displays on a label: //--------------------------------------------------------------------------void __fastcall TForm1::RadioGroup1Click(TObject *Sender) { // Find the position of the item selected int ItemPos = RadioGroup1->ItemIndex; // Get the text of the item selected String strSelected = RadioGroup1->Items->Strings[ItemPos]; // Using a label, display a message associated with the item selected Label1->Caption = "You selected " + strSelected; } //---------------------------------------------------------------------------
One of the most regular reasons for this operation is to make sure that a string is not duplicated and added to a list that already has the Source argument. In the following Copyright © 2003 FunctionX, Inc.
189
Chapter 8: Strings Lists
Borland C++ Builder Programming
example, when the user types a string in an edit box and clicks somewhere else (that is, when the Edit control looses focus), the compiler checks to see if the string in the edit box already exists in the list. If the list of the combo box does not have the string in the edit box, then this string is added to the list: //--------------------------------------------------------------------------void __fastcall TForm1::edtLaptopExit(TObject *Sender) { String Laptop = edtLaptop->Text; int Exists = cbxNetwork->Items->IndexOf(Laptop); if( Exists == -1 ) cbxNetwork->Items->Append(Laptop); } //---------------------------------------------------------------------------
Some operating system configuration files contain lines with the = symbol as part of a string. There is no strict rule on what those files are or what they do. The company or the person who creates such a file also decides what the file is used for and when. For example, a music program would use such a file to configure the keyboard keys as related to the associated software. A communication program could use another type of those files as a list or table of ports. Programmers have to deal with those files for various reasons. Some of those files have strings made of three parts: Left=Right. The value called Right has to be assigned to the value on the left. Sometimes the Left value is called a Key; sometimes it is called a Name. The value on the right of equal is also called a Value. Therefore, a string on this file would have the form Name=Value. Some of these files have the .INI extension but can perfectly have any extension the programmer wanted. Depending on how the file is structured, programmers have to find a certain key or name in the list. The TStrings class is equipped with a function that helps with this operation. The method is IndexOfName() and its syntax is: int __fastcall IndexOfName(const AnsiString Name);
This method takes an AnsiString object as argument. The compiler scans the list looking for a string that has the form Name=Value. If the left part of a string matches the Name argument, the IndexOfName() method returns the first position where such a string was found. The following dialog box is equipped with an edit box and a memo. To add a new key to the list of keys in the memo, the user types the key in the edit box and clicks the Add Key button (the Edit control is named edtNewKey, the Memo control is named mmoConfigure, the button is named btnAddKey, the bottom Edit control is named edtFound):
190
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
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)
Copyright © 2003 FunctionX, Inc.
191
Chapter 8: Strings Lists
Borland C++ Builder Programming
mmoConfigure->Lines->Append(edtNewKey->Text); } //---------------------------------------------------------------------------
Again, depending on how the list is structured, you can ask the compiler to retrieve the name part of a string provided its position. This operation is performed using the Names property. This property is an array of the strings of this type of file. When needed, you can ask the compiler to give you the name part of a certain line. For example, to retrieve the name part of the 5th line, you could write Names[4]. If the item at that position does not have = as part of the string, the compiler would consider that the line is not a valid IndexOfName string and would avoid it. In the following example, the 2nd line of mmoConfigure is examined. If that line is a valid IndexOfName, its Name part displays in the Found At edit box: //--------------------------------------------------------------------------void __fastcall TForm1::btnGetNameClick(TObject *Sender) { edtFound->Text = mmoConfigure->Lines->Names[1]; } //---------------------------------------------------------------------------
By contrast, to retrieve the Value part of an IndexOfName string, use the Values property. This also is an array of the valid = strings of the list. It uses the following syntax: AnsiString Values[AnsiString NamePart];
This time, you must (or should) provide the string you are looking for as if it were the position of the string. This string, which is an AnsiString object, should be the Name part of a string you are looking for. The compiler would scan the list of strings looking for valid IndexOfName strings. If it finds a string that encloses an = symbol, then the compiler would check its Name part. If this Name matches the NamePart of the Values property, then it would return the Value. This is useful if you want to know whether a certain key has already been defined in the file. When providing the NamePart as the string to look for, make sure you do not include = as part of the string Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnGetValueClick(TObject *Sender) { edtFound->Text = mmoConfigure->Lines->Values["Directory"]; } //---------------------------------------------------------------------------
Another usefulness of items of a list is to switch their positions as related to each other. The primary method used to swap items is the Move() method. Its syntax is: void __fastcall Move(int CurrentPos, int NewPos);
This function takes two strings. The first string is considered for its position. When executing, the function moves the first item from the CurrentPos position to the position specified by NewPos. The following event would move the 2nd item of a CheckListBox control to the 5th position: 192
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
//--------------------------------------------------------------------------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); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
193
Chapter 8: Strings Lists
Borland C++ Builder Programming
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);
194
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
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);
Copyright © 2003 FunctionX, Inc.
195
Chapter 8: Strings Lists
Borland C++ Builder Programming
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);
196
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
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; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
197
Chapter 8: Strings Lists
Borland C++ Builder Programming
As you can see, this transfer is not properly interpreted by the Edit control because this control does not have a multiple line capability while the WordWrap property helps to manage the Label control. Another technique used to most effectively transfer a list from a strictly list-based control, such as a ListBox, a ComboBox or a RadioGroup control to a text-based control such as a memo or a RichText control, is to use the CommaText property. When called to use this property, the compiler would scan the list of items. If an item is a one-word string, the compiler would write a comma “,” on its right and start adding the next item. If the item is a string made of more than one word, to delimit it, the compiler would enclose it with double-quotes. This is done until the last string:
In the following example, when the user clicks the Transfer button, the list of items from the ListBox is transferred to both a memo and an edit box using the CommaText property: //--------------------------------------------------------------------------void __fastcall TForm1::btnTransferClick(TObject *Sender) { Memo1->Lines->Add(ListBox1->Items->CommaText); Edit1->Text = ListBox1->Items->CommaText; } //---------------------------------------------------------------------------
If you decide to dismiss a whole list, use the Clear() method. Its syntax is: void __fastcall Clear();
Unlike the Delete() method, the Clear() function completely empties the list of items. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnEmptyTheListClick(TObject *Sender) { Memo1->Lines->Clear(); } //---------------------------------------------------------------------------
8.3
198
List of Strings and File Management
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
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: Copyright © 2003 FunctionX, Inc.
199
Chapter 8: Strings Lists
Borland C++ Builder Programming
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 200
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
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 #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 #include #include #include //--------------------------------------------------------------------------class TForm1 : public TForm
Copyright © 2003 FunctionX, Inc.
201
Chapter 8: Strings Lists
Borland C++ Builder Programming
{ __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. 202
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 8: Strings Lists
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.
Copyright © 2003 FunctionX, Inc.
203
Chapter 8: Strings Lists
204
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
205
Chapter 8: Strings Lists
Borland C++ Builder Programming
PART II The Device Context and its Usefulness Because Microsoft Windows is a graphical operating system, the controls used on its applications for user interaction perform and process a lot of drawing. This section studies the various pieces of information you need in order to draw, not only on rough objects like forms but also on smaller controls. This section also was judged a valuable prerequisite to Windows controls studying.
206
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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:
Copyright © 2003 FunctionX, Inc.
207
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
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);
208
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
209
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
9.2.2 Polylines A polyline is a series of connected lines. The lines are stored in an array of TPoint values. To draw a polyline, you use the TCanvas::Polyline() method. Its syntax is: void __fastcall Polyline(const Types::TPoint* Points, const int Points_Size);
The Points argument is an array of TPoint values. The Points_Size argument specifies the number of members of the array. When executing, the compiler moves the starting point to Points[0]. The first line is drawn from Points[0] to Points[1] as in: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { TPoint Pt[] = { Point(60, 20), Point(60, 122) }; Canvas->MoveTo(Pt[0].x, Pt[0].y); Canvas->LineTo(Pt[1].x, Pt[1].y);
} //---------------------------------------------------------------------------
To draw a polyline, you must have at least two points. If you define more than two points, each line after the first would be drawn from the previous point to the next point until all points have been included. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { TPoint Pt[7]; Pt[0] Pt[1] Pt[2] Pt[3] Pt[4] Pt[5] Pt[6]
= = = = = = =
Point(20, 50); Point(180, 50); Point(180, 20); Point(230, 70); Point(180, 120); Point(180, 90); Point(20, 90);
Canvas->Polyline(Pt, 7); } //---------------------------------------------------------------------------
210
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
Besides the Polyline() method, the Win32 API provides the PolylineTo() function. Its syntax is: BOOL PolylineTo(HDC hdc, CONST POINT *lppt, DWORD cCount);
The hdc argument is a handle to the canvas on which you are drawing. The lppt argument is the name of an array of POINT or TPoint objects. The cCount argument specifies the number of points that would be included in the figure. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { TPoint Pt[7]; Pt[0] Pt[1] Pt[2] Pt[3] Pt[4] Pt[5] Pt[6]
= = = = = = =
Point(20, 50); Point(180, 50); Point(180, 20); Point(230, 70); Point(180, 120); Point(180, 90); Point(20, 90);
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 Copyright © 2003 FunctionX, Inc.
211
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
drawing, which would mean it could starts at the origin (0, 0). For this reason, if you want to control the starting point of the PolylineTo() drawing, you can use the MoveTo() method: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { TPoint Pt[7]; Pt[0] Pt[1] Pt[2] Pt[3] Pt[4] Pt[5] Pt[6]
= = = = = = =
Point(20, 50); Point(180, 50); Point(180, 20); Point(230, 70); Point(180, 120); Point(180, 90); Point(20, 90);
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 212
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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);
} //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
213
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
9.2.4 Polygons The polylines we have used so far were drawn by defining the starting point of the first line and the end point of the last line. There was no relationship or connection between these two extreme points. A polygon is a closed polyline. In other words, it is a polyline defined so that the end point of the last line is connected to the start point of the first line. To draw a polygon, you can use the TCanvas::Polygon() method. Its syntax is: void __fastcall Polygon(const TPoint * Points, const int Points_Size);
This member function uses the same types of arguments as the Polyline() method. The only difference is on the drawing of the line combination. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { TPoint Pt[7]; Pt[0] Pt[1] Pt[2] Pt[3] Pt[4] Pt[5] Pt[6]
= = = = = = =
Point(20, 50); Point(180, 50); Point(180, 20); Point(230, 70); Point(180, 120); Point(180, 90); Point(20, 90);
Canvas->Polygon(Pt, 7); } //---------------------------------------------------------------------------
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
214
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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);
} //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
215
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
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); } //---------------------------------------------------------------------------
216
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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|.
Copyright © 2003 FunctionX, Inc.
217
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
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 BDR_RAISEDINNER BDR_SUNKENINNER BDR_RAISEDOUTER BDR_SUNKENOUTER
Description The interior edge will be raised The interior edge will be sunken The exterior edge will be raised The exterior edge will be sunken
These values can be combined using the bitwise OR operator. On the other hand, you can use the following constants instead: Value EDGE_DUMP EDGE_ETCHED EDGE_RAISED EDGE_SUNKEN
Used For BDR_RAISEDOUTER | BDR_SUNKENINNER BDR_SUNKENOUTER | BDR_RAISEDINNER BDR_RAISEDOUTER | BDR_RAISEDINNER BDR_SUNKENOUTER | BDR_SUNKENINNER
The grfFlags value specifies what edge(s) would be drawn. It can have one of the following values: Value BF_RECT BF_TOP BF_LEFT BF_BOTTOM BF_RIGHT BF_TOPLEFT BF_BOTTOMLEFT BF_TOPRIGHT BF_BOTTOMRIGHT BF_DIAGONAL_ENDBOTTOMLEFT BF_DIAGONAL_ENDBOTTOMRIGHT 218
Description The entire rectangle will be drawn Only the top side will be drawn Only the left side will be drawn Only the bottom side will be drawn Only the right side will be drawn Both the top and the left sides will be drawn Both the bottom and the left sides will be drawn Both the top and the right sides will be drawn Both the bottom and the right sides will be drawn A diagonal line will be drawn from the topright to the bottom-left corners A diagonal line will be drawn from the topleft to the bottom-right corners Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
BF_DIAGONAL_ENDTOPLEFT BF_DIAGONAL_ENDTOPRIGHT
Chapter 9: The Graphical Device Interface
A diagonal line will be drawn from the bottom-right to the top-left corners 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);
Copyright © 2003 FunctionX, Inc.
219
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
The arguments of this method play the same roll as those of the Rectangle() method:
Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { Canvas->Ellipse(20, 20, 226, 144); } //---------------------------------------------------------------------------
Like the rectangle, you can draw an ellipse using a RECT or TRect object it would fit in. The syntax you would use is: void __fastcall Ellipse(TRect Rect);
Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { TRect Recto(328, 125, 28, 8); Canvas->Ellipse(Recto); } //---------------------------------------------------------------------------
220
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
221
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
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: 222
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { Canvas->Pie(40, 20, 226, 144, 155, 32, 202, 115); } //---------------------------------------------------------------------------
9.2.11 Arcs An arc is a portion or segment of an ellipse. This means that an arc is a non-complete ellipse. Because an arc must confirm to the shape of an ellipse, it is defined as it fits in a rectangle and can be illustrated as follows:
To draw an arc, you can use the TCanvas::Arc() method whose syntax is: void __fastcall Arc(int X1, int Y1, int X2, int Y2, int X3, int Y3, int X4, int Y4);
Besides the left (X1, Y1) and the right (X2, Y2) borders of the rectangle in which the arc would fit, an arc must specify where it starts and where it ends. The additional points are set as the (X3, Y3) and (X4, Y4) points of the figure. Based on this, the above arc can be illustrated as follows:
Copyright © 2003 FunctionX, Inc.
223
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { Canvas->Arc(20, 20, 226, 144, 202, 115, 105, 32); } //---------------------------------------------------------------------------
Besides the Arc() method, the Win32 library provides the ArcTo() function used to draw an arc. Its syntax is as follows: BOOL ArcTo(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nXRadial1, int nYRadial1, int nXRadial2, int nYRadial2);
The hdc argument is a handle to the canvas on which you want to draw. This method uses the same arguments as Arc(). The difference is that while Arc() starts drawing at (x3, y3), ArcTo() does not inherently control the drawing starting point. It refers to the current point, exactly like the LineTo() (and the PolylineTo(). methods. Therefore, if you want to specify where the drawing should start, you can call TCanvas::MoveTo() before ArcTo(). Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { HDC hDC = Canvas->Handle;
224
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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: Copyright © 2003 FunctionX, Inc.
225
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
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 AD_CLOCKWISE AD_COUNTERCLOCKWISE
Orientation The figure is drawn clockwise 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);
} //---------------------------------------------------------------------------
226
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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: Copyright © 2003 FunctionX, Inc.
227
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
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:
228
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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 Bézier Curves A bézier line is an arc that is strictly based on a set number of points instead of on an ellipse. A bézier curve uses at least four points to draw on. A bézier line with four points can be illustrated as follows:
Copyright © 2003 FunctionX, Inc.
229
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
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 bézier curve could have been drawn using the following four points:
PolyBezier(): To draw a bézier curve, the TCanvas provides the PolyBezier() method. Its syntax is: void __fastcall PolyBezier(const TPoint* Points, const int Points_Size);
The Points argument is an array of POINT or TPoint values. The Points_Size argument specifies the number of points that will be used to draw the line minus 1. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { TPoint Pt[4] = { Point(20, 12), Point(88, 246), Point(364, 192), Point(250, 48) }; Canvas->PolyBezier(Pt, 3); } //---------------------------------------------------------------------------
230
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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 bézier line requires 4 points, you must add three more. You can continue adding points by three to draw the bézier 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);
Copyright © 2003 FunctionX, Inc.
231
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
The PolyBezierTo() method draws a bézier 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"); } //---------------------------------------------------------------------------
232
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 9: The Graphical Device Interface
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.
Copyright © 2003 FunctionX, Inc.
233
Chapter 9: The Graphical Device Interface
Borland C++ Builder Programming
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.
234
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
Chapter 10: GDI Accessories 10.1 Colors 10.1.1 Overview 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: 27 + 26 + 25 + 24 + 23 + 22 + 21 + 20 = 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: 23
22
21
20
19
18
17
16
15
14
Blue
13
12
11
Green
10
9
8
7
6
5
4
3
2
1
0
Red
Converted to decimal, this number has a value of 255 * 255 * 255 = 16581375. This means that we can have approximately 16 million colors available.
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.
Copyright © 2003 FunctionX, Inc.
235
Chapter 10: GDI Accessories
Borland C++ Builder Programming
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 236
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
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:
Copyright © 2003 FunctionX, Inc.
237
Chapter 10: GDI Accessories
Borland C++ Builder Programming
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 else’s computer. Fortunately, if you want to use one of these colors, you can ask your application to check its value on the user’s 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 appearance’s 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
238
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
3D Objects: Top and Left Edges
3D Objects: Right and Bottom Edges 3D Effect of Buttons and Dialog Boxes: Top and left edges 3D Effect of Buttons and Dialog Boxes: Right and bottom edges Background of Buttons and Dialog Boxes Text of Buttons General Text on Windows Active Title Bar Active Window Border Inactive Window Border Application Background Desktop General Desktop Background Inactive Title Bar Background Inactive Title Bar Text Background of MDI Menu Bar Menu Background Menu Text Menu Highlight Scrollbar Selected Items Background Selected Items Text ToolTip Background ToolTip Text Window: Text on Caption Window Background Window Frame Window Text Right Side of a Gradient Active Window Title Bar Right Side of a Gradient Inactive Window Title Bar Color of Hot-Track Item (See Tree View)
Chapter 10: GDI Accessories
COLOR_BTNFACE COLOR_3DHILIGHT COLOR_3DHIGHLIGHT COLOR_BTNHILIGHT COLOR_BTNHIGHLIGHT COLOR_3DDKSHADOW
clBtnHighlight
cl3DDkShadow
COLOR_3DLIGHT
cl3DLight
COLOR_3DSHADOW COLOR_BTNSHADOW
clBtnShadow
COLOR_BTNFACE
clBtnFace
COLOR_BTNTEXT COLOR_WINDOWTEXT COLOR_ACTIVECAPTION COLOR_ACTIVEBORDER COLOR_INACTIVEBORDER COLOR_BACKGROUND COLOR_DESKTOP COLOR_BACKGROUND COLOR_INACTIVECAPTION
clBtnText
COLOR_INACTIVECAPTIONTEXT COLOR_APPWORKSPACE
COLOR_SCROLLBAR COLOR_HIGHLIGHT COLOR_HIGHLIGHTTEXT COLOR_INFOBK COLOR_INFOTEXT COLOR_CAPTIONTEXT COLOR_WINDOW COLOR_WINDOWFRAME COLOR_WINDOWTEXT COLOR_GRADIENTACTIVECAPTION
clInactiveCaptionText clAppWorkSpace clMenuBar clMenu clMenuText clMenuHighlight clScrollBar clHighlight clHighlightText clInfoBk clInfoText clCaptionText clWindow clWindowFrame clWindowText clGradientActiveCaption
COLOR_GRADIENTINACTIVECAPTION
clGradientInactiveCaption
COLOR_HOTLIGHT
clHotLight
COLOR_MENU COLOR_MENUTEXT
clActiveCaption clActiveBorder clInactiveBorder clBackground clBackground clBackground clInactiveCaption
Besides the system colors defined in the right column, the VCL provides various color names whose values are constant and can be predicted for an application. These colors Copyright © 2003 FunctionX, Inc.
239
Chapter 10: GDI Accessories
Borland C++ Builder Programming
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.1 Text 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"); } //---------------------------------------------------------------------------
240
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
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, "Johnny Carson"); SetBkColor(Canvas->Handle, RGB(0, 0, 128)); SetTextColor(Canvas->Handle, RGB(128, 255, 255)); Canvas->TextOut(50, 60, "The once king of late-night"); } //---------------------------------------------------------------------------
If you want to know the background color applied on the text drawn, you can call the GetBkColor() function. Its syntax is: COLORREF GetBkColor(HDC hdc);
This function returns the color used to highlight the text, if the text is highlighted. The highlighting of text is actually controlled by the SetBkMode() function whose syntax is: int SetBkMode(HDC hdc, int iBkMode );
This function specifies whether the background color should be applied or not. This is set by the iBkMode argument. It can have one of two values. If it is:
OPAQUE: the background would be drawn using the crColor value
Copyright © 2003 FunctionX, Inc.
241
Chapter 10: GDI Accessories
Borland C++ Builder Programming
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, "Johnny Carson", 13, NULL); }
242
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
//---------------------------------------------------------------------------
10.3 The Color Dialog Box 10.3.1 Description 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:
Copyright © 2003 FunctionX, Inc.
243
Chapter 10: GDI Accessories
Borland C++ Builder Programming
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 the form.
and click anywhere on
The most important and most obvious property of the Color dialog box is the selected color once the user has made a choice. When the user opens the dialog you can set the default color on the Object Inspector using the Color property. You can also set this color programmatically as follows: //--------------------------------------------------------------------------void __fastcall TfrmFoodOrders::FormCreate(TObject *Sender) { ColorDialog1->Color = clRed; } //---------------------------------------------------------------------------
When the user has finished using the Color dialog box and clicked OK, you can find out what color was selected by using the TColorDialog::Color property. 244
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
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;
Copyright © 2003 FunctionX, Inc.
245
Chapter 10: GDI Accessories
Borland C++ Builder Programming
} //---------------------------------------------------------------------------
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
246
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
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
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
Copyright © 2003 FunctionX, Inc.
247
Chapter 10: GDI Accessories
Borland C++ Builder Programming
10.4.1 Introduction 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; } //---------------------------------------------------------------------------
248
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
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 user’s 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.
Copyright © 2003 FunctionX, Inc.
249
Chapter 10: GDI Accessories
Borland C++ Builder Programming
The font size is the dimension of characters used to represent the font on a device context. It is specify using the TFont::Size member variable. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { Canvas->Font->Size = 120; Canvas->Font->Name = "Garamond"; Canvas->TextOut(26, 24, "Christine"); } //---------------------------------------------------------------------------
The Style of a font controls how the font displays, in normal, italicized, underlined, stroke out, some of these characteristics or all of them. The VCL manages these properties as a set; meaning you can build them, add those you want or retract those you do not need. The available characteristics are as follows: Characteristic Bold Italic Underline Strikeout
Value fsBold fsItalic fsUnderline fsStrikeOut
Example This text is bold Italicized section The words are underlined Stroke out but happy
Font styles are implemented through the TFontStyles property. To control the Style of font, you must call TFontStyles and use the extraction operators to add or subtract a style. To add a style, you can use the << operator. For example, suppose you want to apply a Bold style to a Memo control. You can use the << operator as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Memo1->Font->Name = "Verdana"; Memo1->Font->Size = 10; Memo1->Font->Style = TFontStyles() << fsBold; } //---------------------------------------------------------------------------
In the same way, you can add other styles using the << operator: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Memo1->Font->Name = "Verdana";
250
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
Memo1->Font->Size = 10; Memo1->Font->Color = clBlue; Memo1->Font->Style = TFontStyles() << fsBold << fsUnderline << fsItalic << fsStrikeOut; } //---------------------------------------------------------------------------
Unlike the Win32 functions as we will see, the TFont class provide support for colors. Therefore, to draw text using a color of your choice, assign its value to the TFont::Color property. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { Canvas->Font->Size = 120; Canvas->Font->Color = clSkyBlue; Canvas->Font->Name = "Garamond"; SetBkMode(Canvas->Handle, TRANSPARENT); Canvas->TextOut(26, 24, "Christine"); Canvas->Font->Color = clBlue; Canvas->TextOut(20, 18, "Christine"); } //---------------------------------------------------------------------------
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,
Copyright © 2003 FunctionX, Inc.
251
Chapter 10: GDI Accessories
Borland C++ Builder Programming 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 FW_DONTCARE FW_EXTRALIGHT FW_LIGHT FW_NORMAL FW_MEDIUM FW_SEMIBOLD FW_BOLD FW_EXTRABOLD FW_BLACK
Value 0 200 300 400 500 600 700 800 900
Constant FW_THIN FW_ULTRALIGHT
Value 100 200
FW_REGULAR
400
FW_DEMIBOLD
600
FW_ULTRABOLD FW_HEAVY
800 900
The fdwItalic value specifies whether the font will be italicized (TRUE) or not (FALSE). The dwbUnderline value is used to underline (TRUE) or not underline (FALSE) the text. The fdwStrikeOut value is specifies whether the text should be stroke out (TRUE) or not (FALSE) with a (horizontal) line. The fdwCharSet parameter specifies the character set used. The possible values are: ANSI_CHARSET, BALTIC_CHARSET, CHINESEBIG5_CHARSET, DEFAULT_CHARSET, EASTEUROPE_CHARSET, GB2312_CHARSET, GREEK_CHARSET, HANGUL_CHARSET, MAC_CHARSET, OEM_CHARSET, RUSSIAN_CHARSET, SHIFTJIS_CHARSET, SYMBOL_CHARSET, TURKISH_CHARSET, HEBREW_CHARSET, and THAI_CHARSET. 252
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
The fdwOutPrecision parameter controls the amount of precision used to evaluate the numeric values used on this function for the height, the width, and angles. It can have one of the following values: OUT_CHARACTER_PRECIS, OUT_DEFAULT_PRECIS, OUT_DEVICE_PRECIS, OUT_OUTLINE_PRECIS, OUT_RASTER_PRECIS, OUT_STRING_PRECIS, OUT_STROKE_PRECIS, OUT_TT_ONLY_PRECIS, and OUT_TT_PRECIS. The fdwClipPrecision parameter is used to specify how some characters may be drawn outside of the area in which they are intended. The possible values used are CLIP_DEFAULT_PRECIS, CLIP_CHARACTER_PRECIS, CLIP_STROKE_PRECIS, CLIP_MASK, CLIP_EMBEDED, CLIP_LH_ANGLES, and CLIP_TT_ALWAYS. The fdwQuality parameter specifies how the function will attempt to match the font's characteristics. The possible values are ANTIALIASED_QUALITY, DEFAULT_QUALITY, DRAFT_QUALITY, NONANTIALIASED_QUALITY, and PROOF_QUALITY. The fdwPitchAndFamily parameter specifies the category of the font used. It combines the pitch and the family the intended font belongs to. The pitch can be specified with DEFAULT_PITCH, VARIABLE_PITCH, or FIXED_PITCH. The pitch is combined using the bitwise OR operator with one of the following values: Value FF_DECORATIVE FF_DONTCARE FF_MODERN FF_ROMAN FF_SCRIPT FF_SWISS
Description Used for a decorative or fancy fonts Let the compiler specify Modern fonts that have a constant width Serif fonts with variable width Script-like fonts Sans serif fonts with variable width
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"); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
253
Chapter 10: GDI Accessories
Borland C++ Builder Programming
Remember that once a device context object, such as a font, has been selected, it remains there until further notice. For example, if you have created and selected a font, any text you draw would follow the characteristics of that font. If you want another font, you must change the previously selected font. The CreateFont() function is used to specify all characteristics of a font in one step. Alternatively, if you want to specify each font property, you can declare a LOGFONT variable and initialize it. It is defined as follows: typedef struct tagLOGFONT { LONG lfHeight; LONG lfWidth; LONG lfEscapement; LONG lfOrientation; LONG lfWeight; BYTE lfItalic; BYTE lfUnderline; BYTE lfStrikeOut; BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality; BYTE lfPitchAndFamily; TCHAR lfFaceName[LF_FACESIZE]; } LOGFONT, *PLOGFONT;
This time, you do not have to provide a value for each member of the structure and even if you do, you can supply values in the order of your choice. For any member whose value is not specified, the compiler would use a default value but you may not like some of the default values. Therefore, you should specify as many values as possible. The member variables can be initialized with their equivalent values we reviewed for the CreateFont() function. After initializing the LOGFONT variable, call the CreateFontIndirect() function. Its syntax is: HFONT CreateFontIndirect(CONST LOGFONT *lplf);
When calling this member function, pass the LOGFONT variable as a pointer, lplf. Like CreateFont(), the CreateFontIndirect() function returns an HFONT value. After calling this function, you can retrieve its return value and initialize the handle of the
254
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
TCanvas::Font member variable. After that assignment, the font is ready for you. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { HFONT font; LOGFONT LogFont; LogFont.lfStrikeOut = 0; LogFont.lfUnderline = 0; LogFont.lfHeight = 42; LogFont.lfEscapement = 0; LogFont.lfItalic = TRUE; LogFont.lfWidth = 22; font = CreateFontIndirect(&LogFont); Canvas->Font->Handle = font; Canvas->TextOut(20, 18, "James Kolowski"); } //---------------------------------------------------------------------------
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 font’s 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. Copyright © 2003 FunctionX, Inc.
255
Chapter 10: GDI Accessories
Borland C++ Builder Programming
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.1 Introduction To support easy selection of font, Microsoft Windows provides the Font dialog box:
256
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
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. Copyright © 2003 FunctionX, Inc.
257
Chapter 10: GDI Accessories
Borland C++ Builder Programming
The Object Inspector presents the same options the user would need to set when displaying the Font dialog box. Imagine that you want to change the default font attributes of any control that descends from the TControl class, for example a memo. At design time, when the object is selected on the form, on the Object Inspector, you can expand the Font property and the Style set if necessary then change the properties as you see fit:
At run time, you can still set the characteristics as you wish. For example, you can change them in response to some intermediate action. On the other hand, you can use the TFontDialog object to let the user customize the font characteristics of text. Once, and however, you have a TFontDialog instance, you can display the Font dialog box by calling the Execute() method. The font dialog box is equipped with two primary buttons: OK and Cancel. After using it, if the user clicks OK, this implies that if there were changes of font, size, color, etc, the user wants them committed to the document. If the user clicks Cancel, this means that you should ignore any actions that were performed on the dialog box. The Execute() method is Boolean. It returns true if the user clicks OK. Otherwise, if the user clicks Cancel, it would return false. Therefore, after the user has used it, you should find out if she clicked OK or Cancel before applying her changes. This inquiry is usually performed with an if conditional statement as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { if( dlgFont->Execute() ) { // What to do if the user clicked OK } } //---------------------------------------------------------------------------
The Name of the selected font is an AnsiString value that indirectly belongs to the TFont class. After the user has clicked OK, you can find out what font was selected and assign it to the Font object you are trying to change. Here is an example:
258
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 10: GDI Accessories
//--------------------------------------------------------------------------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; } } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
259
Chapter 11: GDI Tools
260
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
Chapter 11: GDI Tools 11.1 Pens 11.1.1 The 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.
Copyright © 2003 FunctionX, Inc.
261
Chapter 11: GDI Tools
Borland C++ Builder Programming
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); } //---------------------------------------------------------------------------
262
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
Although most lines are drawn continuously, the VCL and the Win32 library support non-continuous lines. This characteristic is controlled by the style of the pen. For a TPen object, this would be the Style property. For the CreatePen() function or the LOGPEN structure, this would be the fnPenStyle argument or member variable. The possible values of the style are: TPen::Style psSolid
CreatePen() and LOGPEN PS_SOLID
psDash
PS_DASH
psDot
PS_DOT
psDashDot
PS_DASHDOT
psDashDotDot
PS_DASHDOTDOT
psClear
PS_NULL
psInsideFrame
PS_INSIDEFRAME
Illustration
Description A continuous solid line A continuous line with dashed interruptions A line with a dot interruption at every other pixel A combination of alternating dashed and dotted points A combination of dash and double dotted interruptions No visible line A line drawn just inside of the border of a closed shape
The Width or nWidth of a pen specifies how wide its line(s) would be. Its value should be greater than or equal to 1. If you specify a width less than 1, it would be restored to 1. The value of the width cannot be applied to all types of pens. This property is directly influenced by the style. A cosmetic pen can have a width of only 1 pixel. If you specify a higher width, it would be ignored. A geometric pen can have a width of 1 or more pixels but the line can only be solid or null. This means that, if you specify the style as psDash, PS_DASH, psDot, PS_DOT, psDashDot, PS_DASHDOT, psDashDotDot, or PS_DASHDOTDOT but set a width higher than 1, the line would be drawn as PS_SOLID. If you are using the CreatePen() function, to specify the type of pen you are creating, as cosmetic or geometric, use the bitwise OR operator to combine one of the above styles with one of the following:
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) {
Copyright © 2003 FunctionX, Inc.
263
Chapter 11: GDI Tools
Borland C++ Builder Programming
Canvas->Pen->Style = psDashDotDot; Canvas->Pen->Width = 1; Canvas->Pen->Color = TColor(RGB(255, 25, 5)); Canvas->Rectangle(20, 22, 250, 125); } //---------------------------------------------------------------------------
Once a pen has been selected, any drawing performed and that uses a pen would use the currently selected pen. If you want to use a different pen, you can either create a new pen or change the characteristics of the current pen. Here is an example that uses the HPEN: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { HPEN NewPen; LOGPEN LogPen; LogPen.lopnStyle = PS_SOLID; LogPen.lopnWidth = Point(1, 105); LogPen.lopnColor = RGB(235, 115, 5); NewPen = CreatePenIndirect(&LogPen); Canvas->Pen->Handle = NewPen; Canvas->Ellipse(60, 40, 82, 80); Canvas->Ellipse(80, 20, 160, 125); Canvas->Ellipse(158, 40, 180, 80); Canvas->Ellipse(100, 60, 110, 70); Canvas->Ellipse(130, 60, 140, 70); Canvas->Ellipse(100, 90, 140, 110);
} //---------------------------------------------------------------------------
264
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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.1 Introduction A brush is a drawing tool used to fill out closed shaped or the interior of lines. It is similar to picking up a bucket and pouring its contents somewhere. In the case of computer graphics, the area where you position the brush is called the brush origin. The color (or picture) that the brush holds would be used to fill the whole area until the brush finds a limit set by some rule. A brush can be characterized by its color (if used), its pattern used to fill the area, or a picture (bitmap) used as the brush. The VCL provides support for brushes through the TBrush class. The TCanvas class has a TBrush member variable. This means that, any object that can receive drawing, that is, every control that provides a Canvas member variable, already provides a TBrush variable ready for use. If you want to explicitly create a brush, you can declare a TBrush variable and use it to initialize the TCanvas::Brush member variable.
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.
Copyright © 2003 FunctionX, Inc.
265
Chapter 11: GDI Tools
Borland C++ Builder Programming
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(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(RGB(255, 2, 5)); TPoint Pt[3]; // Top Triangle Pt[0] = Point(125, 10); Pt[1] = Point( 95, 70); Pt[2] = Point(155, 70); Canvas->Polygon(Pt, 2); // Left Triangle Pt[0] = Point( 80, 80); Pt[1] = Point( 20, 110); Pt[2] = Point( 80, 140); Canvas->Polygon(Pt, 2); // Bottom Triangle Pt[0] = Point( 95, 155); Pt[1] = Point(125, 215); Pt[2] = Point(155, 155);
266
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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(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);
Copyright © 2003 FunctionX, Inc.
267
Chapter 11: GDI Tools
Borland C++ Builder Programming
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 );
268
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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 form’s 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(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(RGB(0, 128, 192)); Canvas->RoundRect(180, 30, 320, 80, 10, 10); Canvas->Brush->Style = bsDiagCross; Canvas->Brush->Color = static_cast(RGB(0, 128, 0)); Canvas->RoundRect(340, 30, 480, 80, 10, 10); Canvas->Brush->Style = bsVertical; Canvas->Brush->Color = static_cast(RGB(0, 128, 0)); Canvas->RoundRect(20, 120, 160, 170, 10, 10); Canvas->Brush->Style = bsHorizontal; Canvas->Brush->Color = static_cast(RGB(255, 128, 0)); Canvas->RoundRect(180, 120, 320, 170, 10, 10); Canvas->Brush->Style = bsCross; Canvas->Brush->Color = static_cast(RGB(200, 0, 0)); Canvas->RoundRect(340, 120, 480, 170, 10, 10); Canvas->Font->Style = TFontStyles() << fsBold; Canvas->Font->Color = static_cast(RGB(0, 0, 255)); Canvas->TextOut(40, 10, "HS_BDIAGONAL"); Canvas->Font->Color = static_cast(RGB(0, 128, 192)); Canvas->TextOut(205, 10, " bsBDiagonal"); Canvas->Font->Color = static_cast(RGB(0, 128, 0)); Canvas->TextOut(355, 10, " bsDiagCross"); Canvas->Font->Color = static_cast(RGB(255, 0, 255)); Canvas->TextOut(44, 100, " bsVertical"); Canvas->Font->Color = static_cast(RGB(255, 128, 0)); Canvas->TextOut(195, 100, " bsHorizontal"); Canvas->Font->Color = static_cast(RGB(200, 0, 0)); Canvas->TextOut(370, 100, " bsCross"); } //---------------------------------------------------------------------------
4.
Test the application
Copyright © 2003 FunctionX, Inc.
269
Chapter 11: GDI Tools
5.
Borland C++ Builder Programming
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); }
270
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
//---------------------------------------------------------------------------
11.3 Using Pens and Brushes: The Image Editor 11.3.1 Introduction 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.
Copyright © 2003 FunctionX, Inc.
271
Chapter 11: GDI Tools
Borland C++ Builder Programming
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.
272
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
To find out what a button is used for, position the mouse on top. A tool tip, or hint, would display. The buttons are used for various goals and exhibit different behaviors.
Some suchthe as the Pencil, andofthea geometric If youtools change default linelines, width tool, the selected width or shapes (rectangle, rectangle, thickness stays in round memory and canand be ellipse) applied allow to a tool that does not you to specify a width their lines. obviously display thisfor option. This happens if you a great width for a rectangle and then decide to use a Filled Rectangle tool, the last width would apply to the new tool. Therefore, if you do not want to use the same width, select the default before using another tool. Some tools such as the Brush or the Spray allow you to select a thickness for the dot or mark they would apply to a graphic. To access this change, first select the Brush or Spray, then, in the lower section of the Tools Palette, select the the thickness you want.
The main area of the application is made of a wide black rectangle that is used to host the graphics you will be using. Like the top section, the bottom area of the application contains two objects. The Color Palette displays a list of 16 colored buttons (by default)
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.1 Introduction Like a bitmap, an icon is used to display graphics on window objects. While a bitmap can have any dimension the window needs, the size of an icon must be limited. This is because icons assume different roles on an application. Copyright © 2003 FunctionX, Inc.
273
Chapter 11: GDI Tools
Borland C++ Builder Programming
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
274
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:
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
9.
Chapter 11: GDI Tools
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: Copyright © 2003 FunctionX, Inc.
275
Chapter 11: GDI Tools
Borland C++ Builder Programming
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 form’s 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
276
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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
Copyright © 2003 FunctionX, Inc.
277
Chapter 11: GDI Tools
Borland C++ Builder Programming
33. Notice that the executable file uses the the right icon for each display. 34. Return to Bcb
11.5 Cursors 11.5.1 Introduction Cursors are another type of application accessory you can design using pens and brushes in Image Editor. A cursor is a small graphic that represents the position of the mouse on a Windows screen. Because Windows is a graphic-oriented operating system, when it installs, it creates a set of standard or regularly used cursors. These can be seen by opening the Control Panel window and double-clicking the Mouse icon. This opens the Mouse Properties dialog box where you can click the Pointers tab to see a list of standard cursors installed by Windows: 278
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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. Copyright © 2003 FunctionX, Inc.
279
Chapter 11: GDI Tools
Borland C++ Builder Programming
Practical Learning: Creating a Cursor
280
1.
On the main menu of Image Editor, click File -> New… -> Cursor File (.cur)
2.
On the Tools Palette, click the Fill button 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:
and right-click the drawing area to
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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
Copyright © 2003 FunctionX, Inc.
281
Chapter 11: GDI Tools
Borland C++ Builder Programming
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.1 Icons 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.
282
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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:
Copyright © 2003 FunctionX, Inc.
283
Chapter 11: GDI Tools
Borland C++ Builder Programming
7.
With the Curve tool still selected, draw the same diagonal line you drew earlier.
8.
Drag the 5th pixel from top of the line to right and down as if you were drawing a square:
9.
Click the pixel at the intersection of both lines then drag left and up to draw a curved line:
10. While the Curve tool is selected, draw one more diagonal line similar to the previous ones 11. To make the line curved, click in the middle of the diagonal line and slightly drag left and up:
284
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
12. As you may realize, we are drawing a leaf or feather. Therefore, add a few black pixels to decorate the graphic. Using the Fill tool, add a white background to the cursor:
13. To specify the position of the pointer, on the main menu, click Cursor -> Set Hot Spot… 14. Set the Horizontal value to 3 and the Vertical value to 30. Click OK 15. On the main menu, click Cursor -> Test... 16. Draw a few lines to test the cursor and click Close 17. Save the file as Feather 18. Back in Image Editor, make sure the window that has the previously designed cursor has focus Press Ctrl + A to select the cursor. Press Ctrl + C to copy the graphic 19. On the main menu, click File -> New… -> Icon File (.ico) 20. On the Icon Properties dialog box, click the 32 x 32 (Standard Icon) radio button. In the Colors section, click the 16 Color radio button and click OK 21. Press Ctrl + V to paste the graphic 22. On the Tools Palette, click Fill 23. On the Color Palette, right-click the button with a green color and red S 24. In the drawing area, right-click outside of the graphic to make background transparent Copyright © 2003 FunctionX, Inc.
285
Chapter 11: GDI Tools
Borland C++ Builder Programming
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.
286
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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
Copyright © 2003 FunctionX, Inc.
, select only the symbol you just pasted:
287
Chapter 11: GDI Tools
Borland C++ Builder Programming
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
288
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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.1 Introduction 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. Copyright © 2003 FunctionX, Inc.
289
Chapter 11: GDI Tools
Borland C++ Builder Programming
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 290
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 11: GDI Tools
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 #include #include #include 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 of the existing panel on the form
and draw a rectangle on the right side
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");
Copyright © 2003 FunctionX, Inc.
291
Chapter 11: GDI Tools
Borland C++ Builder Programming
Screen->Cursors[WriteItDown] = 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
292
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
Chapter 12: Bitmaps 12.1 Bitmaps Fundamentals 12.1.1 Introduction 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. That’s how it is used on some of the installation wizard boxes such as the graphic on the left section of the WordPerfect 2002 installer:
Copyright © 2003 FunctionX, Inc.
293
Borland C++ Builder Programming
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 lowlevel 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. 294
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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:
Copyright © 2003 FunctionX, Inc.
295
Borland C++ Builder Programming
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:
296
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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.1 Introduction 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
Copyright © 2003 FunctionX, Inc.
297
Borland C++ Builder Programming
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;
298
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
} } //---------------------------------------------------------------------------
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:
Copyright © 2003 FunctionX, Inc.
299
Borland C++ Builder Programming
If the file does not exist when you try accessing it, in other words, if the file or the path you specified is not valid (the file and the path are not checked at compilation time, they are checked when the application is asked to retrieve the bitmap), you would receive an error:
As you can see, the exception thrown is of type EFOpenError (which stands for Exception-File-Open-Error), meaning the information given about opening the file is incorrect somewhere. You can display our own message as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { Graphics::TBitmap *BmpMercedes = new Graphics::TBitmap; try { try { BmpMercedes->LoadFromFile("Mercedes.bmp"); } catch(EFOpenError *Error) { ShowMessage(Error->Message + ".\nThe file path, its name, or its extension” "may be invalid or they don't exist."); } } __finally { delete BmpMercedes; }
} //---------------------------------------------------------------------------
300
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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 application’s 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 #pragma hdrstop #include "Exercise.h" #include "resource.h" //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
301
Borland C++ Builder Programming
#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { Graphics::TBitmap *BmpFilles = new Graphics::TBitmap; try { try { BmpFilles->LoadFromResourceName((int)HInstance, "DEUXFILLES"); Canvas->Draw(0, 0, BmpFilles); } catch(EResNotFound *Error) { ShowMessage(Error->Message + ".\nThe resource file was not found or its " "name is incorrect."); } catch(...) { ShowMessage("The picture cannot be displayed..."); } } __finally { delete BmpFilles; } } //---------------------------------------------------------------------------
302
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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 form’s 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 Copyright © 2003 FunctionX, Inc.
303
Borland C++ Builder Programming
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
304
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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 #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; }
Copyright © 2003 FunctionX, Inc.
305
Borland C++ Builder Programming
} //---------------------------------------------------------------------------
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
306
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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; } }
Copyright © 2003 FunctionX, Inc.
307
Borland C++ Builder Programming
//---------------------------------------------------------------------------
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
308
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:
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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: //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
309
Borland C++ Builder Programming
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.1 Introduction 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.
310
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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.1 Overview 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 zerobased, 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 Copyright © 2003 FunctionX, Inc.
311
Borland C++ Builder Programming
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 312
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
bitmaps that will be used by another control, you can use a friendly dialog box that from allows you to create the list. To do this, you would click the ImageList button the Win32 tab of the Component Palette and add it to your form or other container. To make up the list, you can double-click the icon on the form. This would open the ImageList Editor dialog box where you can easily locate and select the necessary bitmaps or icons. In future lessons, we will use it. If you are planning to use images that are larger than 32x32 pixels, you should create the control programmatically. To do this, declare a pointer to TImageList. Here is an example: //--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components private: TImageList *ImgList; // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); }; //---------------------------------------------------------------------------
Before implementing it, use the new operator on its constructor to indicate its owner. This could be done as follows: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { ImgList = new TImageList(this); } //---------------------------------------------------------------------------
If you plan to use more than one image list, you can then declare either various TImageList variables (or an array of TImageList variables). An example would be: //--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components private: TImageList *SingleImage; TImageList *MultiImages; // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { SingleImage = new TImageList(this); MultiImages = new TImageList(this); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
313
Borland C++ Builder Programming
Like other graphics controls, an image list has dimensions. The width of the image list is the width of each one of its pictures. Remember that all pictures must have the same width. The width is set or controlled by the Width property. This would be specified as follows: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { SingleImage = new TImageList(this); Graphics::TBitmap *SingleBitmap = new Graphics::TBitmap; SingleImage->Width = 225; MultiImages = new TImageList(this); Graphics::TBitmap *MultiBitmaps[4]; MultiBitmaps[0] = new Graphics::TBitmap; MultiImages->Width = MultiBitmaps[0]->Width; MultiBitmaps[1] = new Graphics::TBitmap; MultiImages->Width = MultiBitmaps[1]->Width; } //---------------------------------------------------------------------------
The height of an image list is the normal height of each of its pictures. It is set or controlled by the Height property. There are two kinds of bitmaps or icons that can be used on an image list: masked or nonmasked. A nonmasked image list is made of pictures that each is represented as its own entity. Here is an example:
A masked image list is made of pictures so that each is either doubled or uses two sets of colors. Each picture can be provided in two versions and both versions would represent the same illustration. The first picture is in color and the second would be monochrome. Here is an example:
To indicate that an image list is masked, set the Masked property to true. After specifying that the image list will use the masked attribute, use the ImageType property to indicate whether the picture is doubled or will use two sets of colors. The possible values of this property derive from the TImageType enumerator whose members are: Value itImage itMask
314
Description The picture is single The picture uses a mask
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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:
Copyright © 2003 FunctionX, Inc.
315
Borland C++ Builder Programming
//--------------------------------------------------------------------------__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); ... } //---------------------------------------------------------------------------
To retrieve the bitmap stored at a specific position in the list, call the GetBitmap() method. Its syntax is: void __fastcall GetBitmap(int Index, Graphics::TBitmap* Image);
Before calling this method, you should declare a pointer to TBitmap and pass it as the second argument. The Index value indicates the index of the bitmap in the list. If the bitmap exists, it is returned as the Image parameter. Here are examples (and here is the complete source file): //--------------------------------------------------------------------------#include #pragma hdrstop #include "Main.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Graphics::TBitmap *SingleBitmap = new Graphics::TBitmap; SingleBitmap->LoadFromFile("Picture5.bmp"); SingleImage = new TImageList(this); SingleImage->Width = 225; SingleImage->Height = 175; SingleImage->Masked = True; SingleImage->ImageType = itImage; SingleImage->BkColor = clBlack; SingleImage->AddMasked(SingleBitmap, clBlack); MultiImages = new TImageList(this); MultiImages->Masked = False; Graphics::TBitmap *MultiBitmaps[4];
316
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
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 <= SingleImage->Count ) { SingleImage->GetBitmap(ImgCounter++, Bmp); Image1->Picture->Bitmap = Bmp; } else ImgCounter = 0;
} //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Image1Click(Sender); Image2Click(Sender); } //--------------------------------------------------------------------------void __fastcall TForm1::Image2Click(TObject *Sender) { static int ImgCounter = 0; Graphics::TBitmap *Bmp = new Graphics::TBitmap; if( ImgCounter < MultiImages->Count ) { MultiImages->GetBitmap(ImgCounter++, Bmp); Image2->Picture->Bitmap = Bmp;
Copyright © 2003 FunctionX, Inc.
317
Borland C++ Builder Programming
} else ImgCounter = 0;
} //---------------------------------------------------------------------------
To add an icon to an image list, call the AddIcon() method. Its syntax is: int __fastcall AddIcon(Graphics::TIcon* Image);
The Image parameter is the icon that needs to be added. Each of these methods (Add() and AddIcon()) returns the index of the bitmap or icon that was added if the method succeeded. If it fails, it returns –1 indicating that the icon or bitmap was not added. Once the bitmaps or icons have been added, if you want to find out how many images are in the list, get the TImageList::Count property. If you want to remove a picture from the image list, call the TImageList::Delete() method. Its syntax: void __fastcall Delete(int Index);
The Index value is the index of the picture to be removed. Instead of removing a picture, you can just replace it with another picture. This is done using the TImageList::Replace() method whose syntaxes are: void __fastcall Replace(int Index, Graphics::TBitmap* Image, Graphics::TBitmap* Mask);
The Index value specifies the index bitmap to replace. The Image parameter is the new bitmap. If the bitmap is masked, the second parameter, Mask, specifies what bitmap will serve as mask. If you want to replace an icon, call the ReplaceIcon() method. Once an image list is ready, you can use it directly in an application or make it available to a control that can use it. One way you can use an image list is to display one or more of
318
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 12: Bitmaps
its pictures on a form. To do this, you would call the TImageList::Draw() method. It comes in two syntaxes as follows: void __fastcall Draw(Graphics::TCanvas* Canvas, int X, int Y, int Index, bool Enabled = true); void __fastcall Draw(Graphics::TCanvas* Canvas, int X, int Y, int Index, TDrawingStyle ADrawingStyle, TImageType AImageType, bool Enabled = true);
The Draw() method is used to draw one of the images on a device context. The Canvas parameter specifies the device on which the bitmap or icon will be drawn. The X and the Y values are the point location where the drawing would start. That will be the top-left corner of the displaying bitmap or icon. The Index parameter specifies the index of the picture to be drawn, from the list of images. The list is zero-based, meaning the first image has an index of 0, the second is 1, etc. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Image2Click(TObject *Sender) { static int ImgCounter = 0; Graphics::TBitmap *Bmp = new Graphics::TBitmap; if( ImgCounter < MultiImages->Count ) { MultiImages->GetBitmap(ImgCounter, Bmp); // Image2->Picture->Bitmap = Bmp; MultiImages->Draw(this->Canvas, Image2->Left, Image2->Top, ImgCounter); ImgCounter++; } else ImgCounter = 0;
} //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
319
Borland C++ Builder Programming
PART IV Windows Controls Finally arrived, this is the section that performs a thorough study of Windows controls featured in the Visual Component Library (VCL).
320
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
Chapter 13: Characteristics of Child Controls 13.1 Control Creation 13.1.1 Introduction The Win32 library defines a window as a rectangle object that displays on the screen. In order to give presence to such an object, its window must be explicitly created. There are two main categories of windows you will need to create for your application: parents and children:
Owner and Parent: a window is referred to as a parent when there are, or there can be, other windows that depend on it. The Standard toolbar of C++ Builder is an example of a parent window. It is equipped with buttons and acts as their parent. When a parent is created, it "gives life" to other windows that can depend on it. The VCL provides various types of parent controls. When a parent is destroyed, it also destroys its children. An owner is a control that “owns” another control. The owner must be a parent type. An owner "carries", "holds", or hosts the controls that it owns. When an owner is created, made active, or made visible, it gives existence and visibility to its controls. When an owner gets hidden, it also hides its controls. If an owner moves, it moves with its controls. The controls keep their positions and dimensions inside the owner.
Child: A window is referred to as child when its existence and its visibility depend on another window called its parent or owner. All of the Windows controls you will use in your applications are child controls and they must be parented or owned by another control. A child window can be a parent of another control. For example, the Standard toolbar of C++ Builder is the parent of the buttons on it. If you close or hide the toolbar, its children disappear. At the same time, the toolbar is a child of the application's frame. If you close the application, the toolbar disappears, along with its own children. In this example, the toolbar is a child of the frame but is a parent to its buttons.
13.1.2 Techniques of Creating Controls: Win32 As seen in the previous lesson, there are two main techniques of making a control available to an application: The control can be visually added from the Component Palette or it can be programmatically created. To create a control using the Win32 approach, you can call either the CreateWindow() or the CreateWindowEx() function.
Copyright © 2003 FunctionX, Inc.
321
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
13.1.3 Techniques of Creating Controls: VCL If for any reason you cannot or would not visually add a control at design time, you can create it programmatically. There are various techniques you can use. You can first decide what class will be used as the basis of your control. Once you have the object you want to use, declare a pointer to its class using the new operator. When dynamically creating a control, you must specify its owner. The owner will be responsible hosting or carrying the child control. The name of the owner is passed to the constructor of the control. This would appear as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TSomeControl *ctlShowMe = new TSomeControl (Mom); } //---------------------------------------------------------------------------
After specifying the owner, you must also specify the parent of the control. The parent is responsible for destroying the child control when it (the parent) is destroyed. To specify the parent of a control, call its Parent property and assign it the name of the parent. Normally, the name of the parent is the same as the owner. This would be done as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TSomeControl * ctlShowMe = new TSomeControl (Mom); ctlShowMe->Parent = Mom; } //---------------------------------------------------------------------------
If you declare and completely create a control in an event or a method, the control would be available only in that member function and cannot be accessed outside. To make such a control available to other events or controls owned by the same parent, you should declare it in the private section of its parent. Here is an example: //--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components private: TSomeControl * ctlShowMe; // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); }; //---------------------------------------------------------------------------
After declaring the control, you can sepecify its parent and owner in the constructor of the form as follows: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { ctlShowMe = new TSomeControl(Mom); ctlShowMe->Parent = Mom; }
322
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
//---------------------------------------------------------------------------
13.1.4 Techniques of Creating Controls: Class Derivation If you are using a VCL control, you must know the name of the class on which the control is based (and each control is based on a particular class). If you have examined the types of classes available but none implements the behavior you need, you can first locate one that is close to the behavior you need, then use it as a base to derive a new class. To derive a class from an existing VCL control, you must at least declare a constructor that will specify the owner of the control. An example would be: //--------------------------------------------------------------------------class TPlatform : public TPanel { public: __fastcall TPlatform(TComponent *Owner); }; //---------------------------------------------------------------------------
Besides the constructor, in your class, add the properties, methods, and/or events as you see fit. When implementing the constructor, specify the owner as the base class. You can also use it to globally set a value for a property of the base class. Here is an example: //--------------------------------------------------------------------------__fastcall TPlatform::TPlatform(TComponent *Owner) : TPanel(Owner) { Color = clBlue; } //---------------------------------------------------------------------------
Once the control is ready, you can dynamically create it like any other VCL control. If you want to use a fresh class based on a Win32 control, you can create your own class and either derive it from an existing Win32 object or overloaded the Win32 class that would be used as base (for example, you can overload the HWND handle).
13.2 Characteristics and Properties of Controls 13.2.1 Introduction There are three main categories of controls we will use in this book: existing VCL controls, existing Win32 controls, and custom controls that are not inherently available from the other categories. We will always prefer VCL controls because that is the subject of this book. If a control cannot do what we want, which will hardly happen, then we can use one of the Win32’s. In extreme cases, we may have to create a new control. Once again, to create a Win32 control, you can use either the CreateWindow() of the CreateWindowEx() function. They are defined as follows: HWND CreateWindow( LPCTSTR lpClassName,
Copyright © 2003 FunctionX, Inc.
HWND CreateWindowEx( DWORD dwExStyle,
323
Chapter 13: Characteristics of Child Controls
LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam );
Borland C++ Builder Programming
LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam );
13.2.2 The Control’s Handle A handle is a numeric value used internally by Microsoft Windows to identify a control or a class. Every control must have a handle. You have a choice of using it or not but the compiler uses it. As you can see, the Win32 library defines a window as a handle to a window object, known as HWND. After the window has been created, the CreateWindow() or the CreateWindowEx() function returns an HWND variable. You can use this return value later on to access the created window, for example to change some of its characteristics. To access the handle the handle to a control, when creating it, make sure you return its HWND value. This would be done as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { HWND hWndButton = CreateWindow( . . . ); } //---------------------------------------------------------------------------
Once you have the return value of a control, you can call that value any way you see fit in your program. When creating a VCL application, you will mostly use the properties, methods, and events defined by that library. Because the VCL implements Windows controls through a class called TWinControl, it makes sure it provides you with an HWND handle that allows you to directly access the properties or messages of the control as defined in the Win32 library. The handle to a Windows control is (simply) called Handle.
13.2.3 Control's Names To create a control, the primary piece of information you must provide is its name. This allows you and the compiler to know what control you are referring to when the program is running. Specifying the name of a control may depend on the technique you decide to use to create the control. After adding a control to a form, it automatically receives a name. In the Object Inspector, the control’s name displays in the Name field. The newly added control reflects the name of its button on the Component Palette and adds an incremental number. For example, if you click the Edit button on the Component Palette and click the form, the control would be named Edit1. If you add a second Edit control, it would be 324
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
named Edit2. This causes the default names to be incremental. Since a program usually consists of many controls, it is usually a good idea to rename your controls and give them meaningful and friendlier names. The name should help you identify what the button is used for. To change the name of a control, on the Object Inspector, click the word Name and type a valid C++ name (following the rules of C++ identifiers). If you are using the Win32’s CreateWindow() or CreateWindowEx() function, you can either use one of the existing names of controls or you are create a control that a custom name. The Win32 library provides already defined names of classes you can use to create a control. The Standard controls use the following class names: Class Name STATIC EDIT RichEdit
RICHEDIT_CLASS
LISTBOX COMBOBOX
SCROLLBAR
BUTTON MDICLIENT
Description A static control can be used to display text, a drawing, or a picture As its name suggests, an edit control is used to display text to the user, to request text from the user, or both Like an edit box, a rich edit control displays text, requests it, or does both. Besides all the capabilities of the edit control, this control can display formatted text with characters or words that use different colors or weight. The paragraphs can also be individually aligned. The RichEdit class name is used to create a rich edit control using the features of Release 1.0 of its class This control is used for the same reasons as the RichEdit class name except that it provides a few more text and paragraph formatting features based on Release 2.0 A list box is a control that displays items, such as text, arranged so each item, displays on its own line A combo box is a combination of an edit control and a list box. It holds a list of items so the current selection displays in the edit box part of the control A scroll bar is a rectangular object equipped with a bar terminated by an arrow at each end. It is used to navigate left and right or up and down on a document A button is an object that the user clicks to initiate an action This class name is used to create a child window for an MDI application
Microsoft Windows added a few more class names for objects referred to as Common controls. Fortunately, whether you want to use standard or common controls, the VCL provide friendlier implementations of all of them with even more Windows control than the Win32 provides. To use one of the Win32 classes, pass its name as string to the lpszClassName argument of the CreateWindow() or the CreateWindowEx() functions. Here is an example that would start an edit box: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { CreateWindow("EDIT", ); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
325
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
Whether you are using the VCL or the Win32 libraries, although you can change the name of a control at runtime, refrain from doing it.
13.2.4 Controls Text and Caption Some controls, as we will see, are text-based, meaning they are meant to display or sometimes request text from the user. For such controls, this text is referred to as caption while it is simply called text for some other controls. This property is not available for all controls. If a control displays text, it then has a Caption or a Text field in the Object Inspector. After adding such a control to a form, its Caption or its Text field would hold the same text as its name. At design time, to change the text of the control, click either its Caption or its Text field and type the desired value. For most controls, there are no strict rules to follow for this text. Therefore, it is your responsibility to type the right value. The text provided in a Caption or Text fields of a text-based control can only be set “as is” during design. If you want the text to change while the application is running, you can format it. For example, such a control can display the current time or the name of the user who is logged in. These format attributes cannot be set at deign time. To change the text of a text-based control at run time, either assign a simple string or provide a formatted string to either the Caption or the Text property. Here are two examples: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Panel1->Caption = "Let it Go"; Edit1->Text = Time(); } //---------------------------------------------------------------------------
As we will see when studying messages and events, you can also change the text of a control using the SendMessage() function with the message set as WM_SETTEXT. If you are using the Win32 approach, the text of a text-based control is passed as the lpWindowName argument of the CreateWindow() or the CreateWindowEx() functions: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { CreateWindow("Button", "Submit", ); } //---------------------------------------------------------------------------
13.2.5 Controls Hints and Tool Tips A tool tip is a small yellowish box that displays on a control when the user rests the mouse on such an object for a few seconds. A tool tip is a form of help that allows the user to get a quick and brief characteristic about the control. In VCL applications, a tool tip is called a Hint. Hints in VCL applications can be applied on different levels, on all controls or on just some of them. A hint can be made of just the text that displays when the user rests the
326
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
mouse on a control. A hint can also be made of two parts: the text that displays as a tool tip on the control and a little longer text on the status bar. Every visual control that displays when a form is running has a property called Hint. This property can hold text that would display as the control's tool tip. To display a hint on a control, the first thing you should do is to set the ShowHint property of the control to true. The ShowHint property controls whether its control would display a hint or not. Once the ShowHint property has a true value, you can specify the necessary text on the Hint property. Here is an example:
As seen on this picture, the background color of hint appears yellow. If you want a different color, assign a TColor value of your choice to the global TApplication::HintColor variable. When doing this, remember to select a color that would still make the text readable. Here is an example: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Application->HintColor = clAqua; } //---------------------------------------------------------------------------
The hint displays its word or sentence using a default global font set on the operating system. If you want to choose the font that should be used when displaying the hints, call the TScreen::HintFont property and define the font of your choice. Like a TApplication object, a TScreen variable is already available when you create a form. Here is an example: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
Copyright © 2003 FunctionX, Inc.
327
Chapter 13: Characteristics of Child Controls
{
Borland C++ Builder Programming
Screen->HintFont->Name = "Times New Roman"; Screen->HintFont->Size = 14;
} //---------------------------------------------------------------------------
The Hint can also hold two strings: one that would display both as a tool tip and one that would display on the status bar. You can create text on the Hint property using the following syntax: The Hint | Status Bar Text
This string is made of two actual strings separated by a bar. The string on the left side of the bar is the text that would display on the control when the mouse rests on it for a few seconds. The bar and the second string are optional. If you omit the bar and the right string, the unique string would be used as the hint and the status bar text. The ShowHint property of each control is used to manage hints for that control only. If you use this approach, you would have to set the ShowHint of each control, whose hint you want to provide, to true. If you plan to use hints on many, most, or all controls of a form, the parent form provides a property. If you set the ShowHint property of the form to true, the ShowHint property of all controls on the form would be automatically set to true. In that case, you would not have to set the ShowHint property for each control. In fact, if you plan to use hints for various controls of your form, set the ShowHint of the form to true, unless you have a good reason why you do not need to. The hints of a VCL application are globally controlled by the Hint member variable of the TApplication class. Whenever you create an application, a variable called Application is declared and can take care of such assignments that pertain to the application as a whole. To actually show hints on your controls, you must define a global function that is passed a TObject argument. The name of the function is not important. In the body of the function, assign the Hint member of the global Application variable to the desired panel of your status bar. After defining this function, in the OnCreate event of the form, assign the name of the previously defined function to the OnHint member variable of the global Application variable.
13.2.6 Controls Styles: Childhood A style is a characteristic that defines the appearance, and can set the behavior, of a control. The styles are varied from one control to another although they share some of the characteristics common to most Windows controls. All of the controls you will create need to be hosted by another control. During design, once you position a control on a form, it automatically gets the status of child. If you are 328
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
programmatically creating a VCL control, to specify that it is a child, we saw that you must pass the name of its owner to the control’s pointer and you must assign the name of its parent to the Parent property. If you are creating the control using the CreateWindow() or the CreateWindowEx() function, to specify that the control is a child, add the WS_CHILD flag to the dwStyle argument. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { CreateWindow("Button", "Submit", WS_CHILD, ); } //---------------------------------------------------------------------------
If you have a good reason to do so, you can change the parent of a child control at run time. To do that, simply assign the name of a different control to the object whose parent you want to change. The designated parent must be a type of control that can be a parent since some controls can act as a parent and some cannot. Alternatively, to change the parent of a control, you can call the SetParent() function. Its syntax is: HWND SetParent(HWND hWndChild, HWND hWndNewParent);
The hWndChild argument must be a handle to the control whose parent you want to change. The hWndNewParent argument must be the handle of the new parent. If the control has already been created and you want to know “who” its parent is, you can assign its Parent property to a TWinControl variable. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TWinControl *Poppy = Button2->Parent; if( Poppy == this ) ShowMessage("The form is your daddy");
} //---------------------------------------------------------------------------
Alternatively, to find out what object acts as a control’s parent, call the Win32 API’s GetParent() function. Its syntax is: HWND GetParent(HWND hWnd);
The hWnd argument must be a handle to the control whose parent you want to find out. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { HWND Poppy = GetParent(Button2->Handle); if( Poppy == this->Handle ) ShowMessage("The form is your daddy"); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
329
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
13.2.7 Controls Styles: Visibility A control is referred to as visible if it can be visually located on the screen. A user can use a control only if he or she can see it. As a programmer, you have the role of deciding whether a control must be seen or not and when. At design time, when you add a control to a parent, it is visible by default. This is because its Visible property is set to true in the Object Inspector. In the same way, if you programmatically create a control, by default, it is made visible once you specify its owner and parent as the (main) form. If you do not want a control to primarily appear when the form comes up, you can either set its Visible property to false or set its parent’s visible property to false. Equivalently, at run time, to hide a control, assign a false value to either its Visible property or its parent’s Visible property. Remember that, as stated already, when a parent gets hidden, it also hides its children. On the other hand, a parent can be made visible but hide one or some of its children. To check whether a control is visible at one time, apply a conditional statement (if or while) to its Visible property and check whether its value is true or false. Alternatively, to check whether a control is visible or not, check its Showing state. If the control is visible, its Showing value would be true. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { if( Panel1->Showing ) Panel1->Visible = False; } //---------------------------------------------------------------------------
While the Visible property can be read or changed, Showing is a read-only property. This means that you can assign a true or false value to Visible but you can only check the state of Showing; you cannot change it. The following code will not compile: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { // This will not work because you cannot change Showing if( Panel1->Showing ) Panel1->Showing = False; } //---------------------------------------------------------------------------
A useful feature of the C++ language comes in toggling the value of a Boolean variable. This can be applied to the appearance and disappearance of a control. You can use this to reverse the state of a control’s Boolean property. This will come very handy when using menus and toolbars, etc (imagine how easy it is to hide or display a control or a menu item with one line of code, no pointer, no dynamic creation or declaration of any class). The following example toggles the appearance of a form by checking whether the control (in this case the second form) is displaying or not. If the control (the second form) is visible, it gets hidden and vice-versa: //--------------------------------------------------------------------------void __fastcall TForm1::ToggleControlAppearance() {
330
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
Form2->Visible = !Form2->Visible; } //---------------------------------------------------------------------------
To made a control visible when using either the CreateWindow() or the CreateWindowEx() functions, add the WS_VISIBLE style to its dwStyle argument. Some controls cannot be seen by the user. Examples are object from the Dialogs tab of the Component Palette and some other controls such as the image list. These controls do not have a Visible field in the Object Inspector.
13.2.8 Controls Styles: Availability For the user to use a control, it must allow it. For example, if a control is supposed to receive text, the user can enter characters in it only if this is made possible. To make a control available to the user, the object must be enabled. By default, after adding a control to a form, it is enabled and its Enabled property in the Object Inspector is set to true. An enabled control displays its text or other characteristics in their normal settings. If you want to disable a control, set its Enabled property to false. In the following picture, the two top controls (the edit box and the button) are disabled:
To find out whether a control is enabled or not, check its Enabled property state. By default, a newly created control using the CreateWindow() or the CreateWindowEx() functions is enabled. If you want to disable it, add the WS_DISABLED style to its dwStyle argument.
13.2.9 Tab Ordering As we saw with control design, a user can navigate through controls using the Tab key. When that key has been pressed, the focus moves from one control to the next. By their designs, not all controls can receive focus and not all controls can participate in tab navigation. Even controls that can receive focus must be explicitly included in the tab sequence. At design time, the participation to tab sequencing is controlled by the TabStop property on the Object Inspector. Fortunately, every VCL control that can receive focus is already configured to have this Boolean property set to true. If you want to remove a control from this sequence, set its TabStop value to false. If a control has the TabOrder property set to true, to arrange the navigation order of controls, we saw that you can use the Edit Tab Order dialog box: Copyright © 2003 FunctionX, Inc.
331
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
Figure 9: Dialog Boxes - Edit Tab Order Alternatively, at design time, you can click a control on the form. Then, on the object Inspector, change the value of its TabOrder field. The value must be a positive short integer. If you are creating the control using either the CreateWindow() or the CreateWindowEx() functions, to include it in a tab sequence, add the WS_TABSTOP style.
13.2.10
Controls Location The controls added to a parent window are confined to the area of the body offered by that window. After adding it to a window, the control is positioned in the body of the parent using a Cartesian coordinate system whose origin is located on the top-left corner of the parent window. The horizontal measurements move from the origin to the right. The vertical measurements move from the origin to the bottom. The distance from the control’s left border to the parent’s left border is called the Left property. The distance from the control’s top border to the parent’s top border is called the Top property. The Left and Top values are known as the control’s location. This can be illustrated as follows:
332
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
Figure 10: The location of a control When you click a control on the Component Palette and click its parent window, the Left and Top values are set where the mouse landed. To change the location of a control, as we saw in the previous lesson, you can click and drag it to the new desired location. Alternatively, you can type the desired value in either the Left or the Top fields on the Object Inspector. At design time, if you drag a control to move it, C++ Builder updates the values of its location. If you save the project, the compiler would also save the locations of all objects, including the form, so that the next time you run the application, the compiler would remember to display the form where it was last positioned at design time. To programmatically move a control, which is equivalent to changing the values of the Left or the Top properties at run time, assign the desired respective values. If you set a negative value for the Left field, the left border of the control would be hidden. In the same way, a negative Top value would hide the top border of the control. Make sure you use valid integer values; otherwise you would receive an error when you compile the project. If you are using the CreateWindow() or the CreateWindowEx() functions, to set the left distance, pass the desired value as the X argument. On the other hand, to specify the top distance, pass the desired value for the Y argument.
13.2.11
Controls Dimensions The distance from the left border to the right border of a control is referred to as its Width property. In the same way, the distance from the top to the bottom borders of a control is its Height value. This can be illustrated as follows:
Copyright © 2003 FunctionX, Inc.
333
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
Figure 11: The location and dimension of a control If you click a control’s button on the Component Palette and click its parent window, the control assumes some default dimensions in the body of the parent. As we saw in the previous lesson, to change the dimensions of a control, you can drag one of its borders or corners. Alternatively, on the Object Inspector, you can type the desired values in either or both the Height and the Width fields. If the control has already been created, you can resize it at run time. To change the dimensions programmatically, simply assign the desired value to the Height and/or Width property of your choice. Here is an example that changes the width of the form: //--------------------------------------------------------------------------void __fastcall TForm1::ChangingFormWidth() { Width = 248; } //---------------------------------------------------------------------------
If the value you give to a property is not allowed, the program would throw an error. If you are creating the control using the CreateWindow() or the CreateWindowEx() functions, specify the value for the distance from the left border of the parent window to the left border of the control as the nWidth argument. In the same way, pass the desired value for the distance from the top border of the parent to the top border of the control as the nHeight argument. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
334
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
{
Chapter 13: Characteristics of Child Controls
HWND hWndButton = CreateWindow("Button", "Submit", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 10, 10, 124, 25, );
} //---------------------------------------------------------------------------
If you specify negative values for the left and top distances, either the left or the top borders, respectively, will be hidden.
13.2.12
The Bounding Rectangle of a Control Because a control’s location can be identified with two values, it can also be illustrated as a geometric point on the coordinate system of the screen. A point is a pixel on the monitor screen, on a form, or on any object of your application. A point is represented by its coordinates with regard to the object that "owns" the point:
Figure 12: The Windows coordinate system To identify the concept of a point, the Win32 library provides the POINT structure. This structure is defined as follows: typedef struct tagPOINT { LONG x; LONG y; } POINT, *PPOINT;
The first member, x, represents the horizontal distance of the point from the top-left corner of the object that owns the point. The y member represents the vertical measurement of the point with regards to the top-left corner of the object that owns the point. Besides the Win32's POINT structure, the VCL provides a class that performs the same operations with a more appropriate conception of a C++ class. It is called TPoint. While Win32 is written in C and most of its objects are pure structures in the C sense, the VCL's TPoint is a true and complete C++ class. It is defined as follows: struct TPoint { TPoint() {} TPoint(int _x, int _y) : x(_x), y(_y) {} TPoint(POINT& pt)
Copyright © 2003 FunctionX, Inc.
335
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
{ x = pt.x; y = pt.y; } operator POINT() const { POINT pt; pt.x = x; pt.y = y; return pt; } int x; int y; };
As you can see, you can define or retrieve the position of a TPoint variable using its x and y values. Because it has a constructor, you can also declare it as a C++ variable. To represent the size of an object, the Win32 library provides the SIZE structure defined as follows: typedef struct tagSIZE { LONG cx; LONG cy; } SIZE, *PSIZE;
The cx member variable is usually used as the distance from the left to the right borders of a control. The cy member variable usually represents the distance from the top to the bottom borders of the control. Besides the Win32’s SIZE, you can also use the VCL’s TSize structure defined as follows: struct tagSIZE { long cx; long cy; } typedef tagSIZE TSize;
The member variables of the TSize structure represent the width as cx and the height as cy of an object. As seen so far, a control is a rectangular object that can be located, and whose dimensions are visible, on the screen. A rectangle is a geometric figure that has four sides (for this reason, it is called a quadrilateral figure). It is known for its horizontal and its vertical measures. The horizontal measure of a rectangle is referred to as its length and sometimes its width. The vertical measure is known as its height. In fact, on a Cartesian coordinate, a rectangle is represented by its four corners (x1, y1, x2, y2) as illustrated on the left figure below:
336
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
Figure 13: Windows representation of a rectangle We saw already that each object of your application, including a form, is visually defined by its location and its dimensions on the screen for a form or on the object that owns it. These two aspects are controlled by a geometric rectangle that defines the location and the dimensions of the object. In Win32, from the above right rectangle, the syntax of the RECT structure is defined as follows: typedef struct _RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT;
The RECT structure can be illustrated as follows:
Copyright © 2003 FunctionX, Inc.
337
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
Figure 14: The RECT Structure Notice that the left and the top are the same as the Left and the Top values of a control but the right and the bottom values are not the width and the height of a control. This is not an anomaly. It was done like that on purpose. You can use the Win32 RECT structure wherever you need to define a rectangle in your VCL applications. Besides the RECT structure, the VCL provides its own class to represent a rectangle. It is called TRect and it is defined as follows: struct TRect { TRect() {} TRect(const TPoint& TL, const TPoint& BR) { left=TL.x; top=TL.y; right=BR.x; bottom=BR.y; } TRect(int l, int t, int r, int b) { left=l; top=t; right=r; bottom=b; } TRect(RECT& r) { left = r.left; top = r.top; right = r.right; bottom = r.bottom; } int Width () const { return right - left; } int Height() const { return bottom - top ; } bool operator ==(const TRect& rc) const {
338
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
return left == rc.left && top==rc.top && right == rc.right && bottom==rc.bottom;
} bool operator !=(const TRect& rc) const { return !(rc==*this); } __property LONG Left = { read=left, write=left }; __property LONG Top = { read=top, write=top }; __property LONG Right = { read=right, write=right }; __property LONG Bottom = { read=bottom, write=bottom }; };
The TRect class can be illustrated as follows:
Figure 15: The TRect class It is important to notice that, to conform to the RECT structure, the TRect class provides two constructors: TRect(int l, int t, int, r, int b) and TRect(RECT& r). The first is used to simply reproduce the member variables of the RECT structure and the second is used to create a copy of RECT. Once these two constructors have been defined making the RECT member variables available, the class declares two new methods. The Width() member function returns the distance from the left to the right borders of the rectangle. The Height() method performs the equivalent calculation vertically. One of the ways you can use the TRect class consists of declaring a rectangle using its default constructor and then specifying a value for each on of its members. Here is an example: Copyright © 2003 FunctionX, Inc.
339
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { TRect Recto = TRect; Recto.Top = 100; Recto.Left = 120; Recto.Right = 420; Recto.Bottom = 288; } //---------------------------------------------------------------------------
If you do not know or cannot get the right and the bottom measures of the rectangle but know its width and its height, you can still use them to declare and initialize a rectangle. The TRect class provides one more alternative you can use to declare a rectangle. Suppose you have the coordinates of the top-left and the bottom-right borders of the rectangle, you can declare and initialize it. The constructor you would use is: TRect(TPoint TopLeft, TPoint BottomRight);
Using the concept of a screen defined rectangle, a control is included in a rectangle known as the BoundsRect property. Therefore, at any time, to get the location and the dimensions of a control, call its BoundsRect property, which produces a TRect value. Here is the final code of the Win32 control we were creating: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { CreateWindow("Button", "Submit", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 10, 10, 124, 25, Handle, NULL, HInstance, NULL); } //---------------------------------------------------------------------------
Alternatively, to get the location and dimension of a control, you can call the GetWindowRect() function. Its syntax is: BOOL GetWindowRect(HWND hWnd, LPRECT lpRect);
This function returns a rectangle that holds the location and the dimensions of the control as, lpRect.
13.3 Controls Methods 13.3.1 Overview of Methods As in C++, a method is a function created as a member of a class. Methods are used to access or manipulate the characteristics of an object, a variable, or a pointer to a class. There are mainly two categories of methods you will use in your classes:
340
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
•
If you are using an existing class such as one of those that control the objects from the Component Palette, you can call any of its public methods. The requirements of such a method depend on the class being used
•
If none of the existing methods can perform your desired task, you can add a method to a class. There are two main options available to you: o
If you are using a class that is being derived, this will always be the case of designed forms, you can add a method to it by declaring it in the header file and implementing it in the source file
o
If you are using a control from the Component Palette but find out that, by design, the control lacks a method you need, you can create your own class derived from that control. In the header of the new class, declare the method and implement it in the source class. Then, in the header class of the form, declare a pointer to your new class.
The methods of a class are available only at run time. Except for the constructor, to access a method, first make sure you have the variable or pointer. Then use either the period operator for a variable or the arrow operator for a pointer to access the desired method. The most fundamental and the most common method of a class is its constructor. As all Windows controls of the VCL are based each on a particular class, each one of them has at least one constructor. Dynamically creating a control consists of declaring a pointer to its class using the new operator. If the control is a visual object, you must specify its parent, as we will see. This is done by calling its main constructor. If the class does not represent a visual object, call its default constructor. The second most popular method is the destructor. Based on the rules of RAD programming, you will never need to call the destructor of a control or a class, even if you dynamically create the variable. If you need to destroy an object that was programmatically created, you will use the delete operator.
13.3.2 Window’s Visibility We saw that, in order to use a control, it must be visible. The visibility of a control can be made by setting its Visible property to true. Alternatively, to display a window, you can call the TControl::Show() method. Its syntax is: void __fastcall Show();
If the control is visible, nothing would happen. If it were hidden, then it would be revealed. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Panel1->Show(); } //---------------------------------------------------------------------------
The Show() method internally sets the Visible property to true. Alternatively, to show a window, you can call the ShowWindow() function. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
Copyright © 2003 FunctionX, Inc.
341
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
{
ShowWindow(Panel1->Handle, SW_NORMAL); } //---------------------------------------------------------------------------
On the other hand, we saw that, to hide a control, you can set its Visible property to false. In the same way, to hide a control, you call can the TControl::Hide() method. Its syntax is: void __fastcall Hide();
Keep in mind that hiding a control does not close or destroy it.
13.3.3 Focus The focus is a visual aspect that indicates that a control is ready to receive input from the user. Various controls have different ways of expressing that they have received focus. Buttons controls indicate that they have focus by drawing a dotted rectangle around their caption. Here are examples:
Here is another example of a button-based control whose caption is surrounded with a dotted line.
A text-based control indicates that it has focus by displaying a blinking cursor:
A list-based control indicates that it has focus when one of its items has a surrounding dotted rectangle.
To give focus to a control, the user can click it or press a key. To programmatically give focus to a control, call the TWinControl::SetFocus() method. Its syntax is: void __fastcall SetFocus();
342
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
13.4 Controls Messages and Events 13.4.1 Overview An application is made of various objects or controls. During the lifetime of the application, its controls regularly send messages to the operating system to do something on their behalf. These messages must be processed appropriately. Also, most of the time, more than one application is running on the computer. The controls of such an application also send messages to the operating system. As the operating system is constantly asked to perform these assignments, because there can be so many requests presented unpredictably, the operating system leaves it up to the controls to specify what they want, when they want it, and what behavior or result they expect. A message, like a letter you send to somebody, must provide a few pieces of information in order to be processed. For a Win32 application, these pieces of information are stored in a structure called MSG. It is defined as follows: typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG;
For a Win32 application, each message is processed by a function called a window procedure. A window procedure is a pointer to function, therefore declared as CALLBACK, and it returns a positive 32-bit number. Therefore, a MSG variable must be passed to the window procedure for processing. In a Win32 application, a window procedure that processes a message requires 3 pieces of information with the last piece divided in two (which produces 4 pieces of information):
The first piece of information must state WHO, that is, what control, sent the message
The second argument must specify the name of the message as each Windows message is recognized by a name, which is simply a positive but constant numeric value
The third and the fourth arguments carry information that depends on the message being sent
Here is an example: //--------------------------------------------------------------------------LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { switch(Msg) { case WM_DESTROY: PostQuitMessage(WM_QUIT); break;
Copyright © 2003 FunctionX, Inc.
343
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
default: return DefWindowProc(hWnd, Msg, wParam, lParam); } return 0; } //---------------------------------------------------------------------------
Once again, to make programming a fast process, the VCL provides its own message structure called TMessage and defined as follows: struct TMessage { Cardinal Msg; union { struct {
}; struct {
};
Word WParamLo; Word WParamHi; Word LParamLo; Word LParamHi; Word ResultLo; Word ResultHi;
int WParam; int LParam; int Result;
}; };
A TMessage message must provide two pieces of information, as the TMessage structure shows: the name of the message and one group of information. Unlike the Win32 MSG structure, in a VCL application, the object that sends the message is already known (because it is the one that sent the message). The accompanying items of the message are coded into either the top or the bottom structures. The two anonymous structures inside the union indicate that you use either the top or the bottom structure but not both. Once a control has composed a message, it must send it to the right target, which could be the operating system. In order to send a message, a control must create an event. The control is also said to fire an event. To make a distinction between the two, a message's name will usually be written in uppercase. An example would be WM_MOVE, which stands for Window Message Move. The name of an event usually starts with On, which indicates an action. Remember, the message is what needs to be sent. The event is the action of sending the message.
344
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
13.4.2 A Map of Messages As mentioned already, the messages of a Win32 application are processed by a window procedure. For a VCL application, you can use the same approach. Alternatively, you can use a technique referred to as creating a map of messages. For the compiler to manage messages, they should be listed in a public section of the class definition. The messages are included in a list referred to as a message map. The list of messages, that is, the message map, starts with the BEGIN_MESSAGE_MAP and ends with the END_MESSAGE_MAP macros. The END_MESSAGE_MAP macro takes an argument, which is the original class that holds the primary implementation of the messages. Between the BEGIN_MESSAGE_MAP and the END_MESSAGE_MAP macros, each message is declared using either the MESSAGE_HANDLER or the VCL_MESSAGE_HANDLER macros. Their syntaxes are: MESSAGE_HANDLER(VCLMessageName, TMessage, EventName); VCL_MESSAGE_HANDLER(WindowsMsgName, VCLMessageName, EventName); There are various categories of messages the operating system receives. Some of them come from the keyboard, some from the mouse, and some others from various other sources. For example, some messages are sent by the application itself while some other messages are controlled by the operating system.
13.4.3 Messages Characteristics The most commonly sent messages have already been coded in the objects of the VCL controls so much that you will hardly need to define new messages, at least not in the beginning of your C++ Builder programming adventure. Most of what you will do consists of implementing the desired behavior when a particular message is sent. Therefore, to start, you should know what messages are available, when, and how they work. As mentioned already, each control sends its own messages when necessary. Based on this, some messages are unique to some controls according to their roles. Some other messages are common to various controls, as they tend to provide similar actions. To manage such various configurations, the VCL considers the messages in two broad categories. Those that take an argument and those that do not. The VCL defines events as function pointers (or pointers to function). As it happens, some messages do not require much information to be performed. For example, suppose your heart sends a message to the arm and states, “Raise your hand”. In this case, suppose everything is alright, the arm does not ask, “how do I raise my hand?”. It simply does. This type of message would be sent without any accompanying information. Consider another message where the arm carries some water and says to the mouth, “Swallow the following water”. The mouth would need the water that needs to be swallowed. Therefore, the message must be accompanied by additional information, which is considered an argument. Consider one more message where the heart says to the tongue, “Taste the following food but do not swallow it.” In order to process this message, the tongue would need the food and something to indicate that the food must not be swallowed. In this case, the message must be accompanied by two pieces of information.
Copyright © 2003 FunctionX, Inc.
345
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
To process messages that do not require any additional argument, the VCL creates such an event with the TNotifyEvent type. Such an event is declared as a pointer to function in the classes.hpp library as follows: typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);
The Sender argument is the control that is sending the messages. Besides TNotifyEvent, the other events carry different and appropriate names.
13.4.4 Event Implementation Although there are different ways you can implement an event, there are two main ways you can initiate its coding. If the control has a default event and if you double-click it, the compiler would initiate the default event. Another technique you can use is to click the Events tab of the Object Inspector. This would display a list of the events associated with the selected control: The list is divided in two columns. The name of each event is displayed on the left side. You can click the name of an event to reveal a combo box. If a similar event has already been written, you can click the arrow of the combo box and select it from the list:
Similar events are those that share a behavior. Otherwise, to initiate an event double-click the field on the right column of the name of the desired event.
When an event has been initiated, you would be transported to the Code Editor and the cursor would be positioned in the body of the event, ready to receive your instructions. To customize an event, the compiler divides its structure in three sections: //--------------------------------------------------------------------------void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { // Write code associated with the event here } //---------------------------------------------------------------------------
The coding of an event starts with its return value. Most or all events in C++ Builder return void. All events use the __fastcall convention.
346
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
The return type is followed by the name of the parent class from where the event would be fired. This is mainly the class that controls the form. After the name of the class, the compiler rightly uses the class member access operator (::) to call the event, following a C++ rule. The name of an event is made of a combination of the control that “owns” or fired the event and the name of the event. Each event has at least one argument, the TObject *Sender. Some events use additional arguments that we will review when coding such events.
13.5 Keyboard Messages 13.5.1 Introduction A keyboard is a hardware object attached to the computer. By default, it is used to enter recognizable symbols, letters, and other characters on a control. Each key on the keyboard displays a symbol, a letter, or a combination of those, to give an indication of what the key could be used for. The user typically presses a key, which sends a signal to a program. The signal is analyzed to find its meaning. If the program or control that has focus is equipped to deal with the signal, it may produce the expected result. If the program or control cannot figure out what to do, it ignores the action. Each key has a code that the operating system can recognize. This code is known as the virtual key code and they are as follows: Virtual Key Used for Virtual Key VK_F1 F1 VK_F2 VK_F3 F3 VK_F4 VK_F5 F5 VK_F6 VK_F7 F7 VK_F8 VK_F9 F9 VK_F10 VK_F11 F11 VK_F12 VK_SCROLL Scroll Lock VK_SNAPSHOT VK_PAUSE Pause/Break VK_TAB VK_BACK Backspace VK_CAPITAL VK_SHIFT Shift VK_CONTROL VK_MENU Alt VK_ESCAPE VK_RETURN Enter VK_SPACE VK_INSERT Insert VK_HOME VK_PRIOR Page Up VK_DELETE VK_END End VK_NEXT VK_UP Up Arrow Key VK_RIGHT VK_DOWN Down Arrow Key VK_LEFT VK_LWIN Left Windows Key VK_RWIN VK_APPS Applications Key The following keys apply to the Numeric Keypad Copyright © 2003 FunctionX, Inc.
Used for F2 F4 F6 F8 F10 F12 Prt Scrn (Depends on keyboard) Tab Caps Lock Ctrl Escape Space Bar Home Delete Page Down Right Arrow Key Left Arrow Key Right Windows Key
347
Chapter 13: Characteristics of Child Controls
VK_NUMLOCK VK_NUMPAD0 VK_NUMPAD2 VK_NUMPAD4 VK_NUMPAD6 VK_NUMPAD8 VK_DIVIDE VK_SUBTRACT VK_SEPARATOR
Num Lock 0 2 4 6 8 / -
Borland C++ Builder Programming
VK_NUMPAD1 VK_NUMPAD3 VK_NUMPAD5 VK_NUMPAD7 VK_NUMPAD9 VK_MULTIPLY VK_ADD VK_DECIMAL
1 3 5 7 9 * + .
There are actually more keys than that but the above are the most frequently used. The VCL implements keyboard events using two function pointers, TKeyEvent and TKeyPress that depend on the message.
Practical Learning: Introducing Keyboard Messages 1.
Start a new project with the default form
2.
Save it in a new folder called Keyboard1
3.
Save the Unit as Exercise and save the project as Keyboard
13.5.2 The Key Down Message When a keyboard key is pressed, a message called WMKeyDown is sent. WMKeyDown is a TKeyEvent message that produces the OnKeyDown event. Its syntax is: void __fastcall OnKeyDown(System::TObject* Sender, Word &Key, Classes::TShiftState Shift);
The Key argument specifies the key that was pressed. The OnKeyDown() event can be sent by any key. Alphabetic keys are represented by their character. For example, if the user presses p, the Key argument would be represented as ‘p’. If the user presses a key, the Key argument would have the value of ‘;’. The Shift argument specifies whether the Shift, the Ctrl, or the Alt keys were pressed along with the Key key. It is a Pascal Set whose enumerator has the following members: Value ssShift ssAlt ssCtrl
Description One of the Shift keys was pressed One of the Alt keys was pressed One of the Ctrl keys was pressed
If the user presses two keys as an uppercase letter, such R, the Key argument would have a value of ‘r’. The Shift argument would be used to indicate that the Shift key was down when the letter r was pressed.
Practical Learning: Sending Key Down Messages
348
1.
To experiment with the WMKeyDown message, on the Object Inspector, click the Events tab
2.
Double-click the right field of OnKeyDown and implement the event as follows: Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
//--------------------------------------------------------------------------void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { switch( Key ) { case VK_RETURN: ShowMessage("You pressed Enter"); break; case VK_F1: ShowMessage("Help is not available at the moment"); break; case VK_DELETE: ShowMessage("Can't Delete This"); break; case 'K': // If the user press Ctrl + K if( Shift.Contains(ssCtrl) ) ShowMessage("K was pressed"); break; } } //---------------------------------------------------------------------------
3.
Test the application and return to Bcb
13.5.3 The Key Up Message As opposed to the key down message that is sent when a key is down, the WMKeyUp message is sent when the user releases the key. Like WMKeyDown, WMKeyUp is a TKeyEvent message. It produces the OnKeyUp event whose syntax is: void __fastcall OnKeyUp(System::TObject* Sender, Word &Key, Classes::TShiftState Shift);
The Key argument specifies the key that was pressed. Like the OnKeyDown() event, the OnKeyUp event processes any key. Alphabetic keys are represented by their character. The Shift argument indicates whether the Shift, the Ctrl, or the Alt key participates in a key combination such as Shift + $
13.5.4 The Key Press Message When the user presses a key, the WMKeyPress message is sent. Unlike the other two keyboard messages, the key pressed for this event should (must) be a character key. WMKeyPress produces the OnKeyPress event whose syntax is: void __fastcall OnLKeyPress(System::TObject* Sender, char &Key);
The Key argument must be a letter or a recognizable symbol. Lowercase alphabetic characters, digits, and the lower base characters such as ; , ‘ [ ] - = / are recognized as they are. For an uppercase letter or an upper base symbols, the user must press Shift + the key. The character would be identified as one entity. This means that the symbol % typed with Shift + 5 is considered as one character.
Copyright © 2003 FunctionX, Inc.
349
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
Practical Learning: Sending Key Press Messages 1.
To experiment with the WMKeyPress message, on the Events tab of the Object Inspector, double-click the right field of OnKeyPress
2.
Delete the code in the OnKeyDown event and implement the OnKeyPress event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key) { switch( Key ) { case VK_RETURN: ShowMessage("You pressed Enter"); break; case VK_F1: ShowMessage("Help is not available at the moment"); break; case VK_DELETE: ShowMessage("Can't Delete This"); break; case 'k': ShowMessage("Lowercase k was pressed"); break; case 'K': ShowMessage("Uppercase K was pressed"); break; case '$': ShowMessage("Show me the money"); break; } } //---------------------------------------------------------------------------
350
3.
Test the application and press the Delete or the F1 keys. Notice that nothing happens
4.
Press k
5.
Click OK and press Shift + K
6.
Close the application and return Bcb
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
13.6 Mouse Messages 13.6.1 Introduction The mouse is another object that is attached to the computer allowing the user to interact with the machine. The mouse and the keyboard can each accomplish some tasks that are not normally available on the other and both can accomplish some tasks the same way. The mouse is equipped with two, three, or more buttons. When a mouse has two buttons, one is usually located on the left and the other is located on the right. When a mouse has three buttons, one is in the middle of the other two. The mouse is used to select a point or position on the screen. Once the user has located an item, which could also be an empty space, a letter or a word, he or she would position the mouse pointer on it. To actually use the mouse, the user would press the left, the middle (if any), or the right button. If the user presses the left button once, this action is called Click. If the user presses the right mouse button, the action is referred to as Right-Click. If the user presses the left button twice and very fast, the action is called Double-Click. Mouse events are implemented as TMouseEvent and TMouseMoveEvent function pointers.
13.6.2 The Mouse Down Message Imagine the user has located a position or an item on a document and presses one of the mouse buttons. While the button is pressed and is down, a button-down message is sent. The syntax of this event is as follows: void __fastcall OnMouseDown(System::TObject* Sender, TMouseButton Button, Classes::TShiftState Shift, int X, int Y);
The Button argument specifies what button was clicked. The buttons of the mouse are identified by the TMouseButton enumerator whose members are: Value mbLeft mbRight mbMiddle
Description The left mouse button was clicked The right mouse button was clicked The middle mouse button was clicked
The Shift argument indicates whether mouse button and/or a keyboard key was/were pressed and held down when the Button was clicked. It can have one of the following values: Value ssShift ssAlt ssCtrl ssLeft ssRight ssMiddle ssDouble
Copyright © 2003 FunctionX, Inc.
Description One of the Shift keys was pressed One of the Alt keys was pressed One of the Ctrl keys was pressed The left mouse button was held down The right mouse button was held down The middle mouse button was held down The Button was double-clicked
351
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
The X and the Y argument represent the TPoint(X, Y) point where the mouse was clicked.
Practical Learning: Sending a Mouse Down Message 1.
To experiment with the mouse down effect, on the Events tab of the Object Inspector, double-click the right side of the OnMouseDown field
2.
Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { ShowMessage("Mouse at (" + IntToStr(X) + ", " + IntToStr(Y) + ")"); } //---------------------------------------------------------------------------
3.
Test the program and click various parts of the form
4.
Close it and return to Bcb
13.6.3 The Mouse-Up Message After pressing a mouse button, the user usually releases it. While the button is being released, a button-up message is sent and it depends on the button, left or right, that was down. The event produced is OnMouseUp. The OnMouseUp event uses the same syntax as the OnMouseDown event and processes the same arguments.
13.6.4 The Mouse Move Message Whenever the mouse is positioned and being moved on top of a control, a mouse event is sent. This event is implemented as a TMouseMoveEvent function pointer and its syntax is: void __fastcall OnMouseMove(System::TObject* Sender, Classes::TShiftState Shift, int X, int Y);
The Shift argument is the same as the other mouse messages. The X and Y values identify the location of the mouse at the time this event fires.
13.7 Programmer Defined Messages 13.7.1 Introduction The list of methods and messages available for the objects used in your applications is very impressive because the VCL tries to help process as many messages as possible. Unfortunately, in some areas, the VCL tends to address only the most commonly used messages, which is still very long, as we will see throughout this book.
352
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
The Win32 library provides more messages than the VCL implements. Normally, you should first check if a message you want to process is already available for your control. If it is not, you can create your own message and its event. Once again you have various options. If the message belongs to, or must be processed by, a form, you can create it in the header file of the form. Otherwise, you can create a new control by simply deriving a class from an existing control.
13.7.2 Windows Functions Another way you will manipulate controls consists of calling a Win32 API function. There are two main categories of functions you will call: those that must identify the control that is calling the function and those that do not. If the function requires the control that called it, you can specify the control using its handle. If the function does not need this piece of information, then you can omit it. We will see various types of both categories of functions. Most of the messages we will use in our applications are implemented in various classes of the VCL. Some others are not. Some of the messages are available in the Win32 library and you can use them in your application. This is made possible by calling the SendMessage() function. Its syntax is: LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
The hWnd argument is the object or control that is sending the message. The Msg argument is the message to be sent. The wParam and the lParam values depend on the message that is being sent. The advantage of using the SendMessage() function is that, when sending this message, it would target the procedure that can perform the task and this function would return only after its message has been processed. Because this function can sometimes universally be used, that is by almost any control or object, the application cannot predict the type of message that SendMessage() is carrying. Therefore, (the probable disadvantage is that) you must know the (name or identity of the) message you are sending and you must provide accurate accompanying items. Here is an example that changes the caption of a form using the WM_SETTEXT message: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { const char *Msg = "This message was sent"; SendMessage(Handle, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)Msg); } //---------------------------------------------------------------------------
13.7.3 Prefoming Messages Besides the SendMessage() function, the TControl class provides the Perform() method that allows you to send a message any time. Its syntax is: int __fastcall Perform(unsigned Msg, int WParam, int LParam);
Copyright © 2003 FunctionX, Inc.
353
Chapter 13: Characteristics of Child Controls
Borland C++ Builder Programming
The TControl::Perform() method functions like the SendMessage() function except that, since it is a VCL function, it omits the handle to the control because the control that calls it would be specified already. The first argument, Msg, is the identifier of the message that needs to be processed. It is exactly like the second argument of the SendMessage() function. Like the SendMessage() function, the values of WParam and LParam depend on the message. Here is an example used to close a form: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Perform(WM_CLOSE, 0, 0); } //---------------------------------------------------------------------------
13.7.4 Custom Message Implementation To customize an existing message of a control or to create a new message that is not available for your control, the TControl class provides the WndProc() method. Its syntax is: virtual void __fastcall WndProc(Messages::TMessage &Message);
In order to use this method, you must override it in your own class. Here is an example: //--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components private: // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); virtual void __fastcall WndProc(TMessage &Msg); }; //---------------------------------------------------------------------------
The Message argument is any message you want to process. As done for a Win32 application, the Message value is typically passed to a switch condition and each desired message is treated in a case statement. After processing the messages and before closing the member function, you should (must) call the parent class to process the messages that were not treated. Here is an example that treats the WM_MOVE message and prevents the user from moving the form (by preventing the mouse from capturing the title bar): //--------------------------------------------------------------------------void __fastcall TForm1::WndProc(TMessage &Message) { switch(Message.Msg) { case WM_MOVE: ReleaseCapture(); break; } TForm::WndProc(Msg); } //---------------------------------------------------------------------------
354
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 13: Characteristics of Child Controls
After creating the message, if it is intended for the form to treat, the message is ready and its event would fire appropriately. If the event is for a particular control, you must let the compiler know that you have a window procedure that would process a particular message or the messages of a certain control. To do this, you can assign WndProc to the control’s WindowProc member variable. This could be done in the constructor or the OnCreate() event of the form as follows: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { this->WindowProc = WndProc; } //---------------------------------------------------------------------------
13.7.5 Methods and Messages Combinations The events of Windows controls are designed to be executed at specific periods in response to some occurrence. For example, only when the user presses a mouse button can a message related to this action would occur. Yet, one of the most influential ways of creating an effective program is to anticipate users actions as much as possible. This allows you to correctly respond. To support this idea, the VCL provides a system of combining controls messages and their associated events. When the user initiates an action to send a message, the control that owns the action sends the message and an accompanying method. This method, although belonging to the control, allows other controls or other parts of the program to access the event as if they controlled when such an event would occur. For example, if the user clicks a control, the control composes and sends a click message. Because the control is also equipped with a Click() method, another control of the application can call its Click() event and perform the same action. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Perform(WM_CLOSE, 0, 0); } //--------------------------------------------------------------------------void __fastcall TForm1::Panel1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { Button1->Click(); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
355
Chapter 14: Parent Controls
356
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 14: Parent Controls
Chapter 14: Parent Controls 14.1 Characteristics of Parent Controls 14.1.1 The Window’s Desktop When you start the computer, the first object the operating system creates is the desktop window. This is a wide rectangular area that covers the entire monitor screen. Based on the settings of the Control Panel, the operating system uses either a color or a picture (a bitmap) to fill the window. Once the desktop is ready, other objects or controls can be placed on it. To provide some information about the desktop, the VCL is equipped with the TScreen class. This allows you to get such details as the screen dimensions (its width and height), the form that is displaying on the screen, the type of keyboard being used, etc. To make the screen information available to you any time, every application declares a TScreen global variable so you do not have to declare your own. Alternatively, to get access to the desktop, you can call the GetDesktopWindow() function. Its syntax is: HWND GetDesktopWindow(VOID);
This function simply returns a handle to the desktop window. This handle can give some information about the desktop such as its size, etc. Because GetDesktopWindow() is just a function, it only produces a handle to the desktop. If you want to perform other processing on the desktop, you should use the TScreen class members or you can use some of the Win32 library functions such as SystemParametersInfo().
14.1.2 Application’s Containers A control is referred to as container if it can host other controls. While the desktop is the biggest container of the computer, to develop your applications, you will use your own controls that can act as parent to other controls. The VCL provides various objects that can play this role. The list includes the form, the frame, the group box, the panel, the scroll box, the control bar, the tab control, the page control, the status bar, the toolbar, the cool bar, the page scroller, the tabbed notebook, etc. The list is impressive and these parent controls are usually used for different purposes. In fact, the only real characteristic they share is their ability to host other controls. Except for the form (and consequently the dialog box), most, if not all, of the other containers must be hosted by a form. Therefore, after starting a project or once you get a form, you can place a control container on it (the form). Once placed on a form, these parent windows can host their own controls. Therefore, to use a container, you can either Copyright © 2003 FunctionX, Inc.
357
Chapter 14: Parent Controls
Borland C++ Builder Programming
position your control on the form, in which case the form would act as the parent, or you can first select one of the containers, place it on a form, and then add other controls to it.
14.1.3 The Parent’s Location As seen with child controls, except for the desktop window, every visual object either on the screen or in your application must be located. The location of an object depends on its parent and some other considerations. The main object or frame of an application, which is usually the first form of a project, when it appears on the screen, is located with regards to the screen. Such a form is located on a Cartesian coordinate system whose origin is on the top-left corner of the screen. The horizontal axis moves from the left border to the right. The vertical axis moves from the top border down. The distance from the left border of the screen to the form is the Left measurement. The distance from the top border of the screen to the top border of the form is its left measurement. This can be illustrated as follows:
The above illustration shows a form positioned on a monitor. The desktop acts as the parent window of the form. In the same way, if you place a control on a container, the control is located with regards to its parent, not based on the screen. As seen previously, in such case, the origin of the coordinate system is located on the top-left corner of the parent window. The distance from the left border of the parent window to the left border of the control is the control’s Left property. The distance from the top border of the 358
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 14: Parent Controls
parent window to the top border of the control is the control’s Top property. Here is an example in which a memo control as the child is positioned inside a panel that acts as its parent:
Figure 16: A control positioned in a parent window In the same way, an object positioned on a parent can be seen only if its dimensions are confined to the body of the parent. To manage the display of visible windows, each parent provides a section called the Client Area. For the desktop screen, the client area is the whole screen. For a form, the client area is the body of the form without the title bar. Most other containers provide their whole body as the client area:
Copyright © 2003 FunctionX, Inc.
359
Chapter 14: Parent Controls
Borland C++ Builder Programming
Figure 17: The Client Area
14.2 Control Alignment and Constraint 14.2.1 The Client Area The desktop window provides a rectangular area that it can use to display the computer’s applications. This area is also used to host other objects. Although an object’s borders can span beyond the borders of the desktop, only the area of an object covered by the desktop can be seen. Here is an example of a window whose right side cannot be seen:
Figure 18: A section of a window that cannot be displayed on the screen 360
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 14: Parent Controls
The client area is a rectangle primarily used for its location and dimensions. To get the values of the shape that compose the client area, you have various options and considerations. For example, to get the area that represents the desktop, which would let you know how much real estate is available for your application, you can call the TScreen::DesktopRect member variable. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TRect Recto = Screen->DesktopRect; Caption = "Width: " + IntToStr(Recto.Width()) + " Height: " + IntToStr(Recto.Height());
} //---------------------------------------------------------------------------
Figure 19: Result - DesktopRect The TScreen class also provides such aspects as the location and width of the desktop in the forms of DesktopLeft, DesktopTop, DesktopWidth, and DesktopHeight. The DesktopRect and the DesktopHeight provide the height of the desktop including the task bar. If you want to know the actual area that is made available on the desktop, which does not include the taskbar, use the TScreen::WorkAreaRect or the TScreen::WorkAreaHeight respectively. Otherwise, you can also get the left, the top, and width measures using the WorkAreaLeft, the WorkAreaTop, and the WorkAreaWidth respectively. In reality, the WorkArea_ measures can be more useful if your application takes advantage of more than one monitor. In this case the WorkArea_ measures provide information about the desktop of the primary monitor. Besides the TScreen class, you can use the GetDesktopWindow() function to get a handle to the desktop and find the dimensions of that window. Here is an example that displays the screen resolution: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
Copyright © 2003 FunctionX, Inc.
361
Chapter 14: Parent Controls
{
Borland C++ Builder Programming
Rect Recto; HWND hWndDesktop = GetDesktopWindow(); GetWindowRect(hWndDesktop, &Recto); Caption = "Width: " + IntToStr(Recto.Width()) + " Height: " + IntToStr(Recto.Height());
} //---------------------------------------------------------------------------
It is important to know where the origin of a control is located. During control design, we saw that, when a container is selected and you double-click a visual control on the Component Palette, the new control would be added to the container. On the other hand, if you have a container that is positioned on the form but the container, or any specific container, is not selected, if you double-click a control on the Component Palette, the control would be added to the form even though the new control may be positioned on a container. In this case, the form would become the parent of the new control. Therefore, in order to do anything related to the location and/or dimensions of a control, you must know the coordinate of the origin used as the basis for its location and dimensions. Because only a parent can host some control, it holds an origin and makes it available to its children. To get the origin of a container, call the TControl::ClientOrigin property. To get the location and dimensions of any window that serves as parent, you can call the TControl::ClientRect property. A control hosted by a container can be displayed only inside its parent, if the control’s left or top measures are negative, its left or top borders respectively would be hidden under the left or top sides of the parent. If the dimensions of the child control span beyond the dimensions of the parent window, the control’s right or bottom border will be hidden. We will see that some controls cannot allow their child or children to expand the client area and some other control can display scroll bars so the hidden parts can be navigated to. The ClientRect property mainly provides only the width and the height of a client area since the left and the top measures are set to 0 each as the origin (0, 0) is the base. Alternatively, you can get the width of a control using the TControl::ClientWidth and you can get the height of the control using the TControl::ClientHeight properties.
14.2.2 Control’s Alignment in the Client Area Besides the Top, Left, and Position properties, you can also control the position of a control in the client area of its container using the Align property. This property and its values have the following effects:
362
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 14: Parent Controls
By default, the Align property is set to alNone. In this case, the control can assume any position with regard to its host control
When the Align property is set to alLeft, the control will be fixed to the left border of its host.
A control whose Align property is set to alTop can serve as a toolbar or a floatable window on the top section of its host.
Copyright © 2003 FunctionX, Inc.
363
Chapter 14: Parent Controls
Borland C++ Builder Programming
If the control has the Align property set to alRight, the control would be anchored to the right border of its container.
A control can be used as a status bar that always displays on the bottom section of the form. In this case, its Align property would be set to alBottom.
If a control’s Align property is set to alClient, it would occupy the client area of the container. If another control on the same container already has one of the previous alignments, except alNone, the control whose Align property is set to alClient would occupy only the remaining unoccupied area.
In case of a (regular) form, the Align property controls how the form is positioned with respect to the monitor's screen. When it comes to a form, the Align property should be used only if you have a good reason. For example, to maximize a form at startup, you can set its Align property to alClient, which you can also accomplish using the WindowSate property. The default value of the Align property is alNone, which means the appearance of the form is left to other properties. We will explore the Align property when we get to the controls but remember that it is also available to the form.
364
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 14: Parent Controls
14.2.3 The Client Area’s Constraints The user of your application will have the ability to resize the form when using your application. If the form is not a classic dialog box, by default, the user can resize it to any dimensions allowed by the screen resolution; the minimum is to have the system buttons and just a tiny part of the caption. You can set the minimum height, the maximum height, the minimum width, and the maximum width allowed on the control if the control gets resized. This property is controlled by the Constraints property. The Constraints property is a TSizeConstraints class, which is inherited from the TPersistent class. Although it is a class, you should never declare it. The control that uses a Constraints property will create and initialize an instance of the class. The MinWidth, MinHeight, MaxWidth, and MaxHeight are integer values you can set using the keyboard. To set the constraints of a control that has this property, click the + button of the Constraints field in the Object Inspector, click the field desired and type an integer value. To programmatically control the constraints of a control, call the TSizeConstraints::Property and type the desired value. In the following example, when the user tries to resize the Form1 form, the minimum and maximum dimensions of the form are already set and would not change: //--------------------------------------------------------------------------void __fastcall TForm1::FormResize(TObject *Sender) { Form1->Constraints->MinHeight = 300; Form1->Constraints->MinWidth = 500; Form1->Constraints->MaxHeight = 400; Form1->Constraints->MaxWidth = 565; } //---------------------------------------------------------------------------
14.2.4 Control Anchoring If you position a (visual) control on a form, if the control is positioned on the top left section of the form, when the user resizes the form, the control’s position would appear static, it would not move. This could be a concern if the control is positioned on the right, the bottom or the lower right section of the form. When the user resizes the form, the button’s position would not be updated. Sometimes you will want the control to have the same location and/or distance with regard to the bottom, the right, and/or the lower right corner of the form. This ability is controlled by the Anchors property. The anchoring of a control to its parent or host container is controlled using a set property derived from the TAnchors enumerator. By default, when you add a control to a form, it is “glued” to the top left corner of its container. Since this property is a Set, you can set the control’s position with regards to its container’s right and bottom borders. The possible values of the Anchors property are: Value akTop akLeft akRight akBottom
Copyright © 2003 FunctionX, Inc.
The control’s location will not change With regards to its parent’s top border With regards to its parent’s left border With regards to its parent’s right border With regards to its parent’s bottom border
365
Chapter 14: Parent Controls
Borland C++ Builder Programming
14.2.5 Child Controls and Drag’n’Drop Operations One of the most useful actions performed on a control consists of involving it in drag and drop operations. Some controls can serve as the source or the destination on such an operation. Drag and drop operations are performed using specific properties of child controls associated with their parents. The DockSite property uses a Boolean value to indicate that the control can serve as the host for a drag’n’drop operation. The DragKind property specifies how the control participates in the drag’n’drop operation. If the control serves as a docking host, you should set its DragKind property to dkDock. By contrast, if the control will itself be used when dragging, set its DragKind property to dkDrag. If a control is “draggable”, you can use the DragMode property to specify how the dragging operation on the control would be initiated. If you want the dragging operation to be possible when the user clicks the control, set its DragMode property to dmAutomatic. Otherwise, some controls, depending on the result you are trying to achieve, should be available for dragging only depending on an intermediary action. In such a case, you can write your program so that the dragging operation on a control would be available only after the application or another event or control has called the TControl::BeginDrag() method. In this case, you can set the DragMode property to dmManual.
366
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
Chapter 15: Forms and Dialog Boxes 15.1 Characteristics of Forms 15.1.1 Introduction The form is the primary object of a VCL application. It is rectangular window whose main purpose is to carry, host, or hold other controls to allow the user to interact with the computer. Although there are various types of parent containers we will use, the form is the commonly and regularly used. When C++ Builder starts, it creates a starting form and initializes an application. You can use such as form as you see fit. If you need additional forms, you can create them visually by clicking File -> New -> Form on the main menu. You can also click the New Form button on the Standard toolbar. Alternatively, you can display the New Items dialog box from where you would select the Form icon. Another technique you can use is to create a form programmatically. To do this, declare a pointer to the TForm class using the new operator. Like every other control, you must specify the owner of the form that will be responsible for destroying it. This owner is usually the form that is calling it
15.1.2 The System Icon The top section of a form or a dialog box is called a title bar:
Figure 20: The Title Bar
On the left side of the form's title bar, there is a small picture called an icon. By default, C++ Builder uses an icon shipped with the compiler. If you want to use your own icon, you can specify it using the Icon property. To do this, on the Object Inspector, you can either double-click the (None) value of the Icon field or click the ellipsis button of the field. This would call the Picture Editor dialog box:
Copyright © 2003 FunctionX, Inc.
367
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
Figure 21: Dialog Boxes - Load Picture After selecting the icon and clicking Open, the icon would be made available in the Picture Editor dialog box:
Figure 22: Dialog Boxes - Picture Editor After clicking OK, the new icon would be used as the system icon of the form:
Figure 23: The title bar with a custom icon
15.1.3 The System Menu The icon used on the title bar possesses a menu called the system menu. This menu displays if you click the icon: 368
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
Figure 24: The System Menu The system menu of the icon's title bar allows you to perform the common actions of a regular Windows form. If you do not want to display the icon and its menu, refer to the section on the BorderIcons below.
15.1.4 The Caption On the right side of the system icon, there is long bar that is actually usually referred to as the title bar. This section displays a word or a group of words known as the caption of the form. By default, the title bar displays the name of the form. To customize the text displayed on the title bar, on the Object Inspector, change the text of the Caption property. At design time, you can only set the caption as text. The caption can be any type of string. At run time, you can control and programmatically display anything on the caption of the form. It can consist of an expression or the result of a calculation. To change the caption at run time, you can use a function, a method, or an event. Just type “Caption = “ and the sentence you want. For example, you can display today’s date on the caption as follows: //--------------------------------------------------------------------------void __fastcall TForm1::ChangingFormCaption() { Caption = "Today is " + Date(); } //---------------------------------------------------------------------------
You can also indirectly control the caption. For example, after the user has typed his or her name in an edit box, you can display it in the form’s caption as follows: //--------------------------------------------------------------------------void __fastcall TForm1::ChangingFormCaption() { Caption = Edit1->Text; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
369
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
15.1.5 The System Buttons On the right side caption, there are three system buttons that allow minimizing , , restoring or closing the form as a window. The presence, maximizing absence, or appearance of the system buttons is controlled by the BorderIcons property. To control this property, you can click its + button to expand it to display its four possible values: biSystemMenu, biMinimize, biMaximimize, and biHelp:
Figure 25: The BorderIcons property in the Object Inspector The BorderIcons property is a (Pascal-style) set, which means it can use one or more values in any allowed combination. Each member of the set can have a true or false value. If you set the biSystemMenu property to true, regardless of the values of the other fields of the BorderIcons property, the form would have neither the system icon (which also makes the system menu unavailable) nor the system buttons. If a form does not have a system close button, you should make sure the user has a way to close the form: This dialog box does not present system buttons. The user would not have any inherent way to close it unless the programmer explicitly presents a button to close it, which was taken care of by the presence of the OK and Cancel buttons.
Figure 26: A window without the system buttons Using a combination of the biSystemMenu, the biMinimize and the biMaximimize properties, you can control the availability of the system buttons:
370
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
biSystemMenu = false Notice that there is no system button
biSystemMenu = true biMinimize = false biMaximize = true Notice that the Minimize button is disabled
biSystemMenu = true biMinimize = true biMaximize = false Notice that the Maximize button is disabled
biSystemMenu = true biMinimize = true biMaximize = true Both the Minimize and Maximize buttons are enabled
Copyright © 2003 FunctionX, Inc.
371
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
biSystemMenu = true biMinimize = false biMaximize = false Notice that only the system Close button is available
The biHelp property works only if the form is a dialog box. It does not bear any role on the title bar of a regular form You can also programmatically control the system buttons. Since the BorderIcons property is a set, you must use the overloaded extractor operators. To remove or set a property to false, use the >> operator. To include or set a property to true, use the << operator. For example, to set the biSystemMenu property to false, you can use code such as: biSystemMenu = false
is equivalent to: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { BorderIcons = TBorderIcons() >> biSystemMenu; } //---------------------------------------------------------------------------
biSystemMenu = true biMinimize = false biMaximize = true is equivalent to: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { BorderIcons = TBorderIcons() << biSystemMenu >> biMinimize << biMaximize; } //---------------------------------------------------------------------------
biSystemMenu = true biMinimize = true biMaximize = false is equivalent to: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
372
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
{
BorderIcons = TBorderIcons() << biSystemMenu << biMinimize >> biMaximize; } //---------------------------------------------------------------------------
biSystemMenu = true biMinimize = false biMaximize = false is equivalent to: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { BorderIcons = TBorderIcons() << biSystemMenu >> biMinimize >> biMaximize; } //---------------------------------------------------------------------------
If you do not explicitly define the values of the BorderIcons, by default, the form would be equipped with all system buttons.
15.1.6 Form and Dialog Box Positioning One of the ways you can use the title bar is to move the form at design time. The location of a form is set at design time because the VCL provides another property that follows the directives of the design. At design time, you can position the form anywhere on your screen and when you run the application, the form would appear following that position. This is based on the fact that the position of the form is controlled by the TPosition enumerator. The default position of the form is recognized as the way it was designed if you do not change the Position property. The default value of the Position property is poDesigned.
15.1.7 The Borders One of the most visual aspects of an object's appearance is its border. A border is a line that sets the visual limits of an object. Most objects, including a form, have four borders: left, top, right, and bottom. In VCL applications, the borders of a form are controlled by a property called BorderStyle. This is also the main property that specifies whether you are creating a regular form or a dialog box. To control the appearance of the borders of your form, on the Object Inspector, if you click BorderStyle, you would see that it is a combo box property. The BorderStyle property is an enumerator and you can choose one of its values needed for your form.
Copyright © 2003 FunctionX, Inc.
373
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
By default, a form is designed to be resizable using the bsSizeable value. In this case the user can change its width and its height by dragging one of its borders or corners.
If you set the BorderStyle property to bsDialog, the user will not be able to resize the dialog. This is the default and the mostly used characteristics for a dialog window. You should always use it if you want the window to be a dialog box.
If you set the BorderStyle property to bsDialog and have a Help file you want to use on the dialog box, you can set the biHelp of the BorderIcons property to true. This would display a Whats This button on the title bar.
A BorderStyle set with bsSingle looks like one set with the bsSizeable. The difference is that the user cannot resize it.
A floating window is a form used to let the user move it around while she is working on the main form or dialog. This form is usually modeless, which means the user does not have to close it to continue working. If you want to create a floating modeless window, you should set the form’s BorderStyle to bsSizeToolWin. The window has a short title bar and it can be resized.
374
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
Like the sizable tool window, a form with bsToolWindow has a short title bar and is also a prime candidate for a floating window. Unlike the form with bsSizeToolWin, the user cannot resize a bsToolWindow form.
A Form whose BorderStyle is set to bsNone has neither a title bar nor borders.
To change a property programmatically, assign one of the above values to the BorderStyle variable. Here is an example that would transform the form in a dialog box: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { BorderStyle = bsDialog; } //---------------------------------------------------------------------------
15.1.8 The Window State of a Form When a form appears as it was designed, it is said to be "normal". C++ Builder allows you to have the form minimized or maximized when the application is launched. This ability is controlled by the WindowState property. The default value of this property is wsNormal, which means the form would appear in a normal fashion. If you want the form to be minimized or maximized at startup, in the Object Inspector, select the desired value for the WindowState property. To control the window’s state programmatically, simply assign the wsMaximized or the wsMinimized value to the WindowState property. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { WindowState = wsMaximized; } //---------------------------------------------------------------------------
If you want to check the state of a window before taking action, simply use a conditional statement to compare its WindowState property with the wsNormal, the wsMaximized, or the wsMinimized values. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Panel1Click(TObject *Sender)
Copyright © 2003 FunctionX, Inc.
375
Chapter 15: Forms and Dialog Boxes
{
Borland C++ Builder Programming
if( WindowState == wsNormal ) WindowState = wsMaximized; else if( WindowState == wsMaximized ) WindowState = wsMinimized; else WindowState = wsMaximized;
} //---------------------------------------------------------------------------
15.1.9 The Body of a Form or a Dialog Box The area that a form makes available to its control is its body. As the main container of Windows controls, a form provides some particular properties to the controls it hosts. The client area of a form is represented by the ClientRect property. This property can be used to get the dimensions of the body of the form. Like the TControl::ClientRect property, the TForm::ClientRect property provides the width and the eight of the client area, omitting the location (left and top) which is set to the client origin (0, 0) located on the top-left corner of the body of the form. Alternatively, to get the width of the client area of the form, you can call the ClientWidth property. To get the height of the client area, use the ClientHeight. The client aspects can be illustrated as follows:
Overall, you will hardly be concerned with the dimensions of the client area, unless you want to draw or render an image. If you do not have a particular reason, let C++ Builder take care of the client area.
15.1.10
Form’s Transparency An object is referred to as transparent when you can see through it. If you are working under Windows 2000 or later running on a Pentium or equivalent, you can make your form transparent. To create a transparent form, set the AlphaBlend Boolean property to true from its default false value. Once this property is set to true, you can use the AlphaBlendValue property to set the degree of transparency. The value must be a BYTE integer between 0 and 255. At 0, you would not see the form at all. The only presence of the form would be on the taskbar. At 255, the form would appear as if the property were not applied.
376
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
15.2 Form Methods 15.2.1 Form Creation The form in implemented by the TForm class which itself is derived from TCustomForm. Like every other control, the form is equipped with a constructor that allows you to dynamically create it.
15.2.2 Form Closure When the user has finished using a form, he or she must be able to close it. Closing a form is made possible by a simple call to the Close() method. Its syntax is: void __fastcall Close();
Although this method can be used to close any form of an application, if it is called by the main form, it also closes the application.
15.3 Forms Messages and Events 15.3.1 Form Creation When an application made of a form is launched, the form must be created to display to the user. At the form gets created, it initialize its controls. This is done before the form can display to the screen. At this time the OnCreate() event fires. This is a TNotifyEvent event, which means that it does not take any argument other than the Sender, which is a TObject type. OnCreate() is the default event of a form. This means that if you double-click the form, the code of this event would be created and made ready for you.
Practical Learning: Creating a Form 1.
Create a new project with its default form
2.
Save it in a new folder named FormEvents
3.
Save the unit as Exercise and save the project as FormEvents
4.
To use the OnCreate() event , double-click the middle of the form
5.
Implement the OnCreate event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { ShowMessage("The form has been created"); } //---------------------------------------------------------------------------
6.
Test the application. Close it and return to Bcb
Copyright © 2003 FunctionX, Inc.
377
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
15.3.2 Form Showing After the form has been created, it must display to the user. If the application contains more than one form, you can display it. To display a form, just like any control, we saw that the TControl::Show() method must be called. If the application is being launched, it would call this method to display the main form of the application. If you have more than one form and you want to display the other form, you can call the TControl::Show() method. When the TControl::Show() method is called, the OnShow() event is fired. This allows you to perform any last minute processing before the form can display.
Practical Learning: Showing a Form 11. On the Events tab of the Object Inspector, double-click the right field of OnShow and implement the OnShow() event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormShow(TObject *Sender) { ShowMessage("The form is showing"); } //---------------------------------------------------------------------------
12. Test the application. Then close it and return to Bcb
15.3.3 Form Activation and Deactivation When two or more forms are running on the computer, only one can receive input from the user, that is, only one can actually be directly used at one particular time. Such a window has a title bar with the color identified in Control Panel as Active Window. The other window(s), if any, display(s) its/their title bar with a color called Inactive Window:
378
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
To manage this setting, the windows are organized in a 3-dimensional coordinate system and they are incrementally positioned on the Z coordinate, which defines the (0, 0, 0) origin on the screen (on the top-left corner of your monitor) with Z coordinate coming from the screen towards you. In order to use a form other than the one that is active, it must be activated. To do this, the OnActivate() event must be fired. OnActivate() is a TNotifyEvent event. If there is more than one form or application on the screen, only one can be in front of the others and be able to receive input from the others. If a form is not active and you want to bring it to the top, you must activate it, which sends the OnActivate() event. When a form is being activated, the one that was on top would become deactivated. The form that was on top, when losing focus, would fire the OnDeactivate() event.
Practical Learning: Activating a Form 1.
On the Events tab of the Object Inspector, double-click the right field of OnActivate and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormActivate(TObject *Sender) { ShowMessage("The form is now activated"); } //---------------------------------------------------------------------------
2.
Execute the application to display the form. Then close it
3.
Display the starting code for the OnDeactivate event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormDeactivate(TObject *Sender) { ShowMessage("Our form is deactivated"); } //---------------------------------------------------------------------------
4.
Execute the application to display the form
5.
While the form is displaying, open Notepad to deactivate the form
6.
From the taskbar, click the button of our form to activate it
7.
After experimenting with the form, close it and return to Bcb
15.3.4 Window Painting Whether a form has just been created or it needs to be shown, the operating system must display it on the screen. To do this, the form colors and other visual aspects must be retrieved and restored. This is done by painting it (the form). If the window was hidden somewhere such as behind another window or was minimized, when it comes up, the operating system needs to paint it. When a form gets painted, it fires the OnPaint() event. This event also is a TNotifyEvent type.
Copyright © 2003 FunctionX, Inc.
379
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
Practical Learning: Painting the Form 1.
From the Events tab of the Object Inspector, display the starting code of the OnPaint event and implement it: //--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender) { ShowMessage("Window Painting: A hobby or a habit?"); } //---------------------------------------------------------------------------
2.
15.3.5
Test the application and return to Bcb
Window Sizing When using an application, one of the actions a user can perform on a form is to change its size, provided the form allows it. Also, some time-to-time, if possible, the user can minimize, maximize, or restore a window. Whenever any of these actions occur, the operating system must keep track of the location and size of a window. For example, if a previously minimized or maximized window is being restored, the operating system must remember where the form was previously positioned and what its dimensions were. When the size of a form has been changed, it fires the OnResize() event, which is a TNotifyEvent type.
Practical Learning: Resizing a Form 1.
On the Events tab of the Object Inspector, double-click the right field of OnResize and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormResize(TObject *Sender) { ShowMessage("Changing size or changing sides - Who knows?"); } //---------------------------------------------------------------------------
2.
Execute the application and return to Bcb
15.3.6 Form Closure As mentioned above, the user can close the form after using it. When the form is being closed, it fires the OnClose() event. This gives you the opportunity to perform any last minute processing such as finding out some information about the application or the form itself before the form is actually closed. The OnClose() event is a TCloseEvent function pointer whose syntax is: void __fastcall OnClose(TObject *Sender, TCloseAction &Action)
When the form is being closed, you can use the Action argument to specify how you want the closing to be performed. This argument is a value of the TCloseAction enumerator whose members are: Value 380
Description Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
caNone caHide caFree caMinimize
Chapter 15: Forms and Dialog Boxes
The form must not be closed at all The form must be hidden but not closed The form must be closed and its memory freed The form must be minimized and not closed
Practical Learning: Resizing a Form 1.
On the Events tab of the Object Inspector, double-click the right field of OnClose and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { ShowMessage("Time to close"); } //---------------------------------------------------------------------------
2.
Execute the application and return to Bcb
15.3.7 Form Destruction Once the form has been closed, the operating system must destroy it and reclaim the memory space it was using. This causes the OnDestroy() event to fire. OnDestroy() is a TNotifyEvent. If you had dynamically created some controls using the form’s OnCreate() event, use the OnDestroy() event to destroy them.
15.4 Application of Forms 15.4.1 Multiple Forms When you start Borland C++ Builder, it creates a starting form for you. If one form is not enough for your application, you can add as many as necessary. To add (or to create) a (new) form:
On the main menu, you can click File -> New -> Form
On the main menu, you can also click File -> New -> Other... and, in the New Items dialog box, you can double-click Form
On the View toolbar, you can click the New Form button
.
Any of these techniques will add a new form to your application. If your application is using various forms and you want to display a particular one at design time, on the main menu, you can click View -> Forms, which would open the View Form dialog box. From there, you can select the desired form and click OK. If you visually add two (or more) forms to your application, you may need to link them, allow one to call the other. Since each form is created in its own unit, to let one form know about the existence of another, you must include its unit. A form or unit that wants to communicate with another is called a client of the form. For example, if Unit2 wants to use information stored in, or controlled by, Unit1, Unit2 needs to include Unit1 in its list of header files; and Unit2 becomes a client of Unit.
Copyright © 2003 FunctionX, Inc.
381
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
There are two simple ways you can include a unit’s header in another file. Imagine you have created Form1 stored in Unit1 and Form2 stored in Unit2. If you want to have access to Form2 from Form1, using C++, on top of the source file of Unit1, include Unit2’s header file. C++ Builder provides another technique. After making sure that either Form1 displays on the screen or a Unit1 tab is displaying, on the main menu, you can click File -> Include Unit Hdr… From the Use Unit dialog box, you would click the name of the unit you want to include to the current unit and click OK. Bcb will automatically add the right and selected header to the client.
Practical Learning: Using the Form’s Events 1.
Create a new project with its default form
2.
On the Object Inspector, click Caption and type Rapid Application Development
3.
Click Name and type frmMain
4.
To add another form to your application, on the View toolbar, click the New Form button
5.
Click the Name field and change it to frmSecond
6.
As the new form is still selected, double-click in its middle to initiate its OnCreate event.
7.
Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmSecond::FormCreate(TObject *Sender) { Width = 405; Height = 350; Caption = "I know you called me!"; } //---------------------------------------------------------------------------
8.
To display the main form, on the main menu, click View -> Forms…
9.
On the View Form dialog box, click frmMain and click OK
10. To include the header of the other form, on the main menu, click File -> Include Unit Hdr… 11. From the list, Unit2 should be selected already, otherwise click it Click OK. 12. On the Object Inspector, click the Events tab and double-click the event field of the OnDblClick field 382
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
13. Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormDblClick(TObject *Sender) { frmSecond->ShowModal(); } //---------------------------------------------------------------------------
14. To test the program, press F9. 15. Move the form from its current position by dragging its title bar. 16. To call the second form, double-click in the middle of the form. 17. After viewing the form, close it. Also close the main form. 18. To add another form to your application, on the main menu, click File -> New -> Form. 19. Change the name of the new form to frmFloater 20. On the Object Inspector, click the OnCreate name and double-click the event field. 21. Implement the event as follows: //--------------------------------------------------------------------------#include #pragma hdrstop #include "Unit3.h" // Include the dependent header file #include "Unit2.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TfrmFloating *frmFloating; //--------------------------------------------------------------------------__fastcall TfrmFloating::TfrmFloating(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------void __fastcall TfrmFloating::FormCreate(TObject *Sender) { // The appearance should be that of a floating window BorderStyle = bsToolWindow; // Keep this floating always on top FormStyle = fsStayOnTop; // Change the form's background color Color = TColor(RGB(202, 238, 255)); // Make sure this window aligns with the top-left // corner of the calling form Top = frmSecond->Top; Left = frmSecond->Left; // Set the dimensions of the window Height = 325; Width = 124; } //---------------------------------------------------------------------------
22. We will call the floating window from the second form. On the View toolbar, click the View Unit button 23. On the View Unit dialog, click Unit2 Copyright © 2003 FunctionX, Inc.
383
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
24. Click OK 25. Press F12 to display the second form. 26. Press Alt + F11 to call the Use Unit dialog. Double-click Unit3. 27. On the Object Inspector, double-click the OnClick event. 28. Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmSecond::FormClick(TObject *Sender) { frmFloater->Visible = !frmFloater->Visible; } //---------------------------------------------------------------------------
29. To test the program, press F9 30. To display the second form, double-click the main form. 31. To display the floating form, click the second form. Notice that the floating window appears. 32. Move the second form and try to position it on top of the floating window. Notice that the floating window is always on top of the second window. 33. Click the second form again. Notice that the floating form reappears. 34. Click the second form again to display the floating window. Make sure the floating window displays. 35. Close the second form and close the first form also 36. Display the second form. 37. On the Object Inspector, double-click the event field of OnClose. 38. Implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmSecond::FormClose(TObject *Sender, TCloseAction &Action) { if( frmFloater->Visible ) frmFloater->Close(); } //---------------------------------------------------------------------------
39. Press F9 to test the program. 40. Display the second form. 41. Click the second form to display the floating window.
384
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
42. Close the second form. Notice that the floating window has been closed. 43. Close the main form
15.4.2 Dynamic Forms Besides designing a form visually, sometimes this will not be possible. You may have to create the form dynamically. To create a form dynamically, declare a pointer to a TForm class. If you want to quickly use a form in a function or an event, you can create it easily. There are two options you have. If you want to create a duplicate of the form you already have, you can use it as the base class and simply create an instance of the form using the new operator. When dynamically creating a form, you must specify the object or form that "owns" the form. If you are creating a duplicate of the form, set the owner as the current form or the this pointer. The owner is provided to the constructor. For example, to create a duplicate form using a form’s OnDblClick event, you can implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { TForm1* Former = new TForm1(this); } //---------------------------------------------------------------------------
The problem with a duplicate is that, unless you go through a big deal of code writing, every time you do anything on the form, the duplicate produces the same action. Therefore, the second option consists of creating a fresh form. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { TForm* Former = new TForm(this); } //---------------------------------------------------------------------------
The biggest problem with a locally created form is that it is available only in the event or the function in which it is created. If you try to access it outside, you would receive an error. If you want a form to be available to more than one event or function, you must create it in a header file of an existing form. If you want the form to be available outside of the parent form that “owns” the header file, you must declare it in the public section. Otherwise, you should declare it in the private section: //--------------------------------------------------------------------------#ifndef Unit1H #define Unit1H //--------------------------------------------------------------------------#include #include #include #include //--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components private: // User declarations
Copyright © 2003 FunctionX, Inc.
385
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
TForm* Comet; // A dynamically created form public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------#endif
After declaring the form, you can use the constructor of the host form to initialize the dynamic form. This is done by using the new operator and initializing the TForm class with the this pointer. Here is an example: //--------------------------------------------------------------------------#include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Comet = new TForm(this); } //---------------------------------------------------------------------------
15.4.3 Customizing Dynamic Forms Whatever technique you use to create your form, you can set its properties using code. The most basic thing you should do is to display it. This is done using the TForm::Show() method. If the form was created locally, you must display it in the function or event where it was created: //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { TForm* Former = new TForm(this); Former->Show(); } //---------------------------------------------------------------------------
After creating and using a form, you should make sure the memory allocated to it is regained. The Borland C++ Builder compiler can take care of cleaning your dynamic controls when the application exists. Otherwise, you can delete it manually: delete MyForm;
Furthermore, to avoid any memory leak, you can reclaim the dynamically allocated memory by assigning NULL to the deleted object: //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) {
386
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
TForm1 *Cosmos = new TForm1(this); Cosmos->ShowModal(); delete Cosmos; Cosmos = NULL;
} //---------------------------------------------------------------------------
Imagine you dynamically declare an object, such as a form, as follows: //--------------------------------------------------------------------------class TForm1 : public TForm { ... private: // User declarations TForm* Fake; public: // User declarations __fastcall TForm1(TComponent* Owner); }; //---------------------------------------------------------------------------
We have seen that you can initialize it in the constructor of the form that owns your object: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Comet = new TForm(Form1); } //---------------------------------------------------------------------------
Once the dynamic object has been initialized you can display it any time you choose. For example, you can do this when the user double-clicks in the primary form: //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { Comet->Show(); } //---------------------------------------------------------------------------
Over all, the C++ Builder compiler takes care of deleting the dynamic object when the form that owns it is closed. If you want to make sure that your dynamic object is destroyed, you can use the delete operator as seen above. In reality, since you can use your judgment to decide when the object would be displayed, when the form that owns it is closed, before deleting the dynamic object, you can first find out if the object was really created. This can be performed with a simple if statement. The best place to destroy a dynamically created object that a form owns is when the form is destroyed. When a form is being closed, it fires the OnDestroy event. This is the favorite place to perform any housecleaning necessary and related to the form. Based on this, you can destroy the above Fake object as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormDestroy(TObject *Sender) {
Copyright © 2003 FunctionX, Inc.
387
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
if( Fake ) { delete Fake; Fake = NULL; }
} //---------------------------------------------------------------------------
15.5 The Multiple Document Interface (MDI) 15.5.1 Introduction to MDI-Based Applications A multiple document interface, abbreviated MDI, is an application whose main form directly "owns" other forms. The main form is also considered the parent. The forms that the parent form owns can display only within the rectangular borders of the parent form. The main idea behind an MDI is to allow the user to open different documents and work on them without having to launch another instance of the application: Corel Draw is an example of an MDI. The user can create one document, open another without closing the previous one, and create or open as many documents as the computer memory allows it
As opposed to an MDI, a Single Document Interface (SDI) allows only one document at a time in the application.
388
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
WordPad is an example of an SDI. The user can open only one document at a time. If he wants another WordPad document, he must open an additional instance of WordPad.
Each form that is child of the main form in an MDI can be a fully functional form and most, if not all, child forms are of the same kind. There are two main ways a child document of an MDI displays. To provide information about its state, a child form is equipped with a title bar. The title bar of a child form displays an icon, the name of its document, and its own system buttons. Since it can be confined only within the parent form, it can be minimized, maximized, or restored within the parent form. When a child form is not maximized, it clearly displays within the parent form. If it gets closed and there is no other child form, the parent would appear empty. If there are various child forms, they share the size of the client area of the parent form. If one of the child forms is maximized, it occupies the whole client area of the main main form and it displays its name on the title bar of the main form.
15.5.2 MDI Creation Creating an MDI appication is not the most difficult assignment you will perform in C++ Builder. Any regular form can be made into an MDI. This is simply taken care of by setting its FormStyle property to fsMDIForm and that's it. A form whose style is set as MDI has a sunken client area that indicates that it can host other form:
Copyright © 2003 FunctionX, Inc.
389
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
Visually creating an MDI is so simple that the only thing necessary is to add another form and set its FormStyle property to fsMDIChild. If you run such an application, it would provide all the basic functionality required of an MDI:
The first challenge of creating an MDI consists of performing the various assignments that, on one hand, allow a parent and a child to communicate, and on the other hand, allow the children to communicate. C++ Builder ships with a wizard that allows you to quickly create an MDI-based application. Unfortunately, this wizard creates only a text-based application. It does not give you the option to specify the type of application you want.
15.6 Dialog Boxes 15.6.1 Introduction A dialog box is a form with particular properties. Like a form, a dialog box is referred to as a container. It is the primary interface of user interaction with the computer. By itself, a dialog box means nothing. The controls it hosts accomplish the role of dialog between the user and the machine. Here is an example of a dialog box:
390
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
A dialog box has the following characteristics: •
It is equipped with the system Close button . As the only system button, this button allows the user to dismiss the dialog and ignore whatever the user would have done on the dialog box.
•
It cannot be minimized, maximized, or restored. A dialog box does not have any other system button but Close.
•
It is usually modal, in which case the user is not allowed to continue any other operation on the same application until the dialog box is dismissed.
•
It provides a way for the user to close or dismiss the dialog. Most dialog boxes have an OK and a Cancel buttons, although this depends on the application developer. When the dialog has the OK and the Cancel buttons, the OK button is configured to behave as if the user had pressed Enter. In that case, whatever the user had done would be acknowledged and transferred to the hosting dialog box, window, or application. Pressing Esc applies the same behavior as if the user had clicked Cancel.
15.6.2 Dialog Box Creation There are two main actions you can perform on a form to qualify it as a dialog box; but normally, these are only suggestions, not rules. Based on the Microsoft Windows design and standards, to create a dialog box:
You should set a form’s BorderStyle property to bsDialog. Setting this property automatically removes the system Minimize and Maximize buttons and preserves only the system Close button . This fulfills the first suggested design of a dialog box. If you insist on having other system buttons, you can add them using the BorderIcons property.
The second action you should take is to provide a way for the user to close the dialog box. A dialog box should have at least one button labeled OK. This button allows the user to acknowledge the message of the dialog box and close it by clicking the button. If the user press Enter, the dialog box should also be closed as if the OK button was clicked. To fulfill this second requirement, from the Standard tab of the Component Palette, you can click the button
and click the dialog box.
Often the user will be presented with various options on a dialog box and may be asked to make a decision on the available controls. Most of the time, if you are creating such a dialog box, besides the OK button, it should also have a Cancel button. The OK button should be the default so that if the user presses Enter, the dialog box would be closed as if the user had clicked OK. Clicking OK or pressing Enter would indicate that, if the user had made changes on the controls of the dialog box, those changes would be acknowledged and kept when the dialog box is closed and usually the changed values of the control would be transferred to another dialog box or form. The Cancel button is used to allow the user to dismiss whatever changes would have been made on the controls of the dialog box. The dialog box should also be configured so that if the user presses Esc, the dialog box would be closed as if the user had clicked Cancel. To fulfill these rules for the OK and the Cancel buttons, the Default property of the OK button should be set to true and the Cancel property of the Cancel button should be set to true.
Copyright © 2003 FunctionX, Inc.
391
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
Besides the OK and the Cancel buttons, a dialog box can be created with additional buttons such as Finish or Help, etc. It depends on its role and the decision is made by the application developer.
15.6.3 Modal Dialog Boxes There are two types of dialog boxes: modal and modeless. A Modal dialog box is one that the user must first close in order to have access to any other framed window or dialog box of the same application. One of the scenarios in which you use a dialog box is to create an application that is centered around one. In this case, if either there is no other form or dialog box in your application or all the other forms or dialog boxes depend on this central dialog box, it must be created as modal. Such an application is referred to as dialog-based. Some applications require various dialog boxes to complete their functionality. When in case, you may need to call one dialog box from another and display it as modal. Here is an example: The Paragraph dialog box of WordPad is a modal dialog box: when it is displaying, the user cannot use any other part of WordPad unless he or she closes this object first
After creating a dialog used as an addition to an existing form or an existing dialog box, to call it as modal, use the ShowModal() method.
15.6.4 Modeless Dialog Boxes A dialog box is referred to as modeless if the user does not have to close it in order to continue using the application that owns the dialog box:
392
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 15: Forms and Dialog Boxes
The Find (and the Replace) dialog box of WordPad (also the Find and the Replace dialog boxes of most applications) is an example of a modeless dialog box. If it is opened, the user does not have to close it in order to use the application or the document in the background.
Since the modeless dialog box does not display its button on the task bar, the user should know that the dialog box is opened. To make the presence of a modeless dialog box obvious to the user, it typically displays on top of its host application until the user closes it. Just like the Modal dialog box, to create a modeless dialog box, once you have added a form to your application, to call it as a modeless dialog box, simply call the Show() method. The only thing you need to take care of is the fact that the dialog box can disappear behind the form that called it. The fundamental difference between the ShowModal() and the Show() methods is that the first displays a modal dialog box, which makes sure that the called dialog box cannot go in the background of the main application. By contrast, the Show() method only calls the dialog box every time it is requested. For this reason, it is your responsibility to make sure that the modeless dialog box always remains on top of the application. This is easily taken care of by setting the FormStyle property of the form to fsStayOnTop. There are two main ways a normal modeless dialog box can be dismissed:
If the user has finished using it, he can close it and recall it at will
When the form or application that owns the modeless dialog box is closed, the form or application closes the modeless dialog if it is opened; this means that you do not need to find out whether a modeless dialog box is still opened when the application is being destroyed: either the user or the application itself will take care of cleaning it
15.6.5 C++ Builder Template Dialog Boxes To make your development experience a little faster, C++ Builder ships with a lot of dialog boxes ready for use. These dialog boxes are created as modal and are equipped with an OK and a Cancel buttons. These dialogs are available on the Dialogs property page of the New Items dialog box. To use a C++ Builder dialog template, on the Standard toolbar, you can click the New button . In the New Items dialog box, click the Dialogs tab, select the desired template and click OK. Most dialog boxes are equipped with a bevel for aesthetic purposes and two or three buttons. The OK buttons are configured with the Default Copyright © 2003 FunctionX, Inc.
393
Chapter 15: Forms and Dialog Boxes
Borland C++ Builder Programming
property set to true and the ModalResult property set to mrOk. The Cancel buttons have their Cancel property set to true and their ModalResult set to mrCancel. You can reposition the controls and add new ones to the form as you wish. You can add one of the template dialog boxes to your application. Alternatively, if you want to base your application on one of these templates, you can remove the default form from your application.
Practical Learning: Using a Template Dialog Box
394
1.
Start a new project with the default form
2.
On the main menu, click File -> New -> Other…On the New Items dialog box, click the Dialogs tab. Click Standard Dialog (Vertical):
3.
Click OK.
4.
Press F12 to access the Code Editor. Click Unit1.cpp to display its code.
5.
Once Unit1.cpp is displaying, right-click in the Code Editor and click Close Page. You will be asked whether you want to Save the changes of the Unit1 unit:
6.
Click No
7.
To test the dialog box, on the main menu, click Run -> Run
8.
After using the application, close it and return to Bcb
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 16: Controls Containers
Chapter 16: Controls Containers 16.1 The Form The form, implying the dialog box, is the primary object used to host, hold, or carry other controls of the application. Since we have had a thorough review of forms and dialog boxes already, we will not spend any more time on them.
16.2 The Frame 16.2.1 Introduction A frame is a type of control container that resembles a form. Like a form, when you create a frame, it possesses its own unit where its children can be programmatically managed. Unlike a form, and like all the other containers we will review after this one except the data module, a frame should be embedded on a form that would act as its ultimate parent. Unlike most other containers except for the data module, users do not see a frame and are not aware of its presence. It is used only by the programmer. A frame is used for better management of controls because, like a form, a frame is created as a separate entity with a body independent of a form.
Practical Learning: Introducing Frames 1.
Start a new project with its default form
2.
Save it in a new folder named InterestAndDiscount1
3.
Save the unit as Exercise and save the project as InterestDiscount
4.
Set its properties as follows Caption: Interest and Discount BorderStyle: bsDialog Name: frmMain ShowHint: true
5.
Save All
16.2.2 Frame Creation There are two general steps to making a frame available to your application 1.
You must create a “physical” frame. This can be done from the main menu where you would click File -> New -> Frame. You can also click File -> New -> Other… Then, in the New Items dialog box, you would select Frame. Any of these actions would position an empty rectangular object on the screen. In the same way, you can create additional frames as needed. Once a frame is
Copyright © 2003 FunctionX, Inc.
395
Chapter 16: Controls Containers
Borland C++ Builder Programming
available, you can position and design controls on it as you would proceed for a form. There is no restriction on the types of controls you can place on a frame. 2.
Once the frame exists, to embed it onto a form, from the Standard tab of the Component Palette, you can click the Frame button and click the form. As soon as you click the form, you would be asked, from a dialog box, to specify what frame would be placed where you clicked.
After creating and embedding a frame, you can change its controls in either the form or the frame. Anything you do in one, such as adding, removing, or resizing controls, would be automatically updated on the other. When a frame has focus at design time, you can change its controls as you see fit. From the form on which a frame is embedded, to programmatically access a control placed on that frame, do so indirectly through the frame. For example, the following code would change to blue the background color of an edit control named Edit2 that is placed on a frame named Frame21 and created in Unit2: //--------------------------------------------------------------------------#include #pragma hdrstop #include "Unit1.h" #include "Unit2.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma link "Unit2" #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { Frame21->Edit2->Color = clBlue; } //---------------------------------------------------------------------------
A frame control is based on the TFrame class which is in turn based on TCustomtFrame.
Practical Learning: Using Frames
396
1.
On the main menu, click File -> New -> Frame
2.
Change the Name of the frame to fraSimpleInterest
3.
Design the frame as follows:
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Control Label Edit Label Edit Label Edit Button Label Edit Label Edit
Chapter 16: Controls Containers
Caption or Text Principal 0 Interest Rate 0 Months 0 C&alculate Interest Earned 0 Amount Earned 0
Name lblPrincipal edtPrincipal lblInterestRate edtInterestRate lblMonths edtMonths btnCalculate lblInterestEarned edtInterestEarned lblAmountEarned edtAmountEarned
4.
On the Standard toolbar, click the New button box, double-click Frame
5.
Change its Name to fraDeptStore and design it as follows:
Control Label Edit Label Edit Button
Copyright © 2003 FunctionX, Inc.
Caption or Text Marked Price 0 Tax Rate 0 C&alculate
and, on the New Items dialog
Name lblMarkedPrice edtMarkedPrice lblTaxRate edtTaxRate btnCalculate
397
Chapter 16: Controls Containers
Borland C++ Builder Programming
Label Edit Label Edit
Tax Amount 0 Net Price 0
lblTaxAmount edtTaxAmount lblNetPrice edtNetPrice
6.
To select the main form, on the View toolbar, click the View Form button. In the list, double-click frmMain
7.
While the form is displaying, in the Standard tab of the Component Palette, click the Frames button
8.
Click on the top-left section of the form
9.
In the Select Frame To Insert dialog box, click fraSimpleInterest
10. Click OK 11. Once again, on the Standard tab of the Component Palette, click the Frames button and click an unoccupied area on the right section of the form 12. In the Select Frame To Insert dialog box, double-click fraDeptStore 13. Adjust the positions of the frames as you see fit, using the same techniques we reviewed for moving controls
398
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 16: Controls Containers
14. Test the application. Then close it and return to Bcb 15. Save All
16.3 The Data Module 16.3.1 Introduction Like the form and the frame controls, a data module is a control container that is created independent of a form. Like the form and the frame, once created, a data module has its own unit that can be used to support its child controls. Unlike all other containers we will review here, a data module can receive or host only non-visual controls; that is, controls that the user does not see.
16.3.2 Data Module Creation A data module is particularly easy to create and manage since there is no true graphical design to perform on its children. This simply means that the controls placed on a data module do not have a location or dimensions. They can be placed anywhere in its window: the user will see neither the data module nor its children. To create a data module, on the main menu, you can click File -> New -> Data Module. Alternatively, you can open the New Items dialog box from where you would select Data Module. Any of these two actions would place a rectangular window with a white background on your screen. To use a data module, you can position only non-visual controls to it. In fact, when the data module window is selected or has focus and you click a tab of the Component Palette, only non-visual controls would be available. For example, here is the Standard property page of the Component Palette when a data module is the top window:
Copyright © 2003 FunctionX, Inc.
399
Chapter 16: Controls Containers
Borland C++ Builder Programming
To use a control, click it from the Component Palette and place it on the data module. Here is an example with various controls:
To access a control placed on a data module, do so indirectly through the data module as a control. For example, the following code accesses the ColorDialog1 object from a data module named DataModule2 when the user double-clicks the form. If the user clicks OK after changing the color of the dialog box, the new color would be used to paint the form: //--------------------------------------------------------------------------#include #pragma hdrstop #include "Unit1.h" #include "Unit2.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { if( DataModule2->ColorDialog1->Execute() ) Color = DataModule2->ColorDialog1->Color; } //---------------------------------------------------------------------------
16.4 The Panel Control 16.4.1 Introduction A panel is a visible rectangular object that can provide two valuable services for application design. A panel allows you to design good-looking forms by adjusting colors and other properties such as Align and Style. A panel is also a regularly used control container because it holds and carries controls placed on it. When a panel moves, it does so with the controls placed on it. Panels are not transparent. Therefore, their color can be
400
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 16: Controls Containers
changed to control their background display. A panel is a complete control with properties, methods, and events.
16.4.2 Characteristics of a Panel Unlike the form, the frame, and the data module, during design, a panel must primarily be positioned on another container, which would be a form, a frame, or another panel. At run time, you can “detach” a panel from its parent, which is not possible at design time. To add a panel to a container, you can click the Panel control from the Standard tab of the Component Palette. Then click in the desired location on an existing container. After adding the panel to a container, you may want to “glue” it to one border or corner of its host, as you would do with the Anchors property. Unlike anchoring a control, the Align property allows you to fix a control to one border of its host. Some text-based controls that use a caption allow you to control how such text would be aligned as a paragraph. The Alignment property is a TAlignment enumerator that lets you align text to the left, the center, or the right. If the control, such as a panel would not display a caption, you can leave this property “as is”. To change the Alignment property, click it on the Object Inspector to reveal its combo box. Click the arrow and select the desired value: taLeft, taCenter, or taRight. A panel object is drawn with two borders: an inner and an outer borders. The characteristic of a border is referred to as a bevel effect, which controls the border appearance of a panel. The effects of this characteristic are managed through the BevelInner and the BevelOuter properties. Using their combination, you can produce special effects as follows:
Figure 27: Bevels Effects on a Panel The other property to take into consideration is the BevelWidth value. This is an integer that controls the border of a panel by setting the distance between the inner and outer bevels. Here is the effect of a 2 value on the above pre-selections:
Copyright © 2003 FunctionX, Inc.
401
Chapter 16: Controls Containers
Borland C++ Builder Programming
Figure 28: Effect of the Bevel Width on a Panel The BorderStyle property controls the line used to draw the border of the panel. It can have one of two values. The bsNone value, which is its default, indicates that there will not be a line drawn on the border. When the BorderStyle property is set to a bsSingle value, a line of 1 pixel is drawn around the panel. The above panels were drawn with a bsNone value for the BorderStyle. Here is the effect produced on panels that have a BorderWidth value of 2 and the BorderStyle set to bsSingle:
A panel can be used as a button, in which case the user would click it to initiate an action. A panel can also simply display a message to the user. In any case, if you want to display a word or a group of words, you can use the Caption property to show it. A property that is highly used on panels (and forms) is the Color. If you change the Color property, the new color would cover the face of the panel.
402
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 16: Controls Containers
Practical Learning: Using Panel Controls
1.
Start a new application with its default form
2.
In the Object Inspector, click Caption and type Dockable Windows
3.
From the Standard page of the Component Palette, double-click the Panel control .
4.
Set the Align property of the new Panel to alLeft. Set the BevelOuter property to bvLowered. Also set the DockSite property to true
5.
Delete the Caption value.
6.
While Panel1 is still selected, from the Standard page, double-click the Panel control . Set the Align property to alClient. Set the DragKind to dkDock and the DragMode to dmAutomatic. Optionally set the BevelOuter to bvNone and its Caption to Window Floater
7.
While the Panel2 control is still selected, from the Standard tab of the Component Palette, double-click the Memo control
. Set its Align property to alClient
8.
To test it, press F9. At this time, the Memo1 control is dockable but there is a problem. When the application starts, the Memo does not appear the way we want it, even though we can drag it away and bring it back. Close the form.
9.
Double-click an empty area on the form to access the form's OnCreate event. Implement it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TRect Recto(Left, Top, Left + Panel1->Width, Top + ClientHeight); Panel2->ManualFloat(Recto); Panel2->ManualDock(Panel1, Panel1, alClient); } //---------------------------------------------------------------------------
10. To test the program again, press F9 11. Press F12 to display the form Copyright © 2003 FunctionX, Inc.
403
Chapter 16: Controls Containers
Borland C++ Builder Programming
12. Remember that, at this time, if the user closes the docking or floating window, it disappears completely. To recover from that, you can provide a menu or a button that would easily toggle the appearance or disappearance of the docking window. From the Standard tab of the Component Palette, click Panel and click an unoccupied area on the form 13. On the Object Inspector, click Caption and type Toggle 14. On the form, double-click Toggle to access its OnClick event and implemented it as follows (remember that, in our example, Panel2 is the real docking window while Panel1 is just the host of the floating window): //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Panel2->Visible = !Panel2->Visible; } //---------------------------------------------------------------------------
15. Test the program
16.5 Property Sheets and Property Pages 16.5.1 Overview As your application becomes crowded with various controls, you may find yourself running out of space. To solve such a problem, you can create many controls on a form or container and display some of them only in response to some action from the user. The alternative is to group controls in various containers and specify when the controls hosted by a particular container must be displayed. This is the idea behind the concept of property pages. A property page is a control container that appears as a form or a frame. A property page can appear by itself, as one. Here is an example:
404
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 16: Controls Containers
In most other cases, a property page appears in a group with other pages. It functions like a group of pieces of paper placed on top of each other. Each piece is represented by a tab that allows the user to identify them:
To use a property page, the user clicks the header, called a tab, of the desired page. This brings that page up and sends the others in the background:
If the user needs access to another page, he can click the desired tab, which would bring that page in front and send the previously selected to the back. The pages are grouped and hosted by an object called a property sheet. Like a form, the pages of a property sheet are simply used to carry or hold other controls. The major idea of using a property sheet is its ability to have many controls available in a smaller container.
Copyright © 2003 FunctionX, Inc.
405
Chapter 16: Controls Containers
Borland C++ Builder Programming
16.5.2 Property Sheet Creation Property pages of a property sheet are also referred to as tab controls. In the VCL, a property sheet is created using the TPageControl class. The control from this class, the PageControl control, serves as the property sheet. To implement a property sheet in your application, from the Win32 tab of the Component Palette, you can click PageControl and click in the form that would be used as the property sheet platform. To create, add, or remove the actual property pages of the property sheet, you can right-click the PageControl control:
To create or add a property page, you can click New Page; you can do this continuously until you have added all desired pages.
If you had added a page by mistake or you do not want a particular page anymore, you can remove it. To do this, first click the page's tab. Then right-click in the middle of the PageControl control and click Delete Page.
Many of the effects you will need on pages have to be set on the PageControl and not on individual pages. This means that, to manage the characteristics of pages, you will change the properties of the parent PageControl control. At any time, whether working on the PageControl control or on one of its property pages, you should first know what object you are working on by selecting it. To select the PageControl control itself, you have two main options:
Any time you want to select the PageControl, click an unoccupied area on the right side of the most right tab
While one tab is selected, click another tab
If you want the property sheet to occupy the whole form or to occupy a section of it, you can specify this using the Align property. A PageControl by itself can be used as a control and you can place the necessary controls on it, but this is usually not the reason you would need a PageControl control. If you want the property pages to have bitmaps on their tabs, you should first add a series of images using an ImageList control and then assign that control to the Images property of the PageControl object.
406
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 16: Controls Containers
If you have many property pages and the width of the PageControl cannot show all tabs at the same time, the control would add two navigation arrow buttons to its top right corner to let the user know that there are more property pages:
By default, the navigation buttons would come up because the control uses a property that controls their availability. If you do not want the navigation arrows, you can set the MultiLine property of the PageControl control to true. This would create cascading tabs and the user can simply select the desired property page from its tab:
As you are adding pages to a PageControl control, the tabs of the pages are positioned on the top border of the PageControl area. You can reposition them to the left, the right, or the bottom borders of the control. The placement of the tab is set using the TabPosition property of the PageControl. The possible values of this property are tpTop, tpLeft, tpRight, and tpBottom. TabPosition: tpLeft
Copyright © 2003 FunctionX, Inc.
TabPosition: tpTop
407
Chapter 16: Controls Containers
TabPosition: tpBottom
Borland C++ Builder Programming
TabPosition: tpRight
If you want to create a discrete property sheet and do not want to display the traditional tabs, you can replace the tabs with buttons. This option works only if the TabPosition property is set to tbTop, that is, only if the tabs would have been positioned to the top border of the control. To display buttons instead of tabs, use the Style property. Its values are tsTabs, tsButtons, and tsFlatButtons. The button options produce the following effects: Style: tsButtons
Style: tsFlatButtons
I I f y o u s e l e c t
408
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 16: Controls Containers
If you attempt to set the Style property to a buttons value when the tabs are not positioned on top, you would receive an error message:
After adding the PageControl control to your form and after adding one or more property pages, the property pages are created where the PageControl control is positioned and its dimensions are used by the eventual property pages. This means that, if you want a smaller or larger property sheet, you must modify the dimensions of the PageControl control and not those of the property pages, even though each property page has a location (Left and Top properties) and dimensions Height and Width properties).
16.5.3 Property Pages Creation We saw that, to add property pages to a PageControl object, you can right-click it and click New Page. You can also add pages programmatically, of course. To make use of a property page, for example to add controls to it, you must first select the desired property page. A page is selected when its tab is in front. If there are other property pages, their tabs would be in the back. The page whose tab is in front or selected is also referred to as the Active Page. To select a property page, you have three main options:
Right-click the PageControl control on the form and click Next Page or Previous Page. The pages are considered items of a rotating array. If the second page out of three is displaying and you click Next Page, the third page would be selected. If you are on the third page out of three and you click Next Page, the first page would be selected.
Clicking Previous Page would have the reverse effect of Next Page
On the form, you can click its tab. This would bring the page to the front and send the other(s), if any, to the back
With the PageControl itself having focus on the form, on the Object Inspector, you can select the desired page using the ActivePage property
While the PageControl control is selected, on the Object Inspector, type the array index of the page in the TabIndex property. The pages are store in a zero-based array with the first having an index of 0 and the second an index of 1, etc. If you do not want any tab selected, set this property to a negative integer. If you type a value higher than the total number of pages - 1, the previous page selected, if any, would be selected again.
Like all other controls, the names of property pages are cumulative. As you add them, the first would be named TabSheet1, the second would be TabSheet2, etc. If you plan to programmatically refer to the property pages, you should give them more explicit names. As done with any other control, to set the name of a property page, after selecting it, on the Object Inspector, change the value of the Name property. If you have added pages but do not like their sequence, especially after adding the desired controls, you can move the page and change their sequence. The property pages are stored in a zero-based array. Each page has an index represented by the PageIndex Copyright © 2003 FunctionX, Inc.
409
Chapter 16: Controls Containers
Borland C++ Builder Programming
property. The first page has a PageIndex value of 0, the second, if available, has a value of 1, etc. To move the property page, after selecting it, on the Object Inspector, change its PageIndex value. The value must be an unsigned integer less than the total number of pages. Probably the first obvious characteristic of a property page is the word or string that identifies it to the user. That is, the string that displays on the tab of a property page. This is known as its title or caption. By default, the captions are set cumulatively as the pages are added. Usually you will not use these titles because they are meaningless. To display a custom title for a property page, first select it and, on the Object Inspector, change the value of the Caption property. You can also change the title of a property page programmatically, for example, in response to an intermediary action. To change the title of a page, assign a string to its Caption property. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { pgeSecurity->Caption = "Security Issues"; } //---------------------------------------------------------------------------
If you had associated an ImageList control with the PageControl object and you want to use images on the title of a property page, specify the desired image using the ImageIndex property. You can use the images on some or all property pages. If you do not want an image on a certain property page, set its ImageIndex value to -1. If for some reason you do not want to show a property page but do not want to delete it, you can hide it by setting its TabVisible property to false.
Practical Learning: Creating a Property Sheet 1.
Start a new project with its default form
2.
Change the Caption of the form to Algebra and Geometry
3.
Set its BorderStyle property to bsDialog
4.
Set the dimensions as Height = 402 and Width = 460
5.
From the Standard tab of the Component Palette, add a button with the following properties: Caption = OK, Default = true, Left = 280, Name = btnOK, and Top = 344
6.
Add another button with the following properties: Cancel = true, Caption = Cancel, Left = 364, Name = btnCancel, and Top = 344
7.
Double-click each button, press Tab and type Close();
8.
To save the project, on the Standard toolbar, click the Save All button
9.
Create a folder called AlgebraGeometry
10. Save the unit as Main and save the project as Geometry 11. On the Component Palette, click the Win32 tab. Click PageControl and click on the form. 12. While the new PageControl object is still selected, on the Object Inspector, set its properties as follows: Height = 328, Left = 8, Name = shtGeometry, Top = 8, Width = 432 13. Right-click in the middle of the PageControl on the form and click New Page 14. Add two more property pages 410
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 16: Controls Containers
15. Click TabSheet1 then click in the body of TabSheet1 to select the first property page. 16. Make sure the top combo box of the Object Inspector displays TabSheet1. Click Caption and type Quadrilateral 17. Click Name and type pgeQuadrilateral 18. On the form, click TabSheet2 then click the large area under TabSheet2. Type pgeCircular and change its Caption to Circular 19. Click TabSheet3 and click the wide area under TabSheet3. Change its caption to 3Dimensional and its name to pge3Dimensional 20. Click the empty area on the right-side of the 3-Dimensional tab to select the PageControl control. On the Object Inspector, set the ActivePage to pgeQuadrilateral. 21. From the Additional tab of the Component Palette, click Image and click in the body of the Quadrilateral property page. 22. While the Image1 control is still selected, on the Object Inspector, double-click the right area of Picture and, using the Load button, select the Quadrilateral image from the Images folder 23. Set its properties as follows: Height=297, Left=0, Top=0, Width=161 24. In the same way, add an Image control to the Circular property page and set its Picture to Circular. Set the following properties: Height=297, Left=0, Top=0, Width=161 25. Add an Image control to the 3-Dimensional property page and set its Picture to Dimension3. Set the following properties: Height=297, Left=0, Top=0, Width=161 26. By selecting the Label and Edit controls from the Standard tab of the Component Palette, design each page as follows:
Copyright © 2003 FunctionX, Inc.
411
Chapter 16: Controls Containers
Borland C++ Builder Programming
27. From the Quadrilateral to the 3-Dimensional property pages, from left to right to down on each page, set the names of the Edit and Button controls as follows: edtSquareSide, btnSquareCalculate, edtSquarePerimeter, edtSquareArea, edtRectLength, edtRectHeight, btnRectCalculate, edtRectPerimeter, edtRectArea, edtCircleRadius, btnCircleCalculate, edtCircleCircumference, edtCircleArea, edtEllipseRadius1, edtEllipseRadius2, btnEllipseCalculate, edtEllipseCircumference, edtEllipseArea, edtCubeSide, btnCubeCalculate, edtCubeArea, edtCubeVolume, edtBoxBase, edtBoxHeight, edtBoxWidth, btnBoxCalculate, edtBoxArea, edtBoxVolume 28. Save your project
412
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 16: Controls Containers
16.6 Wizard Pages 16.6.1 Overview
A wizard is a series of dialog pages that display in sequence, one after another. A wizard is usually used to guide a user through a process of performing a task.
16.6.2 Wizard Creation Although wizards are (still) popular, the VCL does not offer strong support for them. That is because in the VCL, like many scenario implementations, wizards are not particularly difficult. Everything depends on what you want to do with a wizard.
Copyright © 2003 FunctionX, Inc.
413
Chapter 17: Aesthetic and Graphics Controls
414
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 17: Aesthetic and Graphics Controls
Chapter 17: Aesthetic and Graphics Controls 17.1 Bevels 17.1.1 Overview To make your development more striking, the VCL provides objects that can be used uniquely for their aesthetic appearance. Such is the case for the Bevel, the Image, and the Shape controls, etc. Some other controls can be used as intermediate or carriers of aesthetic objects that other controls may need but cannot carry on their own. A bevel is a VCL control used to enhance the display of a form by adding a box, a frame or a line. A bevel shares a lot of the other controls’ properties, this means that you can modify them at design and/or run times. To add a bevel object to your form, click the Bevel button the Component Palette and click on a container.
on the Additional tab of
17.1.2 Characteristics of Bevel In its uniqueness, although the bevel does not have much functionality, some of its properties make it a valuable accessory for your form’s look. By default, a bevel does not have a particular alignment on the form; it completely depends on the developer. Unlike the form and panel controls, the bevel control is not a container. Therefore, although you can place controls inside a bevel, the bevel does not control their position or alignment. In other words, the bevel does not “own” them. The two most important properties of a bevel are its shape and its style. The Shape property is a TBevelShape enumerator that controls how the bevel appears. You can set the bevel to appear as a line, to show borders or to be empty. To set the shape of the bevel, click it on the form to select it. On the Object Inspector, click Shape to reveal its combo box and select from the list. The values and their effects are as follows:
Copyright © 2003 FunctionX, Inc.
415
Chapter 17: Aesthetic and Graphics Controls
Borland C++ Builder Programming
To set the bevel’s shape programmatically, use code such as this: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Bevel1->Shape = bsFrame; } //---------------------------------------------------------------------------
The Style property is a TBevelStyle enumerator that specifies whether the bevel will be lowered or raised with regard to the surface of its host container. Its values are bsLowered (the default) and bsRaised. The above bevels were created with the BevelStyle set to bsLowered. Here is the effect when the BevelStyle is set to bsRaised:
416
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 17: Aesthetic and Graphics Controls
Practical Learning: Using a Bevel Control
1.
Create a new application with the starting form.
Copyright © 2003 FunctionX, Inc.
417
Chapter 17: Aesthetic and Graphics Controls
Borland C++ Builder Programming
2.
Set the dimensions of the form as follows: Height: 456 Width: 504 BorderStyle: bsDialog
3.
Click the + sign on the HorzScrollBar and VertScrollBar fields and set each of their Visible properties to false
4.
On the Component Palette, click the Additional tab and double-click the Bevel control
5.
Set the bevel properties as follows: Align: alNone Height: 18 Left: 0 Shape: bsBottomLine Style: bsLowered Top: 184 Width: 490
6.
On the Component Palette, click the Standard tab. Double-click the Panel control
7.
Change the panel’s properties as follows: Caption: &Details >> Cursor: crVSplit Height: 25 Left: 408 Top: 160 Width: 75 Also add a few visual controls on the top and the bottom sections of the form
8.
Double-click an unoccupied area on the form to initiate the form’s OnCreate event. Implement it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Height = 228; Bevel1->Visible = False; } //---------------------------------------------------------------------------
9.
Press F12 to display the form. Double-click the Details panel to initiate its OnClick event. Implement it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Panel1Click(TObject *Sender) { if( Height == 228 ) { Height = 456; Bevel1->Visible = True; Button1->Caption = "&No Details"; } else if( Height == 456 ) { Height = 228; Bevel1->Visible = False; Button1->Caption = "&Details >>";
418
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 17: Aesthetic and Graphics Controls
} } //---------------------------------------------------------------------------
10. To test the application, on the Debug toolbar, click the Run button.
17.1.3 Bevel Methods The Bevel control has no other methods than the constructor and the destructor. All of its methods are derived from its parent and ancestor classes. The bevel control is based on the TBevel class. If you want to dynamically create a bevel, declare a pointer to TBevel. Using the new operator, assign it the bevel constructor specifying the control component as the form on which the bevel will be positioned. You must also specify the parent of the bevel. You can dynamically create a bevel inside of a function or another control’s event. After creating the control, you can programmatically set its properties. If the control was created locally, set these properties in the function or event: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TBevel* Bvl = new TBevel(Form1); Bvl->Parent = this; Bvl->Height = 115; Bvl->Left = 8; Bvl->Shape = bsFrame; Bvl->Style = bsRaised; Bvl->Top = 8; Bvl->Width = 210; } //---------------------------------------------------------------------------
If you had created the control in the header file, you can use a function or event to initialize the control.
17.2 The Image Control 17.2.1 Introduction A picture is an object you display in your application for any desired purpose. In order to display a picture, you must have it somewhere so your application can either locate or import it. To support the display of a picture, the VCL provides the TImage class.
17.2.2 Image Control Fundamentals TImage is a class that carries as much information as necessary to display a picture on a form. The picture can be in any of the popular formats (jpeg, bmp, ico, etc). The easiest way to add a picture to a form is to add an Image control to a form after clicking the Image button
Copyright © 2003 FunctionX, Inc.
from the Additional property page of the Component Palette. When
419
Chapter 17: Aesthetic and Graphics Controls
Borland C++ Builder Programming
you click the container that will hold the Image control, a primary dashed rectangle is drawn as the placeholder of the picture:
The borders of the control display as dash lines to indicate that they will not display when the picture comes up. Like any other visual control, you can resize the Image object using the handles on its borders and/or corners. The most important piece of information the control needs is probably a picture. This can be specified using the Picture property of the Object Inspector. After specifying the picture for the Image object, you may find out that your image is smaller or bigger than the rectangle drawn on the form when you added the Image control. You have many options to solve this. If you want to use the whole actual size of the picture, you can manually resize the border of the Image control to accommodate the picture. Since the border of the Image control will not appear when the picture displays on the form at run time, you can enlarge the size of the Image control to be greater than the picture itself:
If you want to keep the size of the Image, you can instead resize the picture itself to fit in the Image control’s rectangle. This is taken care of by the Stretch Boolean property. The default value of the Stretch property is false, which means that you decide how to deal with the difference between the size of the Image control and the actual size of the picture. Using the Object Inspector, if you set the Stretch property to true, the picture would be automatically resized to use the whole size of the Image control. With Stretch 420
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 17: Aesthetic and Graphics Controls
set to true, even if you manually resize the picture or the Image by dragging the border, the picture and the Image would always have the same size. If you set the Stretch property’s value to true, and if you resize the image by dragging one of the borders, the picture may become distorted. For example, the picture of a slim person may make him or her appear larger (or fat):
If you want to keep the ratio between the length and the height of the picture balanced during resizing, you can use the Proportional Boolean property. Its default value is false, in which case the picture can be resized "as is". To respect the ratio of the dimensions, set the Proportional property to true:
After adding a picture to an Image control, if the picture is smaller than the size of the Image control, the picture would be positioned in the top-left corner of the control. The positioning of the picture inside an Image is controlled by the Center property. Its default value is false, which means the top-left corner of the picture coincides with the top-left corner of the Image rectangular border. If the picture you are using is smaller than the size of the Image control but want to keep the size of the Image control, which could be Copyright © 2003 FunctionX, Inc.
421
Chapter 17: Aesthetic and Graphics Controls
Borland C++ Builder Programming
valid during design, you can position the picture in the horizontal center and vertical middle of the borders of the Image. This is done by setting the Center Boolean property to true.
17.3 The Paint Box Control 17.3.1 Introduction As its name suggests, a paint box is a control whose main purpose is to provide a platform for painting. The user never sees a paint box as it does not have borders and it is accessible only to the programmer. Like many other controls, the paint box provides a Canvas object that receives and manages the painting assignments performed on this control. In fact, the paint box has its own OnPaint() event for convenience. To provide a paint box to your application, on the System tab of the Component Palette, click the PaintBox button
and click on the container that will own it.
17.3.2 Characteristics of a Paint Box Because a paint box’ main purpose is to provide an area for painting, the only inherent characteristic it has is a rectangular area which makes available a Canvas property. This Canvas member gives access to all necessary tools used to paint, including pens, brushes, colors, fonts, access to bitmaps, etc. If you plan to use the whole area of the owner of the paint box, make sure you use the Align property to specify this.
422
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
Chapter 18: Command Controls 18.1 Command Buttons 18.1.1 Overview A button is a Windows control used to initiate an action. From the user’s standpoint, a button is useful when clicked, in which case the user positions the mouse on it and presses one of the mouse’s buttons. There are various kinds of buttons. The most common and most regularly used is a rectangular object. In some programming environments, the classic button is called a Command Button. There are other controls that can serve as click controls and initiate the same behavior as if a button were clicked. Such controls include a label, a static control, or a panel. For a programmer, a button needs a host or container. The container could be a form, a toolbar, etc. Once you have decided where the button will reside, you can use the drag n’ drop process of Borland C++ Builder to add it to your program. The most popular button used in Windows applications is a rectangular control that displays a word or a short sentence that directs the user to access, dismiss, or initiate an action or a suite of actions. In C++ Builder, this control is implemented using the Button control
from the Standard tab of the Component Palette. Therefore, to add a button
to a container, click the Button control and click on the container, which is usually a form. Once you have added the control to your application, you can set its properties using the Object Inspector.
18.1.2 Button Properties For a user, the only things important about a button are the message it displays and the action it performs. The default property of a button is the Caption: this is the word or group of words that display(s) on top of the control, showing the message that would direct the user as to what the button is used for. By default, after adding a button to a form, the Caption property of the Object Inspector has focus; this allows you to just type a caption. When adding a button to a form, the Caption field would have focus only if the last action you performed on the Object Inspector was for a control that does not have Caption. If the last control you were working on has a Caption property but you were setting another property, when you add a button, the Caption field would not have focus. The most popular captions are OK and Cancel. The OK caption is set for a form or a dialog box that informs the user of an error, an intermediary situation, or an acknowledgement of an action that was performed on the dialog that hosts the button. The Cancel caption is useful on a button whose main parent (the form) would ask a Copyright © 2003 FunctionX, Inc.
423
Chapter 18: Command Controls
Borland C++ Builder Programming
question or request a follow-up action from the user. Whenever a dialog box allows the user to dismiss it without continuing the action, you should provide a button with a Cancel caption. The easiest way to change the caption on a control, on the Object Inspector, is to click the word Caption and type the desired text. After adding a button to a form (by design or with code), you can change its caption with code by calling the TControl::Caption property. For example, you can change the caption of a button as follows: //--------------------------------------------------------------------------void __fastcall TForm1::ChangeButtonCaption() { Button1->Caption = "Let Me Go!"; } //---------------------------------------------------------------------------
When a form is hosting many controls, including buttons, it is a good idea to set the control that has focus when the form opens. This is done using the form’s ActiveControl property. By default, as we know about the Tab Order, the order of controls on a container is directed by the order of adding them to the container. Even if a control was added last, and regardless of the Tab Order, you can set a certain control to have focus when the form launches. To set the ActiveControl at design time, give focus to the form. On the Object Inspector, click the ActiveControl field to reveal its combo box; click its combo box and select a control from the list. To set the active control programmatically, call the TForm::ActiveControl property (actually, the ActiveControl property belongs to the TCustomForm class). Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::SetTheActiveControl() { ActiveControl = Button3; } //---------------------------------------------------------------------------
A very important characteristic of a button is the Default property, which is a Boolean type. If you set its value to true, this button would have a thick border. If the user presses Enter any time, the action associated with the button would be executed. This is usually set on a dialog box with a button whose caption displays OK. Another useful property is Cancel. Using a Boolean variable, the Cancel property allows the user to press Esc to perform the same action that would be used to dismiss a dialog box. This is important on a dialog box if the button’s caption is Cancel or Close.
424
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
As mentioned already, a dialog box can be created with one or more buttons. If you create a modal dialog box and equip it with more than one button, when the user closes it, you must know what button the user had clicked. To support this, the VCL provides the ModalResult property. This property autonomously can control the behavior of the closing dialog box depending on the button that was clicked. The ModalResult property is an integer defined as TModalResult and provides various values that each can be used on a button of a dialog box. To set the desired value, after adding a button to a dialog box and while the button is selected, on the Object Inspector, access its ModalResult property and select the desired value.
The possible values of the ModalResult property are: Constant
Value
mrNone mrOk
0 idOK
mrCancel
idCancel
mrAbort mrRetry mrIgnore mrYes mrNo mrAll mrNoToAll mrYesToAll
idAbort idRetry idIgnore idYes idNo mrNo + 1 mrAll + 1 mrNoToAll + 1
Description No special role The user clicked OK or pressed Enter (assuming the button has the Default property set to true) to close The used clicked Cancel or pressed Esc (assuming the button has the Cancel property set to true) to close The user clicked Abort to close The user clicked Retry to close The user clicked Ignore to close The user clicked Yes to close The user clicked No to close The user clicked All to close The user clicked No To All to close The user clicked Yes To All to close
If the form that is hosting the button is not the first form or dialog (in other words, if the form is accessed by a call from another form), you can use the ModalResult property to conveniently associate an action. By default, the ModalResul is set to mrNone. The ModalResult property is an integer that represents a button that the user has clicked on a dependent dialog box. To use a button as a valid integral ModalResult value, set its ModalResult property. When coding the result of the user clicking one of the buttons, call the TForm::ShowModal() method (once again, the ShowModal() method actually belongs to the TCustomForm class) and assign it the corresponding value of the TModalResult integer. The following example uses two forms. The first form has a button used to call the second. The second form has buttons with different ModalResult values. After the user clicks the button on the first form, the second would display, the program simply finds out what button was clicked, and the programmer can act accordingly: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) {
Copyright © 2003 FunctionX, Inc.
425
Chapter 18: Command Controls
Borland C++ Builder Programming
if( Form2->ShowModal() == mrOk ) Caption = "I am OK now"; else if( Form2->ShowModal() == mrCancel ) Caption = "Soldier, dismiss!!!"; else { Form2->Close(); Caption = "What happened? Where are you?"; }
} //---------------------------------------------------------------------------
Practical Learning: Using Buttons
1.
Create a new project with the starting form
2.
From the Component Palette, on the Standard tab, click the Button control
3.
Click on the form. Notice that a button has been drawn on the form.
4.
To move the button, click and hold your mouse on it. Then drag to the desired location, for example, move it to the upper left section of the form.
5.
On the Standard tab of the Component Palette, double-click the Button control.
6.
On the form, move the second button, Button2, and position it under Button1
7.
On the form, click Button1 to select it.
8.
On the Object Inspector, click Caption and type &Close
9.
Notice that its caption has changed. Also notice the Caption field in the Object Inspector.
.
10. In the Object Inspector, click the Name field and type btnClose 11. On the form, click Button2 to select it. 12. Type btnQuit 13. Notice that this time, the name has changed because the Name field had focus. 14. On the Object Inspector, click the Caption field and type &Quit 15. To make this button dismiss its form when the Esc key is pressed, double-click the false value of the Cancel field to change it to true. 16. Click anywhere on the form and change its dimensions to Height = 152 and Width = 315. 17. To test the form, press F9. 18. After viewing the form, close it. 426
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
18.1.3 Button Events The most regular action the user performs on a button is to click it. Based on this, the default event on a button is OnClick. To initiate the OnClick event on a button, you can double-click it. One of the most regular actions you will assign to a button is to close its parent form when the user clicks the button. There are different reasons you would want to close a form. If the form that hosts the button displays an intermediary message to the user, you should provide all the necessary code to follow up after the user has clicked the button; this is the case of property sheets or wizard pages, among others. There are various ways you can close a dialog box from a button. The simplest way is to call the TCustomForm::Close() method. This dismisses the form. To close a form, you can also use the Win32 API’s PostQuitMessage() function. This function takes one argument that is an integer. The argument could be set to almost any integer value although it should be WM_QUIT.
Practical Learning: Using Button Events 1.
On the form, double-click the Close button. Notice that the Code Editor opens.
2.
Press Tab and type: Close();
3.
Click the arrow of the combo box on the top section of the Object Inspector and select btnQuit
4.
Click the Events tab.
5.
Double-click the empty box on the right side of OnClick.
6.
Notice the cursor on the Code Editor.
7.
Press Tab and type: PostQuitMessage(WM_QUIT);
8.
To test the form, on the Debug toolbar, click the Run button.
9.
Click the Close button. Notice that this action closes the form.
10. Click the Run button again to execute the program. 11. Press Esc. Notice that the form closes also and this takes you back to Bcb. 12. Change the PostQuitMessage as follows: PostQuitMessage(0);
13. Execute the program to test it. Click the OK button to close the form. 14. Press F12 to display the form. 15. Double-click the Quit button to get to the Code Editor. Notice that you are taken back to the Code Editor. Edit the PostQuitMessage() function as follows: PostQuitMessage(WM_DESTROY);
16. To test the form press F9. After viewing the form, close it. 17. To add another form, on the View toolbar, click the New Form button.
Copyright © 2003 FunctionX, Inc.
427
Chapter 18: Command Controls
Borland C++ Builder Programming
18. While the new form is still displaying, on the Object Inspector, click the Properties tab and set the following properties for the second form: BorderStyle = bsDialog Caption = Mission and Assignment Height = 260 Width = 395 19. Press and hold Shift. On the Component Palette, click the Button control. Release the Shift key. 20. Click on the form three times to add three buttons 21. On the Component Palette, click the arrow button selection.
to dismiss the Button control
22. Arrange the three buttons on the form to make them visible. 23. Set the properties of the first button as follows: Caption = OK Default = true ModalResult = mrOk 24. Set the properties of the second button as follows Cancel = true Caption = Cancel ModalResult = mrCancel 25. Set the properties of the 3rd button as follows: Caption = Abort ModalResult = mrAbort
26. On the Code Editor, click Unit1.cpp 27. To display the first form, press F12. 28. On the main menu, click File -> Include Unit Hdr…From the Use Unit dialog box, make sure Form2 is selected and click OKTo add another button, from the Standard tab of the component Palette, click the Button control.
428
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
29. Click on the form to add the button and move it to under the Quit button. Change its caption to Form &2 30. Double-click the Form 2 button and implement it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender) { Form2->ShowModal(); } //---------------------------------------------------------------------------
31. Add another button to the form and enlarge it to cover most of the unoccupied area on the form. Set the name of the new button to btnMessage and its caption to Message 32. While the new button is still selected, on the Object Inspector, click the field on the right side of the Font property (it displays (TFont)). 33. Set the characteristics to Font = Times New Roman Font Style = Bold Size = 14 34. Click OK
35. Double-click the new large button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnMessageClick(TObject *Sender) { if( Form2->ShowModal() == mrOk ) btnMessage->Caption = "I came back OK"; else if( Form2->ShowModal() == mrCancel ) btnMessage->Caption = "I was dismissed!!!"; else if( Form2->ShowModal() == mrAbort ) btnMessage->Caption = "Mission Impossible"; else btnMessage->Caption = "What's Happening?"; } //---------------------------------------------------------------------------
36. To test the project, click the Run button 37. After testing the application, close it 38. To create a new project, on the main menu, click File -> New…Make sure Application is selected and click OK. When asked to save the project, click No 39. On the Object Inspector, click Caption and type Form and Buttons Interaction 40. Click Height and type 280 41. Click Width and type 380 Copyright © 2003 FunctionX, Inc.
429
Chapter 18: Command Controls
Borland C++ Builder Programming
42. From the Standard tab of the Component Palette, double-click Button. 43. On the Object Inspector, click Caption and type &Juventus 44. Click Name and type btnTeam 45. Click Left and type 16. Click Top and type 16 46. On the Component Palette, double-click Button 47. On the Object Inspector, click the + of the Anchors properties. 48. Set its characteristics as follows: akLeft = false akTop = false akRight = true akBottom = true. 49. Cchange the following other properties: Caption = &Sidney Left = 288 Name = btnCity Top = 216 50. To test the project, press F9 51. To resize the form, position the cursor on the lower-right corner then drag down and right. Notice that the Sidney button is anchored to the bottom and right borders of the form 52. Close the form 53. On the Component Palette, double-click Button
.
54. Change it as follows: Caption = &Turtle Left = 288 Name = btnAnimal Top = 16 55. On the Component Palette, double-click Button
.
56. Set its properties as follows: Caption = &Hamburger Left = 16 Name = btnFood Top = 216 57. On the Component Palette, double-click Button 58. Change its properties as follows: Cancel = true Caption = Close Name = btnClose
430
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
59. On the form, double-click the Close button 60. Press Tab and type: Close();
61. Press F12 to display the form 62. Double-click Juventus, press Tab and type: BtnClose->Enabled = False;
63. To test the form, on the View toolbar, click the Run button. 64. Click Juventus. Notice that the Close button is disabled. 65. Click the Close button on the form. Notice that it is not working 66. To close the form, click its Windows Close button 67. To toggle enabling and disabling the Close button, change the OnClick event of the btnTeam button with: btnClose->Enabled = !btnClose->Enabled;
68. Press F12 to display the form 69. Double-click the Turtle button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnAnimalClick(TObject *Sender) { if( btnTeam->Caption == "&Juventus" ) btnTeam->Caption = "&Naples"; else if( btnTeam->Caption == "&Naples" ) btnTeam->Caption = "&Juventus"; } //---------------------------------------------------------------------------
70. To test the form, press F12 and press F9. 71. Click the Turtle button a few times and notice that the caption of the team button changes. Click the team (Juventus or Naples) button. Notice that the Close button is disabled and you cannot click it. Click the team button again to enable the Close button. Copyright © 2003 FunctionX, Inc.
431
Chapter 18: Command Controls
Borland C++ Builder Programming
72. Make sure you close the form using the Close button in the middle of the form. 73. Press F12 to display the form and click an unoccupied area of the form. 74. Click the Events tab. 75. Double-click the box on the right side of the OnResize field. 76. In the implementation of the event, press Tab and type: //--------------------------------------------------------------------------void __fastcall TForm1::FormResize(TObject *Sender) { btnAnimal->Left = Width - 92; btnClose->Top = (ClientHeight - btnClose->Height) / 2; btnClose->Left = (ClientWidth - btnClose->Width) / 2; } //---------------------------------------------------------------------------
77. Press F12 to display the form. To test the form, press F9. 78. Resize the form by enlarging or shrinking it. Notice how the turtle and the Close buttons are anchored to the borders of the form.
18.1.4 Button Methods The Button doest not have many methods to customize its behavior. This is because a button does not need more than what you would expect from it: point and click. Probably the only method you would use from a button is the constructor used to dynamically create a button. Most of the other methods used on a button are derived from member variables of the TWinControl class.
18.2 Bitmap Buttons 18.2.1 Introduction A bitmap button is a command button that displays a bitmap and possibly a caption on top. The main difference between a bitmap button and a command button is that, by design, the former displays a bitmap. A bitmap button is based on the TBitBtn class. To create a bitmap button, you can use the BitBtn button
from the Additional tab of the Component Palette.
18.2.2 Bitmap Button Characteristics Because a bitmap button is usually meant to display a bitmap, you may want to provide a bitmap for it. If you want the button to also display a caption, you can proceed as the regular button and use the Caption property on the Object Inspector. There are two primary ways you can provide or add a bitmap to the button. C++ Builder ships with combinations of bitmaps and captions for a bitmap button. To use one of these, after adding a BitBtn control to a container, on the Object Inspector, click Kind and select one of the types of buttons. Some of the buttons you create using one of the Kind types have already been configured to properly respond to the dialog box or form on 432
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
which they are used. This works only if the form or dialog box is added to an existing form. The possible values of the Kind property and their effect on the application are: Value bkCustom bkOK
Picture See Below
ModalResult mrNone mrOk
bkCancel
mrCancel
bkAbort
mrAbort
bkRetry
mrRetry
bkIgnore
mrIgnore
bkYes
mrYes
bkNo
mrNo
bkHelp
Observation No special role This value should be used if either it is the only button on a dialog box or the user will need to acknowledge a message after performing some action on the form. In this case the user should either click this button or press Enter. After this Kind value is set, the Default property is automatically set to true, which gives the button a thick border. After this value is set, the ModalResult of the button becomes mrOk This value should be used if the user will need the opportunity to dismiss whatever action would have been performed on the form. The button that uses this Kind is usually accompanied by another button labelled OK After this Kind value is set, the Cancel property is automatically set to true and the ModalResult of the button becomes mrCancel Used to display Abort and return the mrAbort value Used to display Retry and return the mrRetry value Used to display Ignore and return the mrIgnore value This value should be used the user will be presented with a Yes/No question. The button should not be used with another button labelled OK on the same container. After this Kind value is set, the Default property is automatically set to true and the button gets equipped with a thick border. Used to display No and return the mrAbort value Used to display Help
bkClose
mrNoToAll
bkAll
mrYesToAll
Used to display No to All and return the mrNoToAll value Used to display Yes to All and return the mrYesToAll value
If none of the default bitmaps is satisfying to you, you can replace it with a bitmap of your choice. If you do not like the caption, you can simply click the Caption field in the Object Inspector and type a string of your choice. The bkCustom value of the Kind property allows you to set both the bitmap and the caption without using the pre-selections. In either case, the bitmap of the button is specified using the Glyph property of the Object Inspector. To change it, click the Glyph field and then click its ellipsis button to display the Picture Editor. This allows you to load, select, and open a bitmap of your choice. You can either design your own bitmap or Copyright © 2003 FunctionX, Inc.
433
Chapter 18: Command Controls
Borland C++ Builder Programming
use those that get installed with C++ Builder. By default, they are located in the C:\Program Files\Common Files\Borland Shared\Images\Buttons. To position itself, a bitmap uses a margin distance between its location and the button’s borders. This is controlled by the Margin property. On a bitmap button, you can use only the bitmap, only the caption, or both the bitmap and the caption. If you decide to use both, you have the opportunity to specify how they would appear next to each other. The alignment of the bitmap with regards to the caption is controlled by the Layout property, which is based on the TButtonLayout enumerator. Its possible values are: Value
Appearance
blGlyphLeft
blGlyphRight
blGlyphTop
blGlyphBottom
If you decide to use both a bitmap and a caption, the distance between them is controlled by the Spacing property whose default value is 4 pixels. You can change it to a reasonable value if you need to. The bitmap used should/must have at least a 16x16 pixels size. C++ Builder allows you to use a bitmap made of more than one glyph. In this case, the bitmap must be created like a single picture image list with a height of 16 pixels at most. If it contains two bitmaps, it should have a size of 32x16. The picture used for a bitmap button should not have more than 3 bitmaps. Otherwise, the fourth and beyond glyphs would be ignored. After adding the bitmaps, C++ Builder would find the number of glyphs that the bitmap contains using the “physical” size of the picture based on the fact that each bitmap has a width of 16 pixels. The number of glyphs is set using the NumGlyphs property and it is automatically set by C++ Builder. Based on this, if you add a 16x16 pixels bitmap, its NumGlyphs value would be set to 1. A 32x16 bitmap would have a NumGlyphs value set to 2. A 48x16 bitmap would have the NumGlyph property set to 3. If you think that the number set by C++ Builder is not right, you can change it on the Object Inspector. Here is how the number of glyphs influences the appearance of the bitmap button:
434
If the bitmap contains 1 glyph, the single bitmap would always be used except when the button is disabled. The operating system is in charge of painting the face of the button when it is disabled
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
If the bitmap contains 2 glyphs, the first glyph would be used both when the button is clicked and not clicked. The second glyph would be used when the button is disabled.
If the bitmap contains 3 glyphs, the first would be used when the button displays normally. The second glyph would display while the button is down when it has been clicked. The third would display when the button is disabled.
Practical Learning: Using Speed Buttons 1.
Start a new application with its default form
2.
Start saving it and create a new folder named BitmapButtons1
3.
Save the unit as Main and save the project as BitmapButtons
4.
Open Image Editor and create a new 16 x 16 pixels bitmap
5.
Design it as follows:
6.
Save it as Library in the BitmapButtons folder of the current project
7.
Create a new 48 x 16 pixels bitmap and design it as follows:
8.
Save it as Attached and return to Bcb
9.
While the form is displaying, on the Object Inspector, Set the following properties: Caption = Bitmap Buttons
Copyright © 2003 FunctionX, Inc.
435
Chapter 18: Command Controls
Borland C++ Builder Programming
Name = frmMain BorderStyle: bsDialog Height: 210 Width: 144 ShowHint = true 10. From the Additional tab of the Component Palette, click the BitBtn control and click on the form 11. On the Object Inspector, click Kind and click the arrow of its combo box. Select bkClose 12. Add another BitBtn control
under the first one
13. While the new bitmap button is still selected, on the Object Inspector, click Glyph and click its ellipsis button 14. On the Picture Editor dialog box, click the Load button. In the Load Picture dialog box, click Library
Click Open and click OK
15. Whikle the new button is still selected, on the Object Inspector, click Caption and type Library 16. Set its Hint to Consult the Library and heck that the NumGlyphs property has a value of 3 under the second one. While the new bitmap 17. Add another BitBtn control button is still selected, on the Object Inspector, click Glyph and click its ellipsis button . Using the Load button of the Picture Editor dialog box and the Load Picture dialog box, select the Attached bitmap 18. On the Object Inspector, check that the NumGlyphs property has a value of 3 and change the button’s properties as follows: Caption: Attach Enabled: false Name: btnAttachment Hint: Send With Attachment 19. Add one BitBtn control under the previous one. Set its Glyph to C:\Program Files\Common Files\Borland Shared\Images\Buttons\bulbon.bmp
436
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
20. Change its following properties: Name: btnAllowIt Caption: Allow It Hint: Allow item if ready 21. On the form, double-click the last button to access its OnClick event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnAllowItClick(TObject *Sender) { btnAttachment->Enabled = !btnAttachment->Enabled; } //---------------------------------------------------------------------------
22. Save your project and test it:
23. After using it, close it and return to Bcb
18.3 Speed Buttons 18.3.1 Introduction The speed button control provides the ability to create command buttons that behave either like optional, mutual-exclusive items or like checked objects. A speed button can be configured so that, when it has been clicked, it would stay down until it is clicked again (this behavior is the same as a check box). You can also create a group of speed buttons so that, out of the group, the selected one would display and keep a clicked or down state (like radio buttons) while the other button(s) of the same group is (are) down. This allows only one of the items in the group to appear clicked when chosen. Over all, a speed button’s behavior is configured as that of a regular button. The design of the speed button provides the most different aspect of the control.
18.3.2 Speed Buttons Characteristics To create a speed button, use the SpeedButton control from the Additional tab of the Component Palette to add it to the desired container. Unlike most other actions, when Copyright © 2003 FunctionX, Inc.
437
Chapter 18: Command Controls
Borland C++ Builder Programming
using either a speed or a radio buttons, you should pay attention to the container you are using as it can play a critical role to the behavior of the button. A newly added speed button appears with a 3D border, like the regular command button. You can make it appear flat with a mouse over effect. This appearance is controlled by the Flat property. Its default value is false. If you set it to true, its border would disappear. When the mouse passes over it, its borders would be raised and they would disappear when the mouse is no longer over it. Because its borders disappear with a true value, when you set this property like that, make sure you provide a way for the user to know that the button is present. This can be done using a caption, a bitmap, or both on the face of the button. By default, when a speed button is clicked, it performs its Click action and remains as it was before. As mentioned already, a speed button can be configured to keep its clicked or not clicked state. This means that, after a speed button has been clicked, it can stay down even after the mouse gets set to another control. To provide this down appearance, change the value of the GroupIndex property. The default value of the GroupIndex property is 0, which means that it would appear down only when clicked and while the mouse is pressing it. If you change it to a value other than 0 but with a value used by no other speed button on the same container, after the the button has been clicked, it would stay clicked until the user clicks it again. Alternatively, you can create a series of speed buttons that behave in what is described as “mutually exclusive”. In this case, when one button has been clicked, it stays down and the other buttons stay up. If another button of the same group gets clicked, it becomes down; the previous down button and all the other buttons, if more than one, would stay up. To provide this mutual-exclusive functionality, set the same value of GroupIndex to all button that should be part of the group. The value of the GroupIndex property can be specified in decimal or hexadecimal format. At design time, if you set it to a hexadecimal format, C++ Builder would convert it to its decimal value. After changing the GroupIndex value of a button or a group of buttons, you can give a primary down state to the button or to one of the buttons of the group or keep it or them to its, or their, default appearance. The state of the button can be set using the Down property. At design time and at run time, you can set its value to true or false. At run time, you can check the value of the Down property to find out whether the button is down or not. If you have created a group of speed buttons and gave them the same GroupIndex value, as mentioned already, they behave in a mutual exclusive scenario. At any given time, one button in the group would always be down while the others are up. If the user clicks a button that is already down, nothing would happen with regards to their appearance. This is the default behavior of the group. If you want the buttons to be up all of them in response to an action of your choice, use the AllowUp Boolean property. Here is what this characteristic provides:
438
If the AllowUp property is set to false: o
If the user clicks a button that is not down in the group, it would become down and the other button(s) would become up
o
If the user clicks a button that is already down in the group, nothing would change with regards to their appearances
If the AllowUp property is set to true:
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
o
If the user clicks a button that is not down in the group, it would become down and the other button(s) would become up
o
If the user clicks a button that is already down in the group, the button would become up like the other button(s) and no button would be down
By default, a newly added speed button has a Height of 22 pixel and a Width of 23 pixels. These dimensions are appropriate to display a 16x16 pixels bitmap. A speed button is usually used to display a bitmap but you can use it to show a bitmap and a caption. After adding the button to a container, if you want it to display only a caption, you can type it in the Caption property of the Object Inspector. If you prefer only a bitmap, use the Glyph property to select and specify the bitmap. If you want both a caption and a bitmap, you should resize the button enough to accommodate them. If you plan to use a bitmap on the speed button, its Glyph, its Spacing, its NumGlyphs, its Margin, and its Layout properties function similar to the same properties of the bitmap button. The Glyph property of the speed button provides a small addition. Like the bitmap button, the speed button can use a bitmap made of one or more glyphs. Unlike the bitmap button, the speed button can use up to four glyphs and here is how they work:
If the bitmap contains 1 glyph, the bitmap should have a size of 16x16 pixels (width x height). This single bitmap would always be used except when the button is disabled. The operating system is in charge of displaying or painting the face of the button when it is disabled.
If the bitmap contains 2 glyphs, the bitmap should have a size of 32x16 pixels. The first glyph would be used both when the button is clicked or down and when the button is not clicked or is not down. The second glyph would be used when the button is disabled.
If the bitmap contains 3 glyphs, it should have a size of 48x16 pixels. The first would be used when the button displays normally. The second glyph would display when the button is disabled. The third glyph would display while the button is down when it has been clicked.
If the bitmap contains 4 glyphs, it should have a size of 64x16 pixels. This style is valid only if the button is a member of a group (like radio buttons). The first would be used when the button displays normally. The second glyph would display when the button is disabled. The third glyph would display while the button is down when it has been clicked, exactly like the button with a NumGlyphs value equal to 3 but with the following exception. If the button is a member of a group, the third glyph would display while the mouse is still “pushing” it; that is, while the button is down but held by the mouse. The fourth glyph is used only if the button is part of a group and when it is down after the mouse has been released
18.4 Property Sheet and Wizards Buttons 18.4.1 Buttons on a Property Sheet A priori, a property page is just a control container and does not need buttons to function. Here is an example of a property sheet without communication buttons:
Copyright © 2003 FunctionX, Inc.
439
Chapter 18: Command Controls
Borland C++ Builder Programming
Most other property sheets are equipped with buttons like a regular dialog box. As such, a property sheet is usually equipped with the OK and Cancel buttons. Sometimes it is additionally equipped with an Apply button. And sometimes it has a Help button.
18.4.2 Property Sheet Buttons Implementation You add the buttons on a property sheet the same way you would for a dialog box. Simply click the Button control from the Standard tab or the BitBtn bitmap button from the Additional tab of the Component Palette and click the area of the property page where you want to place the button(s). Normally, in the strict sense, the buttons that perform the exchange of information with other parts of the application are positioned on the property sheet and not on the property page(s). That is, the buttons are positioned outside of the property page. The buttons can be positioned on the right side of the property sheet. That is the case for the Colors dialog box of Microsoft Word:
Most of the time, the buttons are positioned on the bottom section of the property sheet.
440
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
Generally, the button labeled OK, if present, should have the Default property set to true. The Cancel button should have the Cancel property set to true. By convention, the OK button, if available should be either the top most or the left most. If the Apply button is used, it should be the right most, which should have the Cancel button between both. Like any regular dialog box, the property page(s) of a property sheet is(are) equipped with Windows controls whose values the user may be asked to modify. When a property sheet is equipped with buttons, there are rules you should follow when implementing their behavior, to be in accordance with other Windows applications:
After using the property sheet, if the user wants to validate the changes that were made on the property pages, he can click OK. The OK button closes the property sheet and acknowledges whatever the user would have done on the controls the page(s) is(are) hosting. If the user did not make any changes, clicking OK should not make any difference, unless you configured some controls to automatically change their values when the user opens the property sheet
When the property sheet comes up, if (many property sheets do not have an Apply button, as you can check on most dialog boxes of C++ Builder) it is equipped with it, the Apply button should be disabled, indicating to the user that, so far, there has not been any change made on any of the control(s) of the property page(s)
After the property sheet is opened, once the user changes any value of any of the controls, the Apply button should be enabled, indicating to the user that the property sheet is now “dirty” (in computer programming, a document is referred to as dirty when it is not the way it was when it was opened). Here is an example:
Copyright © 2003 FunctionX, Inc.
441
Chapter 18: Command Controls
Borland C++ Builder Programming
While the user is making changes, if she wants to validate or check them without closing the property sheet, she can click the Apply button. This means that, clicking the Apply button (if available) would acknowledge the changes of values of the controls, just like the OK button. The difference is that the property sheet would not be closed. After clicking it, the Apply button should become disabled again, indicating that, at this time, the controls are clean
442
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
The user can continue making changes and clicking the Apply button if necessary
If the property sheet is equipped with an OK and a Cancel button without an Apply button, if the user makes changes on some or all of the controls but does not want to validate them, he can click Cancel to dismiss the dialog box. In this case, any change(s) made on the control(s) would be discarded.
If the property sheet is equipped with an OK, a Cancel, and an Apply buttons, if the user makes changes and clicks Apply, we saw that the changes would be acknowledged and validated. This would then disable the Apply button, again. If the user makes changes and clicks Cancel, only the new changes would be discarded; any change made before the Apply button was clicked would be validated and sent to the object that called the property sheet.
C++ Builder provides a fast means of creating a propertysheet and equips it with the OK, the Cancel, and the Help buttons. To use it, open the New Items dialog box and click the Forms property page. Then double-click Tabbed Pages.
18.4.3 Wizards Buttons As seen when reviewing controls containers, a wizard is a series of dialog boxes that assist the user with performing a task that requires intermediary steps. To implement its functionality, a wizard displays some button in the lower section of the dialog box. Depending on the programmer, different wizards use a few or more buttons. Usually a wizard has buttons such as Back, Next, and Cancel. Here is an example:
Copyright © 2003 FunctionX, Inc.
443
Chapter 18: Command Controls
Borland C++ Builder Programming
Practical Learning: Creating a Wizard 1.
Create a new project with its default form
2.
From the Standard page of the Component Palette, double-click the Panel control.
3.
Set the following properties for the panel (leave the other properties intact): BevelOuter: bvNone Caption: empty Height: 265 Left: 0 Name: pnlFirstPage Top: 0 Width: 430
4.
From the Component Palette, click the Edit control and click anywhere on the panel. This is a simple control that will serve as an indication when we are in the first page.
5.
Click the panel on the form to make sure it is selected.
6.
On the main menu, click Edit -> Copy.
7.
Click an empty area on the form to deselect the panel and select the form.
8.
On the main menu, click Edit -> Paste.
9.
With the new pasted panel still selected, change its properties as follows: Left: 0 Name: pnlSecondPage Top: 0
10. From the Component Palette, click the ScrollBox control and click on the panel. 11. Click an empty area on the form to select the form. 12. From the main menu, click Edit -> Paste. 13. For the newly pasted panel, change the properties as follows: Left: 0 Name: pnlThirdPage Top: 0
444
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
14. On the Component Palette, click the GroupBox control and click on the current panel. 15. On the Component Palette, click the Additional tab. 16. Click the Bevel control
.
17. Click an unoccupied area in the lower section of the form. 18. Change the properties of the Bevel control as follows: Height: 25 Left: 0 Shape: bsTopLine Top: 272 Width: 430 19. On the Component Palette, click the Standard tab. 20. Click the Button control. 21. Click in the lower section of the form 22. Change the properties of the button as follows: Caption: &Back Left: 112 Name: btnBack Top: 280 23. Add another button on the right side of the Back button and change its name to btnNext 24. Add another button of the right side of the Next button and change its name to btnFinish 25. Add one more button on the right side of the finish button. Set its Cancel property to true and its name to btnCancel
18.4.4 Wizard Implementation When a wizard starts, the user is presented with the first page. After using it, he or she can click Next to move to the subsequent page. The user can continue clicking Next to complete the necessary task(s) on each page. At any time and except on the first page, the user can click the Back button to move backward. Most wizards have a Cancel button that would allow the user to dismiss the dialog box. In this case, whatever change the user would have performed on the controls hosted by the wizard would be discarded. Many wizards are also equipped with a Finish button. There are two scenarios in which the Finish button can be used:
If the button displays at all times, that is, regardless of the page that is displaying, the presence of a Finish button means the user can click it to close the dialog box and end the task(s) performed. Here is an example of such a wizard:
Copyright © 2003 FunctionX, Inc.
445
Chapter 18: Command Controls
Borland C++ Builder Programming
In most other wizards, when the user gets to the last page, the Next button would be changed to Finish. This allows the user to click Finish to close the dialog box. Here is an example:
Clicking Finish closes the wizard and sends the results or values of the controls on the pages to whatever is the purpose of the wizard.
446
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
Practical Learning: Allowing Setup of a Print 1.
Double-click the Cancel button to access its OnClick() event
2.
Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnCancelClick(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
3.
To bring back the form, press F12
4.
Double-click an empty area on the form to access the form's OnCreate() event
5.
Implement the FormCreate() event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { // We are in the first page now pnlFirstPage->Visible = True; pnlSecondPage->Visible = False; pnlThirdPage->Visible = False; btnBack->Enabled = False; } //---------------------------------------------------------------------------
6.
Press F12 to display the form
7.
Double-click the Back button to access its OnClick() event
8.
Implement it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnBackClick(TObject *Sender) { if( pnlSecondPage->Visible == True ) { pnlFirstPage->Visible = True;
Copyright © 2003 FunctionX, Inc.
447
Chapter 18: Command Controls
Borland C++ Builder Programming
pnlSecondPage->Visible = False; pnlThirdPage->Visible = False; btnBack->Enabled = False; btnNext->Enabled = True; } else if( pnlThirdPage->Visible == True ) { pnlFirstPage->Visible = False; pnlSecondPage->Visible = True; pnlThirdPage->Visible = False; btnBack->Enabled = True; btnNext->Enabled = True; btnFinish->Enabled = True; } } //---------------------------------------------------------------------------
9.
Press F12 to bring back the form
10. Double-click the Next button and implement its OnClick() event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnNextClick(TObject *Sender) { // If you are in the first page if( pnlFirstPage->Visible == True ) { pnlFirstPage->Visible = False; pnlSecondPage->Visible = True; pnlThirdPage->Visible = False; btnBack->Enabled = True; btnNext->Enabled = True; btnFinish->Enabled = True; } else if( pnlSecondPage->Visible == True ) { pnlFirstPage->Visible = False; pnlSecondPage->Visible = False; pnlThirdPage->Visible = True; btnBack->Enabled = True; btnNext->Enabled = False; btnFinish->Enabled = False; } else// if( pnlThirdPage->Visible == True ) { btnNext->Enabled = True; btnBack->Enabled = True; btnFinish->Enabled = True; } } //---------------------------------------------------------------------------
11. Press F12 to bring back the form 12. Double-click the Finish button and implement its OnClick() event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnFinishClick(TObject *Sender) { pnlFirstPage->Visible = False; pnlSecondPage->Visible = False;
448
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 18: Command Controls
pnlThirdPage->Visible = True; btnBack->Enabled = True; btnNext->Enabled = False;
} //---------------------------------------------------------------------------
13. To test the application, press F9.
Copyright © 2003 FunctionX, Inc.
449
Chapter 19: Collections-Based Controls
450
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
Chapter 19: Collections-Based Controls 19.1 The Main Menu 19.1.1 Overview A menu of a computer program is a list of actions that can be performed on that application. To be aware of these actions, the list must be presented to the user upon request. A menu is considered a main menu, when it carries most of the actions the user can perform on a particular application. Such a menu is positioned on the top section of the form in which it is used. By design, although a main menu is positioned on a form, it actually belongs to the application. A main menu is divided in categories of items and each category is represented by a word. Here is an example:
On the C++ Builder's IDE, the categories of menus are File, Edit, Search, View, etc. To use a menu, the user first clicks one of the words that displays on top. Upon clicking, the menu expands and displays a list of items that belong to that category. Here is an example where the View menu of WordPerfect was clicked therefore got expanded:
Copyright © 2003 FunctionX, Inc.
451
Chapter 19: Collections-Based Controls
Borland C++ Builder Programming
There is no strict rule on how a menu is organized. There are only suggestions. For example, actions that are related to file processing, such as creating a new file, opening an existing file, saving a file, printing the open file, or closing the file usually stay under a category called File. In the same way, actions related to viewing documents can be listed under a View menu.
19.1.2 Main Menu Creation To create a main menu in the VCL, you can use the TMainMenu class. In C++ Builder, the main menu is created using the MainMenu control
. To get it, on the Standard
tab of the Component Palette, click the MainMenu button and click on the form. It does not matter where you drop the MainMenu icon because it will not be seen what the program runs. To create the list of items that belong to the menu object, open the Items property. This would display a window specially equipped for creating a main menu. C++ Builder greatly simplifies the creation of a menu by providing menu templates. A menu template is an already created menu that you can simply select and add to your application. To use one of these, right-click the Menu Designer window and click Insert From Template:
452
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
Figure 29: Menu Designer C++ Builder provides already made menus for file, edit, and help processing:
Figure 30: Insert Template You can still create a menu manually either without using the template or to complete the template. To create a menu category, on the Menu Designer window, click the top line and, on the Object Inspector, click Caption, type a word or a group of words and press Enter or click somewhere else. On the menu items, a letter on each menu is underlined. This letter allows the user to access the menu using a keyboard. For example, if the letter e is underline in a File menu as in File, the user can access the File by pressing the Alt then the E keys. A shortcut is a key or a combination of keys that the user can press to perform an action that would also be performed using a menu item. When creating a menu using the Menu Designer window, on the Object Inspector, you can specify a shortcut using the Shortcut property.
Copyright © 2003 FunctionX, Inc.
453
Chapter 19: Collections-Based Controls
Borland C++ Builder Programming
Practical Learning: Creating a Main Menu 1.
Start a new project with the default form.
2.
On the Component Palette, click the Standard property sheet. Click the MainMenu button
and click on the form
3.
On the form, double-click the MainMenu1 icon
4.
As the first item is selected, on the Object Inspector, click Caption, type &Staff and press Enter.
5.
On the Menu Designer window, click Staff and click the empty box under Staff.
6.
Type &New Hire
7.
On the Object Inspector, click ShortCut. Click the arrow of the ShortCut property and select Ctrl + N
8.
On the Menu Designer window, click the item under New Hire.
9.
On the Object Inspector, click Caption and type &Records
10. Using the Object Inspector, set the ShortCut to Ctrl + R 11. On the Menu Designer window, click the item under Records and on the Object Inspector, click Caption. 12. Type Time &Sheet... and on the Object Inspector, specify the ShortCut as Ctrl + M 13. On the Menu Designer window, click Records and press Insert 14. On the Object Inspector, click Caption and type Searc&h... 15. To add a separator, click the first empty line under Time Sheet. On the Object Inspector, click Caption, type - and press Enter 16. Click the empty item under the previously added separator line, type E&xit and press Enter 17. To move the Search item, on the Menu Designer window, click and drag Search down:
18. When the Time Sheet item is highlighted, release the mouse. 19. To start a new menu category, click the item on the right side of Staff 454
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
20. On the Object Inspector, click Caption, type &Books and press Enter. 21. Click Books and click the empty item under it. 22. Type &New and press Enter 23. Type Show All &Titles and press Enter. 24. To create a submenu, click New and press Ctrl and the right arrow key. 25. Click the new empty item on the right side of New. 26. Type &Title and press Enter 27. Type &Author and press Enter 28. Type &Category and press Enter 29. Clicking each item and using the Object Inspector, specify the shortcuts as follows:
Figure 31: A Menu Designed 30. Click the box on the right side of Books and set its caption to &View 31. Click View and click the empty box under it 32. On the Object Inspector, click Caption, type &Toolbar and press Enter 33. Click the box under Toolbar, type &Status Bar and press Enter. 34. On the Menu Designer window, click Toolbar to select it 35. To put a check mark on a menu item, set the Checked property to true. 36. Also, for the Status Bar item, set the Checked property to true. 37. To move the whole View menu category, click and drag View to the left:
Copyright © 2003 FunctionX, Inc.
455
Chapter 19: Collections-Based Controls
Borland C++ Builder Programming
Figure 32: Moving a Menu Category 38. When Books is highlighted, release the mouse. 39. Close the Menu Designer Window. Notice that a menu for the form has been created.
19.1.3 Coding a Main Menu Item Throughout this book, you will find out that there are other ways or intermediary ways to create a menu, such as using the ActionList. Consequently, there are also other ways to write code for a menu. If you create a menu as we have just done, to write code for one of the menu items, from the Menu Designer window, you can double-click the menu item. This would open the OnClick event of the menu item in the Code Editor and you can start writing the desired code for that item. If you have already created the menu and you have closed the Menu Designer window, on the form, you can open the menu category and click the menu item as if you were regularly trying to use the menu. This would open the Code Editor with the OnClick event of the menu item
Practical Learning: Coding a Main Menu Item 1.
On the form, click Staff and click Exit
2.
Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Exit1Click(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
3.
456
Test the form. To close it, click Staff -> Exit
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
19.1.4 Popup and Context-Sensitive Menus A menu is considered or qualifies as popup if or because it can appear anywhere on the form as the programmer wishes. Such a menu is also referred to as context-sensitive because its appearance and behavior depends on where it displays on the form or on a particular control. The first difference between a main menu and a popup menu is that a popup menu appears as one category or one list of items and not like a group of categories of menus like a main menu. Secondly, while a main menu by default is positioned on the top section of a form, a popup menu does not have a specific location on the form. To use a popup menu, usually the user can right-click the section of the form or the object on which the menu is configured to appear. A popup menu is based on the TPopupMenu VCL class. To visually create a popup menu, on the Standard tab of the Component Palette, click the PopupMenu button and click on the form. Once you have a PopupMenu object, you can double-click it, which would open the Menu Designer. In the Menu Designer window, to create a menu, click an empty item and, on the Object Inspector, type a text on the Caption field. Everything on an item of a popup menu can be configured like an item of a main menu. Unlike a main menu, a popup menu provides a single list of items. If you want different popup menus for your form, you have two options. You can create various popup menus or programmatically change your single popup menu in response to something or some action on your form. There is nothing particularly specific with writing code for a popup menu item. You approach it exactly as if you were dealing with a menu item of a main menu. You can write code for an item of a popup menu independent of any other item of a main menu. If you want an item of a popup menu to respond to the same request as an item of a main menu, you can write code for one of the menu items (either the item on the main menu or the item on the popup menu) and simply call its OnClick() event in the event of the other menu item
Practical Learning: Creating a Popup Menu 1.
On the Standard tab of the Component Palette, click the PopupMenu button and click on the form.
2.
On the form, double-click the PopupMenu button.
3.
On the Menu Designer window, as the first item is select, on the Object Inspector, click Caption, type &Font... and press Enter
4.
Click the empty box under Font to select it. Type &Options and press Enter.
5.
Click the empty box under Intermediate. Type &Properties and press Enter.
6.
Click the empty box under Properties and, on the Object Inspector, click Caption, type - and press Enter.
7.
Set the caption of the last item to Finis&h
8.
While still in the Menu Designer window, click Finish.
Copyright © 2003 FunctionX, Inc.
457
Chapter 19: Collections-Based Controls
9.
Borland C++ Builder Programming
On the Object Inspector, click the Events tab and click the OnClick field.
10. Click the arrow of the right field and select Exit1Click. 11. Close the Menu Designer window 12. Click an empty area on the form to select the form 13. On the Object Inspector, click the Properties tab and click PopupMenu. 14. Click the arrow of the PopupMenu property and select PopupMenu1 15. Test the form. Right-click in the middle of the form and click Finish.
19.2 Toolbars 19.2.1 Introduction A toolbar is a Windows control that allows the user the perform some actions on a form by clicking a button instead of using a menu. What a toolbar provides is a convenient group of buttons that simplifies the user's job by bringing the most accessible actions as buttons so that, instead of performing various steps to access a menu, a button on a toolbar can bring such common actions closer to the user:
Toolbars usually display under the main menu. They can be equipped with buttons but sometimes their buttons or some of their buttons have a caption. Toolbars can also be equipped with other types of controls. There are various ways you can create a toolbar. The simplest and the most common means of getting a toolbar is by using the TToolBar VCL class. To create a toolbar, on the Win32 tab of the Component Palette, click ToolBar and click on the form. By default, a toolbar is positioned on the top section of the form because it takes the alTop value of the Align property. Like a form, a toolbar is only a container and does not provide much role by itself. To make a toolbar efficient, you should equip it with the necessary controls. The most common control used on a toolbar is a button. To add a button to a toolbar, right-click the toolbar and click New Button. The new button would appear empty. A button on a toolbar is a rectangular object with a picture on top. The picture on the button should suggest what the button is used for but this is not always possible. Providing a picture is more important. The easiest way to provide pictures for your toolbar is by creating an image list and associating it with the toolbar. If a toolbar is given an image list, whenever you right-click the toolbar and click Add Button, the image corresponding to the index of the list would display on the new button. You can keep adding buttons in this fashion. A separator is a line that separates buttons (or controls) as groups of objects on a toolbar. To create a separator, you can right-click the toolbar and click New Separator. As a toolbar can be equipped with various types of controls, there are various types of buttons you can place on a toolbar. The types of buttons can be configured using the Style property on the Object Inspector.
458
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
Practical Learning: Creating a Toolbar 1.
To create a new popup menu, on the Standard tab of the Component Palette, doubleclick PopupMenu
2.
Using the Object Inspector, change the name of the new popup menu to mnuNewPopup
3.
Double-click the mnuNewPopup icon. Create a menu with the &Staff, &Customer, &Book, and &Author
4.
Then close the Menu Designer window.
5.
To create a new popup menu, on the Standard tab of the Component Palette, doubleclick PopupMenu
6.
Change its name to mnuViewPopup and double-click it
7.
Create two menu items as &Toolbar and &Status Bar
8.
Set the Checked property of each to true then close the Menu Designer window
9.
To create a new toolbar, on the Component Palette, click the Win32 tab.
10. Click the ToolBar button and click on the form. 11. On the Object Inspector, set the Flat property to true. 12. Set the Height to 25 13. Set the Images to imgMainMenu 14. Change the name of the toolbar to tbrStandard 15. To add a new button, right-click the toolbar and click New Button 16. Right-click the toolbar again and click New Button 17. Right-click the toolbar again and click New Button 18. While the new button is still selected, on the Object Inspector, change its ImageIndex to 3 instead of 2 19. Right-click the toolbar again and, this time, click New Separator 20. Right-click the toolbar again and click New Button Copyright © 2003 FunctionX, Inc.
459
Chapter 19: Collections-Based Controls
Borland C++ Builder Programming
21. While the new button on the toolbar is still selected, on the Object Inspector, click Style and set its value to tbsDropDown 22. Still on the Object Inspector, click DropDownMenu and set its value to mnuNewPopup 23. Click an empty area on the toolbar to select it. 24. On the Object Inspector, set its PopupMenu to mnuViewPopup
19.2.2 Toolbar Programming The controls on a toolbar are programmed with regards to their own kind. This means that the coding of a control of a toolbar is not directly related to the toolbar: the toolbar is simply a host to the controls positioned on it. If the control whose code you want to write is a button, you can simply proceed with the OnClick event of the button. If the button should perform the same action as a menu item, and if you have already written code for the menu item, you can simply select its Click event in the OnClick event field of the toolbar button. Otherwise, you can write completely independent code for a button on a toolbar.
Practical Learning: Coding a Toolbar 1.
On the main menu of the form, click View -> Toolbar
2.
Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Toolbar1Click(TObject *Sender) { tbrStandard->Visible = !tbrStandard->Visible; Toolbar1->Checked = !Toolbar1->Checked; } //---------------------------------------------------------------------------
460
3.
On the form, double-click the mnuViewPopup icon to open the Menu Designer window
4.
Click Standard and, using the Object Inspector, set the OnClick event of Standard to Toolbar1Click Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
5.
Test the form. Right-click the toolbar and click Standard. On the main menu of the form, click View -> Standard
6.
After viewing the form, close it.
19.3 Status Bars 19.3.1 Introduction A status bar is a Windows control that usually displays on the bottom section of a form or the container that is hosting it. It is used to display short pieces of information to the user. A status bar can be a simple long bar that covers the whole bottom section of a form. As such, a status bar can be considered one long panel. A status bar in based on the TStatusBar VCL class. To create a status bar, on the Win32 tab of the Component Palette, click the StatusBar button and click on the form. As soon as you click the form, the control would be positioned on the bottom section of the host control. Most of your status bars will be created on, or hosted by, a form but you can add a status bar to any control that can serve as a parent for other controls.
19.3.2 Characteristics of a Status Bar A newly added status bar is made of a long bar that assumes an alBottom value for the Align property. By default, a new status bar is made of a single bar, called a simple panel. On the status bar's panel, you can display various types of text such as short messages informing the user about some things that are going on. If you plan to use a single panel on your status bar, on the Object Inspector, you should set the SimplePanel property to true. If the SimplePanel of a StatusBar control has a value of true, you can use the SimpleText property to display a string on the status bar. You can specify such text at design time by typing a string in the value section of the SimpleText property. When the form runs, such a string would permanently display on the status bar:
Copyright © 2003 FunctionX, Inc.
461
Chapter 19: Collections-Based Controls
Borland C++ Builder Programming
Figure 33: Status Bar You can also programmatically display or change the string that a status bar displays. To do this, simply assign the desired string to the SimpleText property of the StatusBar control. In the following example, a different string displays on the status bar depending on if the user double-clicked the form or right-clicked it: //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { StatusBar1->SimpleText = "The form is already active"; } //--------------------------------------------------------------------------void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( Button == mbRight ) StatusBar1->SimpleText = "There is no popup menu for this form"; } //---------------------------------------------------------------------------
Practical Learning: Creating a Status Bar 1.
Click an empty area on the form to select it
2.
On the Component Palette, click the Win32 property sheet
3.
Double-click the StatusBar button
4.
While the new control is still selected, on the Object Inspector, click Name and type DefaultStatusBar and press Enter
5.
On the form, click View -> Status Bar and implement its Click event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::StatusBar1Click(TObject *Sender) { DefaultStatusBar->Visible = !DefaultStatusBar->Visible; StatusBar1->Checked = !StatusBar1->Checked; } //---------------------------------------------------------------------------
6.
462
On the form, click the newly added status bar. On the Object Inspector, set its SimplePanel property to true. Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
7.
Find and double-click the mnuViewPopup
8.
On the Menu Designer window, click Status Bar. On the Object Inspector, click the Event tab and, in the OnClick event, select StatusBar1Click
9.
Click an empty area on the form to select the form
10. On the Object Inspector, set the value of the ShowHint property to true. 11. On the form, click an empty area on the toolbar and, on the Object Inspector, set the ShowHint property to true. 12. Press F12 to display the Code Editor If the Class Explorer is not displaying, on the main menu, click View -> ClassExplorer In the Class Explorer, expand everything. 13. In the Class Explorer, right-click TForm1 and click NewMethod... 14. In the Method Name edit box, type ShowToolTips 15. In the Arguments edit box, type TObject* Sender 16. Set the Function Result to void 17. Click the Private radio button and the __fastcall check box
Figure 34: Add Method - ShowToolTips 18. Click OK and implement the function as follows: //--------------------------------------------------------------------------void __fastcall TForm1::ShowToolTips(TObject* Sender) { DefaultStatusBar->SimpleText = Application->Hint; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
463
Chapter 19: Collections-Based Controls
Borland C++ Builder Programming
19. Press F12 to display the form 20. Double-click in the middle of the form and implement its OnCreate event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Application->OnHint = ShowToolTips; } //---------------------------------------------------------------------------
21. Press F12 to display the form 22. On the form, double-click the MainMenu1 icon 23. In the Menu Designer window, click New Hire 24. On the Object Inspector, click Hint and type Used when hiring a new employee 25. In the same way, click each menu item and set their Hint as follows: Menu Item
Hint
New Hire Records Search Time Sheet Exit Toolbar Status Bar Show All Titles Title Author Category
Used when hiring a new employee Display the records of all employees Search an employee in our records Fill out a time sheet Quit the application Toggles the toolbar Toggles the status bar Display information on all books Register a new book Register a new author Create a new category
26. Close the Menu Designer window 27. On the toolbar, click the first (left) button. 28. On the Object Inspector, click Hint, type New Employee | Used when hiring a new employee and press Enter 29. Click the second button on the toolbar and set its Hint to Records|Display the records of all employees 30. Set the Hint of the third button to Category | Create a new category 31. Set the Hint of the fourth button to New Book|Create a record for a new book 32. Press F9 to test your form
19.3.3 Status Bar Panels Instead of a long unified bar, a status bar can be divided in sections each serving a specific purpose. A section of a toolbar is called a panel. To use a more elaborate status bar, you must set its SimplePanel property to false. To create different panels for a status bar, on the form, you can double-click the StatusBar control. Alternatively, after selecting the control on the form, on the Object Inspector, you can click the ellipsis button of the Panels field. Once in the Panels window, you can click the Add New button. You can also right-click in the Panels 464
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
window and click Add. You can also press Insert. Either of these actions performed for the first time would create a formal panel for the status bar. At this time, instead of using the SimpleText property of the status bar, you can refer to the Panels collection of the TStatusBar class. If you want to use more than one section on a status bar, you must divide the status bar in as many sections as necessary. To do this, you can use the Add New button or Insert key to create each subsequent section. If you create a section by mistake or do not need it anymore, select it in the Panels window and either click the Delete Selected button or press Delete. You can also right-click an undesired item and click Delete on the popup menu. The panels are added incrementally as you insert them. If you create more than one panel but do not like their sequence, you can rearrange them using the arrow buttons on the toolbar of the Panels window. You can also right-click an item and use the popup menu to move an item. A status bar equipped with different sections is a collection or array of panels. Each panel belongs to an array of Items. Based on this array, each panel can be configured independently of the other(s). In fact, the main thing that unites the panels is their belonging to the same parent object: the status bar control. To access a particular panel of the status bar, you can refer to it using its index in the Items array. The first panel has an Items index of 0, the second has an index of 1, etc. For example, imagine you have a status bar with four panels. To access the 3rd panel, you can refer to it using StatusBar>Panels->Items[2]. You can specify the individual characteristics of each panel. After creating or adding a new panel, it assumes a default length of 50 pixels. The length of a panel is specified using the Width property. The Width of panels is directly related to the panel and can be dependent on the other panels. The most common item displayed on a panel is a string. For a status bar panel, the string to display is defined by the Text property. If you plan to use something else than a string to display on a panel, you can specify this using the Style property. The default value of a panel's Style is psText which indicates that the panel is used to display text. Instead of text, you can draw on the panel after setting the Style value to psOwnerDraw. By default, the text on a panel aligns to the left as a normal text of a paragraph does. The alignment of text is controlled by the Alignment property that provides three possibilities: taLeftJustify, taCenter, taRightJustify. By default, the text of a panel in sunken into the panel. This appearance of text is controlled by the Bevel property. Its default value is pbLowered. If you want the panel's surface to be raised, set it Bevel property to pbRaised. If you do not want any of these characteristics, sunk or raised, set its Bevel property to pbNone.
19.4 Action Lists 19.4.1 Introduction An action list is a collection of actions that can be performed on an application. It is a convenient means of configuring actions that can occur throughout the life of an Copyright © 2003 FunctionX, Inc.
465
Chapter 19: Collections-Based Controls
Borland C++ Builder Programming
application. You saw earlier that to use menus and toolbars for different assignments, the actions had to sometimes be performed individually on the controls that shared the same functionality. An action list does not solve all problems but reduces the steps performed to find a common solution to menu items and toolbar buttons combinations.
Practical Learning: Introducing Action Lists 1.
Start a new project with its default form
2.
To save it, on the Standard toolbar, click the Save All button
3.
Create a new folder called Notice1 and display it in the Save In combo box
4.
Save the unit as Main and save the project as Notice
5.
Set the Caption of the form to Notice
6.
Change the Name of the form to frmMain
7.
Set the ShowHint property of the form to true
8.
On the Win32 tab of the Component Palette, double-click ImageList
9.
On the form, double-click ImageList1
10. Click Add. Locate the resources that accompany this book. From the Bitmaps folder, click New and click Open 11. In the same way, add the Save bitmap to the list of images 12. Click OK
19.4.2 The List of Actions To create a list of actions, you can use the TActionList class. It is represented on the Standard tab of the Component Palette by an ActionList button . To use it, click ActionList from the Component Palette and click on the form. To create the actual list, double-click the ActionList button on the form, which would open the Action List Editor window. To simplify the creation of a list of actions, C++ Builder provides a collection of preconfigured actions. These items can appropriately respond to certain controls on your form without much code, provided the control is the type of action for which the Action item was created. To use one of the action templates, right-click anywhere in the Action List Editor and click New Standard Action. On the Standard Action Classes window, select the desired template and click OK.
Practical Learning: Creating a Memo-Based Application
466
1.
On the Standard tab of the Component Palette, click ActionList the form
and click on
2.
While ActionList1 is still selected, on the Object Inspector, click Images and select ImageList1
3.
On the form, double-click ActionList1 to open the Action List Editor Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
4.
On the toolbar of the Action List Editor, click the New Action button
5.
While the new Action1 item is still selected, on the Object Inspector, click Name and type FileNew
6.
Click Caption and type &New
7.
Click Category and type File
8.
Click Hint and type New|Creates a new document
9.
Click Shortcut, then click the arrow of its combo box and select Ctrl + N
10. In the ImageIndex combo box, select 0 11. Right-click the left frame of the Action List Editor and click New Action 12. While the new Action1 item is still selected, on the Object Inspector, click Caption and type &Save 13. In the Category combo box, select File 14. Click Name and type FileSave 15. Click Hint and type Save|Saves the current document 16. Set its ImageIndex to 1 17. Click Shortcut, then click the arrow of its combo box and select Ctrl + S 18. On the toolbar of the Action List Editor, click the arrow of the New Action button
Figure 35: Action List Editor 19. Click New Standard Action… 20. In the Standard Action Classes dialog box, scroll down to the Internet node. 21. Double-click SendMail 22. Right-click the right frame of the editor and click New Standard Action… 23. In the Standard Action Classes dialog box, scroll down to the File node. Click TFileOpen. Press and hold Ctrl. Then click TFileSaveAs, TFilePrintSetup, and TFileExit. Release Ctrl Copyright © 2003 FunctionX, Inc.
467
Chapter 19: Collections-Based Controls
Borland C++ Builder Programming
Figure 36: Standard Action Classes 24. Click OK 25. On the left frame of the Action List Editor, click File to display its list of related actions 26. On the right frame, click FileSaveAs1 to select it. Using the Object Inspector, set its Shortcut to F12 27. While the File node is still selected in the left frame, in the right frame, click FileOpen1 to select it and, on the toolbar of the editor, click the Move Up button . Click File again to see the result
468
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
Figure 37: Action List Editor With New Items 28. Right-click one of the frames of the editor and click New Standard Action… 29. Click TEditCut. Press and hold Shift. Then click TEditDelete and release Shift 30. Click OK 31. Move the EditUndo1 item to be on top of the list 32. Move the EditDelete1 item and place it under EditPaste1
Figure 38: Action List Editor - Items Configuration 33. Close the Action List Editor window and save All
Copyright © 2003 FunctionX, Inc.
469
Chapter 19: Collections-Based Controls
Borland C++ Builder Programming
13. From the Standard tab of the Component Palette, click MainMenu the form
and click
14. While the MainMenu1 icon is still selected, on the Object Inspector, set its Images property to ImageList1 15. On the form, double-click MainMenu1 to open the Menu Editor 16. Right-click the selected box on the Menu Editor and click Insert From Template… 17. On the Insert Template dialog box, double-click File Menu 18. Click the empty box on the right side of File to select it. Display the Insert Template dialog box again and double-click Edit Menu 19. Delete Repeat, Paste Special, Go To, the separator under Go To, Links, and Object 20. On the Menu Editor, click File and click New to select it 21. On the Object Inspector, click the Action field and select FileNew from its combo box 22. In the same way, selecting the menu items and using the Action field from the Object Inspector, associate the menu items to their corresponding actions as follows: Menu Item Open… Save Save As… Print Setup… Exit Undo Cut Copy Paste
Action FileOpen1 FileSave FileSaveAs1 PrintSetup1 FileExit1 EditUndo1 EditCut1 EditCopy1 EditPaste1
23. In the Edit menu, click the separator under Paste and press Insert. Set its Action to EditDelete1 24. Close the Menu Editor 25. Save All 26. From the Win32 tab of the Component Palette, click ToolBar empty area on the form
and click an
27. Using the Object Inspector, set the Height value to 40 28. Change the Name of the toolbar to tbrStandard 29. Set the ShowCaptions property to true 30. Set its Image property to ImageList1 31. Right-click the toolbar and click New Button. On the Object Inspector, set its MenuItem property to FileNew 32. Complete the buttons as follows:
470
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 19: Collections-Based Controls
Figure 39: Toolbar Buttons With Captions 33. Press F12 to display the Code Editor. In Class Explorer, expand the Classes node and TfrmMain. Right-click TfrmMain and click New Method... 34. Set the Method Name to ShowHints. Specify its Argument as TObject *Sender and the Function Result to void and click OK. 35. Implement the method as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::ShowHints(TObject * Sender) { StatusBar->SimpleText = Application->Hint; } //---------------------------------------------------------------------------
36. Press F12 to get back to the form. Double-click in the middle of the form and implement its OnCreate event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { Application->OnHint = ShowHints; } //---------------------------------------------------------------------------
37. Press F12 to display the form and Save All
19.4.3 Action Lists Messages and Events As their name implies, the actions of a list are simply meant to perform central actions that other objects may need. To manage their behavior, they are usually implemented through a function pointer called TActionEvent. This function pointer is defined as follows: typedef void __fastcall (__closure *TActionEvent)(Classes::TBasicAction* Action, bool &Handled);
This means that an event created from this function will require two arguments. The first argument, Action, is in fact a pointer to the TBasicAction class. The TBasicAction class carries information about the action that needs to be carried. Information can be used to specify whether the action must be executed anew or only updated, whether there was a change to take into consideration or the change that occurred must be dismissed. The Handled argument specifies how the action will be carried, whether it will execute or simply update the action. When an action needs to be carried, the object that initiates the action fires an OnExecute() event, which is a TActionEvent type. If the action needs to be updated, the OnUpdate() event isfired, which also is a TActionEvent type. When a change occurres in the action list, an OnChange() event is fired. This is a TNotifyEvent type. Copyright © 2003 FunctionX, Inc.
471
Chapter 19: Collections-Based Controls
472
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
Chapter 20: Text-Based Controls 20.1 Labels 20.1.1 Introduction Borland C++ Builder ships with many string-oriented classes for most of the controls used to perform any necessary string manipulation. AnsiString is the main string classes used by those controls as it provides the caption property of all controls that need to set or control their caption. The label is one of those controls. A label is a control that serves as a guide to the user. It provides static text that the user cannot change but can read to get information on the form. The programmer can also use it to display simple information to the user. Most controls on the form are not explicit at first glance and the user would not know what they are. Therefore, you can assign a label to a control as a help to the user. To add a label to a container, click the Label button from the Standard tab of the Component Palette and click on the object that would host it. You can also dynamically create a label. A label is based on the TLabel class that is a child of the TCustomLabel class which itself is derived from the TGraphicControl class.
20.1.2 Label Characteristics The most important characteristic of a label control is the text it displays. This is what the user would read. The text of a label is its Caption property and is its default. To set a label’s caption, after adding the control to a container, click Caption in the Object Inspector and type the desired value. As we mentioned when studying controls characteristics, at design time, the text you type in the Caption is considered “as is”. If you want to create a more elaborate and formatted string, you would have to do it programmatically. When you type the caption of a label, it is continually resized to accommodate its string. If you edit the label, as you delete or add characters, the label resizes itself. If you want to fix the size of the label regardless of its caption, set the Boolean property AutoResize to false. By default, this property is set to false on most controls that use it; but on a label, it is set to true. Once you have set AutoResize to true, if you change the text of the label, only the portion that fits in the allocated space would be seen. Of course, you can resize it manually. Before or after typing the caption of a label, you can resize its allocated space to your liking. This is because a string occupies a rectangular area. Here is an example:
Copyright © 2003 FunctionX, Inc.
473
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
By default, the caption of a label is positioned starting on the left side of its allocated rectangle. Alternatively, you can position it to the center or the right side inside of its rectangle. This positioning is controlled by the Alignment property which is based on the TAlignment enumerator. It can have the following values: taLeftJustify
taCenter
taRightJustify
Because the caption of a label is confined to a rectangle, you can increase the height of that rectangle and align text to the top, the middle or the bottom side of that allocated rectangle. The vertical alignment of a label is controlled by the Layout property which is based on the TTextLayout enumerator and defined as follows: enum TTextLayout { tlTop, tlCenter, tlBottom };
The effects of the Alignment and the Layout properties are as follows: Alignment Layout
taLeftJustify tlTop
Alignment Layout
taLeftJustify tlCenter
Alignment Layout
taLeftJustify tlBottom
Alignment Layout
taCenter tlTop
Alignment Layout
taCenter tlCenter
Alignment Layout
taCenter tlBottom
Alignment Layout
taRightJustify tlTop
Alignment Layout
Alignment Layout
taRightJustify tlBottom
taRightJustify tlCenter
If you have allocated a rectangular area wider and taller than the actual string of the label, you can display it on more than one line. This ability is controlled by the WordWrap Boolean property. Its default value is false, which makes the label display on a single line. When the value of the WordWrap property is set to true, the text of the label can span multiple lines. If you want to create a shadow effect on a label, you can place one label on top of another. Using the Transparent property, which is a Boolean value, you should make the top label transparent. Other fancy characteristics you can apply to a label include its font and color. The Label control is also equipped with a Canvas property. This allows you to display a bitmap or
474
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
to perform any needed drawing in the allocated rectangle of the label. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { Label2->Canvas->Pen->Color = clBlue; Label2->Canvas->Brush->Color = clSkyBlue; Label2->Canvas->Brush->Style = bsDiagCross; ClientToScreen(Point(Label2->Left, Label2->Top)); Label2->Canvas->Rectangle(Label2->ClientRect); } //---------------------------------------------------------------------------
Figure 40: Using the Canvas of a Label
Figure 41: Painting the Canvas of a Label
Practical Learning: Labels Design 1.
Start Borland C++ Builder if you did not yet. To start a new program, on the main menu, click File -> New -> Application
2.
To save the current project, on the Standard toolbar, click Save All
3.
Click the Create New Folder button
4.
Type Employment Application and press Enter twice to display the new folder in the Save In combo box
5.
To rename the current unit, in the File Name box, replace the name with Main and press Enter
6.
Type Employment as the name of the project and press Enter
Copyright © 2003 FunctionX, Inc.
to create a folder
475
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
7.
From the Standard tab of the Component Palette, click the Label control
8.
Click the form
9.
As the new label is still selected, on the Object Inspector, click Caption and type Date &Hired
10. To move the new label, click and drag it to the left section of the form. To position it precisely, using the Object Inspector, set the label’s properties as follows: Left = 16, Top = 96 11. To add another label, on the Standard tab, double-click the Label control 12. On the Object Inspector, click the Caption field and type &First Name: 13. Set its Left property to 16 and its Top to 120 14. Add the following other labels:
Figure 42: The Employment Application With Labels 15. To add another label, click the Label control and click on the top section of the form. 16. On the Object Inspector, click the Caption field, type Employment Application 17. Click the gray box on the right side of the Font field 18. Click the ellipsis button
to open the Font dialog
19. Change the font to Times New Roman 20. In the Font Style combo box, click Bold. 21. In the Font Size combo box, select 24 22. Click the arrow of the Color combo box and select Blue
476
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
23. Click OK 24. On the Object Inspector, click the Name field and type lblMainTitle 25. Double-click the box on the right side of the Transparent field. Instead of false, it should now display true. 26. On the form, click the Employment Application label to select it. 27. On the main menu, click Edit -> Copy 28. Click an unoccupied area on the form to make sure nothing is selected. 29. On the main menu, click Edit -> Paste. 30. As the new label is still selected, click the + on the Font field. 31. Under the Font field, click Color to reveal its combo box and select clGray 32. Click the – on the Font field 33. Click the Name field and type lblTitleShadow 34. Click the arrow on the top section of the Object Inspector and select lblMainTitle 35. Set following properties Left: 16 Top: 8 36. Click the arrow on the top section of the Object Inspector and select lblTitleShadow 37. Set its properties as follows: Left: 19 Top: 11 38. Right-click on the group of those big labels. One of them will be selected. On the context menu, click Send To Front. If the blue label does not come in front, rightclick again and click Bring To Back until the blue label is on top of the white label:
Copyright © 2003 FunctionX, Inc.
477
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
Figure 43: A Label With Shadow 39. To add another label on the form, on the Component Palette, double-click the Label control
.
40. While the new label is still selected, click the Caption field and type Tenley Associates 41. Click the Cursor field 42. Click the arrow of the cursor field and field select crHandPoint 43. Click the + on the Font field to expand it and set its properties as follows: Color: clRed Name: Garamond Size: 10 44. Click the + button on the Style field to expand it and set its fsBold field to true 45. Double-click the field on the right side of fsUnderline to change it to true. 46. Click the – on the Font field to collapse 47. Click the Left field and type 416 48. Click the Top field and type 8 49. Add the following other labels: Name
Caption
Font
lblCompAdr lblCompCity lblCompPhone
4412 Conic Rd Suite 204D Silver Spring MD 20910 (301) 309-0330
MS Serif MS Serif MS Serif
Size
Left
Top
7 7 7
414 420 440
24 34 46
50. Click an unoccupied area on the form 51. To add a horizontal line on the form, add a label and for its caption, type ____ (Shift + -) continuously until a horizontal line spans the form. Position that line under the big blue label. 52. To test the project, press F9:
478
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
Figure 44: The Employment Application With Labels 53. To save the project, on the Standard toolbar, click Save All.
20.1.3 Label Methods The Label is based on the TLabel class and does not have any methods per se, only a constructor and a destructor. You should never directly call either the TLabel constructor or the ~TLabel destructor. These are only used if you want to dynamically create a label. To dynamically create a label locally, in a function or an event, declare a pointer to TLabel object and use the new operator, specify the component that owns the instance of the control. To initiate the control, you must specify its container or parent. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TLabel* Lbl = new TLabel(Form1); Lbl->Parent = Form1; Lbl->Caption = "LaFace, Inc."; Lbl->Left = 75; Lbl->Top = 32; } //---------------------------------------------------------------------------
20.1.4 Label Messages and Events The Label control has various events that are part of its repertoire, although they are hardly used. You can configure a label to behave like a (web) link using its OnClick event. For example the Win32 ShellExecute() function can be used to perform an operation on a file. The following OnClick event on a label would launch WordPad: //--------------------------------------------------------------------------void __fastcall TForm1::lblWordPadClick(TObject *Sender)
Copyright © 2003 FunctionX, Inc.
479
Chapter 20: Text-Based Controls
{
Borland C++ Builder Programming
ShellExecute(NULL, NULL, "C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe", NULL, NULL, SW_SHOWNORMAL);
} //---------------------------------------------------------------------------
The label inherits two valuable events from its parent the TCustomLabel class. Although the label cannot receive focus, when the mouse is positioned over it, it fires the OnMouseEnter() event, which is a TNotifyType. When the mouse is taken away from the top of the label, it fires the OnMouseLeave() event, which also is a TNotifyEvent type.
Practical Learning: Coding a Label Control 1.
On the form, double-click the (red) Tenley Associates label to access its OnClick event
2.
Implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmEmployment::Label3Click(TObject *Sender) { ShellExecute(NULL, "open", "http://www.tenleyassociatesinc.com", NULL, NULL, SW_MAXIMIZE); } //---------------------------------------------------------------------------
3.
While the Tenley Associates label still has focus, on the Events tab of the Object Inspector, double-click the right side of the OnMouseEnter field and implement its event as follows: //--------------------------------------------------------------------------void __fastcall TfrmEmployment::Label3MouseEnter(TObject *Sender) { Label3->Font->Color = clBlue; Label3->Font->Style = TFontStyles() << fsUnderline; } //---------------------------------------------------------------------------
4.
In the same way, access the OnMouseLeave event of the Tenley Associates label and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmEmployment::Label3MouseLeave(TObject *Sender) { Label3->Font->Color = clBlack; Label3->Font->Style = TFontStyles() >> fsUnderline; } //---------------------------------------------------------------------------
5.
480
Test the program
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
6.
Chapter 20: Text-Based Controls
Close it and return to Bcb
20.2 The Static Text Control 20.2.1 Introduction The label control we used earlier is a completely native VCL object and not particularly related to Win32. The Win32 library provides its own support to labelled text through a window class named STATIC. To support the Win32 STATIC control, the VCL provides the StaticText control. Over all, the VCL’s Label and StaticText controls are very similar. The biggest difference is that StaticText is a Windows control, like the STATIC class of the Win32 API while the VCL’s Label control is not. To create a static text, at design time, from the Additional tab of the Component Palette, you can click the StaticText button
and click the container that would host it.
20.2.2 Characteristics of a Static Text As stated already, the static text provides the same functionality as the label control. Its text is aligned using the Alignment property. It also has the following properties similar to those of the label: AutoSize, FocusControl, and ShowAccelChar. Among the differences between both controls, the StaticText does not have a Canvas property because you cannot draw into it. The StaticText control has a “physical” and identifiable border, unlike the label. This characteristic is controlled by the BorderStyle property which is an enumerator called TStaticBorderStyle and defined as follows: enum TStaticBorderStyle { sbsNone, sbsSingle, sbsSunken };
Its effects are as follows: sbsNone
sbsSingle
sbsSunken
Probably the biggest difference for us is that the StaticText, which is a descendent of TWinControl’s has a Handle property. This allows you to access the properties of the Win32’s STATIC class and apply them to the VCL’s StaticText control. For example, you can draw an etched border around the static text control as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { LONG GWL = GetWindowLong(StaticText1->Handle, GWL_STYLE); GWL |= SS_ETCHEDFRAME; SetWindowLong(StaticText1->Handle, GWL_STYLE, GWL);
Copyright © 2003 FunctionX, Inc.
481
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
} //---------------------------------------------------------------------------
In the same way, you can use the rectangular area of the StaticText control to draw, display an icon or a bitmap, or perform any other complicated task allowed by the Win32’s STATIC class. This example demonstrates that through one or another control, the VCL provides most , if not any, of the functionality you need from Windows controls. For example, instead of writing so many lines of code to get the above look, you could have used either a panel, a group box, a radio group, or a bevel controls, to get the above frame.
20.3 Edit Boxes 20.3.1 Introduction An edit box is a Windows control used to get or display text for the user’s interaction. At its most regular use, an edit box serves as a place to fill out and provide information. Such a use is common on employment applications, login dialog boxes, etc. Like most other controls, the role of an edit box is not obvious at first glance; that is why it should be accompanied by a label that defines its purpose. From the user’s standpoint, an edit box is named after the label closest to it. Such a label is usually positioned to the left or the top side of the edit box. From the programmer’s point of view, an edit box is a placeholder used for various things. For example, you can show or hide it as you see fit. You can also use it only to display text without allowing the user to change it. To create an edit box, once again, Bcb provides a lot of choices. The most fundamental edit box is designed with the Edit control palette.
of the Standard tab of the component
20.3.2 Edit Box Characteristics The most important aspect of an edit box is its text, whether it is displaying or requesting it. This is the Text property. When you add an edit control, C++ Builder initializes it with the name of the control; this would be Edit1 for the first edit box, Edit2 for the second, etc. If you want the control to display some text when the form launches, type the text in the Text property field in the Object Inspector. Otherwise, if you want the edit box to be empty when it comes up for the first time, delete the content of the Text field.
482
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
By default, a newly created edit box is used to both display and receive text from the user. If you want to user to read text without being able to change it, set the ReadOnly Boolean property to true. Its default value is false. As mentioned already, an edit box should be accompanied by a label that indicates what it is used for. To support this relationship, the Label control provides various properties. An accelerator character is a symbol of the label that provides easy access to its edit box. On the label, such a character is underlined. An example would be First Name. The idea is that, if the user presses Alt key in combination with the label’s underlined characters, the edit box it accompanies would receive focus. To create an accelerator key, choose one of the label’s characters and precede it with an ampersand character when setting its caption. An example would be &First Name. If you want a label to display the accelerator character instead of a plain ampersand, set the label’s ShowAccelChar property to true. If you set it to true but need to display an ampersand, type two & characters where the ampersand would be shown. The ShowAccelChar property of a label is only used to indicate that the label will display an accelerator character and the & symbol typed on the label creates that accelerator character. To indicate which edit box would receive focus when the accelerator character of the label is invoked; the label control provides the FocusControl property. To use this property, select the label on the container. Then, on the Object Inspector, click the FocusControl field to display its combo box. From there, you can select one of the existing controls. The control, such as an edit box, must be able to receive focus. The CharCase property of an edit box control allows the content of an Edit control to apply a character case of your choice. The CharCase property is controlled by the TEditCharCase enumerator defined as follows: enum TEditCharCase { ecNormal, ecUpperCase, ecLowerCase };
By default, it is set to ecNormal, which respects whatever character case is typed in an edit box. Otherwise, you can set it to ecUpperCase or ecLowerCase to set the edit box’ characters to uppercase or lowercase respectively. Text typed in an edit box appears with its corresponding characters unless you changed the effect of the CharCase property from ecNormal. This allows the user to see and be able to read the characters of an edit. If you prefer to make them un-readable, you can use the PasswordChar property. Although this property is a char type of data, changing it actually accomplishes two things. Its default value is #0, which means that the characters typed in it will be rendered in their alphabetic corresponding and readable characters. If you change it, for example to *, any character typed in it would be un-readable and be replaced by the value of this property. You can use any alphabetic character or digit to represent the characters that would be typed but you must provide only one character. Alternatively, you can specify an ASCII character instead. To do this, type # followed by the number representing the ASCII character. For example, #98 would display b for each character. When using a form, the user can press Tab to move from one control to another. By default, when such a control receives focus from the user pressing Tab, the whole text in an edit control is selected. This is controlled by the AutoSelect property. If you do not want that, you can set the AutoSelect Boolean property to false. One of the actions the user may be asked to perform on an edit box is to type text in it. If the control already contains text, the user can read it. Another common action the user Copyright © 2003 FunctionX, Inc.
483
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
performs is to select text contained in the control. The user must have the option to select only part of the text or its whole content. To do this, the user can click and hold the mouse on one end of the selection and drag to the desired end. This allows for partial selection. The area where the user start dragging during selection is represented by the SelStart property. When the user ends the selections, the length of the text selected is represented by the SelLength property. The portion of text selected is represented by the SelText property, which is an AnsiString type. These properties allow you either to programmatically select text or to get information about text that the user would have selected in the edit box.
Practical Learning: Designing Edit Boxes 1.
The Employment application should still be opened To add an Edit box, on the Standard tab of the Component Palette, click the Edit control
2.
Click on the form
3.
To reposition the control, click and drag the Edit control to the right side of the Date Hired label
4.
As the Edit control is still selected, on the Object Inspector, click the Name field and type edtDateHired
5.
Click the Text field and press Delete to empty its content.
6.
Using the same process, add new labels and edit boxes as follows. The name of an edit box starts with edt follows by the caption of the label omitting the special characters:
Figure 45: The Employment Application With Edit Controls 7.
Set the FocusControl property of each label to Edit control on its right.
8.
To save the project, on the Standard toolbar, click Save All.
9.
To test the form, press F9.
10. Save the project.
484
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
20.3.3 The Edit Control and its Functionality The edit control is based on the TEdit class whose immediate parent is TCustomEdit. Like every VCL class, it has a constructor and a destructor. The constructor can be used to dynamically create an edit control. The TEdit class provides other methods derived from the control’s parent or from ancestor classes. We saw earlier the properties related to the selection of text from an edit control performed either by you or by a user. Alternatively, the user may want to select the whole content of the control. To programmatically select the whole text of an edit control, call the SelectAll() method. Its syntax is: void __fastcall SelectAll();
The contents of an edit box can be empty or contain some text. If you want it empty, use the Clear() method. This would delete the whole contents of the control: //--------------------------------------------------------------------------void __fastcall TForm1::btnClearClick(TObject *Sender) { Edit1->Clear(); } //---------------------------------------------------------------------------
If the user or another control or action had selected text on the edit control, you can delete the selection using the ClearSelection() method. This method first finds if some text is selected. If so, the selected text would be discarded. If nothing is selected, the method would not do anything. To delete a selected text, you can write: //--------------------------------------------------------------------------void __fastcall TForm1::btnClearSelectionClick(TObject *Sender) { Edit1->ClearSelection(); } //---------------------------------------------------------------------------
20.3.4 Edit Control Events The Edit control is equipped with many events. Some of these events are from its parent class the TCustomEdit class and some others are from ancestor classes. The OnEnter() event occurs when a text-based control receives focus. This happens when the user clicks the edit box, after previously pressing Tab, to give focus to the control. The OnChange() event occurs as the user is typing text in the control. This happens as the user is changing the content of an edit control; this sends a message that the content of the edit box has changed or has been updated. You can use this event to check, live, what the user is doing in the edit box. For example, if you create a dialog with a first and last names edit boxes, you can use another edit box to display the full name. The controls could be drawn as follows:
Copyright © 2003 FunctionX, Inc.
485
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
Figure 46: Personal Information You can implement the OnChange event of the first name edit box as follows: //--------------------------------------------------------------------------void __fastcall TfrmPersInfo::edtFirstNameChange(TObject *Sender) { edtFullName->Text = edtFirstName->Text + " " + edtLastName->Text; } //--------------------------------------------------------------------------When the second edit box is being edited, you can implement its OnChange event as follows: //--------------------------------------------------------------------------void __fastcall TfrmPersInfo::edtLastNameChange(TObject *Sender) { edtFullName->Text = edtFirstName->Text + " " + edtLastName->Text; } //---------------------------------------------------------------------------
The OnExit event occurs when the control loses focus. In the case of an edit box, this could happen if the control has focus and the user presses Tab; the edit box would lose focus.
20.4 The MaskEdit Control 20.4.1 Introduction The MaskEdit is a special Edit object that provides more control over text entered or displaying in an edit box. The biggest difference between the Edit and the MaskEdit controls is the masking possibilities available on the latter. To add a MaskEdit control to your form, from the Additional tab of the Component Palette, click the MaskEdit button
and click on the form.
20.4.2 MaskEdit Characteristics The most important property of a MaskEdit control, which sets it apart from the (traditional) Edit control, is its ability to control what the user can and cannot enter in the text side. To configure this text, on the Object Inspector, click the EditMask field to reveal its ellipsis button. You have two options:
486
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
If you are familiar with the masking properties of this control, you can type a value using the appropriate symbols. Otherwise you should use an appropriate dialog box to guide you. You have two alternatives. You can double-click the empty area of the EditMask field or you can click the ellipsis button. This would call the Input Mask Editor dialog box
Figure 47: The Input Mask Editor Dialog Box Once there, you have various alternatives. The easiest way is to select one of the available formats in the Sample Masks list. Once you select a sample, its formatted mask displays in the Input Mask edit box. If the format is satisfying, you can click OK. Otherwise, you can add or delete symbols in the Input Mask edit box as you see fit.
If none of the samples matches your desired format and you know the symbols used, you can type your own. You can also check masks created for foreign languages to see if one of them would have the mask you need. To do this, click the Masks… button. This would call the Open Mask File dialog box:
Figure 48: The Open Mask File Dialog Box Click one file with a .dem extension and click OK. With the new mask in the Input Mask Editor, examine the samples in the Sample Masks list and select one. You can still customize any of the available masks. Copyright © 2003 FunctionX, Inc.
487
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
Another alternative is to create your own list of masks. To do this, follow the format used to create a mask. This is: Name | Sample | Mask
This line is made of three sections. The first and the second, then the second and the third are separated by a beam. To see a sample file, using Notepad, locate the C:\Program Files\Borland\Cbuilder6\Bin folder. After changing the Files of Type to All files, click one of the files with .dem extensions:
Figure 49: The Open Dialog Box Click Open:
Figure 50: The Mask Edit File Create a new file following the example. Each mask is typed on its own line and press Enter at the end of each mask. To save the file, locate the C:\Program Files\Borland\Cbuilder5\Bin folder. In the Save dialog box, change the Save As Type to All Files. Type a name in one-word followed by an extension .dem extension. To use your list of masks, invoke the Input Mask Editor, click Masks… Locate the C:\Program 488
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
Files\Borland\Cbuilder5\Bin folder. Change the Files of Types to All files. Click the file you created and click Open. You can also set a mask programmatically using the symbols appropriate for the masks. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { // Mask for an 8 character file name + 3-character extension // The first character is automatically converted to uppercase // After the first character, the user can enter an alphabetical // character or a digit to complete the 8 characters. // The other 7 characters and the extensions are converted to lowercase edtFileName->EditMask = ">L
As a text-based control, the content of the MaskEdit control is represented by the Text property, which is an AnsiString object. Following the EditMask you had set in the Input Mask Editor editor, you can use a default text that would display when the control opens. To set this text, on the Object inspector, click Text and type a value that abides by the rules of the EditText field. At design time, C++ Builder will assist you and make sure that the value you type is appropriate. At runtime also, the user will have to follow the rules of the mask. When a mask is configured for a MaskEdit control, the compiler reinforces the rule to make sure the user would follow number and types of characters allowed in the edit box. If you add a MaskEdit control but do not apply a mask to it, you can limit the number of characters that the user can enter in the box. The maximum number of characters allowed is set using the MaxLength property. This property has any effect only if no mask is applied to the control. At design time, type an integer value for the property. At runtime, assign an appropriate value to the control. The IsMasked Boolean property can be used to check whether a MaskEdit control has been configured with a mask already.
Practical Learning: Using the MaskEdit Control 1.
Open the employment application we were working on
2.
Click the edit box on the right side of the Date Hired label and press Delete. Also delete the edit boxes on the right sides of the MI, the Home Phone, the Employee #, the ZIP, the Work Phone, and the Ext labels
3.
On the Component Palette, click the Additional tab. Click the MaskEdit button .
4.
Click on the right side of the Date Hired label on the form.
5.
On the Object Inspector, click the Name and type edtDateHired
6.
Click EditMask field to display the ellipsis button . Click the ellipsis button. On the Input Mask Editor dialog, click Date. In the Input Mask edit box, change the two zeros to four zeros:
Copyright © 2003 FunctionX, Inc.
489
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
Figure 51: The Input Mask Editor Dialog Box 7.
Click OK
8.
As the MaskEdit control still has focus, on the Object Inspector, click the Text field and press Delete. Click its ellipsis button. Set the text to 12/05/1996:
Figure 52: The Masked Text Editor Dialog Box 9.
Click OK.
10. On the Component Palette, click the MaskEdit control and click on the right side of the Home Phone label. On the Object Inspector, click the Text field and delete the content of the field. Click the EditMask field and click the ellipsis button. Click Phone and click OK. 11. On the form, click the new masked edit box of the Home Phone box. On the main menu, click Edit -> Copy. On the main menu again, click Edit -> Paste. Move the new paste edit box to the right side of the Work Phone label. 12. Add another MaskEdit control to the right side of the Employee # label. Click the EditMask and type 00-000;1;_ 13. Add a MaskEdit control to the right side of the MI label. Set its EditMask to >L and delete the content of the Text field. 14. Add a MaskEdit control on the right side of the ZIP label. Set its Text field to 00000 and set its EditMask to 99999 15. Add a MaskEdit control to the right side of the Ext label. Delete its Text field and set its EditMask to #####;1;_
490
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
Figure 53: The Employment Application With Mask Controls 16. Complete the form as follows:
Figure 54: The Employment Application Improved 17. The external bevel around the edit boxes has a Lowered Style while the external one has a Raised Style. The Close button resides on a Panel control. The OnClick event of the Close button is implement with the Close(); line. 18. To test the application, on the Debug toolbar, click the Run button. 19. After viewing the form, close it.
20.4.3 MaskEdit Methods On its own, the MaskEdit control has only two methods: the constructor and the destructor. The TMaskEdit constructor is used to dynamically create a MaskEdit object. This requires an instance of a TMaskEdit class. Using the new operator, specify the owner of the control. You must also specify the parent of the variable. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
Copyright © 2003 FunctionX, Inc.
491
Chapter 20: Text-Based Controls
{
Borland C++ Builder Programming
TMaskEdit *Mascot = new TMaskEdit(Form1); Mascot->Parent = Form1;
} //---------------------------------------------------------------------------
A MaskEdit object created like this is just a classic Edit control. If you want to make a true MaskEdit object, set its properties as needed, namely an EditMask and possibly a Text properties. This time, not only will you have to work in a non-visual setting but you should also make sure that the EditMask and the Text properties are synchronized. Sometimes, this would involve trial-and-error.
20.4.4 MaskEdit Events The main event of the MaskEdit control occurs when the content of the control has been altered. The compiler takes care of validating the characters and symbols while the user is editing the edit box. When the user has finished and presses Enter or Tab to possibly shift the focus to another control, a notification is sent to the operating system. If the value entered in the control does not conform to the EditMask property, an error is thrown and the application may stop responding. For this reason, you should use the MaskEdit control only when appropriately needed; or you should write an event handler or function that would deal with errors of this control.
20.5 The IP Address Control 20.5.1 Introduction The IP Address control is an object that allows the user to enter an IP address or you to retrieve one. The control appears like an edit box or a masked edit box divided in four (semi-independent) sections:
The IP Address control is aware of the basic rules of IP address formatting. To proceed with the control, the user can click the first field and start typing. Each section allows only digits. By default, the control is configured to allow only numbers that range from 0 to 255 but you the programmer can change this allowed range. It does not allow a negative character. If the user enters a value higher than the allowed, the number is reset. Each field can allow only three digits. Once a valid value has been entered in a field, the focus shifts to the next section to the right.
20.5.2 Operations on an IP Address Control To create an IP Address control, you can either use the traditional Win32 approach or create your own component that would implement the behavior set by the Win32 API. Because in this book we will not create custom components, we will use the Win32 technique. Therefore, to create an IP Address control, call the CreateWindow() or the CreateWindowEx() function and specify the class name as WC_IPADDRESS. Here is an example: 492
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
//--------------------------------------------------------------------------#ifndef Unit1H #define Unit1H //--------------------------------------------------------------------------#include #include #include #include //--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components void __fastcall FormCreate(TObject *Sender); private: HWND hWndIPAddress; // User declarations public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------#endif //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { hWndIPAddress = CreateWindowEx(0, WC_IPADDRESS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 14, 120, 20, Handle, NULL, HInstance, NULL); } //---------------------------------------------------------------------------
Figure 55: The IP Address Control Some of the jobs you will perform on an IP Address control consist of checking whether the control contains an address, setting an IP address in its fields, retrieving the current address from its fields, or deleting an address from its fields. Before doing anything on an IP Address control, you may want to check whether its fields are filled with some entries. To do this, you can send an IPM_ISBLANK message using the SendMessage() function. Because you are only checking the contents of the
Copyright © 2003 FunctionX, Inc.
493
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
control, the wParam and the lParam parameters are not used and must be passed with 0 values. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) { BOOL IsThereAnAddress = SendMessage(hWndIPAddress, IPM_ISBLANK, 0, 0); if( IsThereAnAddress == True ) ShowMessage("There is no IP address"); } //---------------------------------------------------------------------------
On the other hand, if the control already contains an IP address, whether it is complete or not, before performing such operations as changing its values or else, you may want to completely delete the current IP address. This is done by sending an IPM_CLEARADDRESS message to the control using the SendMessage() function. The wParam and the lParam parameters are not used and will be passed as 0. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { SendMessage(hWndIPAddress, IPM_CLEARADDRESS, 0, 0); } //---------------------------------------------------------------------------
By default, when the IP Address control is created, it is blank: its field do not contain values. Whether the control is empty or not, if you want to fill it up, you must first create an address. This is done by calling the MAKEIPADDRESS macro. Its syntax is: LPARAM MAKEIPADDRESS(BYTE b0, BYTE b1, BYTE b2, BYTE b3);
This macro simply requires 4 byte values as arguments. Each value should range from 0 to 255. Here is an example: MAKEIPADDRESS(212, 56, 198, 92);
The MAKEIPADDRESS macro is only used to create an address. If you want to pass that address to an IP Address control, you can send it an IPM_SETADDRESS message using the SendMessage() function. The wParam argument is not used and must be passed as 0. The address to be set is passed as the lParam argument, which results from a call to the MAKEIPADDRESS macro. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { LPARAM lpAdr = MAKEIPADDRESS(212, 56, 198, 92); hWndIPAddress = CreateWindowEx(0, WC_IPADDRESS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 14, 120, 20, Handle, NULL, HInstance,
494
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
NULL); SendMessage(hWndIPAddress, IPM_SETADDRESS, 0, lpAdr); } //---------------------------------------------------------------------------
If the control already contains an address and you want to retrieve it, send it an IPM_GETADDRESS message using the SendMessage() function. The syntax you would use is: lResult = SendMessage((HWND) hWndControl, (UINT) IPM_GETADDRESS, 0, (LPARAM) lParam);
This version of the SendMessage() function returns the number of non-empty fields of the address. Each one of the four fields of an IP address has a particular value. The values of these fields can be identified using the following macros: BYTE BYTE BYTE BYTE
FIRST_IPADDRESS(LPARAM lParam); SECOND_IPADDRESS(LPARAM lParam); THIRD_IPADDRESS(LPARAM lParam); FOURTH_IPADDRESS(LPARAM lParam);
Each one of these macros retrieves the value stored in their respective fields. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { DWORD CurAddress; LRESULT SM = SendMessage(hWndIPAddress, IPM_GETADDRESS, 0, (LPARAM)(LPDWORD)&CurAddress); BYTE BYTE BYTE BYTE
IPPart1 = FIRST_IPADDRESS((LPARAM)CurAddress); IPPart2 = SECOND_IPADDRESS((LPARAM)CurAddress); IPPart3 = THIRD_IPADDRESS((LPARAM)CurAddress); IPPart4 = FOURTH_IPADDRESS((LPARAM)CurAddress);
ShowMessage("First:\t\t" + AnsiString(IPPart1) + "\n" + "Second:\t\t" + AnsiString(IPPart2) + "\n" + "Third:\t\t" + AnsiString(IPPart3) + "\n" + "Fourth:\t\t" + AnsiString(IPPart4) + "\n" + "Non-Blank Fields: " + AnsiString(SM));
} //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
495
Chapter 20: Text-Based Controls
Borland C++ Builder Programming
Figure 56: Decoding an IP Address When the fields of an IP Address control are empty, the user can fill them up with some vales. To use it, the user must give it focus and start typing. Once he has provided the three digits of a field, the caret moves to the next field. This is automatically done. If you want to programmatically set the focus to a particular field in response to some user’s action, you can send an IPM_SETFOCUS message to the control. The fields of the control are in a zero-based array where the most left field has an index of 0 and the most right field has an index of 3. To set focus to a field, pass its index as the wParam parameter. The lParam parameter is not used. By default, each field of an IP address has a range of values from a minimum whose default value is 0 and a maximum whose default value is 255. The control is so much aware of these values that, if the user tries entering a value that is lower than the set range, the control would not respond. For example if the user tries typing – for a negative value, it would not be accepted. On the other hand, if the user tries to enter a value that is higher than the range, the control would reset the field to the set maximum. If you want to control the range of values allowed in a particular field, create it using the MAKEIPRANGE macro. Its syntax is: LPARAM MAKEIPRANGE(BYTE low, BYTE high);
The MAKEIPRANGE macro is only used to create a range of values. In order to actually assign this range to an IP Address control, send an IPM_SETRANGE message to the control using the SendMessage() function. The wParam parameter is used to specify the index of the field whose range you want to set. The lParam parameter contains the new range set by the return value of the MAKEIPRANGE macro. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { LPARAM wRangeForField3 = MAKEIPRANGE(52, 212); hWndIPAddress = CreateWindowEx(0, WC_IPADDRESS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 14, 120, 20,
496
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 20: Text-Based Controls
Handle, NULL, HInstance, NULL); SendMessage(hWndIPAddress, IPM_SETRANGE, 2, wRangeForField3); } //---------------------------------------------------------------------------
Normally, you will usually need to control the range of values for a field in response to the user’s entry in the provious field. This assignment is not handled by the control because it lets the programmer take this role and responsibility.
20.5.3 IP Address Control Events Although the IP Address control is not an edit box, both share a few characteristics such as their appearance. Like every visual and non-static control, in order to use an IP Address control, the user must give it focus. This can be done by clicking a field in the control. When the IP Address control receives focus, it sends an EN_SETFOCUS message, which produces an event similar to the Edit control’s OnEnter() event. Like the edit control, when the user starts typing a new value in the IP Address control, it sends an EN_CHANGE message which produces an OnChange() event (similar to that of the Edit control). If the user starts changing the value of a field, the control sends the IPN_FIELDCHANGED notification message. If the user clicks away, which causes the control to lose focus, it sends an EN_KILLFOCUS message. This produces an event similar to the OnExit() event of the VCL’s Edit control.
Copyright © 2003 FunctionX, Inc.
497
Chapter 21: Text-Based Applications
498
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
Chapter 21: Text-Based Applications 21.1 The Memo Control 21.1.1 Overview Like the Edit control, the Memo control is used to display or receive text. Unlike the Edit control, the memo can be used to display multiple lines of text. Like the Edit control, the Memo control is based on the TCustomEdit class. But there is an intermediary class between the TCustomEdit and the TMemo class. It is the TCustomMemo class. To add a memo to a form or another container, from the Standard tab of the Component Palette, click the Memo button
and click on the desired position on the form.
Practical Learning: Creating a Memo Object 1.
Start a new a new project with its default form
2.
Save it in a new folder called Notice3
3.
Save the unit as Exercise and save the project as Notice
4.
Change the Caption of the form to Notice and change its Name to frmMain
5.
to the From the Win32 tab of the Component Palette, add an ImageList control form and fill it with the following bitmaps: New, Open, Save, Exit, Undo, Cut, Copy, and Paste from the resources that accompany this book. Close the ImageList Editor using the OK button
6.
From the Standard tab of the Component Palette, add a MainMenu control to the form. Set its Images property to ImageList1 and double-click it to open the menu designer
7.
Right-click the Menu Designer window and click Insert From Template. Doubleclick Edit Menu. Delete the following menu items: Repeat, Paste Special, Go To…, the separator, Link, and Object
8.
Click the Edit menu item to select it. Right-click an empty area in the Menu Editor window again and click Insert From Template. Double-click File Menu
9.
In the Menu Designer, use your intuition to set the BitmapIndex value of each menu item that can use one and close the Menu Editor:
Copyright © 2003 FunctionX, Inc.
499
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
10. From the Win32 tab of the Component Palette, click the Toolbar button and click in the middle of the form. On the Object Inspector, set its properties as follows: Height: 40 Name: tbrStandard ShowCaptions: true Images: ImageList1 11. Right-click the toolbar and click New Button. Set its MenuItem property to New1. Continue right-clicking and changing the MenuItem properties to produce the following toolbar:
Figure 57: Toolbar With Buttons and Caption 12. To add a memo, on the Standard tab of the Component Palette, click the Memo button
and click in the middle of the form
21.1.2 Characteristics of a Memo Control By default, the Memo control is positioned where you drop it at design time. The Align property specifies how the memo will be aligned with regard to its container. If the memo is used as the main area of a text editor application, you should set its Align property to alClient. The user mostly reads and/or enters text in the memo when interacting with the control. At design time, you can set the text that would display when the memo comes up. To enter this text, on the Object Inspector, click the Lines field to reveal its ellipsis button that allows you to open the String List Editor dialog box. If you want the control to be empty at startup, delete the content of the String List Editor and click OK. Otherwise, type the desired text and click OK. The user, on the other hand has the ability to type text to alter the content of the memo box. This is possible only if the ReadOnly property is set to true, which is the default. If
500
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
you want to prevent the user from altering the text in the memo, set the ReadOnly property to false. You can also do this programmatically. When a memo box opens, the compiler registers the content of the control. If the user has the ability to change the text in the control and if the user changes it, the compiler flags the control as Modified. This allows you to take actions. You can acknowledge this by programmatically setting the Modified property to true. If another control or some other action alters the contents of the memo, you can make sure that this property reflects the change. You can change this programmatically as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Memo1KeyPress(TObject *Sender, char &Key) { Memo1->Modified = True; } //---------------------------------------------------------------------------
Although the user can enter any number of characters into a memo box, you can set a maximum number that would prevent the user from going over this number of characters. At design time, you can set the maximum number of characters in the MaxLength field. Programmatically, you can change it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Memo1KeyPress(TObject *Sender, char &Key) { Memo1->MaxLength = 24; } //---------------------------------------------------------------------------
If the control will be used to enter text, the user can press Enter at the end of a line to move to the next line. This ability is controlled by the Boolean WantReturns property. By default, this property is set to true, which means the user can press Enter when typing text. If you do not want to validate the Enter key in the Memo control, set this property to false. To set it programmatically, assign the desired value to the WantReturns property: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Memo1->WantReturns = False; } //---------------------------------------------------------------------------
When the WantReturns property is set to false, if the user presses Enter while the memo has focus, the Enter action would be transferred to the form as the parent. The form in turn can find out what to do. For example, you may have configured the form to perform a particular action when the Enter key is pressed. The typical example is by setting a button’s Default property to true. In this case, pressing Enter from the Memo control would cause the action associated with the button. Even if the WantReturns of a memo is set to false, the user can still press Ctrl + Enter to move to the next line. The user is accustomed to pressing Tab to insert tab characters in the text. By default, when the user presses Tab when interacting with your application, the focus moves from one control to the next, following the TabOrder values of the form. Even when using a memo to perform text editing, if the user presses Tab, the focus would switch to another control or to the form. If you want a memo to receive focus when the user presses the Tab key, set the WantTabs property from false (the default), to true. Copyright © 2003 FunctionX, Inc.
501
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
When entering text in a Memo control, the characters start on the left side of the memo and are subsequently added on the right side. The ability to align text is controlled by the Alignment property. For a Memo control, the alignment is configured using the TAlignment enumerator: enum TAlignment { taLeftJustify, taRightJustify, taCenter };
This property works exactly as we reviewed it for the Edit control. As the user enters text in a memo box, the compiler considers that a paragraph starts from the user typing a character until he or she presses Enter. Therefore, a paragraph could be an empty space, a character, a word, a line of text, a whole page or an entire book. Depending on the width of the Memo control, the text is incrementally added to the right side of each previous character. If the caret gets to the right border of the control, the text automatically continues to the next line, although it is still considered as one paragraph. To start a new paragraph, the user has to press Enter. The ability for the text to continue on the next line when the caret encounters the right border of the memo is controlled by the WordWrap property whose default Boolean value is set to true. If you do not want text to wrap to the subsequent line, set the WordWrap property to false. You can also set it programmatically as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Memo1->WordWrap = False; } //---------------------------------------------------------------------------
Practical Learning: Configuring a Memo Control 1.
On the form, click the Memo1 control to select it. On the Object Inspector, set its Align property to alClient
2.
Double-click the box to the right side of the Lines to display the String List Editor
3.
Delete the Lines line and click OK
4.
Change its Name to mmoNotice
5.
Make sure the WantTabs property is set to true and set the WantTabs property to true
21.1.3 Memo Methods The Memo control is based on the TMemo class. Like all VCL controls, TMemo has a constructor and a destructor. The constructor is mostly used to dynamically create a memo box. To do this, declare a pointer to TMemo and use the new operator to specify its owner. You must also specify the parent of the variable.Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnNotesClick(TObject *Sender) { TMemo *Notes = new TMemo(Form1); Notes->Parent = Form1; Notes->Left = 350;
502
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
Notes->Top = 20; Notes->Height = 120; Notes->Width = 320;
} //---------------------------------------------------------------------------
The other operations the user can perform on a memo can be controlled by methods such as Clear() or SelectAll() that we reviewed for the edit control and they function the same. The TMemo class natively supports regular operations performed on a text control such as undoing an action, cutting or copying text from the control, pasting text from another control. The methods used to accomplish these assignments are Undo(), CutToClipboard(), CopyToClipboard(), PasteFromClipboard().
Practical Learning: Configuring a Memo control 1.
Right-click anywhere on the form and click Tab Border. In the Edit Tab Order dialog box, move mmoNotice to the top of the list unless it is set already and click OK
2.
On the main menu of the form, click Edit -> Undo and implement the OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::Undo1Click(TObject *Sender) { mmoNotice->Undo(); } //---------------------------------------------------------------------------
3.
On the main menu of the form, click Edit -> Cut and implement the OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::Cut1Click(TObject *Sender) { mmoNotice->CutToClipboard(); } //---------------------------------------------------------------------------
4.
On the main menu of the form, click Edit -> Copy and implement the OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::Copy1Click(TObject *Sender) { mmoNotice->CopyToClipboard(); } //---------------------------------------------------------------------------
5.
On the main menu of the form, click Edit -> Paste and implement the OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::Paste1Click(TObject *Sender) { mmoNotice->PasteFromClipboard(); } //---------------------------------------------------------------------------
6.
Execute the program. Test cutting, copying, and pasting text
Copyright © 2003 FunctionX, Inc.
503
Chapter 21: Text-Based Applications
7.
Borland C++ Builder Programming
Close it and save all
21.1.4 Memo Events Like the edit control, the only event that the Memo control can claim on its owns is the OnChange() event which is a TNotifyEvent type. This occurs when the user changes the content of the control. This event is useful if you want to display a notification that the text is not as it was when the form opened. When the application focus moves from one to another control or the application itself to a control, the OnEnter event is fired. When the control looses focus, an OnExit() event is fired.
21.2 The Rich Text 21.2.1 Introduction Text is considered rich if it can afford a series of colorful and multi-style features that make it more attractive that a regular ASCII text. Such a text can have some of its sections in different colors. Its paragraphs can have customized attributes or arranged independent of each other. Although you can create a complete rich but static text, the common use of a rich text is to let the users process most of the formatting they need. In VCL applications, a rich text is based on the TRichEdit class. To add rich text to your application, you can use the RichEdit button from the Win32 tab of the Component Palette. After adding a RichEdit control to a form, you can use the Object Inspector to customize it, using its Properties.
Practical Learning: Introducing the RichEdit Control 1.
Start a new application with its default form
2.
To save it, on the Standard toolbar, click the Save All button
3.
Create a new folder called Editor1 and display it in the Save In combo box
4.
Save the unit as Main and save the project as Editor
5.
Change the Caption of the form to Editor – Untitled and change its Name to frmMain
6.
Set the ShowHint property to true
7.
In the header file, declare a private AnsiString variable named CurrentFileName //--------------------------------------------------------------------------#ifndef MainH #define MainH //--------------------------------------------------------------------------#include #include #include #include //--------------------------------------------------------------------------class TfrmMain : public TForm
504
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
{ __published: // IDE-managed Components private: AnsiString CurrentFileName; // User declarations public: // User declarations __fastcall TfrmMain(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TfrmMain *frmMain; //--------------------------------------------------------------------------#endif
8.
In the constructor of the form, initialize the variable as: //--------------------------------------------------------------------------#include #pragma hdrstop #include "Main.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TfrmMain *frmMain; //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { // The following string is not allowed for a Windows file // This will allow us to know when the name of the file has been changed CurrentFileName = ""; } //---------------------------------------------------------------------------
9.
Display the form and double-click its body to access its OnCreate() event
10. Because we will use the features of the rich edit control stored in a DLL, implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { int iRichDLL = (int)LoadLibrary("RICHED20.DLL"); if( !iRichDLL ) // If you could not load RICHED20.DLL { ShowMessage("Could not load the RICHED20.DLL"); Application->Terminate(); return; } } //---------------------------------------------------------------------------
11. On the Win32 tab of the Component Palette, click RichEdit body of the form. Change its Name to rchEditor
and click in the
12. On the Win32 tab of the Component Palette, double-click ImageList the form, double-click ImageList1
Copyright © 2003 FunctionX, Inc.
and, on
505
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
13. Using the Add button, add the following bitmaps: New, Save, Redo, NumBullet, Mail, FirstIndent, IndentLeft, IndentRight, and Paragraph 14. Click OK 15. From the Standard tab of the Component Palette, double-click ActionList and, while the new ActionList1 is still selected, on the Object Inspector, set its Images property to ImageList1 16. On the form, double-click ActionList1 to create a list of actions 17. In the ActionList window, right-click an empty area and click New Action. Set its properties as follows: Caption: &New Category: File Hint: New|Create a new document ImageIndex: 0 Name: FileNew Shortcut: Ctrl+N 18. In the same way, create a new action and set its properties as follows: Caption: &Save Category: File Hint: Save|Save the current document ImageIndex: 1 Name: FileSave Shortcut: Ctrl+S 19. Once again, create a new action and set its properties as follows: Caption: &Redo Category: Edit Hint: Redo|Redo the previous action ImageIndex: 2 Name: EditRedo Shortcut: Ctrl+Y 20. Add Another action and set its properties as follows: Caption: &Numbering Category: Format Hint: Numbering|Format a numeric list ImageIndex: 3 Name: FormatNbr 21. Add one more action and set its properties as follows: Caption: &Numbering Category: Format Hint: Numbering|Format a numeric list ImageIndex: 3 Name: FormatNbr 22. Add one more action and set its properties as follows: Caption: &Standard Category: View Checked: true Hint: Standard|Toggle the Standard toolbar Name: ViewStandard 23. Add one more action and set its properties as follows: Caption: &Formatting Category: View 506
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
Checked: true Hint: Formatting|Toggle the Formatting toolbar Name: ViewFormatting 24. Add one more action and set its properties as follows: Caption: Status &Bar Category: View Checked: true Hint: Status Bar|Toggle the Status Bar Name: ViewStatusBar 25. Right-click any frame in the Action List Editor and click New Standard Action. From the Standard Action Classes dialog box, under the Internet node, double-click TSendMail 26. In the left frame of the ActionList Editor, click Internet and, in the right frame, click SendMail1. On the Object Inspector, set ImageIndex to 4 27. Right-click an empty area in the ActionList Editor and click New Standard Action. Once in the Standard Action Classes dialog box, click Edit. Press and hold Shift. Then click TRichEditAlignCenter and release Shift:
28. Click OK 29. Right-click in the ActionList Editor again and click New Standard Action. Click TFileOpen. Press and hold Ctrl. Then click TFileSaveAs, TFilePrintSetup, TFileExit, TSearchFind, TSearchFindNext, TSearchReplace, TSearchFindFirst, TColorSelect, TFontEdit, and TPrintDlg. Release Ctrl and click OK 30. In the ActionList Editor window, on the left frame, click File. In the right frame, click FileSaveAs1. 31. In the Object Inspector, click the + button of Dialog to expand it. 32. Click DefaultExt and type rtf
Copyright © 2003 FunctionX, Inc.
507
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
33. Click Filter and click its ellipsis button 34. Complete the Filter Editor dialog box as follows:
35. Click OK 36. Click Title, type Save File As and press Enter 37. Click the - button of Dialog to collapse it and click the Events property page 38. Double-click the empty box on the right side of OnAccept() and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FileSaveAs1Accept(TObject *Sender) { // Save the file as we review in the lesson on strings rchEditor->Lines->SaveToFile(FileSaveAs1->Dialog->FileName); // Change the name of the file CurrentFileName = FileSaveAs1->Dialog->FileName; // Retrieve the name of the file to display on the title bar AnsiString FName = ExtractFileName(CurrentFileName); Caption = "Editor - " + FName; } //---------------------------------------------------------------------------
39. Press F12 to display the form. Save the project. 40. To proceed with the traditional Save As implementation, on the Component Palette, and click on the form. While the click Dialogs. Click the SaveDialog button new SaveDialog1 control is still selected, on the Object Inspector, click the Properties tab and click DefaultExt. Type rtf 41. Click Filter and click its ellipsis button follows:
508
. Complete the Filter Editor dialog box as
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
Figure 58: The Filter Editor Dialog Box 42. Click OK 43. Click Title, type Save File As and press Enter. 44. Double-click ActionList1 to return to the ActionList Editor window. On the left frame, click File. On the right frame, click FileSave1 and, in the Object Inspector, click the Events tab. Double-click the empty box on the right side of OnExecute and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FileSaveExecute(TObject *Sender) { // Find out if the current document has never been saved // but is not empty if( rchEditor->Modified && CurrentFileName == "" ) { // Since the document is dirty, display the Save As dialog box if( SaveDialog1->Execute() ) { // Retrieve the new name file and display // the file in the rich edit control rchEditor->Lines->SaveToFile(SaveDialog1->FileName); // Change/Update the global and complete name of the file, // including its path CurrentFileName = SaveDialog1->FileName; // Extract the name of the file AnsiString FName = ExtractFileName(CurrentFileName); // Display the name of the current file on the title bar Caption = "Editor - " + FName; } } else { // It appears that this document already had a name // but the document was previously modified // Therefore, simply save it internally rchEditor->Lines->SaveToFile(CurrentFileName); } }
Copyright © 2003 FunctionX, Inc.
509
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
//---------------------------------------------------------------------------
45. Press F12 to display the form. Click the form to select it 46. To handle the closing of the application, click the Events tab. Then, double-click the empty field of the OnClose event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormClose(TObject *Sender, TCloseAction &Action) { // Is the document dirty? if( rchEditor->Modified ) { // Since the document is dirty, find out if the user wants to save it int Response = Application->MessageBox( "The document has changed. Do you want to save it?" "\nClick\n" "Yes:\tTo save the document and close the application.\n" "No:\tNot to save the document but close the application.\n" "Cancel:\tNot to do anything", "Editor - Saving a File", MB_YESNOCANCEL | MB_ICONQUESTION); // If the user wants to save it if( Response == IDYES ) { // Behave as if the user had clicked File -> Save FileSave1Execute(Sender); // Free the action Action = caFree; } // If the user doesn't want to save the document else if( Response == IDNO ) Action = caFree; // Simply free the action else Action = caNone; // The user cancelled the action: do nothing } else // There is no action to take Action = caFree;
} //---------------------------------------------------------------------------
47. Display the ActionList Editor. Make sure that File is selected on the left frame 48. To handle the creation of a new document, click the FileNew. In the Object Inspector, click the Events tab if necessary. Double-click the empty box on the right side of OnExecute and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FileNewExecute(TObject *Sender) { // When the user clicks the New button to start a new document, // find out if the document is dirty if( rchEditor->Modified ) { // Since the document is dirty, find out if the user wants to save it int Response = Application->MessageBox( "The document has changed. Do you want to save it?" "\nClick\n" "Yes:\tTo save the document and create a new one.\n" "No:\tNot to save the document but create a new one.\n"
510
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
"Cancel:\tNot to do anything", "Editor - Saving a File", MB_YESNOCANCEL | MB_ICONQUESTION); // If the user wants to save the document if( Response == IDYES ) { // Behave as if the Save action was initiated FileSave1Execute(Sender); // After saving the document, delete the whole document rchEditor->Clear(); // Reset the current file name to garbage CurrentFileName = ""; // Show on the title bar that the file is clean Caption = "Editor - Untitled"; // Reset the Modified flag of the Rich Edit control rchEditor->Modified = False; } // If the user doesn't want to save the document else if( Response == IDNO ) { // Delete the whole contents of the rich edit text rchEditor->Clear(); // Fill the file name with garbage CurrentFileName = ""; // Show on the title bar that the document is clean Caption = "Editor - Untitled"; // Reset the Modified flag rchEditor->Modified = False; } // The user clicked Cancel: Don't do nothing else Action = caNone; } else { rchEditor->Clear(); CurrentFileName = ""; Caption = "Editor - Untitled"; rchEditor->Modified = False; }
} //---------------------------------------------------------------------------
49. In the ActionList Editor, in the right frame, click FileOpen1. Move FileOpen1 to be just under FileNew1 in the Actions list 50. In the Object Inspector, click the Properties tab and expand the Dialog property if necessary. Click DefaultExt and type rtf 51. Click Filter and click its ellipsis button follows:
Copyright © 2003 FunctionX, Inc.
. Complete the Filter Editor dialog box as
511
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
52. Click OK 53. Click Title, type Open an Existing Document and press Enter. 54. Click the - button of Dialog to collapse it and click the Events tab 55. Double-click the event field of OnAccept and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FileOpen1Accept(TObject *Sender) { // Before opening a new document, find out if the current one is dirty if ( rchEditor->Modified ) { // Since the document is dirty, prompt the user to save int Response = Application->MessageBox( "The document has changed. Do you want to save it?" "\nClick\n" "Yes:\tTo save the document and open the new one.\n" "No:\tNot to save the document but open the new one.\n" "Cancel:\tNot to do anything", "Editor - Saving a File", MB_YESNOCANCEL | MB_ICONQUESTION); // If the user wants to save the current document if( Response == IDYES ) { // Behave as if the Save action was initiated FileSaveExecute(Sender); rchEditor->Lines->LoadFromFile(FileOpen1->Dialog->FileName); CurrentFileName = FileOpen1->Dialog->FileName; AnsiString FName = ExtractFileName(CurrentFileName); Caption = "Editor - " + FName; rchEditor->Modified = False; } // If the user doesn't want to save the document else if( Response == IDNO ) { // Open the new document after letting the user select it rchEditor->Lines->LoadFromFile(FileOpen1->Dialog->FileName); // Change the file name of our archives CurrentFileName = FileOpen1->Dialog->FileName; // Get the name of the file that the user selected
512
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
AnsiString FName = ExtractFileName(CurrentFileName); // Display the file name on the title bar Caption = "Editor - " + FName; // Reset the Modified flag of the rich edit control rchEditor->Modified = False;
} } // Apparently the current document is not dirty else { rchEditor->Lines->LoadFromFile(FileOpen1->Dialog->FileName); CurrentFileName = FileOpen1->Dialog->FileName; AnsiString FName = ExtractFileName(CurrentFileName); Caption = "Editor - " + FName; rchEditor->Modified = False; } } //---------------------------------------------------------------------------
56. Close the Action Editor window. Save All and display the form 57. From the Standard tab of the Component Palette, click the MainMenu and click the form.
button
58. On the Object Inspector, click the Properties tab and set the Images of the MainMenu1 item to ImageList1 59. On the form, double-click MainMenu1. 60. Right-click an empty area in the Menu Designer and click Insert From Template. In the Insert Template dialog box, double-click File Menu 61. Click the last item under Exit and, on the Object Inspector, set its Action property to SendMail1 62. Set the caption of the empty box under SendMail to - to add a separator. Move the Send Mail menu item and its separator above the Exit menu item 63. Set the Actions to the menu items as follows: New = FileNew, Open = FileOpen1, Save = FileSave, Save As = FileSaveAs1, Print = PrintDlg1, Print Setup = FilePrintSetup1, Exit = FileExit1
Copyright © 2003 FunctionX, Inc.
513
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
Figure 59: The Menu Designer 64. Click the box on the right side of File. Right-click an empty area in the Menu Editor and click Insert From Template. 65. In the Insert Template dialog box, double-click Edit Menu. 66. Delete the Paste Special, Go To, Links, and Object menu items 67. In the Edit menu of the Menu Designer, Set the Actions as follows: Undo = EditUndo, Repeat = EditRedo, Cut = EditCut1, Copy = EditCopy1, Paste = EditPaste1, Find = SearchFind1, Replace = SearchReplace1 68. Using the empty boxes under the Replace menu item, Add the following actions: SearchFindFirst1, SearchFirstNext1, EditDelete1, EditSelectAll1. Arrange the menu item to have the following order:
514
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
Figure 60: Menu Designer 69. Click the box right to Edit. In the Object Inspector, click Caption and type &View and press Enter 70. Click View and click the box under View. Using the Object Inspector, set its Action to ViewStandard. Set the Action of the next empty box to ViewFormatting. Set the action of the next empty box to ViewStatusBar 71. Close the Menu Editor 72. From the Standard tab of the Component Palette, click PopupMenu on the form
and click
73. On the form, right-click PopupMenu1 and click Menu Designer 74. As the first item is selected, on the Object Inspector, set the Action to EditCut1. Set the Action of the second menu item to EditCopy1. Set the Action of the third menu item to EditPaste1. 75. Set the Caption of the 4th menu item to - to add a separator. 76. Set the 5th menu item to ColorSelect1. Set the new menu item’s Action to FontEdit1. Set the Action of the next item to RichEditBullets1 77. Close the Menu Designer 78. From the Win32 property page of the Component Palette, click ToolBar click an empty area on the form.
and
79. On the Object Inspector, set the Images property of the toolbar to ImageList1. Set its Flat property to true and set its Height to 24. Set the Name to tbrStandard 80. Right-click the toolbar, position the mouse on Edit and click Copy. 81. Right-click an empty area on the form, position your mouse on Edit and click Paste.
Copyright © 2003 FunctionX, Inc.
515
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
82. While the new toolbar is still selected, on the Object Inspector, change its Name to tbrFormatting 83. Right-click the top toolbar and click New Button. Set its Action property to FileNew. Add other buttons and set their Action values to FileOpen1, FileSave, a separator, EditCut1, EditCopy1, EditPaste1, a separator, EditUndo1, EditRedo, a separator, and PrintDlg1:
Figure 61: The Editor Applicaton Toolbar 84. Add the buttons and separators to the bottom toolbar and set their Action values to the RichEditBold1, RichEditItalic1, RichEditUnderline1, RichEditStrikeOut1, a separator, RichEditBullets1, FormatNbr, a separator, RichEditAlignLeft1, RichEditAlignCenter1, and RichEditAlignRight1, a separator 85. Right-click the bottom toolbar again and click New Button. Set the ImageIndex to 5 (FirstIndex) and change its Name to btnFirstIndent 86. Add a New Button to the bottom toolbar. Set the ImageIndex to 6 and change its Name to btnIndentLeft 87. Add one more button for the ImageIndex 7 and named btnIndentRight
Figure 62: The Editor Application Toolbar Expanded
88. From the Win32 tab of the Component Palette, click StatusBar empty area on the form
and click an
89. Using the Object Inspector, change its Name to StatusBar 90. On the form, double-click the status bar 91. In the Panels Editor, right-click and click Add and, on the Object Inspector, set the Width to 320 92. Right-click in the Panels Editor again and click Add. Change the Width to 100 93. Right-click the Panels Editor again and click Add. 94. Close the Panels Editor and save the project 95. In the Class Explorer, right-click TfrmMain and click New Method...
516
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
Figure 63: The Add Method Dialog Box - ShowHint 96. Set the Method Name to ShowHints. Specify its Argument as TObject *Sender and the Function Result to void. Click the __fastcall check box and click OK. 97. Implement the method as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::ShowHints(TObject * Sender) { StatusBar->Panels->Items[0]->Text = Application->Hint; } //---------------------------------------------------------------------------
98. Press F12 to get back to the form. Double-click in an unoccupied area of the form and change its OnCreate event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { int iRichDLL = (int)LoadLibrary("RICHED20.DLL"); if( !iRichDLL ) // If you could not load RICHED20.DLL { ShowMessage("Could not load the RICHED20.DLL"); Application->Terminate(); return; } Application->OnHint = ShowHints;
Copyright © 2003 FunctionX, Inc.
517
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
} //---------------------------------------------------------------------------
99. Press F12 to display the form 100. On the form, double-click ActionList1 to open the Action List Editor. In the left frame, click View. In the right frame, click ViewStandard. In the Object Inspector, click the Events tab. Double-click the event side of the OnExecute field and implement its event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::ViewStandardBarExecute(TObject *Sender) { tbrStandard->Visible = !tbrStandard->Visible; ViewStandard->Checked = !ViewStandard->Checked; } //---------------------------------------------------------------------------
101. In the right frame of the ActionList Editor, double-click ViewFormatting and implement its OnExecute event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::ViewFormattingExecute(TObject *Sender) { tbrFormatting->Visible = !tbrFormatting->Visible; ViewFormatting->Checked = !ViewFormatting->Checked; } //---------------------------------------------------------------------------
102. In the right frame of the ActionList Editor, double-click ViewStatusBar and implement its OnExecute event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::ViewStatusBarExecute(TObject *Sender) { StatusBar->Visible = !StatusBar->Visible; StatusBar1->Checked = !StatusBar1->Checked; } //---------------------------------------------------------------------------
103. Save your project 104. On the form, click the rich edit control to select it. In the Properties tab of the Object Inspector, click the Font field. Then click the ellipsis button 105. On the Font dialog box, scroll down in the Font combo box and select Times New Roman. In the Font Style combo box, select Regular. In the Size combo box, click 10. Make sure no style is selected in the Effects section and make sure the Color combo box displays Black:
518
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
Figure 64: The Font Dialog Box 106. Click OK 107. Save All
21.2.2 Rich Text Implementation The most fundamental property on any text-based control is the text it holds, for a RichEdit, the text is stored in objects called lines and represented by the Lines property. For a RichEdit control, a Lines item is in fact an individual object based on the TStrings class. We will learn many techniques of manipulating a TStrings object when we study lists. For a text-based control, a paragraph is a series of words that start with a letter or empty space until the flow of text is interrupted, which is usually considered a carriage return, or simply the end of the document. The management of such a paragraph is performed for a TRichEdit object using the Paragraph property. The paragraph is actually controlled by a class called TParaAttributes. By itself, this paragraph controls the alignment of the block, whether the block is part of a bulleted list, and such details as Tab measurements or indentation. To set or change the properties of a paragraph, you must first select it. To select a paragraph, you do not need to formally select it or any portion of its text. As long as the cursor is positioned inside of the paragraph, any paragraph attribute you set or change would apply to the whole paragraph. To manipulate more than one paragraph at the same time, you or your user must select them. The paragraphs do not need to be wholly selected. As long as a section is selected on each, the paragraphs are considered selected. The most common property of a paragraph is its alignment, which states whether the paragraph is positioned to the left, the center, or the right. This capability is controlled by the Alignment property. Its three possible values are taLeftJustify, taCenter, and taRightJustify. To align text at design time, select the desired value using the Alignment property on the Object Inspector. Unfortunately, this would apply to the whole contents of the RichEdit control. Most of the time, you will want to let users change a paragraph's alignment while they are using your application. To change the alignment at run time, Copyright © 2003 FunctionX, Inc.
519
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
assign the desired value to the Alignment property. In the following examples, the alignment of the selected paragraph is set in response to the user clicking one of the buttons: //--------------------------------------------------------------------------void __fastcall TForm1::btnAlignLeftClick(TObject *Sender) { rchEditor->Paragraph->Alignment = taLeftJustify; } //--------------------------------------------------------------------------void __fastcall TForm1::btnCenterClick(TObject *Sender) { rchEditor->Paragraph->Alignment = taCenter; } //--------------------------------------------------------------------------void __fastcall TForm1::btnAlignRightClick(TObject *Sender) { rchEditor->Paragraph->Alignment = taRightJustify; } //---------------------------------------------------------------------------
Indentation consists of pushing a paragraph from one of the margins of the document. The TParaAttributes class allows you to indent the first line of a paragraph using the FirstIndent property. It is an integer value that sets or controls the amount of indentation of the first line. The LeftIndent property is an integer number that controls how much a paragraph is indented from the left margin of the document. The RightIndent value controls a similar indentation from the right margin. To create an unordered list of items in your document, you can use the TParaAttributes::Numbering property. This property controls the assignment of a bulleted list. You have two options, If the paragraph is a regular one and you want to create a list, assign the nsBullet value to the Numbering property. If the paragraph is already a list but you want to convert it into a regular paragraph, assign the nsNone value to the property. Here is an example that applies when the user clicks a button called BulletList: //--------------------------------------------------------------------------void __fastcall TForm1::BulletList1Click(TObject *Sender) { if( rchEditor->Paragraph->Numbering == nsBullet ) rchEditor->Paragraph->Numbering = nsNone; else rchEditor->Paragraph->Numbering = nsBullet; } //---------------------------------------------------------------------------
One of the main characteristics of a rich text is the ability to set or control individual characteristics of sections of its text. The rich characteristics of text are controlled by the TTextAttributes class. This property allows you to change the font, its color, size, and/or style. To manipulate the text attributes, the text must be selected first. This means that the change applies only if there is a formal selection. For example, you can set or change the Bold style of the selected text when the user clicks a button called btnBold as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnBoldClick(TObject *Sender)
520
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
{
Chapter 21: Text-Based Applications
if( btnBold->Down ) rchEditor->SelAttributes->Style = TFontStyles() << fsBold; else rchEditor->SelAttributes->Style = TFontStyles() >> fsBold;
} //---------------------------------------------------------------------------
Besides the proper characteristics of a rich text, the TRichEdit also shares or inherits the characteristics of a memo or an edit control. Borland C++ Builder simplifies the configuring of a RichEdit control through the use of an ActionList control. The ActionList has many attributes already configured to seamlessly function with a RichEdit present on a form.
Practical Learning: Creating a RichEdit-Based Application 1.
On the form, click rchEditor to select the RichEdit control
2.
On the Object Inspector, change its Align property to alClient. Set HideSelection to false. Double-click the right field to Lines, delete the text and click OK. Set the PopupMenu property to PopupMenu1. Set the WantTabs property to true.
3.
In the Object Inspector, click the Events tab. Display the ActionList Editor. In the left frame, click Dialog. In the right frame, click FontEdit1. In the Object Inspector, double-click on the right field to BeforeExecute
4.
Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FontEdit1BeforeExecute(TObject *Sender) { // Get the characteristics of the select text // Apply them to the Font dialog box FontEdit1->Dialog->Font->Name = rchEditor->SelAttributes->Name; FontEdit1->Dialog->Font->Style = rchEditor->SelAttributes->Style; FontEdit1->Dialog->Font->Size = rchEditor->SelAttributes->Size; FontEdit1->Dialog->Font->Color = rchEditor->SelAttributes->Color; } //---------------------------------------------------------------------------
5.
Go back to the ActionList Editor. On the Events tab of the FontEdit1, double-click the right field of OnAccept and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FontEdit1Accept(TObject *Sender) { // Do the inverse of the BeforeExecute event // If the user clicks OK, get the characteristics of the font // Apply them to the selected text of the Rich Edit control rchEditor->SelAttributes->Name = FontEdit1->Dialog->Font->Name; rchEditor->SelAttributes->Style = FontEdit1->Dialog->Font->Style; rchEditor->SelAttributes->Size = FontEdit1->Dialog->Font->Size; rchEditor->SelAttributes->Color = FontEdit1->Dialog->Font->Color; } //---------------------------------------------------------------------------
6.
Press F12 to access the form. On the form, double-click MainMenu1. Click the box on the right side of View. In the Object Inspector, click the Properties tab and click Caption. Type F&ormat and press Enter.
Copyright © 2003 FunctionX, Inc.
521
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
7.
On the Menu Designer, click Format. Under the Format menu, click the empty box and, on the Object Inspector, set its Action to FontEdit1
8.
Close the Menu Designer.
9.
On the bottom toolbar, double-click the FirstIndent button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnFirstIndentClick(TObject *Sender) { rchEditor->Paragraph->FirstIndent += 10; } //---------------------------------------------------------------------------
10. On the bottom toolbar, double-click the IndentLeft button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnIndentLeftClick(TObject *Sender) { rchEditor->Paragraph->LeftIndent += 10; } //---------------------------------------------------------------------------
11. On the bottom toolbar, double-click the IndentRight button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnIndentRightClick(TObject *Sender) { rchEditor->Paragraph->RightIndent += 10; } //---------------------------------------------------------------------------
12. Save All
21.2.3 Rich Text Management Because it can be asked to perform various and sometimes complex assignments, a rich edit control can process two categories of messages: those originally built-in the control and those that have been added over the years. This is why you may have to first load a rich edit DLL from the operating system because the control has mostly been updated since you installed your programming environment. All of the basic functionality of a text-based control is natively supported in the rich edit control as we have seen or used it so far. Common operations include cutting or copying from another document, pasting text to the current document, formatting characters and paragraphs. The new release of the control may have added functionality that the version in your VCL implementation does not have. To allow users to print its content, the RichEdit control is equipped with the Print() method. This method only requires the name of the document that is being printed. One of the regular actions users perform on a document is to do, undo, or redo something on the application. The ability to undo an action is already implemented in the original version of the rich edit control. To all a user to redo an action, you can send an EM_REDO message to the control using the SendMessage() function. The wParam and the lParam parameters are not used and must be passed as 0.
522
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
Practical Learning: Implementing Rich Text Messages 1.
Display the ActionList Editor and, on the left frame, click Dialog. On the right frame, click PrintDlg. On the Object Inspector, click the Events tab and double-click the event side of the OnAccept field
2.
Implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::PrintDlg1Accept(TObject *Sender) { rchEditor->Print(CurrentFileName); } //---------------------------------------------------------------------------
3.
Display the ActionList Editor. In the left frame, click Edit. In the right frame, double-click EditRedo and implement its OnExecute() event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::EditRedoExecute(TObject *Sender) { SendMessage(rchEditor->Handle, EM_REDO, 0, 0); } //---------------------------------------------------------------------------
4.
On the main menu, click File -> New -> Other… In the New Items dialog box, click the Dialogs tab. Click Standard Dialog (Vertical) and click OK
5.
On the Object Inspector, change its Name to dlgFont and change its Caption to Font
6.
Save it as Font
7.
Test the application and return to Bcb
21.2.4 Text Formatting Advanced text formatting on a Microsoft rich edit control is performed using the CHARFORMAT or the CHARFORMAT2 structures. Because we are interested in the latest features, we will use CHARFORMAT2. It is defined as follows: typedef struct _charformat2 { UINT cbSize; DWORD dwMask; DWORD dwEffects; LONG yHeight; LONG yOffset; COLORREF crTextColor; BYTE bCharSet; BYTE bPitchAndFamily; TCHAR szFaceName[LF_FACESIZE]; WORD wWeight; SHORT sSpacing; COLORREF crBackColor; LCID lcid; DWORD dwReserved; SHORT sStyle; WORD wKerning; BYTE bUnderlineType; BYTE bAnimation; BYTE bRevAuthor;
Copyright © 2003 FunctionX, Inc.
523
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
BYTE bReserved1; } CHARFORMAT2;
To use this structure, declare a variable of it and use its cbSize member variable to specify its size. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { CHARFORMAT2 cfm2; cfm2.cbSize = sizeof(CHARFORMAT2); } //---------------------------------------------------------------------------
Once the compiler is aware of the size of the structure, use the dwMask member variable to specify the type of formatting you want to perform. Formatting examples include all caps, bold, italic, subscript, etc. After selecting the type of formatting that will applied, initialize the dwEffects member variable to the corresponding format. (Microsoft has highly improved the documentation on the RichEdit control libraries so much that, to save space on the book, we will not repeat that documentation here. Instead, we will provide examples).
Practical Learning: Formatting Text 1.
Display the main form. Double-click ImageList1. From the resources that accompany this book, add the AllCaps,
2.
Right-click the bottom toolbar and click Separator
3.
Right-click it again and click New Button. Set its ImageIndex to 29 (AllCaps). Change its Name to btnAllCaps. Double-click it again and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnAllCapsClick(TObject *Sender) { Richedit::CHARFORMAT2 cfm2; cfm2.cbSize = sizeof(cfm2); cfm2.dwMask = CFM_ALLCAPS; cfm2.dwEffects = CFE_ALLCAPS; SendMessage(RichEdit1->Handle, EM_SETCHARFORMAT, static_cast(SCF_SELECTION), reinterpret_cast(&cfm2)); } //---------------------------------------------------------------------------
4.
Test the application and return to Bcb
21.2.5 Paragraph Formatting Besides the regular techniques provided by the TRichEdit class, paragraph formatting on a rich edit control can be performed using the PARAFORMAT or the
524
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
PARAFORMAT2 structures. Because everything available in the first is implemented in the second, we will use the PARAFORMAT2 structure. It is defined as follows: typedef struct _paraformat { UINT cbSize; DWORD dwMask; WORD wNumbering; WORD wEffects; LONG dxStartIndent; LONG dxRightIndent; LONG dxOffset; WORD wAlignment; SHORT cTabCount; LONG rgxTabs[MAX_TAB_STOPS]; LONG dySpaceBefore; LONG dySpaceAfter; LONG dyLineSpacing; SHORT sStyle; BYTE bLineSpacingRule; BYTE bOutlineLevel; WORD wShadingWeight; WORD wShadingStyle; WORD wNumberingStart; WORD wNumberingStyle; WORD wNumberingTab; WORD wBorderSpace; WORD wBorderWidth; WORD wBorders; } PARAFORMAT2; #define wEffects wReserved
To use it, declare a PARAFORMAT2 variable and use the cbSize member variable to specify the size of the structure. After this, use the dwMask to specify the type of formatting you want to perform.
Practical Learning: Formatting Paragraphs 1.
Make sure the Editor project you created is still opened. Display the main form and double-click MainMenu1
2.
In the Menu Designer, click Format and click the first empty box under the Format menu. On the Object Inspector, click Caption and type &Paragraph...
3.
Set the ImageIndex of the Paragraph menu item to 8 and close the Menu Designer
4.
To use one of the dialog templates, on the main menu of C++ Builder, click File -> New -> Other... On the New Items dialog box, click Dialogs and double-click Standard Dialog (Horizontal)
5.
While the new dialog box is still selected, on the Object Inspector, change its Caption value to Paragraph and change its Name property to dlgParagraph
6.
Save it as Paragraph
7.
From the Standard tab of the Component Palette, add three labels widht captions as &Left:, &Right:, and &First Line:
8.
Add an edit box on the right side of each of the last three labels. Change their names to edtLeft, edtRight, edtFirstLine. Position and resize the controls as you see fit:
Copyright © 2003 FunctionX, Inc.
525
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
Figure 65: The Editor Application - Paragraph 9.
Right-click anywhere on the dialog box and click Tab Order. Arrange the controls sequence in the following order: edtLeft, edtRight, edtFirstLine, OKBtn, and CancelBtn:
Figure 66: The Edit Tab Order Dialog Box 10. While the dlgParagraph form is still displaying, on the main menu, click File -> Include Unit Hdr... In the Use Unit dialog box, make sure Main is selected and click OK 11. Click the Events tab of the Object Inspector and double-click the empty field of OnActivate 12. Implement the event as follows: //--------------------------------------------------------------------void __fastcall TdlgParagraph::FormActivate(TObject *Sender) { // Make sure the values of the edit boxes // reflect the indentation of the paragraph in the background edtLeft->Text = frmMain->rchEditor->Paragraph->LeftIndent;
526
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
edtRight->Text = frmMain->rchEditor->Paragraph->RightIndent; edtFirstLine->Text = frmMain->rchEditor->Paragraph->FirstIndent; } //---------------------------------------------------------------------
13. On the View toolbar, click the View Form button 14. On the View Form dialog box, click frmMain and click OK 15. On the main menu, click File -> Include Unit Hdr... 16. In the Use Unit dialog box, make sure Paragraph is selected and click OK 17. On the main menu of the form, click Format -> Paragraph... and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Paragraph1Click(TObject *Sender) { dlgParagraph->ShowModal(); } //---------------------------------------------------------------------------
18. Display the ActionList Editor. In the left frame, click Format. In the right frame, double-click FormatNbr, and implement its OnExecute() event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::ToolButton26Click(TObject *Sender) { PARAFORMAT2 pfm2; pfm2.cbSize = sizeof(pfm2); pfm2.dwMask = PFM_NUMBERING; pfm2.wNumbering = 3; SendMessage(RichEdit1->Handle, EM_SETPARAFORMAT, 0, reinterpret_cast(&pfm2));
} //---------------------------------------------------------------------------
19. Test your application and return to Bcb 20. Save your project.
21.3 The Find Dialog Box 21.3.1 Introduction When facing large text, users sometimes need to find a word, a group of words, or a section inside of the displayed text. Microsoft Windows provides a dialog box that can help with such a task. It is the Find dialog box:
Copyright © 2003 FunctionX, Inc.
527
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
Figure 67: The Find Dialog Box
21.3.2 Searching for Text To search for a word or a group of words in a text, the user must first have a searchable document. To proceed, the user would call the Find dialog which can be available from a toolbar button or a menu item. The Find dialog box is equipped with a text box called Find What. In this text box, the user can type a word or an expression. She can specify whether the search should look for the whole word or not. This is set using the Match Whole Word Only check box. If she types a word or an expression and is sensitive to the case of characters, she can make sure that the dialog box would need to match only the word or expression with letter exactly as typed, in uppercase and lowercase. After specifying the options, the user can click the Find Next button. If a match is found, the found match should be selected and highlighted in the text in the background. The user can continue searching down the text by clicking Find Next continuously. Once all matches have been found, or if no match was found, a message box should let the user know. By default, the search proceeds from the top section of the document to the bottom. The user can reverse this direction by clicking the Up radio button in the Direction section. The Find dialog box is modeless, which allows the user to work on the background text, such as the found match, without closing the dialog box. After performing a search, the user can click Cancel.
21.3.3 Word and Expression Search The Find dialog box is represented in the VCL by the TFindDialog class. To make this dialog box available at design time, from the Dialogs property page of the Component Palette, you can click FindDialog and click on the Form. The word or expression to find is a string known as FindText. If you have a default text you want to display in the Find What text box when the dialog box comes up, you can provide it in the FindText property of the Object Inspector. In the same way, when the user is performing a search, the text specified in the Find What text box is represented as the FindText value. Because the Find dialog box is modeless, the user can decide to keep it open while she is working in the background text. Sometimes, the dialog box can be obstructing. Fortunately, with code, you can control the location of this dialog box to make it less annoying. The location of the dialog box is set or controlled using the Position property. The Position is a TPoint object that specifies the vertical measurement from the top of the document to the top border of the dialog box, and the horizontal measurement from the left border of the document to the left border of the Find dialog box. If you want to control only the vertical distance of the dialog box from the top border of the document, 528
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
you can specify a natural number as the Top property. The TFindDialog object provides options to control the availability of the check boxes and radio buttons of the dialog box.
Practical Learning: Allowing Text Search 1.
Display the ActionList Editor. In the left frame, click Search. In the right frame, click SearchFind1
2.
On the Object Inspector, in the Properties tab, expand Dialog and expand Options. Set the frFindNext property to true
3.
click Events. Under the expanded Dialog, double-click the right empty field to OnFind and implement its event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::SearchFind1FindDialogFind(TObject *Sender) { int MatchPos, StartPos, EndPos; if( rchEditor->SelLength ) StartPos = rchEditor->SelStart + rchEditor->SelLength; else StartPos = 0; EndPos = rchEditor->Text.Length() - StartPos; MatchPos = rchEditor->FindText(SearchFind1->Dialog->FindText, StartPos, EndPos, TSearchTypes() << stMatchCase); if( MatchPos != -1 ) { rchEditor->SelStart = MatchPos; rchEditor->SelLength = SearchFind1->Dialog->FindText.Length(); } } //---------------------------------------------------------------------------
6.
Save the project. Test the application and return to Bcb.
21.4 The Replace Dialog Box 21.4.1 Overview Another regular operation users perform on text is to find a word or an expression and replace it with another word or an expression. This is possible through the use of the Replace dialog box:
Copyright © 2003 FunctionX, Inc.
529
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
Figure 68: The Replace Dialog Box
To replace a word or an expression, the user first displays the Replace dialog. It is equipped with two text boxes. In the Find What text box, the user would type the word or the expression that should be searched in the whole text. In the Replace With edit box, the user can type another word or an expression that would replace a possible match of the Find What string. The user can proceed as if she were using the Find dialog box and click the Find Next button to find a match in the document. If a match is found, the user can click Replace to replace the matched word or expression. If the Replace edit box is empty, the match would be deleted. On the other hand, if the Replace With edit box contains a string, upon clicking Replace, the matched text would be replaced by the Replace With string. After the text has been found or replaced, the dialog box would attempt to find the next match. If the user wants to replace all occurrences of the Find What string, she can click Replace All. At any time, the user can click Cancel to dismiss the dialog box or continue working in the background text without necessarily closing the dialog because it is modeless.
21.4.2 Making Text Replacement Possible To make it possible for users to find and replace text in a document, the VCL provides the TReplaceDialog class, which is represented by the ReplaceDialog object from the Component Palette. Therefore, to make replacement of text available, from the Dialogs tab, double-click ReplaceDialog. The ReplaceDialog object uses the same properties as the FindDialog object and adds to the FindDialog options. Because of its functionality, the FindReplace control adds the ReplaceText property. This carries the string that would replace the possible found text.
Practical Learning: Allowing Text Replacement 1.
Display the ActionList Editor. In the right frame, click SearchReplace1
2.
In the Properties tab of the Object Inspector, under the expanded Options, set the frFindNext to true
3.
Click the Events tab and double-click the empty field on the right side of OnFind
4.
Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::SearchReplace1ReplaceDialogFind(TObject *Sender) { int MatchPos, StartPos, EndPos;
530
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 21: Text-Based Applications
if( rchEditor->SelLength ) StartPos = rchEditor->SelStart + rchEditor->SelLength; else StartPos = 0; EndPos = rchEditor->Text.Length() - StartPos; MatchPos = rchEditor->FindText(SearchReplace1->Dialog->FindText, StartPos, EndPos, TSearchTypes() << stMatchCase); if( MatchPos != -1 ) { rchEditor->SetFocus(); rchEditor->SelStart = MatchPos; rchEditor->SelLength = SearchReplace1->Dialog->FindText.Length(); } } //---------------------------------------------------------------------------
5.
On the Object Inspector, in the Events tab, double-click the box on the right side of OnReplace and implement its event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::SearchReplace1ReplaceDialogReplace(TObject *Sender) { // Find out if the Find What text is selected in the rich edit control if( rchEditor->SelText == SearchReplace1->Dialog->FindText ) { // Since a match was found, get ready to replace it with the content // of the Replace With edit box // First find out if the user clicked the Replace button if( SearchReplace1->Dialog->Options.Contains(frReplace) ) { // Since the user clicked Replace, replace only the selection rchEditor->SelText = SearchReplace1->Dialog->ReplaceText; // Perform a new search SearchReplace1ReplaceDialogFind(Sender); } // Find out if the user clicked Replace All instead else if( SearchReplace1->Dialog->Options.Contains(frReplaceAll) ) { // Since the user clicked Replace All, replace all occurrences // of the Find What edit box with the Replace With edit box do { // Find an occurrence and replace it rchEditor->SelText = SearchReplace1->Dialog->ReplaceText; // Find another occurrence before repeating SearchReplace1ReplaceDialogFind(Sender); } while( !rchEditor->SelText.IsEmpty() ); // Let the user know that all occurrences have been replaced ShowMessage("No more text to replace");
} } else // If no text is selected or none was matched, let the user know ShowMessage("No text to replace"); }
Copyright © 2003 FunctionX, Inc.
531
Chapter 21: Text-Based Applications
Borland C++ Builder Programming
//---------------------------------------------------------------------------
6.
532
Save the project. Test the application and return to Bcb.
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 22: Track-Based Controls
Chapter 22: Track-Based Controls 22.1 The UpDown Control 22.1.1 Overview A spin button, also called UpDown, is a Windows control equipped with two opposite or . The user clicks one of the arrow buttons at one time to arrow buttons increase or decrease the current value of the control. The value held by the control is also called its position. The values of a spin button range from a minimum to a maximum. When the up or right arrow is clicked, the value of the control increases. If the user clicks and holds the mouse on the up or right pointing pointing arrow button, the value of the control keeps increasing until it reaches its maximum and then stops. The opposite behavior applies when the user clicks or holds the mouse on the down or left-pointing arrow button. Because a spin button is only equipped with arrows, it does not inherently show its value. Therefore, this control is usually accompanied by another, text-based, control, usually an edit box, that indicates its position
.
Practical Learning: Introducing UpDown Buttons 1.
Start a new project with its default form
2.
Save it in a new folder called ColorPreview1
3.
Save the unit as Exercise and save the project as ColorPreview
4.
Change the Caption of the form to Color Previewer and change its name to frmMain
5.
Set the BorderStyle of the form to bsDialog
6.
Open Image Editor. Create a new 32 x 32 icon and design it as follows:
Copyright © 2003 FunctionX, Inc.
533
Chapter 22: Track-Based Controls
Borland C++ Builder Programming
7.
Design its equivalent 16 x 16 icon as follows:
8.
Save the icon as previewer in the folder of the current project
9.
In C++ Builder, access the project options (Project -> Options) dialog box
10. Change the Title to Color Previewer and set the icon to the above
11. Click OK 534
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 22: Track-Based Controls
12. From the Standard tab of the Component Palette, click Panel panel in the top section of the form. Delete its caption
and draw a wide
13. Change its Name to pnlPreview 14. Using its Color property in the Object Inspector, change its color to clSilver 15. Add a BitBtn to the form and set its Kind to bkClose
16. Save All
22.1.2 Characteristics of an UpDown Control To create an UpDown button, you can use the UpDown control from the Win32 tab of the Component Palette. Although not required, this control should be accompanied by another object that would display the current value of the updown control. The most commonly used accompanying object is an edit control but you can use any control you judge appropriate. After adding the UpDown control on a container such as a form, use the Object Inspector to control its properties. The most usual way to add the Edit control is to position it on the left side of the UpDown button If using an edit box or a label, the accompanying control is usually positioned to the left of the updown object:
The UpDown control appears with two arrow buttonss pointing up and down, respectively. This feature is controlled by the Orientation property. An alternative is to point the arrows to left and right. To do this, use the Orientation property to set the arrows to your liking. Probably the most important piece of information you would need from an updown control is the value it is holding at a particular time. As mentioned already, the updown control navigates from a minimum to a maximum values. The values of the control are Copyright © 2003 FunctionX, Inc.
535
Chapter 22: Track-Based Controls
Borland C++ Builder Programming
short integer numbers. These numbers range from a minimum controlled by the Min property to a maximum value controlled by the Max property. By default, a freshly added UpDown control on a from has its Min and Max values set to 0 and 100 respectively. You can set the minimum value of the control to –32768 and the maximum to 32767. These values are set using the Min and Max fields of the Object Inspector. You can change them programmatically as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { UpDown1->Min = -224; UpDown1->Max = -1; } //---------------------------------------------------------------------------
If you use numbers in the thousands, the control that accompanies the UpDown (such as the edit control) will display the values using the comma to separate the thousands. This is because the UpDown control is configured, by default, to separate the thousands. If you do not want this feature, change the value of the Thousands property from true to false. When using the UpDown button, the user clicks one of the arrows of the control to increase or decrease the value. By default, the value increases or decreases by 1. If you want the value to augment by more than 1, set an integer value using the Increment property. To set the Increment value programmatically, you can use code as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { UpDown1->Max = 125; UpDown1->Min = 10; UpDown1->Increment = 5; } //---------------------------------------------------------------------------
When an UpDown control is accessed, the value it holds can be set by its Position. You can use this property to specify what value the control would use at startup. It should be an integer between the Min and the Max values. You can also set it programmatically as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { UpDown1->Max = 125; UpDown1->Min = 10; UpDown1->Increment = 5; UpDown1->Position = 55; } //---------------------------------------------------------------------------
The Position property also allows you to find out the value of the UpDown control at any time. After setting the Increment value, when the user clicks the arrow buttons, the value would increase accordingly. When the maximum value is reached, the control would use the Wrap property to find out what to do:
536
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 22: Track-Based Controls
If the Wrap Boolean property is set to false (the default), the increment would stop at the Max value even if Max is not divisible by the Increment value. The same goes for the Min value
If the Wrap property is set to true and if the user increases the value of the control, the incrementing would stop to the last value divisible by the Increment value but less than the Max. The same would apply when decrementing the value of the control.
As mentioned already, an updown control does not visually display its value. Therefore, you can add a text-based or other control to it. This accompanying object is specified using the Associate property. The associated control would display the current value of the UpDown control. To associate a control to the UpDown control, first create or add the desired control to your application. Then, at design time on the Object Inspector, you can click the Associate field to display its combo box. Click the arrow and select the desired control. You can also associate a control programmatically using code such as this: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { UpDown1->Associate = Edit1; } //---------------------------------------------------------------------------
The UpDown control usually has its associated control on the left side. This is controlled by the AlignButton property. Alternatively, you can ask it to have the accompanying control on its right side by setting the AlignButton property to udRight. At design time, both controls still display as they are designed. If you change the AlignButton value, the control would apply the value only at runtime. One of the nicest features that make the UpDown button easy to use is that the user can change its values by pressing the up and down arrow keys of the keyboard. This ability is by default set to true from the Boolean ArrowKeys property. If you want to prevent the user from using the keyboard to increase or decrease the value of the UpDown control, set the ArrowKeys property to false.
Practical Learning: Designing an UpDown control 1.
From the Standard tab of the Component Palette, click the Edit control click under the panel on the form
2.
Change the name of the new edit control to edtRed and set the content of the Text field to 192
3.
On the Win32 tab, click the UpDown control edit box on the form
4.
On the Object Inspector, change its name to updRed
5.
Make sure the AlignButton field displays udRight.
6.
Click Associate and select edtRed
7.
Make sure the Increment field display 1. Change the Max value to 255 and accept the Min value as 0
8.
Change the Position value to 192
Copyright © 2003 FunctionX, Inc.
and
and click on the right side of the
537
Chapter 22: Track-Based Controls
9.
Borland C++ Builder Programming
On the form, select both the edit and the updown controls. Press Ctrl + C to top. Then press Ctrl + V to paste
10. Change the name of the new edit control to edtGreen 11. Change the name of the new updown control to updGreen and set its Associate property to edtGreen (it should be set already but check it) 12. Paste again. 13. Change the name of the new edit control to edtBlue 14. Change the name of the new updown control to updBlue and make sure its Associate property is set to edtBlue
Figure 69: The Color Previewer Application 15. To test the new controls, press F9 16. Close it and return to Bcb
22.1.3 The UpDown Control Methods The UpDown control is based on the TUpDown class. This class has only two methods: a constructor and a destructor. The TUpDown constructor is used to dynamically create an UpDown button. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TUpDown *Counter = new TUpDown(this); Counter->Parent = this; TEdit *Displayer = new TEdit(this); Displayer->Parent = this; Displayer->Left = 16; Displayer->Top = 16; Displayer->Width = 32; Counter->Left = Displayer->Left + Displayer->Width; Counter->Top = Displayer->Top; Counter->Min = 12; Counter->Max = 248; Counter->Increment = 2; Counter->Position = 36;
538
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 22: Track-Based Controls
Counter->Associate = Displayer; } //---------------------------------------------------------------------------
22.1.4 The UpDown Control Events The main event of the UpDown control occurs when the user clicks one of the arrows. Whether the user clicks the up pointing arrow to increase or the down pointing arrow to decrease the value or position of the control, the OnClick() event is fired. The pointing arrows are represented using the TUDBtnType enumerator that has two values. The up or right pointing arrow is recognized as btNext while the down or left pointing arrow is referred to as btPrev. When the user clicks one of the arrows, you can write code to perform an action depending on the button that was clicked. Using this OnClick() event, you do not have to associate the UpDown control with an edit box to display integers; you can use the event to format the value or configure any other behavior you see fit. For example, instead of displaying an integer, you can display a floating number, a string, anything, that is traditionally not allowed. When the user clicks one of the arrows of the UpDown control, the operating system is notified just before this action occurs. This notification is done through the OnChanging() event. This allows you to perform a last minute configuration before the value or position of the control changes. You can also use this event to deny changing the value of the control. The OnChangingEx() event also fires just before the value of the UpDown control changes. This time, you can set a new value for the control if the change is successful. It is important and professionally convenient to make sure that the user can use the up and down arrow keys of the keyboard to increase or decrease the value of the UpDown control. If the user presses and holds the up arrow key, the UpDown control would be incrementing its value until either the user releases the key or the control reaches its maximum limit. Here is an example of how to track the OnMouseUp mouse event of the UpDown control: //--------------------------------------------------------------------------void __fastcall TForm1::UpDown1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { ShowMessage("You stopped spinning at " + AnsiString(edtSpin->Text)); } //---------------------------------------------------------------------------
Practical Learning: Configuring an UpDown control 1.
On the form, double-click the most left updown control to access its OnClick event
2.
On the form again, double-click the middle updown control
3.
Once again, on the form, double-click the right updown control
4.
Implement their OnClick events as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::updRedClick(TObject *Sender, TUDBtnType Button)
Copyright © 2003 FunctionX, Inc.
539
Chapter 22: Track-Based Controls
{
Borland C++ Builder Programming
TColor CurrentColor = TColor(RGB(updRed->Position, updGreen->Position, updBlue->Position));
pnlPreview->Color = CurrentColor; } //--------------------------------------------------------------------------void __fastcall TfrmMain::updGreenClick(TObject *Sender, TUDBtnType Button) { TColor CurrentColor = TColor(RGB(updRed->Position, updGreen->Position, updBlue->Position)); pnlPreview->Color = CurrentColor; } //--------------------------------------------------------------------------void __fastcall TfrmMain::updBlueClick(TObject *Sender, TUDBtnType Button) { TColor CurrentColor = TColor(RGB(updRed->Position, updGreen->Position, updBlue->Position)); pnlPreview->Color = CurrentColor; } //---------------------------------------------------------------------------
5.
Test the application
Figure 70: The Color Previewer Application Completed 6.
Close it and return to Bcb
22.2 The Spin Button
540
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 22: Track-Based Controls
22.2.1 Characteristics of a Spin Button The Spin button is a Windows control used to increase and decrease values by clicking an up or a down buttons. Visually the spin button resembles the UpDown control. There have some differences with regard to their respective configuration. To add a spin button to your application, use the CSpinButton from the Samples tab of the Component Palette. You can place the control on a form or any other container. Two of the properties that differentiate the CSpinButton from the UpDown controls is that the CSpinButton control allows you to add your own button indicators as bitmaps. To specify which bitmap must point up on the control, click the ellipsis button on the UpGlyph field of the Object Inspector. You can also specify a bitmap that would point down using the DownGlyph property:
The application we are about to develop is for a CD publishing small business. This company manufactures compact discs for self-promoting musicians and small business that want to sell their own CDs. When taking an order of a new CD, the company charges: •
$20/CD if the customer is ordering less than 20 units
•
$15/CD if the customer is ordering up to 50 units
•
$12/CD if the customer is ordering up to 100 units
•
$8/CD if the customer is ordering up to 500 units
•
$5/CD for any order over 500 units
Practical Learning: Introducing the Spin Button 1.
Start a new project with irs default form
2.
Save it in a new folder named CDPublisher1
3.
Save the unit as Exercise and save the project as CDPublisher
4.
Change the Caption of the form Compact Disc Publisher
5.
Change its Name to frmMain and set its BorderStyle to bsDialog
6.
Design the form by adding the following controls:
Figure 71: The Compact Disc Publisher Application Copyright © 2003 FunctionX, Inc.
541
Chapter 22: Track-Based Controls
Control Bevel Label Edit Label Edit Label Edit Button 7.
Borland C++ Builder Programming
Caption or Text Number of Items: 0 Unit Price 20.00 Total Price: 0.00 Kind = bkClose
Name
edtQuantity EdtUnitPrice EdtTotal
Save all
22.2.2 The Spin Button Methods The spin button is equipped with a constructor and a destructor. The TCSpinButton class’ constructor is typically used to dynamically create a spin button. To do that, declare a pointer to a TCSpinButton class, specifying the owner of the control and its parent. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TCSpinButton *LetMe = new TCSpinButton(Form1); LetMe->Parent = Form1; } //---------------------------------------------------------------------------
22.2.3 The Spin Button Events The biggest difference between an UpDown control and a spin button is the way each handles the incrementing and decrementing of its values. Simply put, the spin button does not have a value to the sense of a progress control; you must set, assign, and configure the value or position of the spin button on your own. While the main event of an UpDown control occurs when the user clicks one of its arrows, to apply a specific behavior, you can either consider the whole event or find out what button was clicked. The spin button uses two different events for each button. Clicking the up pointing arrow fires the OnUpClick() event while the OnDownClick event fires when the user clicks the down pointing arrow.
Practical Learning: Using the Spin Button
542
1.
On the Component Palette, click the Samples tab
2.
Click CspinButton the form
3.
On the Object Inspector, change its name to spnQuantity
4.
Display the header file of the form. In the private section of the header file, declare a Value variable of type int. Also, declare a method named EvaluatePrice() of type void that uses __fastcall:
and flick on right side of the Number of Items edit box on
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
private: int Value; void __fastcall EvaluatePrice();
5.
Chapter 22: Track-Based Controls
// User declarations
In the form’s source file, initialize the value to 0 and implement the new method as follows: //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { Value = 0; } //--------------------------------------------------------------------------void __fastcall TfrmMain::EvaluatePrice() { int Quantity; double UnitPrice, TotalPrice; Quantity = edtQuantity->Text.ToInt(); if( Quantity < 20 ) UnitPrice = 20; else if( Quantity < 50 ) UnitPrice = 15; else if( Quantity < 100 ) UnitPrice = 12; else if( Quantity < 500 ) UnitPrice = 8; else UnitPrice = 5; TotalPrice = Quantity * UnitPrice; edtUnitPrice->Text = edtUnitPrice->Text.FormatFloat("#,##0.00", UnitPrice); edtTotal->Text = edtTotal->Text.FormatFloat("#,##0.00", TotalPrice);
} //---------------------------------------------------------------------------
6.
On the form, click the SpinButton control to select it. On the Object Inspector, click the Events tab
7.
Double-click the empty field on the right side of OnUpClick and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::spnQuantityUpClick(TObject *Sender) { edtQuantity->Text = Value++; EvaluatePrice(); } //---------------------------------------------------------------------------
8.
On the Object Inspector, double-click the field of the OnDownClick event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::spnQuantityDownClick(TObject *Sender) { if( Value > 0 ) edtQuantity->Text = Value--;
Copyright © 2003 FunctionX, Inc.
543
Chapter 22: Track-Based Controls
Borland C++ Builder Programming
else edtQuantity->Text = 0; EvaluatePrice(); } //---------------------------------------------------------------------------
9.
To test the form, on the main menu, click Run -> Run
Figure 72: The Compact Disc Publisher Application Completed 10. After experimenting with the spin button, close the form
22.3 The Spin Edit Control 22.3.1 Introduction Without a doubt, the SpinEdit control is the easiest spin control of the three sets of UpDown controls. There is hardly any configuration to do. To add a SpinEdit to your application, from the Samples tab of the Component Palette, click the CSpinEdit button and click in the desired location on the form.
22.3.2 Characteristics of the SpinEdit Control The CSpinEdit control is used to display integer values in the edit box section of the control. The displaying value is controlled by the MinValue and the MaxValue properties that can be set in the Object Inspector. You can also specify their values programmatically as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { CSpinEdit1->MinValue = -12; CSpinEdit1->MaxValue = 228; } //---------------------------------------------------------------------------
By default, the values of a SpinEdit control increase by 1 and decrease by –1. If that value is not appropriate for your application, you can change it using the Increment property in the Object inspector.When a SpinEdit control appears at startup, the control displays the value 0 even if its MinValue is set to a value other than 0. To set the default 544
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 22: Track-Based Controls
position of the SpinEdit control, use the Value property. You can control it at design time or at runtime. If you set the Value property to a value that is less than the MinValue, the value would be automatically set to the minimum value. If you try to set it to a value greater than the MaxValue in the Object Inspector, the Value would be reset to the MaxValue.
22.3.3 The Spin Edit Methods Like all VCL controls, the CSpinEdit object has a constructor and a destructor, based on the TCSpinEdit class. The constructor is typically used to dynamically create the control at runtime. To do this, declare a pointer to a TCSpinEdit class. You must specify the owner and the parent of the control. You can create such a control in an event or a function. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TCSpinEdit* Staple = new TCSpinEdit(this); Staple->Parent = this; } //---------------------------------------------------------------------------
You must include the CSPIN.h header file to the form or unit that would be using the Spin Edit control.
22.4 Track Bars 22.4.1 Introduction A track bar is a Windows control used to slide a small bar or pointer, also called a thumb, along a continuous line. To use the trackbar, the user can drag the thumb in one of two directions. This changes the position of the thumb. The user can also click a position along the control line to place the thumb at a desired location. Alternatively, when the trackbar has focus, the user can use the arrow keys to move the thumb. As far as positions are concerned, there are two types of track bars, depending on te orientation: horizontal or vertical:
Copyright © 2003 FunctionX, Inc.
545
Chapter 22: Track-Based Controls
Borland C++ Builder Programming
Figure 73: Track Bars Orientations A trackbar is configured with a set of values from a minimum to a maximum. Therefore, the user can make a selection included in that range. Optionally, a trackbar can be equipped with small marks called ticks. These can visually guide the user for the available positions of the thumb. A trackbar can be used as a progress control to help the user monitor an activity. A trackbar also allows the user to specify a value that conforms to a range. When equipped with small indicators, also called ticks, a trackbar can be used to control exact values that the user can select in a range, preventing the user from setting just any value.
Practical Learning: Introducing Track Bars
546
1.
Start a new project with its default form
2.
Save it in a new folder named CarInventory1
3.
Save the unit as Exercise and save the project as CarInv
4.
Open Image Editor and design 32 x 32 pixels icon and its 16 x 16 associated icon as follows:
5.
Save the icon as CarInvent
6.
Using the Project Options dialog box, set the Title to Car Inventory and set the icon as the above Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 22: Track-Based Controls
7.
Change the name of the form to frmMain and change its caption to Car Inventory
8.
Also change its BorderStyle to bsDialog
9.
From the resources that accompany this book, copy the following files from the Pictures folder to the folder of the current project: Civic, Elantra, Escort, Focus, GdMarquis, E350, Navigator, Sentra, and Rio
10. In the header file of the form, create a structure named TCarInventory and declare its array variable named Car that contains 10 items. Also declare: //--------------------------------------------------------------------------struct TCarInventory { AnsiString Make; AnsiString Model; unsigned int CarYear; unsigned int Doors; Graphics::TBitmap *CarPicture; }; //--------------------------------------------------------------------------class TfrmMain : public TForm { __published: // IDE-managed Components private: TCarInventory Car[10]; // User declarations public: // User declarations __fastcall TfrmMain(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TfrmMain *frmMain; //--------------------------------------------------------------------------#endif
11. In the constructor of the form, initialize the array as follows: //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { Car[0].Make = "Honda"; Car[0].Model = "Civic"; Car[0].CarYear = 1998; Car[0].Doors = 4; Car[0].CarPicture = new Graphics::TBitmap; Car[0].CarPicture->LoadFromFile("Civic.bmp"); Car[1].Make = "Nissan"; Car[1].Model = "Sentra"; Car[1].CarYear = 1997; Car[1].Doors = 4; Car[1].CarPicture = new Graphics::TBitmap; Car[1].CarPicture->LoadFromFile("Sentra.bmp"); Car[2].Make = "Ford"; Car[2].Model = "Focus"; Car[2].CarYear = 2002; Car[2].Doors = 4; Car[2].CarPicture = new Graphics::TBitmap; Car[2].CarPicture->LoadFromFile("Focus.bmp"); Car[3].Make = "Mercury"; Car[3].Model = "Grand Marquis"; Car[3].CarYear = 2000; Car[3].Doors = 4; Car[3].CarPicture = new Graphics::TBitmap; Car[3].CarPicture->LoadFromFile("GdMarquis.bmp"); Car[4].Make
Copyright © 2003 FunctionX, Inc.
= "Kia"; Car[4].Model = "Rio";
547
Chapter 22: Track-Based Controls
Borland C++ Builder Programming
Car[4].CarYear = 1997; Car[4].Doors = 4; Car[4].CarPicture = new Graphics::TBitmap; Car[4].CarPicture->LoadFromFile("Rio.bmp"); Car[5].Make = "Ford"; Car[5].Model = "E350"; Car[5].CarYear = 2000; Car[5].Doors = 5; Car[5].CarPicture = new Graphics::TBitmap; Car[5].CarPicture->LoadFromFile("E350.bmp"); Car[6].Make = "Ford"; Car[6].Model = "Escort"; Car[6].CarYear = 2002; Car[6].Doors = 4; Car[6].CarPicture = new Graphics::TBitmap; Car[6].CarPicture->LoadFromFile("Escort.bmp"); Car[7].Make = "Hyundai"; Car[7].Model = "Elantra"; Car[7].CarYear = 1999; Car[7].Doors = 4; Car[7].CarPicture = new Graphics::TBitmap; Car[7].CarPicture->LoadFromFile("Elantra.bmp"); Car[8].Make = "Ford"; Car[8].Model = "Escape"; Car[8].CarYear = 2003; Car[8].Doors = 4; Car[8].CarPicture = new Graphics::TBitmap; Car[8].CarPicture->LoadFromFile("Escape.bmp"); Car[9].Make = "Lincoln"; Car[9].Model = "Navigator"; Car[9].CarYear = 2000; Car[9].Doors = 5; Car[9].CarPicture = new Graphics::TBitmap; Car[9].CarPicture->LoadFromFile("Navigator.bmp");
} //---------------------------------------------------------------------------
12. Design the form by adding the following controls Refer to the text in the edit controls for their name Add an Image control. Set its Center property to true. Set its Transparent property to true Add a BitBtn control
and set its Kind to bkClose
Figure 74: The Car Inventory Application
548
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 22: Track-Based Controls
13. Save All
22.4.2 Characteristics of a Track Bar To add a trackbar to a form, from the Win32 tab of the Component Palette, double-click the TrackBar button
.
After placing a TrackBar control on a form or other container, by default, its assumes a horizontal position. The position of the trackbar is controlled by Orientation property implemented through the TTrackBarOrientation enumerator: enum TTrackBarOrientation { trHorizontal, trVertical };
To change the direction of the control, on the Object Inspector, set the Orientation property to the desired value. For example, to make it vertical, change the field from trHorizontal to trVertical. To change this property at runtime, assign the desired value to the property. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TrackBar1->Orientation = trVertical; } //---------------------------------------------------------------------------
The Min property controls the minimum positional value of the control while the Max value controls the opposite. The user is allowed to slide only between these two values. These two properties are set in Object Inspector using their respective fields. By default, the minimum value is set to 0 and the maximum is 10. As integers, the lowest minimum allowed is INT_MIN which is –2147483647. The maximum allowed value is INT_MAX which is 2147483647. To change the minimum or maximum values programmatically, assign the desired value to the appropriate property. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TrackBar1->Min = -1205; TrackBar1->Max = -540; } //---------------------------------------------------------------------------
Always make sure that the minimum value is lower than the maximum. This safe measure will allow you to better control the current value of the control. At design time, if you try inversing the values, C++ Builder would reset them. For example, if the Min field is 12 and you try setting it to 48 when the Max field is 25, the Min field would be reset to its original value 12. At runtime, if you try setting wrong values as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TrackBar1->Min = 55; TrackBar1->Max = 12; }
Copyright © 2003 FunctionX, Inc.
549
Chapter 22: Track-Based Controls
Borland C++ Builder Programming
//---------------------------------------------------------------------------
The minimum would be set to the previous minimum value the property had and the new maximum value would be kept. If you do not know for sure which value would be greater due to an intermediary action of the user, you can write a conditional statement that would control the minimum and the maximum values. When using the trackbar, the user can slide the thumb in the desired direction, thus changing the value of the control. While it is moving, you can control the incrementing of the thumb. By default, the thumb advances or regresses by 1 unit each time it is scrolled. This unit is controlled by the Frequency property. The thumb’s visual display appears as a small standing pentagon with two straight borders. Its size is set using the ThumbLength property; the smaller the value, the narrower the thumb. The visual appearance of the thumb is controlled by the SliderVisible property whose Boolean value is by default set to true. Therefore, if you wish to hide the thumb, set its SliderVisible property to false. A trackbar is also equipped with small bars “|” that serve as indicators of the position occupied by the slider. These bars are called ticks. By default, the tick marks are positioned on the same side the slider is pointing. This conditional position of the ticks is controlled by the value of TickMarks property set from the TTickMark enumerator: enum TTickMark { tmBottomRight, tmTopLeft, tmBoth };
By default, when you add a new TrackBar control to a form, it is horizontally oriented, the slider points down, the tick marks are positioned under the sliding field. In this setting, the TickMarks property is set to tmBottomRight. To place the tick marks above the sliding field, change the value of the TickMarks property to tmTopLeft; this also has the effect of reversing the direction of the slider. As a third option, you can have the tick marks on both sides of the slider. To get this, set the TickMarks property to tmBoth. With this value, the thumb becomes a small rectangle (changing from its pentagon shape). The sliding field of a track bar is a rectangle with a background. It stays white even as the user slides the thumb to change the control’s value.
Practical Learning: Implementing a Track Bar
550
1.
On the Win32 tab of the Component Palette, click the TrackBar button click on the lower-left section of the form
2.
Set its properties as follows: Height: 40 ThumbLength: 16 TickMarks: tmBoth
and
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
3.
Chapter 22: Track-Based Controls
Save all
22.4.3 Track Bar Events A track bar is important when you can get its value and use it in a transaction. The most fundamental operation you can do is to display its value in an edit or a label controls. When the value of the track bar changes, we will use its OnChange() event to track its position and display its value on a label.
Practical Learning: Using Track Bar Events 1.
On the form, double-click the trackbar to access its OnChange event
2.
Implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::TrackBar1Change(TObject *Sender) { int CurPos = TrackBar1->Position - 1; edtMake->Text = Car[CurPos].Make; edtModel->Text = Car[CurPos].Model; edtYear->Text = IntToStr(Car[CurPos].CarYear); edtDoors->Text = IntToStr(Car[CurPos].Doors); Image1->Picture->Bitmap = Car[CurPos].CarPicture; } //---------------------------------------------------------------------------
3.
On the form, double-click an unoccupied area to access its OnCreate event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { edtMake->Text = Car[0].Make; edtModel->Text = Car[0].Model; edtYear->Text = IntToStr(Car[0].CarYear); edtDoors->Text = IntToStr(Car[0].Doors);
Copyright © 2003 FunctionX, Inc.
551
Chapter 22: Track-Based Controls
Borland C++ Builder Programming
Image1->Picture->Bitmap = Car[0].CarPicture; } //---------------------------------------------------------------------------
4.
Test the application
Figure 75: The Car Inventory Application Completed
552
5.
Close it and return to Bcb
6.
Save All
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
Chapter 23: Progress-Based Controls 23.1 Timers 23.1.1 Introduction A timer is a non-spatial object that uses recurring lapses of time in a computer or in your application. To work, every lapse of period, the control sends a message to the operating system. The message is something to the effect of "I have counted the number of lapses you asked me to count". As opposed to the time that controls your computer, a timer is partly but greatly under your control. Users do not see nor do they use a timer as a control. As a programmer, you decide if, why, when, and how to use this control.
Practical Learning: Introducing the Timer Control 1.
Start a new application with the default form
2.
Save it in a new folder named TrafficLight1
3.
Save the unit as Exercise and save the project as TrafficLight
4.
Change the Caption of the form to Traffic Light
5.
Change its BorderStyle property to bsDialog
6.
Change its dimensions to Height = 256 and Width = 112
7.
From the Statndard tab of the Component Palette, double-click the Panel control
8.
Change the properties of the panel as follows: BevelInner = bvNone BevelOuter = bvLowered BevelWidth = 5 Caption = Empty Color = clBlack Height = 209 Left = 17 Top =8 Width = 81
9.
While the panel is still selected on the form, from the Additional tab of the Component Palette, double-click the Shape control
10. Change the following properties for the new Shape: Brush: Color Copyright © 2003 FunctionX, Inc.
= clGray 553
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
Brush: Style = bsSolid Height = 65 Left =8 Top =8 Width = 65 11. On the panel, click the newly added Shape to make sure it is selected. Press Ctrl + C to copy it to the clipboard 12. Click anywhere on the panel to select it. 13. Press Ctrl + V to paste the Shape 14. While the new Shape is still selected, change its Left value to 8 and its Top value to 72 15. Click an empty area on the panel to select it and press Ctrl + V. 16. While the new shape is still selected, change its Left value to 8 and its Top value to 136 17. Click the panel on the form and ress Ctrl + V again 18. Change the properties of the new shape as follows: Brush: Color = clRed Height = 57 Left =8 Name = shpRed Shape = stCircle Top = 12 19. On the form, click the red circle and press Ctrl + C. Then press Ctrl + V to add a new Shape 20. Change its properties as follows: Brush: Color = clSilver Height = 57 Left =8 Name = shpYellow Shape = stCircle Top = 76 21. On the form, click the Panel and press Ctrl + V 22. Change the properties of the new Shape to: Brush: Color = clSilver Height = 57 Left =8 Name = shpGreen Shape = stCircle Top = 140 23. Save All
23.1.2 Characteristics of a Timer The timer in VCL applications is made available through the TTimer class. To add it to your application at design time, from the System property page of the Component Palette, click Timer
554
and click on the form.
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
The Timer control has two properties that are particularly important for its functionality. A timer is an object used to count lapses of time and send a message when it has finished counting. The amount of time allocated for counting is called an interval. The Interval is probably the most important characteristic of the Timer control because it measures and controls the total time needed to perform a complete count. The Interval is measured in milliseconds. Like any counter, the lower the value, the faster the count will finish, and the higher the value, the longer the count (if you ask one kid to count from 1 to 10 and you ask another to count from 1 to 20 and shout when finished, the first kid would finish first and would shout first). The amount of interval you specify will depend on what you are trying to do. One of the uses you can make of a Timer control is to decide when it should start counting. In some applications, you may want the control to work full-time while in some other applications, you may want the control to work only in response to an intermediate event. The ability to stop and start a Timer control is set using the Enabled Boolean property. When, or as soon as, this property is set to true, the control starts counting. If, when, or as soon as, the Enabled property is set to false, the control stops and resets its counter to 0.
Practical Learning: Using Timer Controls 1.
From the System tab of the Component Palette, double-click Timer
2.
On the Object Inspector, click the Events tab
3.
Although the dialog box will be equipped with the system Close button, we should provide our own mean of closing the application.
4.
On the form, click the red circle.
5.
Press and hold Shift, then click the other two circles
6.
On the Object Inspector, double-click the empty area on the right side of OnMouseDown
7.
Change the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::shpGreenMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { Close(); } //---------------------------------------------------------------------------
8.
Press F12 to display the form
9.
Double-click the Timer on the form to access its OnTimer event
10. Change its code as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Timer1Timer(TObject *Sender) { // If the current color is red if( shpRed->Brush->Color == clRed ) { // Change the color to green Timer1->Interval = 3500; shpRed->Brush->Color = clSilver;
Copyright © 2003 FunctionX, Inc.
555
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
shpYellow->Brush->Color = clSilver; shpGreen->Brush->Color = clGreen; } // But if the color is green else if( shpGreen->Brush->Color == clGreen ) { // Change the color to yellow Timer1->Interval = 2000; shpRed->Brush->Color = clSilver; shpYellow->Brush->Color = clYellow; shpGreen->Brush->Color = clSilver; } // Otherwise, if the color is yellow else // if(shpYellow->Brush->Color == clYellow { // Change the color to red Timer1->Interval = 5000; shpRed->Brush->Color = clRed; shpYellow->Brush->Color = clSilver; shpGreen->Brush->Color = clSilver; } } //---------------------------------------------------------------------------
11. Test your program
12. Close it and return to Bcb 13. Save All
23.1.3 The Tick Counter The Win32 API provides a special function used to count a specific number of lapses that have occurred since you started your computer. This information or counter is available through the GetTickCount() function. Its syntax is: DWORD GetTickCount(VOID);
This function takes no argument. If it succeeds in performing its operation, which it usually does, it provides the number of milliseconds that have elapsed since you started 556
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
your computer. Just like the VCL Timer control, what you do with the result of this function is up to you and it can be used in various circumstances. For example, computer games and simulations make great use of this function. After retrieving the value that this function provides, you can display it in a text-based control. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnElapsedClick(TObject *Sender) { unsigned long Elapsed = GetTickCount(); edtElapsed->Text = IntToStr(Elapsed); } //---------------------------------------------------------------------------
Practical Learning: Counting the Computer's Ticks 1.
Start a new application with the default form
2.
Save it in a new folder named CompTicks1
3.
Save the unit as Exercise and save the project CompTicks
4.
Design the form as follows: set its BorderStyle to bsDialog. Change its name to frmMain and set its Caption to Counting Computer Ticks
5.
Add a GroupBox control
6.
Add a Timer control Interval to 20
from the System tab of the Component Palette. Set its
7.
Add an Edit control
and change its Name to edtComputerTime
8.
Add another Edit control
9.
Press F12 to access the Code Editor. In the private section of the form, declare an unsigned integer as follows:
and set its Caption to Elapsed Time
and change its Name to edtApplicationTime
private: unsigned int TimeTheComputerStarted; // User declarations public: // User declarations __fastcall TfrmMain(TComponent* Owner); }; //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
557
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
10. On the form, double-click the Timer1 icon to access its OnTimer event and implement the source file as follows: //--------------------------------------------------------------------------#include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TfrmMain *frmMain; //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { TimeTheComputerStarted = GetTickCount(); } //--------------------------------------------------------------------------void __fastcall TfrmMain::Timer1Timer(TObject *Sender) { unsigned long CurrentTickValue = GetTickCount(); unsigned int Difference = CurrentTickValue - TimeTheComputerStarted; edtComputerTime->Text = IntToStr(CurrentTickValue); edtApplicationTime->Text = IntToStr(Difference);
} //--------------------------------------------------------------------------void __fastcall TfrmMain::BitBtn1Click(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
11. Press F9 to test the application
12. After testing the application, close it and return to Bcb 13. To make the values easier to read, change the form as follows. Delete both Edit boxes and replace them with Label controls lblApplicationTime respectively:
558
named lblComputerTime and
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
14. Change the code of the OnTimer event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::Timer1Timer(TObject *Sender) { unsigned long CurrentTickValue = GetTickCount(); unsigned int Difference = CurrentTickValue - TimeTheComputerStarted; unsigned int ComputerHours, ComputerMinutes, ComputerSeconds; unsigned int ApplicationHours, ApplicationMinutes, ApplicationSeconds; ComputerHours = (CurrentTickValue / (3600 * 999)) % 24; ComputerMinutes = (CurrentTickValue / (60 * 999)) % 60; ComputerSeconds = (CurrentTickValue / 999) % 60; ApplicationHours = (Difference / (3600 * 999)) % 24; ApplicationMinutes = (Difference / (60 * 999)) % 60; ApplicationSeconds = (Difference / 999) % 60; AnsiString ComputerTime, ApplicationTime; ComputerTime = IntToStr(ComputerHours) + " hours, " + IntToStr(ComputerMinutes) + " minutes " + IntToStr(ComputerSeconds) + " seconds"; ApplicationTime = IntToStr(ApplicationHours) + " hours " + IntToStr(ApplicationMinutes) + " minutes " + IntToStr(ApplicationSeconds) + " seconds"; lblComputerTime->Caption = ComputerTime; lblApplicationTime->Caption = ApplicationTime; } //---------------------------------------------------------------------------
15. Test the application
16. After testing the application, close it 17. Save All Copyright © 2003 FunctionX, Inc.
559
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
23.2 Progress Bars 23.2.1 Overview A progress bar is a control that displays (small) rectangles that are each filled with a color. These (small) rectangles are separate but adjacent each other so that, as they display, they produce a bar. To have the effect of a progress bar, not all these rectangles display at the same time. Instead, a numeric value specifies how many of these (small) rectangles can display at one time. There are two types of progress bars and various characteristics they can have. For example, although most progress bars are horizontal, the control can assume a vertical orientation
As mentioned already, a progress bar is made of small colored rectangles. These rectangles can display distinctively from each other although they are always adjacent. The programmer can also specify what color would fill the small rectangles. To make it less confusing, all of the small rectangles display in the same color. The small rectangles can be "glued" to produce a smooth effect, in which case they would not appear distinct. To create a progress bar, use the ProgressBar control
from the Win32 tab.
Practical Learning: Introducting Progress Bars
560
1.
Start a new project with its default form
2.
Save it in a new folder named BodyMonitor1
3.
Save the unit as Exercise and save the project as BodyMonitor
4.
Open Image Editor. Design a 32 x 32 icon and its associated 16 x 16 icon as follows:
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
5.
Save it as BMon
6.
Open the Project Options dialog. In the Application property page, set the title to Body Monitor Simulation
7.
Change the icon to the above
8.
Change the form’s properties as follows: Name: frmMain Caption: Body Monitoring ShowHint: true Position: poScreenCenter
9.
Save All
23.2.2 Progress Bar Properties By default, a newly added progress bar assumes a horizontal position. This aspect is controlled by the Orientation property which is a TProgressBarOrientation enumerator defined as follows: enum TProgressBarOrientation { pbHorizontal, pbVertical };
The default value of the Orientation property is pbHorizontal. This is equivalent to not specifying an orientation when programmatically creating the control using either the VCL or the Win32 libraries. If you want the progress bar to appear vertical, at design time, set the Orientation value to pbVertical. If you are creating the progress bar using the Win32 library, OR the PBS_VERTICAL style. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { CreateWindowEx(0, PROGRESS_CLASS, NULL, WS_CHILD | WS_VISIBLE | PBS_VERTICAL, 20, 20, 18, 170, Handle, NULL, HInstance, NULL); } //---------------------------------------------------------------------------
As mentioned already, a progress bar appears as a series of small adjacent rectangles. By default, these rectangles dispplay distinctively. If on the other hand you want to “glue” them and produce a smooth bar, use the Smooth Boolean property whose default value is Copyright © 2003 FunctionX, Inc.
561
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
false, making the small rectangles separate. If you set this property to true, the bar would appear continuous. If creating the control using the CreateWindow() or CreateWindowEx() Win32 function, you can OR the PBS_SMOOTH style. To display its small rectangles or the smooth bar, the progress bar uses a preset color, which is usually blue. If you prefer to use a different color, call the SendMessage() function with the PBM_SETBARCOLOR message. The syntax you would is: SendMessage(HWND hWnd,
PBM_SETBARCOLOR, wParam = 0, lParam = (LPARAM)(COLORREF)clrBar; As you can see from this syntax, the wParam argument is not used and must be passed as 0. The desired color for the bar is specified using the lParam argument. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { SendMessage(ProgressBar1->Handle, PBM_SETBARCOLOR, 0, clRed); } //---------------------------------------------------------------------------
To show its effect, the progress bar draws its small rectangles on a bar. These small shapes are from a starting position to an end. This means that the progress bar uses a range of values. This range is controlled by the Min and the Max properties whose default values are 0 and 100 respectively. At design time, you can set them using the limits of an unsigned short integer, that is, from 0 to 65,535. In Win32, the range of values of a progress bar is set using the PBM_SETRANGE message using the following syntax: SendMessage(HWND hWnd, PBM_SETRANGE, wParam = 0, lParam = MAKELPARAM(nMinRange, nMaxRange);
Alternative, you can send the PBM_SETRANGE32 message to set the range of the progress bar. This time, the syntax used would be: SendMessage(HWND hWnd, PBM_SETRANGE32, wParam = (WPARAM)(int) iLowLim, lParam = (LPARAM)(int) iHighLim);
For a horizontal progress bar, the small rectangles are drawn from left to right. For a vertical progress bar, the small rectangles are drawn from bottom to top. At one particular time, the most top or the most right rectangle of a progress bar is referred to as its position. At design time, to set a specific position for the control, change the value of the Position property whose default is 0. The position must always be between the Min and Max values. If you set it to a value lower than the Min, the Object Inspector would reset it to Min. In the same way, if it is set to a value higher than Max, it would be reset to the Max value. At run time, you can assign the desired value to the Position property. Once again, avoid specifying a value that is out of range. Because a progress bar is usually meant to indicate the progress of an activity, when drawing its small rectangles, it increases its current position in order to draw the next rectangle, except if the control is reset. The number of units that the control must increase
562
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
value is controlled by the Step property. By default, it is set to 1. Otherwise, you can set it to a different value of your choice.
Practical Learning: Creating Progress Bars 1.
On the Win32 tab of the Component Palette, click the ProgressBar button click on the form
2.
Change its properties as follows: Height: 225 Left: 16 Max: 250 Name: pgrBlood Orientation: pbVertical Position: 128 Smooth: true Top: 24 Width: 18
3.
In the same way, add other progress bars and design the form as follows:
Control Label Label Label Label Label Copyright © 2003 FunctionX, Inc.
Name lblBlood lblHeart lblKidney lblBrain lblLLung
Caption 000 000 000 000 000
and
Additional Proeprties
563
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
Label Label Label Label Label
lblRLung lblPancreas lblLiver lblBladder lblStomach
ProgressBar
pgrBlood
ProgressBar
pgrHeart
ProgressBar
pgrKidney
ProgressBar
pgrBrain
ProgressBar
pgrLLung
ProgressBar
pgrRLung
ProgressBar
pgrPancreas
ProgressBar
pgrLiver
ProgressBar
pgrBladder
ProgressBar
pgrStomach
Shape
shpBlood
Shape
shpHeart
Shape
shpKidney
Shape
shpBrain
Shape
shpLLung
Shape
shpRLung
Shape
shpPancreas
Shape
shpLiver
Shape
shpBladder
Shape
shpStomach
000 000 000 000 000 Max: 650 Position: 288 Max: 240 Position: 204 Max: 450 Position: 120 Max: 1000 Position: 760 Max: 750 Position: 428 Max: 750 Position: 320 Max: 800 Position: 224 Max: 1200 Position: 240 Max: 550 Position: 350 Max: 1250 Position: 650 Brush: Color: clGray Shape: stCircle Hint: Start Blood Monitoring Brush: Color: clGray Shape: stCircle Hint: Start Heart Monitoring Brush: Color: clGray Shape: stCircle Hint: Start Kidney Monitoring Brush: Color: clGray Shape: stCircle Hint: Start Brain Monitoring Brush: Color: clGray Shape: stCircle Hint: Start Left Lung Brush: Color: clGray Shape: stCircle Hint: Start Right Lung Brush: Color: clGray Shape: stCircle Hint: Start Pancreas Monitoring Brush: Color: clGray Shape: stCircle Hint: Start Liver Monitoring Brush: Color: clGray Shape: stCircle Brush: Color: clGray Shape: stCircle Hint: Start Stomach Monitoring
Bevel 564
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Label Label Label Label Label Label Label Label Label Label Label Panel
4.
Add 10 timers
Control Timer Timer Timer Timer Timer Timer Timer Timer Timer Timer Copyright © 2003 FunctionX, Inc.
Chapter 23: Progress-Based Controls
pnlClose
Blood Heart Kidney Brain Lungs Left Right Pancreas Liver Bladder Stomach Close
to the form and configure them as follows:
Name tmrBlood tmrHeart tmrKidney tmrBrain tmrLLung tmrRLung tmrPancreas tmrLiver tmrBladder tmrStomach
Enabled False False False False False False False False False False
Interval 650 240 450 1000 750 750 800 1200 550 1250 565
Chapter 23: Progress-Based Controls
5.
Borland C++ Builder Programming
Double-click an unoccupied area on the form and implement its OnCreate() event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { SendMessage(pgrBlood->Handle, PBM_SETBARCOLOR, 0, clRed); SendMessage(pgrHeart->Handle, PBM_SETBARCOLOR, 0, clGreen); SendMessage(pgrKidney->Handle, PBM_SETBARCOLOR, 0, clYellow); SendMessage(pgrBrain->Handle, PBM_SETBARCOLOR, 0, clGray); SendMessage(pgrLLung->Handle, PBM_SETBARCOLOR, 0, clFuchsia); SendMessage(pgrRLung->Handle, PBM_SETBARCOLOR, 0, clFuchsia); SendMessage(pgrPancreas->Handle, PBM_SETBARCOLOR, 0, clBlue); SendMessage(pgrLiver->Handle, PBM_SETBARCOLOR, 0, clAqua); SendMessage(pgrBladder->Handle, PBM_SETBARCOLOR, 0, clLime); SendMessage(pgrStomach->Handle, PBM_SETBARCOLOR, 0, clNavy); } //---------------------------------------------------------------------------
6.
Save all
23.2.3 Progress Bar Methods and Messages The ProgressBar control is based on the TProgressBar class. Like every VCL class, it is equipped with a constructor that can be used to dynamically create the control. We have seen that a progress bar implements its behavior by drawing small adjacent rectangles. This control does not know and does not decide when to draw these indicators. Therefore, after creating a progress bar, you must provide a means of changing its value, that is, a way to increment its position. Although it is usually used to show the evolution of a task, it does not actually have an internal mechanism to monitor such an activity. Another control is usually used to trigger this. Nevertheless, when the value of a progress bar changes, the control refers to the Step property to increment its Position. Based on this Step value, when it is time to increment, the progress bar calls its StepIt() method. Its syntax is: void __fastcall StepIt(void);
If you want to increase the progress bar’s position by a value other than Step, you can call the StepBy() method. Its syntax is: void __fastcall StepBy(int Delta);
Pass the desired incremental value as the Delta argument.
Practical Learning: Implementing Progress Bars 1.
On the form, double-click the tmrBlood timer to access its OnTimer event and implement it as follows: //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { Randomize();
566
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
} //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrBloodTimer(TObject *Sender) { int BloodLevel = random(650); pgrBlood->Position = BloodLevel; if( BloodLevel > 480 ) shpBlood->Brush->Color = clRed; else shpBlood->Brush->Color = clGreen; lblBlood->Caption = lblBlood->Caption.sprintf("%d.%d", BloodLevel/100, random(50));
} //---------------------------------------------------------------------------
2.
Again, on the form, double-click the tmrHeart timer to access its OnTimer event event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrHeartTimer(TObject *Sender) { int HeartLevel = random(240); pgrHeart->Position = HeartLevel; if( HeartLevel > 180 ) shpHeart->Brush->Color = clRed; else shpHeart->Brush->Color = clGreen; lblHeart->Caption = lblHeart->Caption.sprintf("%d\260", HeartLevel); } //---------------------------------------------------------------------------
3.
In the same way, initiate the OnTimer event of the tmrKidney timer event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrKidneyTimer(TObject *Sender) { int KidneyLevel = random(450); pgrKidney->Position = KidneyLevel; if( KidneyLevel > 400 ) shpKidney->Brush->Color = clRed; else shpKidney->Brush->Color = clGreen; lblKidney->Caption = lblKidney->Caption.sprintf("%d\045", KidneyLevel); } //---------------------------------------------------------------------------
4.
Initiate the OnTimer event of the tmrBrain timer event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrBrainTimer(TObject *Sender) {
Copyright © 2003 FunctionX, Inc.
567
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
int BrainLevel = random(1000); pgrBrain->Position = BrainLevel; if( BrainLevel > 550 ) shpBrain->Brush->Color = clRed; else shpBrain->Brush->Color = clGreen; lblBrain->Caption = lblBrain->Caption.sprintf("<%d>", BrainLevel-450); } //---------------------------------------------------------------------------
5.
Initiate the OnTimer event of the tmrLLung timer event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrLLungTimer(TObject *Sender) { int LLungLevel = random(750); pgrLLung->Position = LLungLevel; if( LLungLevel > 600 ) shpLLung->Brush->Color = clRed; else shpLLung->Brush->Color = clGreen; lblLLung->Caption = lblLLung->Caption.sprintf("%d.%d\"", LLungLevel, 2 + random(5));
} //---------------------------------------------------------------------------
6.
Initiate the OnTimer event of the tmrRLung timer event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrRLungTimer(TObject *Sender) { int RLungLevel = random(750); pgrRLung->Position = RLungLevel; if( RLungLevel > 500 ) shpRLung->Brush->Color = clRed; else shpRLung->Brush->Color = clGreen; lblRLung->Caption = lblRLung->Caption.sprintf("%d.%d\"", RLungLevel, 2 + random(5)); } //---------------------------------------------------------------------------
7.
Initiate the OnTimer event of the tmrPancreas timer event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrPancreasTimer(TObject *Sender) { int PancreasLevel = random(800);
568
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
pgrPancreas->Position = PancreasLevel; if( PancreasLevel > 600 ) shpPancreas->Brush->Color = clRed; else shpPancreas->Brush->Color = clGreen; lblPancreas->Caption = lblPancreas->Caption.sprintf("\273%d", PancreasLevel);
} //---------------------------------------------------------------------------
8.
Initiate the OnTimer event of the tmrLiver timer event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrLiverTimer(TObject *Sender) { int LiverLevel = random(1200); pgrLiver->Position = LiverLevel; if( LiverLevel > 1100 ) shpLiver->Brush->Color = clRed; else shpLiver->Brush->Color = clGreen; lblLiver->Caption = lblLiver->Caption.sprintf("%d\264", LiverLevel); } //---------------------------------------------------------------------------
9.
Initiate the OnTimer event of the tmrBladder timer event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrBladderTimer(TObject *Sender) { int BladderLevel = random(550); pgrBladder->Position = BladderLevel; if( BladderLevel > 450 ) shpBladder->Brush->Color = clRed; else shpBladder->Brush->Color = clGreen; lblBladder->Caption = lblBladder->Caption.sprintf("\247%d\252", BladderLevel); } //---------------------------------------------------------------------------
10. Initiate the OnTimer event of the tmrStomach timer event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::tmrStomachTimer(TObject *Sender) { int StomachLevel = random(1250); pgrStomach->Position = StomachLevel; if( StomachLevel > 1100 )
Copyright © 2003 FunctionX, Inc.
569
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
shpStomach->Brush->Color = clRed; else shpStomach->Brush->Color = clGreen; lblStomach->Caption = lblStomach->Caption.sprintf("%d\274", StomachLevel); } //---------------------------------------------------------------------------
11. On the form, click the shape control above the Blood label. In the Object Inspector, click the Events tab. Double-click the right field to OnMouseDown and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::shpBloodMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( tmrBlood->Enabled ) { tmrBlood->Enabled = False; shpBlood->Brush->Color = clGray; shpBlood->Hint = "Start Blood Monitoring"; } else { tmrBlood->Enabled = True; shpBlood->Hint = "Stop Blood Monitoring"; } } //---------------------------------------------------------------------------
12. In the same way, initiate the OnMouseDown event of the shape above the Heart label and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::shpHeartMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( tmrHeart->Enabled ) { tmrHeart->Enabled = False; shpHeart->Brush->Color = clGray; shpHeart->Hint = "Start Heart Monitoring"; } else { tmrHeart->Enabled = True; shpHeart->Hint = "Stop Heart Monitoring"; } } //---------------------------------------------------------------------------
13. Also, initiate the OnMouseDown event of the shape above the Kidney label and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::shpKidneyMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
570
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
{
Chapter 23: Progress-Based Controls
if( tmrKidney->Enabled ) { tmrKidney->Enabled = False; shpKidney->Brush->Color = clGray; shpKidney->Hint = "Start Kidney Monitoring"; } else { tmrKidney->Enabled = True; shpKidney->Hint = "Stop Kidney Monitoring"; }
} //---------------------------------------------------------------------------
14. Initiate the OnMouseDown event of the shape above the Brain label and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::shpBrainMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( tmrBrain->Enabled ) { tmrBrain->Enabled = False; shpBrain->Brush->Color = clGray; shpBrain->Hint = "Start Brain Monitoring"; } else { tmrBrain->Enabled = True; shpBrain->Hint = "Stop Brain Monitoring"; } } //---------------------------------------------------------------------------
15. Initiate the OnMouseDown event of the shape above the Left label and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::shpLLungMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( tmrLLung->Enabled ) { tmrLLung->Enabled = False; shpLLung->Brush->Color = clGray; shpLLung->Hint = "Start Left Lung Monitoring"; } else { tmrLLung->Enabled = True; shpLLung->Hint = "Stop Left Lung Monitoring"; } } //---------------------------------------------------------------------------
16. Initiate the OnMouseDown event of the shape above the Right label and implement it as follows: //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
571
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
void __fastcall TfrmMain::shpRLungMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( tmrRLung->Enabled ) { tmrRLung->Enabled = False; shpRLung->Brush->Color = clGray; shpRLung->Hint = "Start Right Lung Monitoring"; } else { tmrRLung->Enabled = True; shpRLung->Hint = "Stop Right Lung Monitoring"; } } //---------------------------------------------------------------------------
17. Initiate the OnMouseDown event of the shape above the Pancreas label and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::shpPancreasMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( tmrPancreas->Enabled ) { tmrPancreas->Enabled = False; shpPancreas->Brush->Color = clGray; shpPancreas->Hint = "Start Pancreas Monitoring"; } else { tmrPancreas->Enabled = True; shpPancreas->Hint = "Stop Pancreas Monitoring"; } } //---------------------------------------------------------------------------
18. Initiate the OnMouseDown event of the shape above the Liver label and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::shpLiverMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( tmrLiver->Enabled ) { tmrLiver->Enabled = False; shpLiver->Brush->Color = clGray; shpLiver->Hint = "Start Liver Monitoring"; } else { tmrLiver->Enabled = True; shpLiver->Hint = "Stop Liver Monitoring"; } } //---------------------------------------------------------------------------
19. Initiate the OnMouseDown event of the shape above the Bladder label and implement it as follows: 572
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
//--------------------------------------------------------------------------void __fastcall TfrmMain::shpBladderMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( tmrBladder->Enabled ) { tmrBladder->Enabled = False; shpBladder->Brush->Color = clGray; shpBladder->Hint = "Start Bladder Monitoring"; } else { tmrBladder->Enabled = True; shpBladder->Hint = "Stop Bladder Monitoring"; } } //---------------------------------------------------------------------------
20. Initiate the OnMouseDown event of the shape above the Stomach label and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::shpStomachMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if( tmrStomach->Enabled ) { tmrStomach->Enabled = False; shpStomach->Brush->Color = clGray; shpStomach->Hint = "Start Stomach Monitoring"; } else { tmrStomach->Enabled = True; shpStomach->Hint = "Stop Stomach Monitoring"; } } //---------------------------------------------------------------------------
21. Double-click the (bottom) panel to access its OnClick event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::Panel1Click(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
22. Execute the application 23. To test a progress bar, click the shape button under it. To stop it, click its shape button again
Copyright © 2003 FunctionX, Inc.
573
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
24. After using the form, close it and return to Bcb 25. Save All
23.3 Scroll Bars 23.3.1 Introduction A scrollbar is a control that allows the user to navigate a document in two directions by clicking a button that displays an arrow. The control is equipped with one button at each of its ends. Between the buttons, there is a (long) bar and on the bar, there is sliding object called a thumb:
574
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
Figure 76: Scroll Bars Example To use a scroll bar, the user can click one of the arrows. This causes the thumb to move towards the button that was clicked. The user can also click and hold the mouse on a button. This causes the thumb to move continuously, as long as the button is held down, towards the button, until the thumb cannot move any farther. The user can also drag the thumb in one direction to move it or she can click between a button and the thumb. This causes the thumb to move faster that clicking a button. The thumb of a scroll bar can be positioned only along the scroll bar, between the scroll bar’s button. Based on their orientation, there are two types of scroll bars: horizontal and vertical. The horizontal scroll bar allows the user to navigate a document left and right. The vertical scroll bar allows navigating up and down. Based on their relationship with the parent control or owner, there are two types of scroll bars: those that are (automatically) associated with their parent or owner and scroll bar controls that are manually added by the programmer.
23.3.2 Automatically-Added Scroll Bars To effectively implement their functionality, some controls must be equipped with one or two scroll bars. As we will see with list-based controls such as list boxes, combo boxes, tree views, list views, etc, when the items of their list exceed the allocated client area of the control, the list should display a scroll bar to give access to the hidden part of their list. This type of automatically added scroll bar is usually positioned on the right side of the control for most Latin-based languages including US English.
Copyright © 2003 FunctionX, Inc.
575
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
The Palette property page of Borland C++ Builder’s Environment Options dialog box shows the Pages list box and the Components list view. Because each control has a long list that it cannot show completely, it is equipped with a vertical scroll bar. This allows the user to display the hidden list when needed.
Figure 77: Dialog Boxes - Environment Options These types of scroll bars are automatically added by the operating system to the control that needs it, unless the programmer explicitly prevented their showing. Some controls are ready to display a scroll bar upon request. Such controls include the form, the Memo, the RichEdit, the ScrollBox, etc. When designing one of these controls, you can ask to display or hide either or both scroll bars as you see fit. This type of scroll bar is implemented through the TScrollingWinControl class.
23.3.3 Text-Based Applications and Scroll Bars Because they are always likely to display a long text, the Memo and the RichEdit controls of the VCL are natively ready to display scroll bars, either or both. This is easily done using the ScrollBars property. It provides four options as follows: Value ssNone ssHorizontal ssVertical ssBoth
576
Comments No scroll bar will be displayed This is the default value A horizontal scroll bar will display at the bottom of the control or document A vertical scroll bar will display on the right side of the control or document A horizontal scroll bar will display at the bottom of the control and a vertical scroll bar will display on the right side of the control or document
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
Thanks to rapid application development (RAD) and object-oriented programming (OOP), you do not have to permanently set the scroll bar(s). You can act in response to the user doing something and decide when to display or hide either or both scroll bars.
Practical Learning: Using Scroll Bars on a Text-Based Application 1.
Open the Editor1 application you created in previous lessons. If you wo not have it, open the Editor1 project from the resources that accompany this book.
2.
Display the main form and click the rchEditor control. On the Object Inspector, set the ScrollBars to ssVertical
3.
On the form, double-click the MainMenu1 icon on the form. On the Menu Designer, click the View menu item and add a separator on the first empty box. Then add a menu item named mnuWordWrap and whose Caption is &Word Wrap then set its Checked property to true. Close the Menu Designer
4.
On the main menu of the form, click View -> Word Wrap and implement its event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::mnuWordWrapClick(TObject *Sender) { rchEditor->WordWrap = !rchEditor->WordWrap; mnuWordWrap->Checked = !mnuWordWrap->Checked; if( rchEditor->WordWrap ) rchEditor->ScrollBars = ssVertical; else rchEditor->ScrollBars = ssBoth; } //---------------------------------------------------------------------------
5.
Test the application. Close it and return to Bcb
23.4 The Scroll Bar Control 23.4.1 Introduction Microsoft Windows provides another type of scroll bar, considered a complete control in its own right. Like all other controls, this one must be explicity created and is not added automatically but it provides most of the same basic functionality as the operating system automatically added scroll bars. To create a scroll bar control, on the Standard tab of the Component Palette, click the ScrollBar button and click a container. The scroll bar is one of the earliest controls of the Microsoft Windows operating system. To create it using the Win32 approach call the CreateWindow() or the CreateWindowEx() functions and specify the class name as SCROLLBAR. If you decide to create it in Win32, you would need to configure all of its functionality. In the VCL, all operations that are needed on a scroll bar control are ready to be used.
Copyright © 2003 FunctionX, Inc.
577
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
Practical Learning: Using Scroll Bars 1.
Start a new project with the default form
2.
Save it in a new folder named BodyTag1
3.
Save the unit as Main and save the project as BodyTag
4.
Change the following properties for the form: BorderStyle = bsDialog Caption = Body Tag Formatter Height = 316 Name = frmMain Position: poScreenCenter ShowHint = true Width = 350
5.
Open Image Editor and create a new icon. Design the 32 x 32 and the 16 x 16 sizes as follows: 32 x 32
578
16 x 16
6.
Save the icon as BodyTag in the folder of the current project
7.
From the Project menu, access the project options. From the Application tab, seth the Title to Body Tag Formatter. set the Icon to the above
8.
Design the form as follows:
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Control
Name
Label Bevel Panel
pnlPreview
BitBtn GroupBox Label Edit Label Edit Label Edit GroupBox Label Edit Label Edit Label Edit Label Edit
9.
Chapter 23: Progress-Based Controls
edtHexaRed edtHexaGreen edtHexaBlue
edtNumRed edtNumGreen edtNumBlue
Caption or Text Preview
Other Properties Shape: bsBottomLine Color: clWhite Hint: Current Color Kind: bkClose
Hexadecimal Red FF Green FF Blue FF Numeric Red 255 Green 255 Blue 255 Color
edtBody
Hint: Hexadecimal formula of current color
Save All
23.4.2 Characteristics of the Scroll Bar Control By default, when you add a scroll bar to a form, the control assumes the horizontal position. This position is controlled by the Kind property whose default value is sbHorizontal. To change the direction of the control to vertical, set this property to Copyright © 2003 FunctionX, Inc.
579
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
sbVertical. The Kind property is controlled by the TScrollBarKind enumerator defined as follows: enum TScrollBarKind { sbHorizontal, sbVertical };
To set this property programmatically, assign the desired value. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { ScrollBar1->Kind = sbVertical; } //---------------------------------------------------------------------------
When using a scroll bar, the user can navigate from one end of the control to the other end. These are the control’s minimum and maximum values. For a horizontal scrollbar, the minimum is the far left position that the bar can assume. For a vertical scrollbar, this would be most bottom position. The maximum would be the opposite. These two values are controlled by the Min and Max properties. By default, a newly added scrollbar allows scrolling from 0 to 100. To change these values at design time, type an integer number for each field in the Object Inspector. The lowest number the Min property can have is – 2147483648 and the highest number for Max would be 2147483647. The primary technique the user applies to a scrollbar is to click one of the arrows at the ends of the control. As the bar slides inside of the control, it assumes an integer position from Min to Max. At design time, you can use the Position property to set the position that the scrollbar would assume when the form opens. If you set the Position to a value less than the Min, the Object Inspector would restore it to the Min. If you set a Position greater than the Max, the Object Inspector would assign it the Max value. To programmatically set the position of the bar, assign the desired value, which must be between Min and Max, to the Position property. At run time, when the user scrolls the control, you can find the position of the thumb by getting the value of the Position property. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Label1->Caption = ScrollBar1->Position; Label2->Caption = ScrollBar2->Position; } //---------------------------------------------------------------------------
580
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
Figure 78: The Position of a Scroll Bar When the user clicks an arrow of a scrollbar, the bar slides one unit. This unit is called SmallChange and is set to 1 by default. If you want the bar to slide more than one unit, change the SmallChange property to an integer value between Min and Max. The higher the value, the faster the sliding would occur because the bar would jump by SmallChange units. There are two main ways the user can scroll faster using scrollbars: by pressing either page buttons or by clicking the scrolling region. The amount covered using this technique is controlled by the LargeChange property. Once again, the user can scroll only between Min and Max. This means that you can set this value only to an integer from Min to Max. To find the scrolling amount, the compiler would divide the actual scrolling range (the difference between the Max and Min) by the LargeChange value. When the user clicks in the scrolling region or presses the Page Up or Page Down keys, the bar would jump by LargeChange up to the scrolling amount value. You can change the LargeChange property programmatically as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { ScrollBar1->Kind = sbVertical; ScrollBar1->Min = -122; ScrollBar1->Max = 240; ScrollBar1->Position = 38; ScrollBar1->SmallChange = 4; ScrollBar1->LargeChange = 20; } //---------------------------------------------------------------------------
The bar inside the scroll region has a size relative to the Min and Max values. By default, it is a square of the approximate size of the arrow buttons. This size of the bar is controlled by the PageSize property. Approximately, this represents the percentage of the scrolling range (the difference between the Max and Min). You can change this value at design time in the Object Inspector, by an integer value between Min and Max. To change it programmatically, assign the desired integer to the PageSize property. Here is an example:
Copyright © 2003 FunctionX, Inc.
581
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
Practical Learning: Using Scroll Bars 1.
From the Standard tab of the Component Palette, click the ScrollBar control and, on the form, click on the right side of the panel
2.
On the Object Inspector, change its properties as follows: Kind = sbVertical Max = 255 Min = 0 Name = scrRed Position = 255
3.
On the form, click the scrollbar control to select it
4.
Press Ctrl + C to copy the control and press Ctrl + V to paste it on the form
5.
Move the new scroll bar to the right of the previous one. Change its Name to scrGreen
6.
Click on the form and press Ctrl + V. Move the new scroll bar to the right of the others. Change its Name to scrBlue
7.
Add one label on top of each ScrollBar control. Set their captions to R, G, and B, respectively:
Figure 79: The Body Tag Formatter Application 8.
Save All
23.4.3 Methods to Manage a Scroll Bar The ScrollBar control is equipped with a constructor, TScrollBar, that is typically used to dynamically create a scrollbar. To do this, declare a pointer to a TScrollBar object. Specify the owner of the control, which is usually the form on which it is positioned. 582
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
Also specify the parent of the control. This is usually the form but can also be any container that is hosting the scroll bar. This dynamic creation can be inside of an event or a function. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TScrollBar * Scroller = new TScrollBar(Form1); Scroller->Parent = Form1; } //---------------------------------------------------------------------------
After declaring the variable, you can set its properties as desired. Another method of the TScrollBar class is the SetParams(). It allows you to set the Position, Min, and Max values using one function. Here is an example of using it; //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TScrollBar * Scroller = new TScrollBar(Form1); Scroller->Parent = Form1; Scroller->Left = 24; Scroller->Width = 228; Scroller->Top = 35; Scroller->Kind = sbVertical; Scroller->Max = 1500; Scroller->Position = 780; Scroller->SmallChange = 5; Scroller->LargeChange = 150; Scroller->PageSize = 250; Scroller->SetParams(780, 0, 1500); } //---------------------------------------------------------------------------
23.4.4 Scroll Bar Events The most fundament event of a scrollbar occurs when the bar slides. This happens when:
The user clicks one of the arrows.
The user presses one of the arrow keys.
The user presses Page Up or Page Down
Every time the user performs one of these actions, the position of the bar changes unless it is already at one of the extremes. When the position of the bar has changed, a message is sent to the operating system that the bar has changed its position. Using the OnChange event of the scrollbar, you can tell the compiler what to do. Fundamentally you can find out the new position and display it on a label. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::ScrollBar1Change(TObject *Sender) { Label1->Caption = ScrollBar1->Position; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
583
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
The OnScroll event occurs when the user scrolls the bar. This event is appropriate if or when you want to capture the specific action that caused the scrolling action. These actions relate to the TScrollCode enumerator: enum TScrollCode {scLineUp, scLineDown, scPageUp, scPageDown, scPosition, scTrack, scTop, scBottom, scEndScroll};
The syntax of the OnScroll event is: void __fastcall (__closure *TScrollEvent)(System::TObject* Sender, TScrollCode ScrollCode, int &ScrollPos);
Practical Learning: Using the Scroll Bar Events 1.
On the form, double-click the left ScrollBar control and implement its OnChange event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::scrRedChange(TObject *Sender) { // While the user is scrolling the Red scroll bar // get the integer value or position of the scroll bar edtNumRed->Text = 255 - scrRed->Position; // Get the Position value of the Red scroll bar. // Format it to a HEX value // Display the result in the Red Edit control of the RGB section edtHexaRed->Text = IntToHex(255 - scrRed->Position, 2); // Get the current Position of each scroll bar // Combine these values to create an RGB color // Preview the resulting color in the Panel pnlPreview->Color = TColor(RGB(255 - scrRed->Position, 255 - scrGreen->Position, 255 - scrBlue->Position)); // Get the current Position of each scroll bar // Convert this Position to a HEX value // Using these HEX values, create an RGB (Web) color // Display the resulting RGB color in the Color edit box, // preceded by a # sign edtBody->Text = "#" + IntToHex(255 - scrRed->Position, 2) + IntToHex(255 - scrGreen->Position, 2) + IntToHex(255 - scrBlue->Position, 2);
} //---------------------------------------------------------------------------
2.
On the form, double-click the middle ScrollBar control and implement its OnChange event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::scrGreenChange(TObject *Sender) { edtNumGreen->Text = 255 - scrGreen->Position; edtHexaGreen->Text = IntToHex(255 - scrGreen->Position, 2);
584
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
pnlPreview->Color = TColor(RGB(255 - scrRed->Position, 255 - scrGreen->Position, 255 - scrBlue->Position)); edtBody->Text = "#" + IntToHex(255 - scrRed->Position, 2) + IntToHex(255 - scrGreen->Position, 2) + IntToHex(255 - scrBlue->Position, 2); } //---------------------------------------------------------------------------
3.
On the form, double-click the right ScrollBar control and implement its OnChange event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::scrBlueChange(TObject *Sender) { edtBlue->Text = 255 - scrBlue->Position; edtHexaBlue->Text = IntToHex(255 - scrBlue->Position, 2); pnlPreview->Color = TColor(RGB(255 - scrRed->Position, 255 - scrGreen->Position, 255 - scrBlue->Position)); edtBody->Text = "#" + IntToHex(255 - scrRed->Position, 2) + IntToHex(255 - scrGreen->Position, 2) + IntToHex(255 - scrBlue->Position, 2);
} //---------------------------------------------------------------------------
4.
Test the application
5.
After using the form, close it and save the project. Press F12 to display the form
6.
Click the Red edit box in the Numeric GroupBox. On the Object Inspector, click the Events tab. Double-click the OnKeyPress event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::edtNumRedKeyPress(TObject *Sender, char &Key) { // Allow only digits as entries if( (Key != '0') && (Key != '1') && (Key != '2') && (Key != '3') && (Key != '4') && (Key != '5') && (Key != '6') && (Key != '7') && (Key != '8') && (Key != '9') && (Key != VK_DELETE) && (Key != VK_BACK) ) Key = '\0'; scrRed->Position = 255 - edtNumRed->Text.ToInt(); scrRedChange(Sender); } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
585
Chapter 23: Progress-Based Controls
7.
Borland C++ Builder Programming
In the Events tab of the Component Palette, double-click the OnChange event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::edtNumRedChange(TObject *Sender) { // If the user tries to have this box empty, // set its value to 0 if( edtNumRed->Text.IsEmpty() ) edtNumRed->Text = IntToStr(0); else if( edtNumRed->Text.ToInt() > 255 ) // No value > 255 allowed edtNumRed->Text = 255; scrRed->Position = 255 - edtNumRed->Text.ToInt(); scrRedChange(Sender);
} //---------------------------------------------------------------------------
8.
Implement the OnKeyPress event of the Green edit control from the same GroupBox control as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::edtNumGreenKeyPress(TObject *Sender, char &Key) { if( (Key != '0') && (Key != '1') && (Key != '2') && (Key != '3') && (Key != '4') && (Key != '5') && (Key != '6') && (Key != '7') && (Key != '8') && (Key != '9') && (Key != VK_DELETE) && (Key != VK_BACK) ) Key = '\0'; scrGreen->Position = 255 - edtNumGreen->Text.ToInt(); scrGreenChange(Sender);
} //---------------------------------------------------------------------------
9.
Implement the OnChange event of the Green edit box from the same GroupBox control as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::edtNumGreenChange(TObject *Sender) { if( edtNumGreen->Text.IsEmpty() ) edtNumGreen->Text = IntToStr(0); else if( edtNumGreen->Text.ToInt() > 255 ) edtNumGreen->Text = 255; scrGreen->Position = 255 - edtNumGreen->Text.ToInt(); scrGreenChange(Sender); } //---------------------------------------------------------------------------
10. Implement the OnKeyPress event of the Blue edit box from the same GroupBox as follows: 586
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 23: Progress-Based Controls
//--------------------------------------------------------------------------void __fastcall TfrmMain::edtBlueKeyPress(TObject *Sender, char &Key) { if( (Key != '0') && (Key != '1') && (Key != '2') && (Key != '3') && (Key != '4') && (Key != '5') && (Key != '6') && (Key != '7') && (Key != '8') && (Key != '9') && (Key != VK_DELETE) && (Key != VK_BACK) ) Key = '\0'; scrBlue->Position = 255 - edtBlue->Text.ToInt(); scrBlueChange(Sender); } //---------------------------------------------------------------------------
11. Implement the OnChange event of the Blue edit box from the same GroupBox control as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::edtBlueChange(TObject *Sender) { if( edtBlue->Text.IsEmpty() ) edtBlue->Text = IntToStr(0); else if( edtBlue->Text.ToInt() > 255 ) edtBlue->Text = 255; scrBlue->Position = 255 - edtBlue->Text.ToInt(); scrBlueChange(Sender); } //---------------------------------------------------------------------------
12. Test the form
Copyright © 2003 FunctionX, Inc.
587
Chapter 23: Progress-Based Controls
Borland C++ Builder Programming
Figure 80: The BodyTag1 Application 13. After using it, close it and save the project
588
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
Chapter 24: Selection-Based Controls 24.1 Radio Buttons 24.1.1 Introduction A radio button is a control that appears as a round box . Normally, a radio button is accompanied by one or more other radio buttons that appear and behave as a group. The user decides what button is valid by clicking one of them. When a button has been clicked, its round box fills with a (big) dot: . When one button is selected, the other round buttons of the (same) group are empty. The user can select another by clicking a new choice, which empties the previous selection. This technique of selecting is referred to as mutually-exclusive. To indicate what a particular radio button is used for, it is usually accompanied by a label. This static text informs the user as to what the control is used for. Because they come in a group, radio buttons should be organized in a specific container. To implement their mutual exclusiveness, radio buttons should be positioned in either a RadioGroup, GroupBox, or a Panel controls. If you add the radio buttons on this type of container, by default, they will be treated as a group. You should refrain from positioning radio buttons directly on a form or a frame. At design time, to create a group of radio buttons, first position a container, such as a panel or a group box, on the form. If you want each radio button to behave like a full control, first position a GroupBox or a panel controls on the form. To add each radio button, from the Standard tab of the Component Palette, click the RadioButton and click inside of the container.
24.1.2 Characteristics of Radio Buttons From the programmer’s standpoint, the most important property of any control is its Name. Therefore, after adding the radio buttons to a container, you can change their names using the Object Inspector. Two properties are of particular importance to both you and the user: the label and the state of the control. The label is text that specifies what a particular radio button is used for. To set the label of a radio button, on the Object Inspector, click Caption and type the desired label. Repeat this for each radio button. If you want to change a radio button’s caption at runtime, you can do so programmatically as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { rdoGender1->Caption = "&Man"; }
Copyright © 2003 FunctionX, Inc.
589
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
//---------------------------------------------------------------------------
The second of the most important properties of a radio button is its state: whether it is selected or not. When the user clicks a radio button, it becomes exclusively selected. This is seen by the dot inside its rounded box . When a radio button is selected, it is said to be checked. By default, a newly created radio button is not checked. You can select a radio button using the Checked property. This same property allows you to decide what button would be selected when the form opens. As a Boolean property, to set the Checked state of a radio button, set this value to true. At runtime, to set a particular radio button as checked, assign a true value to its Checked property: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { rdoGender2->Checked = True; } //---------------------------------------------------------------------------
Once one button has been checked in a group, even if there was no selection in the beginning, one radio button is always selected in the group. The user cannot deselect all radio buttons. Only the developer can do this at runtime. By setting the Checked property of a radio button to false, you can remove its selection. Otherwise, you can assign a false value to the Checked property of each radio button. When designing your radio buttons, to manage the space, you can distribute them on more than one column. If you want to use various columns on a group of radio buttons created using a GroupBox or a panel controls, you can visually position each radio button on the container. Programmatically, you can also change the Left and Top values of each control as desired. By default, a radio button is configured so that the label would be positioned on the right side of the rounded box. This is controlled by the Alignment property. If you want the label on the left side, set the radio button’s Alignment property accordingly. The possible values are: taRightJustify and taLeftJustify.
Practical Learning: Creating Radio Buttons 1.
Start a new project with the default form
2.
To save it, on the Standard toolbar, click the Save All button.
3.
Click the Create New Folder button. Type Operations and press Enter twice
4.
Click Unit1 to select it. Type Main and press Enter
5.
Type Operations as the name of the project and press Enter
6.
On the Standard tab of the Component Palette, click the Panel control
7.
On the form, click and drag from the top left section to the middle center section.
8.
On the Object Inspector, click Caption and press Delete to delete the caption of the panel.
9.
On the Component Palette, double-click the RadioButton control
.
10. On the Object Inspector, click Caption and type &Addition
590
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
11. Click Name and type rdoAddition 12. Move the new radio button to the top section of and inside the panel. 13. On the Component Palette, click the Radio Button 14. Click inside the panel 15. Click Name and type rdoSubtraction 16. Click Caption and type &Subtraction 17. In the same way, add another RadioButton control to the panel. Set its Caption to &Multiplication and its Name to rdoMultiplication 18. Add one more RadioButton Caption to &Division
to the panel. Set its Name to rdoDivision and its
19. Move the panel to the right section of the form. 20. Notice that it moves with its “children”. You will redesign the form as follows:
21. Add another panel ------------, and Result:
with four labels captioned Number &1:, Number &2:, ------
22. Add three Edit controls inside the panel and on the right side of the corresponding labels. These edit boxes will be named edtNumber1, edtNumber2, and edtResult 23. Add another panel
to an empty area of the form
24. While the new and last panel is selected, click the Align field. Click its arrow and select alBottom
Copyright © 2003 FunctionX, Inc.
591
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
24.1.3 Radio Buttons Methods The primary method you will use with a radio button is the TRadioButton constructor. This member function is mostly used to dynamically create a radio button. Since all objects in C++ Builder must be called using pointers, to create a radio button, declare a pointer to TRadioButton and use the new operator to specify the control that owns the radio button. A bad idea would be to assign it to the form. As we have reviewed, you should never (or hardly) position a radio button or a group of radio buttons on a form. Therefore, before creating a radio button, you should have a container on your form. You can add such a host at design time or at run time. If you want to programmatically create radio buttons, you must know that each button will be created as its own object, besides their container.
24.1.4 Radio Button Events A radio button is just a special form of button. Its most used event is OnClick. After creating each radio button as a control, you can use its OnClick event to configure its functionality.
Practical Learning: Implementing Radio Button Events 1.
On the form double-click the Addition radio button to access its OnClick event
2.
Implement it as follows:
//--------------------------------------------------------------------------void __fastcall TfrmOperations::rdoAdditionClick(TObject *Sender) { double Num1; double Num2; double Result; if( edtNumber1->Text == "" ) Num1 = 0; else Num1 = StrToFloat(edtNumber1->Text); if( edtNumber2->Text == "" ) Num2 = 0; else Num2 = StrToFloat(edtNumber2->Text); Result = Num1 + Num2; edtResult->Text = Result; } //---------------------------------------------------------------------------
3.
Click the arrow on the top section of the Object Inspector and select rdoSubtraction
4.
Click the Events tab
5.
Double-click the empty box on the right side of OnClick
6.
Implement the function just like the Click event of the addition but change the operation as follows: Result = Num1 - Num2;
7. 592
On the top combo box of the Object Inspector, select rdoMultiplication Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
8.
Double-click the box of the OnClick field
9.
Implement the Click event like the previous two but change the operation as follows: Result = Num1 * Num2;
10. In the same way, access the OnClick event for the rdoDivision control and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmOperations::rdoDivisionClick(TObject *Sender) { double Num1; double Num2; double Result; if( edtNumber1->Text == "" ) Num1 = 0; else Num1 = StrToFloat(edtNumber1->Text); if( edtNumber2->Text == "" ) { MessageBox(NULL, "The Number 2 Edit Box should not be empty", "Algebraic Operations", MB_OK); edtNumber2->SetFocus(); } else if( edtNumber2->Text == 0 ) { MessageBox(0, "Cannot divide by zero", "Algebraic Operations", MB_OK); edtNumber2->SetFocus(); } else { Num2 = StrToFloat(edtNumber2->Text); Result = Num1 / Num2; edtResult->Text = Result; } } //---------------------------------------------------------------------------
11. To display the form, press F12 12. Double-click the panel on the bottom section of the form to access its OnClick event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmOperations::Panel3Click(Tobject *Sender) { Close(); } //---------------------------------------------------------------------------
13. To test the form, press F9. 14. After using it, close the form and save your project
24.2 The Radio Group Control
Copyright © 2003 FunctionX, Inc.
593
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
24.2.1 Introduction There are two main ways you can create a group of radio buttons. We saw above that you can place them either on a group box or on a panel. Alternatively, the VCL provides a control specially made to create radio buttons: the RadioGroup control. To use it, first select it from the Standard tab of the Component Palette and position it on a form or other container. A RadioGroup control is a special control used to create a group of radio buttons.
Practical Learning: Using a Radio Group Control 1.
Open the BodyTag1 application you created previously. If you do not have it, open the BodyTag1 project from the exercises that accompany this book. Display the form
2.
On the form, delete the label marked Color. Change the Text property of the bottom Edit to
3.
Enlarge the form and select all controls (Ctrl + A). Then move all controls to the right side as follows:
4.
Save All
24.2.2 Characteristics of the RadioGroup Control The RadioGroup control has its own mechanism of creating and managing its radio buttons. The radio buttons are created as strings. Therefore, after placing and drawing a RadioGroup control on a form or a frame, you can open its String List Editor from its
594
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
Items property. You can then type a label for each radio button on its own line. Here is an example:
Figure 81: The String List Editor Once you click OK, the radio buttons would be created and proportionately added to the RadioGroup. Although the strings appear as radio buttons, they only act like them with full functionality but they are not controls to their full extent. This means that these radio buttons do not have a name or any Windows control property, method, of events on their own. Once the list of strings has been created, the container will take care of positioning the radio buttons so they can fit inside the host. If you type too many or too long strings for the host, resize the container. The list of radio buttons of a RagioGroup control is a TStrings type represented on the TRadioGroup class as the property. Therefore, you can also programmatically create the list of radio buttons using the TStrings::Add() method as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { grpEmplStatus->Items->Add("Part-Time"); grpEmplStatus->Items->Add("Full-Time"); grpEmplStatus->Items->Add("Contractor"); grpEmplStatus->Items->Add("Consultant"); } //---------------------------------------------------------------------------
Like the other radio buttons, one created using the RadioGroup control can also be selected. Since these radio buttons are stored in a string list, you can set which radio button is selected by changing the integer value of the ItemIndex property. Since no radio button is selected by default, its value is –1. The items in the string are counted starting at 0, then 1, and so on. For example, to set the second radio button as checked, set the ItemIndex property of the RadioGroup control to 1. This property can be changed Copyright © 2003 FunctionX, Inc.
595
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
only after the list is created. If you create the list programmatically, you can also decide which radio button would be selected when the list shows up. This is done by assigning a short integer value to the ItemIndex property. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { grpEmplStatus->Items->Add("Part-Time"); grpEmplStatus->Items->Add("Full-Time"); grpEmplStatus->Items->Add("Contractor"); grpEmplStatus->Items->Add("Consultant"); grpEmplStatus->ItemIndex = 2; } //---------------------------------------------------------------------------
This value should less than the total number of radio buttons. For example, if the RadioGroup control contains 4 strings, the ItemIndex value should be less than 4; in this case the value 0, 1, 2, or 3 would select a radio button, a –1 value would remove the dot from any radio button. To distribute the radio buttons on different columns, you can use the Columns property on the Object Inspector. You can also do it at runtime: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { grpEmplStatus->Items->Add("Part-Time"); grpEmplStatus->Items->Add("Full-Time"); grpEmplStatus->Items->Add("Contractor"); grpEmplStatus->Items->Add("Consultant"); grpEmplStatus->ItemIndex = 2; grpEmplStatus->Columns = 2; } //---------------------------------------------------------------------------
Practical Learning: Implementing a Radio Group Control
596
1.
From the Standard tab of the Component Palette, click the RadioGroup control and click the empty area on the left side of the form
2.
On the Object Inspector, double-click (TStrings) from the Items field
3.
Type Background and press Enter. Complete the list with Text, Link, Active Link, and Visited Link
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
4.
Click OK
5.
While the RadioGroup control is still selected, on the Object Inspector, change its properties as follows: Caption = Body Attributes ItemIndex = 0 Name = grpBodyAttributes TabOrder = 0
6.
Add an Edit control to the right side of each item of the RadioGroup control. From top to bottom, Name the Edit controls edtBackground, edtText, edtLink, edtALink, and edtVLink respectively
7.
Set their Text value to the following respective values: #FFFFFF, #000000, #0000FF, #008000, and #FF0000. Resize and move the controls as necessary:
Copyright © 2003 FunctionX, Inc.
597
Chapter 24: Selection-Based Controls
8.
Borland C++ Builder Programming
and From the Standard tab of the Component Palette, click the Memo control click below the Body Attributes RadioGroup control. Using the Lines property, delete its contents. Change its other properties as follows: Color = clWhite Name = mmoPreview ReadOnly = true
9.
From the Standard tab of the Component Palette, click Edit and click on the existing Memo control. Change the properties of the new Edit control as follows: Name = edtPreviewText ReadOnly = true Text = Body tag formatter and colorizer
10. Add another Edit control inside the Memo with the following properties: Font -> Color = clBlue Name = edtPreviewLink ReadOnly = true Text = Sample text as link 11. Add another Edit control inside the Memo with the following properties Font -> Color = clGreen Name = edtPreviewALink ReadOnly = true Text = Active link that is being visited 12. Add another Edit control inside the Memo with the following properties: Font -> Color = clRed Name = edtPreviewVLink ReadOnly = true
598
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
Text = This link has already been visited
13. Set the BorderStyle property of all edit controls inside of the memo to bsNone 14. Add a BitBtn control using the Copy bitmap as Glyph with a Caption of Cop&y and change its Name to btnCopy 15. Move and position the controls as you see fit 16. Double-click the Copy button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnCopyClick(TObject *Sender) { edtBody->SelectAll(); edtBody->CopyToClipboard(); } //---------------------------------------------------------------------------
17. We will need some global variables that can hold the hexadecimal values of colors. Therefore, in the private section of the TfrmMain class, declare the following variables: AnsiString HexBG, HexText, HexLink, HexALink, HexVLink;
18. Whenever the user decides to use a scroll bar, the Preview panel, the Body Attribute group box, and all of the Edit controls are concerned. Therefore, instead of performing the same operation in the OnChange event of each control, we will use a central function that can take care of these. Then we can simply call this function whenever the position of a scroll bar has changed In the Class Explorer, right-click TForm1 and click New Method... In the Method Name, type ApplyColor 19. Set the Function Result as void and click the __fastcall check box. Click OK and implement the function as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::ApplyColor()
Copyright © 2003 FunctionX, Inc.
599
Chapter 24: Selection-Based Controls
{
Borland C++ Builder Programming
//TODO: Add your source code here // Retrieve the current hexadecimal colors from their Edit controls HexBG = edtBackground->Text; HexText = edtText->Text; HexLink = edtLink->Text; HexALink = edtALink->Text; HexVLink = edtVLink->Text; // Get the integral position of each ScrollBar control int Red = 255 - scrRed->Position; int Green = 255 - scrGreen->Position; int Blue = 255 - scrBlue->Position; // Display the position of each ScrollBar // in its corresponding Edit control edtNumRed->Text = Red; edtNumGreen->Text = Green; edtNumBlue->Text = Blue; // Get the hexadecimal equivalent of each ScrollBar control AnsiString HexRed = IntToHex(Red, 2); AnsiString HexGreen = IntToHex(Green, 2); AnsiString HexBlue = IntToHex(Blue, 2); // Display the hexadecimal value in the appropriate Edit controls edtHexaRed->Text = HexRed; edtHexaGreen->Text = HexGreen; edtHexaBlue->Text = HexBlue; // Change the color of the Preview panel // according to the values of the ScrollBar positions pnlPreview->Color = TColor( RGB(Red, Green, Blue) ); // Get the position of each ScrollBar control // Create a hexadecimal color starting with # // And display the color in the appropriate Edit control switch( grpBodyAttributes->ItemIndex ) { case 0: edtBackground->Text = "#" + IntToHex(255 - scrRed->Position, 2) + IntToHex(255 - scrGreen->Position, 2) + IntToHex(255 - scrBlue->Position, 2); mmoPreview->Color = pnlPreview->Color; edtPreviewText->Color = pnlPreview->Color; edtPreviewLink->Color = pnlPreview->Color; edtPreviewALink->Color = pnlPreview->Color; edtPreviewVLink->Color = pnlPreview->Color; HexBG = edtBackground->Text; break; case 1: edtText->Text = "#" + IntToHex(255 - scrRed->Position, 2) + IntToHex(255 - scrGreen->Position, 2) + IntToHex(255 - scrBlue->Position, 2); edtPreviewText->Font->Color = TColor( RGB(Red, Green, Blue) ); HexText = edtText->Text; break;
600
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
case 2: edtLink->Text = "#" + IntToHex(255 - scrRed->Position, 2) + IntToHex(255 - scrGreen->Position, 2) + IntToHex(255 - scrBlue->Position, 2); edtPreviewLink->Font->Color = TColor( RGB(Red, Green, Blue) ); HexLink = edtLink->Text; break; case 3: edtALink->Text = "#" + IntToHex(255 - scrRed->Position, 2) + IntToHex(255 - scrGreen->Position, 2) + IntToHex(255 - scrBlue->Position, 2); edtPreviewALink->Font->Color = TColor( RGB(Red, Green, Blue) ); HexALink = edtALink->Text; break; case 4: edtVLink->Text = "#" + IntToHex(255 - scrRed->Position, 2) + IntToHex(255 - scrGreen->Position, 2) + IntToHex(255 - scrBlue->Position, 2); edtPreviewVLink->Font->Color = TColor( RGB(Red, Green, Blue) ); HexVLink = edtVLink->Text; break; } // Update the contents of the bottom Edit control edtBody->Text = "";
} //---------------------------------------------------------------------------
20. Change the contents of the OnChange event for each ScrollBar control as follows: //--------------------------------------------------------------------------void __fastcall TForm1::scrRedChange(TObject *Sender) { ApplyColor(); } //--------------------------------------------------------------------------void __fastcall TForm1::scrGreenChange(TObject *Sender) { ApplyColor(); } //--------------------------------------------------------------------------void __fastcall TForm1::scrBlueChange(TObject *Sender) { ApplyColor();
Copyright © 2003 FunctionX, Inc.
601
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
} //---------------------------------------------------------------------------
21. Test the application 22. After using the form, close it and save your project 23. When the user clicks a button from the Body Attributes RadioGroup control, we need to display its color on the Preview panel. To do this, we first need to find out what radio button was clicked. Once we know this button, we will retrieve the color of its font from the Preview Memo, translate that color into red, green, and blue values, and then use those values to automatically update the scroll bars and the edit boxes. While we are at it, we also need to update the corresponding Edit control in the RadioGroup box. Since this functionality will be used by all radio buttons in the group, we will use a global function to which we can pass two TEdit objects. One of the TEdit arguments will represent the Edit control from the Preview Memo whose radio button was clicked. The other TEdit argument will be the result produced after translating the color into integers In the private section of the TForm1 class, declare a function as follows: void __fastcall ClickOption(TColor Clr, AnsiString Result);
24. Implement the function as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::ClickOption(TColor Clr, AnsiString Result) { long lngColor; int Red, Green, Blue; pnlPreview->Color = Clr; lngColor = Clr; Red = lngColor % 256; Green = (lngColor / 256) % 256; Blue = lngColor / 65536; scrRed->Position = Red; scrGreen->Position = Green; scrBlue->Position = Blue; edtRed->Text = Red; edtGreen->Text = Green; edtBlue->Text = Blue; edtHexaRed->Text = IntToHex(Red, 2); edtHexaGreen->Text = IntToHex(Green, 2); edtHexaBlue->Text = IntToHex(Blue, 2); Result = "#" + IntToHex(Red, 2) + IntToHex(Green, 2) + IntToHex(Blue, 2); } //---------------------------------------------------------------------------
25. Now, we need to call this function whenever a radio button is clicked On the form, double-click the Body Attributes group box and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::grpBodyAttributesClick(TObject *Sender) { // If the user clicks a button from the RadioGroup control // find out what button the user clicked
602
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
// set color of the panel to that of the radio button that was clicked TColor BGColor = mmoPreview->Color; mmoPreview->Color = BGColor; edtPreviewText->Color = BGColor; edtPreviewLink->Color = BGColor; edtPreviewALink->Color = BGColor; edtPreviewVLink->Color = BGColor; switch(grpBodyAttributes->ItemIndex) { case 0: ClickOption(mmoPreview->Color, edtBackground->Text); HexBG = edtBackground->Text; break; case 1: ClickOption(edtPreviewText->Font->Color, edtText->Text); HexText = edtText->Text; break; case 2: ClickOption(edtPreviewLink->Font->Color, edtLink->Text); HexLink = edtLink->Text; break; case 3: ClickOption(edtPreviewALink->Font->Color, edtALink->Text); HexALink = edtALink->Text; break; case 4: ClickOption(edtPreviewVLink->Font->Color, edtVLink->Text); HexVLink = edtVLink->Text; break; }
} //---------------------------------------------------------------------------
26. Test your application
27. After using it, close and return to Bcb 28. Save All Copyright © 2003 FunctionX, Inc.
603
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
24.2.3 RadioGroup Methods The fastest and most convenient way to dynamically create a group of radio buttons consists of using a RadioGroup control. If you do not have this control already, either by adding it at design time or by creating anyhow, you can use the new operator to assign an instance of the TRadioGroup to the owner of this control. The compiler needs to know the owner that would have the responsibility of cleaning the RadioGroup once it is not needed anymore. This time, the owner should be the form, unless the RadioGroup control will be hosted by another container. Also, specify the parent of the control. If you will need the radio buttons in just one function or event, you can create the RadioGroup control in that function. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnCreateGroupClick(TObject *Sender) { TRadioGroup* Group = new TRadioGroup(Form1); Group->Parent = Form1; } //---------------------------------------------------------------------------
If you are planning to use the control in more than one location, declare a TRadioGroup object in the private or public sections of the form or unit that would use it: private: // User declarations TRadioGroup *grpMaritalStatus; public: // User declarations __fastcall TForm1(TComponent* Owner);
To create the list that represents the radio buttons, use the TStrings::Items property of the RadioGroup control. You can do this in the function where you create the control locally: //--------------------------------------------------------------------------void __fastcall TForm1::btnCreateGroupClick(TObject *Sender) { TRadioGroup* Group = new TRadioGroup(Form1); Group->Parent = Form1; Group->Caption = "Membership"; Group->Items->Add("Senior"); Group->Items->Add("Adult"); Group->Items->Add("Tean"); Group->Items->Add("Child"); Group->Columns = 2; Group->Left = 8; Group->Top = 20; } //---------------------------------------------------------------------------
If the RadioGroup was created globally, use the appropriate function or event to initialize it: //--------------------------------------------------------------------------void __fastcall TForm1::FormDblClick(TObject *Sender) {
604
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
grpMaritalStatus = new TRadioGroup(Form1); grpMaritalStatus->Parent = Form1; grpMaritalStatus->Items->Add("Single"); grpMaritalStatus->Items->Add("Married"); grpMaritalStatus->Items->Add("Divorced"); grpMaritalStatus->Items->Add("Widow"); grpMaritalStatus->Left = 220; grpMaritalStatus->Top = 20; grpMaritalStatus->Height = 100; grpMaritalStatus->Width = 124; grpMaritalStatus->Caption = "Marital Status"; } //---------------------------------------------------------------------------
24.2.4 RadioGroup Messages and Events Like most other visual controls, the RadioGroup fires the OnClick event when it is clicked. This event makes all radio buttons of the group to be considered as one. Therefore, when the user clicks this control, you can simply access the ItemIndex property to find out what button is checked.
24.3 Check Boxes 24.3.1 Introduction A check box is a Windows control that allows the user to set or change the value of an item as true or false. Although it can appear by itself, a check box sometimes comes in a group with others, allowing the user to select as many choices as are available. Depending on the application, a little square appears. The user makes a selection by clicking in the square which toggles a check mark . Toggling means that if the square were empty , after clicking it, a check mark would appear in it . Otherwise, the check mark would be removed. Like a radio button, a check box is usually accompanied by a label to indicate what the check control is used for. From the user’s standpoint, a check box is selected when its check mark is set; and the item is not selected when its square is empty. From the developer standpoint, a check mark has two (Boolean) values: true or false (or TRUE or FALSE, or True or False). When a check mark is selected, its value is true. Otherwise, its value is false. A check mark is a special button and can be programmed as a regular control. Depending on the application’s needs, you can display or hide it, enable or disable it as necessary. You can adjust the control’s behavior depending on other controls on the same form, the same application, or external factors. Check boxes provide “non-exclusive” choice, which means that each check box can behave as independent as needed with regards to the other check boxes of the same container. If you are creating just one check box, you can place it where you want on the form. If you are creating more than one check box that are addressing the same issue, you should include them in a rectangular container so their belonging to the same group would be obvious to the user. The group can be hosted by a GroupBox, a bevel, a RadioGroup, or a Panel controls. If you place the controls in a Bevel or a RadioGroup, Copyright © 2003 FunctionX, Inc.
605
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
since these two are true containers for other controls, you will not be able to move all controls as one object during design.
Practical Learning: Introducing Check Boxes 1.
Start a new project with its default form
2.
Save it in a new folder named FastFood1
3.
Save the unit as Exercise and save the project as FastFood
4.
Open Image Editor. Design a 32 x 32 and 16 x 16 icon as follows:
5.
Save it as FastFood in the folder of the current project
6.
Create a new 16 x 16 size bitmap and design it as follows:
7.
Save it as Ingredients in the folder of the current project
8.
In the project options (Project -> Options), access the Application tab. Set the Title to Fast Food Corner and sect the icon to the above FastFood icon
9.
Change the form’s following properties: BorderStyle: bsDialog Caption: Fast Food Restaurant – Customer Menu Name: frmMain Position: poScreenCenter
10. Design the dialog box as follows: 606
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
Control Group Box Radio Button Radio Button
Name Don’t Care rdoBun rdoRoll
Caption Bread B&un &Roll
Group Box Radio Button
Don’t Care rdoBeefPatty
Meat Bee&f Patty
Radio Button
rdoGrilledChicken
Radio Button Group Box
rdoChickedBreast Don’t Care
&Grilled Chicken C&hicken Breast Ingredients
Other Properties Alignment: taLeftJustify Alignment: taLeftJustify Checked: true Alignment: taLeftJustify Checked: true Alignment: taLeftJustify Alignment: taLeftJustify
11. Save All
24.3.2 Characteristics of Check Boxes If you are planning to have just one check box, from the Standard tab of the Component and click on the desired section of the form or Palette, click the CheckBox control container. If you want to use more than one check box, you should first place a group control on your form, then add the desired CheckBox controls. The most obvious property of the check box is its state as being checked or not. By default, a check box is not checked (it is empty). At design time, you can make sure that a check box appears checked or not by changing the Boolean value of the Checked property in the Object Inspector. When you set this property, it would be updated automatically on the form. You or the user can also control this property at runtime. To change the Checked property programmatically, simply assign a true or false value to the Checked proeprty. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { CheckBox1->Checked = True; }
Copyright © 2003 FunctionX, Inc.
607
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
//---------------------------------------------------------------------------
When a check box is clicked, its Checked property has a value of true. Otherwise, the value is false. Since the Checked property is a Boolean value, you can toggle its state based on an intermediary action from the program, the user, or the computer: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { CheckBox1->Checked = !CheckBox1->Checked; } //---------------------------------------------------------------------------
Another important property of a check box is its title, which is controlled by the Caption property. This can easily be set at design or runtime. The position of the caption is controlled by the Alignment property. By default, the control's caption is aligned to the right side of the check box:
Figure 82: Right Aligned
Figure 83: Left Aligned
To change the caption alignment of a check box, use the Alignment property of the Object Inspector. The values are taRightJustify for the right alignment and the taLeftJustify. Fundamentally, a check box can have only one of two states: checked or unchecked. When a check box’ Checked property is dependent of other controls or actions, sometimes, you cannot categorically set it to Checked or not Checked. Imagine that, in a certain company, for an employee to qualify for stock options, she must be have a fulltime status and must have been in the company for at least two years. If an (important) employee fulfills one of these requirements but not the other requirements, you can gray out the Stock Options check box. since the control would not be completely checked, you can show it as half checked:
608
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
This property is controlled by the AllowGrayed property. In the Object Inspector, change the (Boolean) AllowGrayed property of the desired check box to true or false (the default). Programmatically, to set this property, assign it a true value (because otherwise the false value is set by default). At design time or when the user is interacting with your application, you can control the display of a check box using one of three states. Unlike being checked or unchecked, like the AllowGrayed property, you can use an intermediary state that would help you and/or the user know that the control’s condition cannot be definitely decided. This is set using the State property. This property, based on the TCheckBoxState enumerator, allows you to set a check box as unchecked (the default) by assigning the cbUnchecked value. To definitely check it, set this property to cbChecked. As a 3rd alternative, you can assign it a cbGrayed value.
Practical Learning: Creating Check Boxes 1.
On the Standard toolbar, click the New button . In the New Items dialog box, click Dialogs and double-click Standard Dialog (Vertical)
2.
Save it as Ingredients
3.
Change its Name to dlgIngredients. Change its Caption to Ingredients Selection Here is how you will design it:
4.
From the Standard tab of the Component Palette, click the CheckBox button and click inside the existing bevel on the form. Change its proeprties as follows: Alignment: taLeftJustify Caption: &Lettuce Checked: true
Copyright © 2003 FunctionX, Inc.
609
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
Name: chkLettuce 5.
Add another check box under the first one and set its properties as follows: Alignment: taLeftJustify Caption: &Onion Name: chkOnion
6.
Add another check box under the previous one and set its properties as follows: Alignment: taLeftJustify Caption: &Tomato Checked: true Name: chkTomato
7.
Add another check box under the previous one and set its properties as follows: Alignment: taLeftJustify Caption: &Pickles Name: chkPickles
8.
Display the main form (View -> Forms… -> frmMain -> OK)
9.
In the top section inside the Ingredients group box, add a check box
10. Set its properties as follows: Alignment: taLeftJustify AllowGrayed: true Caption: &Regulars Name: chkRegulars 11. Complete the design of the dialog box as follows:
610
Control CheckBox
Name chkSweetener
Caption or Text &Sweetener
CheckBox CheckBox GroupBox BitBtn RadioButton
chkCheese chkBacon Don’t Care btnIngredients rdoMayonnaise
Ch&eese B&acon Options &Ingredients &Mayonnaise
RadioButton RadioButton
rdoKetchup chkMustard
&Ketchup Mus&tard
Other Property Alignment: taLeftJustify Checked: true Alignment: taLeftJustify Alignment: taLeftJustify Glyph: Ingredients Alignment: taLeftJustify Checked: true Alignment: taLeftJustify Alignment: taLeftJustify
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Bevel BitBtn Label Edit
Chapter 24: Selection-Based Controls
Shape: bsFrame Kind: bkClose edtTotalPrice
Total Price: $2.35
12. On the main form, double-click the Ingredients button and implement its OnClick() event as follows: //--------------------------------------------------------------------------#include #pragma hdrstop #include "Exercise.h" #include "Ingredients.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TfrmMain *frmMain; //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------void __fastcall TfrmMain::btnIngredientsClick(TObject *Sender) { dlgIngredients->ShowModal(); } //---------------------------------------------------------------------------
13. Test the application. Close it and return to Bcb 14. Save All
24.3.3 Check Box Methods The check box control has only its constructor and its destructor as methods. The constructor allows you to dynamically create the control. To do this, use the new operator to assign a TCheckBox object to the named instance of the control. You must also specify what control owns the check box. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TCheckBox* Checker = new TCheckBox(Form1); Checker->Parent = Form1; } //---------------------------------------------------------------------------
If the check box will be hosted by a container other than the form, specify this as the parent: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TCheckBox* Checker = new TCheckBox(Form1);
Copyright © 2003 FunctionX, Inc.
611
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
Checker->Parent = GroupBox1; } //---------------------------------------------------------------------------
If you do not have or cannot get a container at design time, you can also dynamically create one that would host the dynamic check boxes. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TGroupBox* Group = new TGroupBox(Form1); Group->Parent = Form1; Group->Left = 16; Group->Top = 32; Group->Caption = "Preferred Sports"; TCheckBox* chkFootball = new TCheckBox(Form1); chkFootball->Parent = Group; chkFootball->Left = 16; chkFootball->Top = 16; chkFootball->Alignment = taRightJustify; chkFootball->Caption = "Football"; chkFootball->Checked = True; TCheckBox* chkHandball = new TCheckBox(Form1); chkHandball->Parent = Group; chkHandball->Left = 16; chkHandball->Top = 36; chkHandball->Caption = "Handball"; } //---------------------------------------------------------------------------
If you want to know the alignment applied on a check box, call the GetControlsAlignment() method. Its syntax is: TAlignment __fastcall GetControlsAlignment(void);
Most of the other methods a check box uses derive from its ancestors the TControl and the TWinControl classes.
24.3.4 Check Box Events As far as Microsoft Windows is concerned, a check box is just a modified button. This allows it to use the common characteristics of command buttons and radio controls. Based on this, when the user clicks a check box, an OnClick() event fires. All the other events derive from its ancestors the TControl and the TWinControl classes.
Practical Learning: Implementing Check Boxes 1.
612
If the user completely removes the check mark on the Sweetener check box, this suggests that the customer does not want this item on the sandwich. Consequently, the radio buttons in the Options group should be disabled. When the user clicks a check box, whether the control was already checked or not, the OnClick() event fires. Therefore, in this case, the first thing you should do it to check the state of the
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
check button and then implement a behavior accordingly. Display the main form and double-click the Sweetener check control. 2.
Implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::chkSweetenerClick(TObject *Sender) { // If the sweetener check box is not checked if( chkSweetener->Checked == False ) { // Disable the Options radio buttons rdoMayonnaise->Enabled = False; rdoKetchup->Enabled = False; rdoMustard->Enabled = False; } else { // Otherwise, enable the Options radio buttons rdoMayonnaise->Enabled = True; rdoKetchup->Enabled = True; rdoMustard->Enabled = True; } } //---------------------------------------------------------------------------
3.
To keep track of the user’s selection of ingredients, we will use four global variables that each represents a check box from the Ingredients dialog. In the header file of the main form, declare four private Boolean variables as follows: private: Boolean bLettuce, bOnion, bTomato, bPickles;
4.
In the constructor of the main form, initialize the variables with a false value each: //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { bLettuce = False; bOnion = False; bTomato = False; bPickles = False; } //---------------------------------------------------------------------------
5.
When the user clicks the Regulars check box, we will update the global variables appropriately. On the form, double-click the Regulars check box and implement its OnClick() event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::chkRegularsClick(TObject *Sender) { // If the Regulars check box is completely unchecked if( chkRegulars->State == cbUnchecked ) { // Set the global Boolean variables to false each bLettuce = False; bOnion = False; bTomato = False; bPickles = False;
Copyright © 2003 FunctionX, Inc.
613
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
} // If the Regulars check box is completely checked else if( chkRegulars->State == cbChecked ) { // Set the global Boolean variables to true each bLettuce = True; bOnion = True; bTomato = True; bPickles = True; } // Otherwise, refer to the state of the check boxes // from the Ingredients dialog box. Whatever they are else { bLettuce = dlgIngredients->chkLettuce->Checked; bOnion = dlgIngredients->chkOnion->Checked; bTomato = dlgIngredients->chkTomato->Checked; bPickles = dlgIngredients->chkPickles->Checked; } } //---------------------------------------------------------------------------
6.
When the Regulars check box is not checked at all, no basic ingredient is selected. When this control is checked, all ingredients will be added to the sandwich. If at least one ingredient is selected and at least one ingredient is not selected, the Regulars check box should appear grayed. When the user clicks the Ingredients button, we will display the Ingredients Selection dialog box and allow the user to select the ingredients. If the user clicks OK to dismiss the dialog box, we will aply the above scenario. On the form, double-click the Ingredients button and change its OnClick event as follws: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnIngredientsClick(TObject *Sender) { // Before displaying the dialog box, synchronize its // check boxes with the global variables dlgIngredients->chkLettuce->Checked = bLettuce; dlgIngredients->chkOnion->Checked = bOnion; dlgIngredients->chkTomato->Checked = bTomato; dlgIngredients->chkPickles->Checked = bPickles; // Call the Ingregients Selection dialog box for the user // If the user clicked OK when closing the dialog box, dlgIngredients->ShowModal(); if( dlgIngredients->ModalResult == mrOk ) { // If the user clicks OK, update the global values of the check boxes bLettuce = dlgIngredients->chkLettuce->Checked; bOnion = dlgIngredients->chkOnion->Checked; bTomato = dlgIngredients->chkTomato->Checked; bPickles = dlgIngredients->chkPickles->Checked; // if no check box is checked if( (bLettuce == False) && (bOnion == False) && (bTomato == False) && (bPickles == False) )
614
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
chkRegulars->State = cbUnchecked; // then uncheck this one // if all check boxes are checked else if( (bLettuce == True) && (bOnion == True) && (bTomato == True) && (bPickles == True) ) chkRegulars->State = cbChecked; // then check this one else // if at least one check box is checked and at one is unchecked chkRegulars->State = cbGrayed; // then set this one as indeterminate
} // If the user clicked Cancel, don't do nothing
} //---------------------------------------------------------------------------
7.
Now we can calculate the price of the sandwich. In the header file of the main form, declare a private member function of type void __fastcall named EvaluatePrice
Figure 84: Add a Method - EvaluatePrice 8.
Implement the method as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::EvaluatePrice() { //TODO: Add your source code here double PriceBread, PriceMeat, PriceCheese, PriceBacon, TotalPrice; // The price of bread is $0.85 PriceBread = 0.85; // To get the price of the meat, find out what button
Copyright © 2003 FunctionX, Inc.
615
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
// is selected in the Meat group box if( rdoBeefPatty->Checked == True ) // The beef patty is $1.50 PriceMeat = 1.50; else if( rdoGrilledChicken->Checked == True || rdoChickenBreast->Checked == True ) // Cheicken breast and grilled chicken are $1.80 each PriceMeat = 1.80; else // Just in case PriceMeat = 0.00; // There is no extra cost for the Regular ingredients // and nothing to add for the sweetener // On the other hand, // if the customer wants cheese, $0.30 is added to the price if( chkCheese->Checked == True ) PriceCheese = 0.30; else // otherwise, the customer don't want no cheese PriceCheese = 0.00; // If the customer wants bacon, $0.45 is added to the price if( chkBacon->Checked == True ) PriceBacon = 0.45; else PriceBacon = 0.00; // Now, we can calculte the total price TotalPrice = PriceBread + PriceMeat + PriceCheese + PriceBacon; edtTotalPrice->Text = FloatToStrF(TotalPrice, ffCurrency, 8, 2);
} //---------------------------------------------------------------------------
9.
To update the price and its display live whenever the user makes a new selection, double-click the following controls: Beef Patty, Grilled Chicken, Chicken Breast, Cheese, and Bacon
10. In the body of each, simply call the above EvaluatePrice() method: //--------------------------------------------------------------------------void __fastcall TfrmMain::rdoBeefPattyClick(TObject *Sender) { EvaluatePrice(); } //--------------------------------------------------------------------------void __fastcall TfrmMain::rdoGrilledChickenClick(TObject *Sender) { EvaluatePrice(); } //--------------------------------------------------------------------------void __fastcall TfrmMain::rdoChickedBreastClick(TObject *Sender) { EvaluatePrice(); } //--------------------------------------------------------------------------void __fastcall TfrmMain::chkCheeseClick(TObject *Sender) { EvaluatePrice(); } //---------------------------------------------------------------------------
616
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 24: Selection-Based Controls
void __fastcall TfrmMain::chkBaconClick(TObject *Sender) { EvaluatePrice(); } //---------------------------------------------------------------------------
11. Test the application:
Figure 85: The Fast Food Restaurant Application 12. Close it and return to Bcb 13. Heighten the form and add the following controls to the top section
Figure 86: The Fast Food Application Improvement Control Label Edit Label MaskEdit Copyright © 2003 FunctionX, Inc.
Name
Caption or Text Processed By:
Other Properties
edtClerk Order Date: edtOrderDate
EditMask: !99/99/0000;1;_ 617
Chapter 24: Selection-Based Controls
Borland C++ Builder Programming
14. Save All
618
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
Chapter 25: List-Based Controls 25.1 List Boxes 25.1.1 Overview A list box presents a list of items to choose from. Each item displays on a line. The user makes a selection by clicking in the list. Once clicked, the item or line on which the mouse landed becomes highlighted, indicating that it is the current choice. After an item has been selected, to make a different selection, the user would click another. She can also press the up and down arrow keys to navigate through the list and make a selection. A list box can also be configured to allow multiple selections.
Figure 87: The Date and Time Dialog Box One of the main reasons for using a list box is to display a list of items to the user. Sometimes the list would be very large. If the list is longer than the available space on the control, the control would be equipped with a scroll bar that allows the user to navigate up and down to access all items of the list. You will have the option of deciding how many items to display on the list.
25.1.2 List Box Creation A list box is immediately derived from the TCustomListBox class and that is where its properties are set. There are three main ways you add a list box to your form. from the Standard tab of the To create a list box, you can click the ListBox control Component Palette and click on the form. Once the list box is positioned on a container, you can move it by clicking and dragging the control. You can also resize it using any of the techniques we learned to add, position, move, and resize controls on a component. If the list will cover many items, design it so its height can display 8 items at a time. Otherwise, for a list of 8 or less items, use only the necessary height that would accommodate all of the items.
Copyright © 2003 FunctionX, Inc.
619
Chapter 25: List-Based Controls
Borland C++ Builder Programming
25.1.3 List Box Properties The most fundamental and the most obvious aspect of a list box is the list of items it displays. The list of items of this control is a TStrings object. If you know the list of items for the control, there are two easy ways you can create it. At design time, on the Object Inspector, you can click the Items property to reveal its ellipsis button on the right field . To create the list, click this button to call the String List Editor, type each item desired, on its own line by pressing Enter:
Figure 88: The String List Editor Dialog Box To programmatically add the items to a list box, use the TStrings::Add() method. If the list were empty, the new items would be added to the list. If the list already contained one or more items, the new items would be added, by default, to the end of the existing one(s). Here is an example of creating a list of items or adding new ones to a list: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { ListBox1->Items->Add("Chad"); ListBox1->Items->Add("Equatorial Guinea"); ListBox1->Items->Add("Egypt"); ListBox1->Items->Add("Madagascar"); } //---------------------------------------------------------------------------
Alternatively, to add an item or items to a list box, you can call the Win32 API’s SendMessage() function with the LP_ADDSTRING as the message. The wParam argument is not used and can be passed as 0. The string to add must be null-terminated and must be cast to LPARAM. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { SendMessage(ListBox1->Handle, LB_ADDSTRING, 0, (LPARAM)("Edem Kodjo")); } //---------------------------------------------------------------------------
620
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
As mentioned already, the user mostly interacts with a list box by selecting an item. At any time you can find out whether a particular item has been selected. This is done using the TCustomListBox::Selected Boolean property. When the Items of a list box appear in alphabetical order, the list is said to be sorted. By default, the items of a list box are not sorted. To arrange the list to ascending order, set the Sorted property’s value to true. As a Boolean data type, you can set the sorting feature programmatically as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { ListBox1->Sorted = True; } //---------------------------------------------------------------------------
If you create an unsorted list, then at one time get it sorted (for example, you can give the user the ability to sort the list, by clicking a button), the list would be sorted. If an item is added to the sorted list, the compiler would automatically insert to the right position following the order. If at another time you allow the user to “unsort” the list, the list would keep its current order. If another item is added when the list is not sorted, the item would be positioned at the end of the list. If you want the list to have its original state, you would have to reset it through code.If you change the Color property of the ListBox control, its whole background would appear with that color. If you want each item to have its own color, you would have to change the style of the list and properly configure it. When you create a list of items, they appear in one range of columns. If the number of items exceeds the height, a scrollbar would appear on the control. One alternative you can use is to span the list on more than one column. This is set using the Columns property. By default, the Columns value is set to 0, which means the items appear in one column. If you position the control on a form whose DockSite property is set to true with the control having a DragKind property dkDock and the DragMode property set to dmAutomatic, the user will be able to move the control and position it anywhere inside the form. By default, the user can select only one item in the list. This is controlled by both the ExtendedSelect and the MultiSelect properties. If you want the user to be able to select more than one item, you should set the MultiSelect property to true since the ExtendedSelect property would already be set to true. After the user has selected more than one item, you can use the TCustomList::SelCount to find out the number of items that the user would have selected. The list boxes are designed in three types of style. The default is the lbStandard in which case each item in the list is an AnsiString object. On the other hand, if you want each item of the list to display a graphic or a color, you must set the style to an owner draw. The lbOwnerDrawFixed allows you to set a desired height for each item of the list. This height is controlled through the ItemHeight property. You can set a different height for each item if you set the list style to lbOwnerDrawVariable. The ItemIndex is a property that identifies which item is selected in a list box. This is a property of highly particular interest. If you try performing an operation that requires that an item be selected and no item is selected, the program would throw an error. The items in a list box are counted from 0, then 1, etc. The first item, at position 0, can be identified as ListBox1->ItemIndex = 0. If no item is selected in the list, the Copyright © 2003 FunctionX, Inc.
621
Chapter 25: List-Based Controls
Borland C++ Builder Programming
TCustomListBox::ItemIndex has a value of –1.During introduction, we saw that if a List Box includes more items than the vertical size, the Height, allows displaying, the control would be equipped with a vertical scroll that would allow the user to move up and down and access the whole list. If at least one of the items is wider than the control can display, this might not be obvious to the user. You would have to provide the control with a horizontal scroll bar. The Win32 provides a SendMessage() function that can take care of this. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { SendMessage (ListBox1->Handle, LB_SETHORIZONTALEXTENT, 200, 0); } //---------------------------------------------------------------------------
The items of a list box are AnsiString objects created from the TStrings class. This allows you to use the properties of the TStrings class. We will study the TStrings class in another lesson.
25.1.4 List Box Methods The only methods a list box has on its own are its constructor and its destructor. The TListBox is typically used to dynamically create a list box. To create a list box inside of an event or a function, use the new operator. To do this, declare a pointer to a TListBox class. When declaring this object, the constructor of the TListBox needs to know the component that owns the control; this is usually the host or container of the list box. If the List Box will be placed on a form called Form1, you can create the List Box as follows: //--------------------------------------------------------------------------void __fastcall TForm1::CreateTheList() { TListBox* Liste = new TListBox(this); Liste->Parent = Form1; } //---------------------------------------------------------------------------
If the list box will be positioned on another type of container, such as a panel called Panel1, you can create it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::CreateTheList() { TListBox* Liste = new TListBox(this); Liste->Parent = Panel1; } //---------------------------------------------------------------------------
If you create the list box in a function, a method, or an event, the list will exist only inside of that function: you cannot manipulate it from another function, method, or event. If you want the control to be accessed by many functions declare a TListBox object in the header file of the form where the control will be hosted.. Here is an example: //--------------------------------------------------------------------------#ifndef Unit1H #define Unit1H //--------------------------------------------------------------------------#include
622
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
#include #include #include //--------------------------------------------------------------------------class TForm1 : public TForm { __published: // IDE-managed Components private: // User declarations TListBox *Listing; public: // User declarations __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------#endif
After declaring the control, you should initialize it in the form’s constructor. This allows you to specify the container that will host the control: //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Listing = new TListBox(Form1); Listing->Parent = Form1; Listing->Left = 120; Listing->Top = 80; } //---------------------------------------------------------------------------
When an application terminates, Borland C++ Builder takes care of destroying all of the objects that are part of the application. If the objects were created at design time, they are owned by the form. Since the form is owned by the application, the application would destroy the form including its hosted controls. If a control was placed on another container such as a panel, the panel is owned by the form and the form by the application. The destruction would still be smooth when the application executes. If you dynamically create a control, you must specify the owner of the control; otherwise the program would not compile. Since you will have specified the owner or container of the control, when the application exits, the compiler would destroy the form and its control, which would include the control you dynamically created. Therefore, you do not have to worry about the state of the dynamic controls when your application exits. This does not mean that your application would never produce a memory leak, but it would be highly unlikely. If you want to explicitly destroy your dynamically created object, the best place to destroy a global object is to use the delete operator on the OnDestroy event of the form: //--------------------------------------------------------------------------void __fastcall TForm1::FormDestroy(TObject *Sender) { delete Listing; Listing = NULL; } //---------------------------------------------------------------------------
If the list box was dynamically created inside of a function, you cannot access it outside of that function. If it were declared in the header file, you can use any appropriate Copyright © 2003 FunctionX, Inc.
623
Chapter 25: List-Based Controls
Borland C++ Builder Programming
method, event, or function to fill the list or to manipulate the control. For example, you can fill out the Listing control we declared earlier as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Listing->Items->Add("C++ Builder"); Listing->Items->Add("Delphi"); Listing->Items->Add("JBuilder"); Listing->Items->Add("Kylix"); } //---------------------------------------------------------------------------
Since the items of a list box are derived from the TStrings class, you can use this class’ methods to perform the desired operations on the control. The strings in a list box are counted starting at 0, then 1, and so on.To add an item at the end of the list, you can write code such as: //--------------------------------------------------------------------------void __fastcall TForm1::btnAddLesothoClick(TObject *Sender) { lstCountries->Items->Add("Lesotho"); } //--------------------------------------------------------------------------To insert an item in the 2nd position you can write code such as: //--------------------------------------------------------------------------void __fastcall TForm1::btnAddSecondClick(TObject *Sender) { lstCountries->Items->Insert(1, "Mauritania"); } //--------------------------------------------------------------------------To delete the 4th item of the list, you could write: //--------------------------------------------------------------------------void __fastcall TForm1::btnDelete4Click(TObject *Sender) { lstCountries->Items->Delete(3); } //---------------------------------------------------------------------------
If the list contains less than 4 items, the TStrings::Delete() method would ignore the operation. If you have an Edit control called edtCountry, you can let the user add its content to the list box when he double-clicks the edit box. The code could look as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Edit1DblClick(TObject *Sender) { lstCountries->Items->Add(edtCountry->Text); } //---------------------------------------------------------------------------
If the user is filling out a list box while typing additional items in an Edit control, the best and fastest way to add a new item is when the user presses Enter after typing the item. To implement this behavior, you should find out what key the user pressed. If the user presses Enter when the edit box has focus, you should make sure the edit box contains a string. The code could appear like this:
624
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
//--------------------------------------------------------------------------void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key) { if( Key == 13 ) // If the user presses the Enter key { if(Edit1->Text != "") // If Edit1 contains something { lstCountries->Items->Add(Edit1->Text); // Add the content of Edit1 to ListBox1 Edit1->Text = ""; // Reset Edit1 and make it empty } else // Since Edit1 is empty, display a message Panel1->Caption = "There is nothing to add"; } } //---------------------------------------------------------------------------
The TCustomListBox::Clear() method is used to clear the control of its whole content: //--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender) { lstCountries->Clear(); } //---------------------------------------------------------------------------
25.1.5 Operations on List Boxes The most regular operation users perform on a list box is to select items. This selection is performed by the user clicking one of the items. Using code, you can also select an item in the list. To select the 6th item of the list, you could write the code as: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { lstCountries->ItemIndex = 6; } //---------------------------------------------------------------------------
When performing your operations, sometimes you will want to find out if a particular item is selected. You can do this using the ordinal position of the item. For example, to find out if the 3rd item is selected, you can write: //--------------------------------------------------------------------------void __fastcall TForm1::Button7Click(TObject *Sender) { if( lstCountries->ItemIndex == 2) Panel1->Caption = "The third item is selected"; } //---------------------------------------------------------------------------
If an item is selected, you can allow the user to perform an operation on it. If a string (that is, any string) is not selected, most operations would fail and the program would crash. Therefore, usually you should make sure an item is selected. An item is selected when the value of the TCustomListBox::ItemIndex integer value is not negative. Operations include displaying the selected item of the list box to an edit box: Copyright © 2003 FunctionX, Inc.
625
Chapter 25: List-Based Controls
Borland C++ Builder Programming
//--------------------------------------------------------------------------void __fastcall TForm1::Button6Click(TObject *Sender) { // First find out whether an item is selected if(lstCountries->ItemIndex != -1) // Put the selected item in the edit Edit1->Text = lstCountries->Items->Strings[lstCountries->ItemIndex]; } //---------------------------------------------------------------------------
inserting a new string above the selected item: //--------------------------------------------------------------------------void __fastcall TForm1::Button6Click(TObject *Sender) { lstCountries->Items->Insert(lstCountries->ItemIndex, Edit1->Text); } //--------------------------------------------------------------------------or under the selected string: //--------------------------------------------------------------------------void __fastcall TForm1::Button6Click(TObject *Sender) { lstCountries->Items->Insert(lstCountries->ItemIndex + 1, Edit1->Text); } //---------------------------------------------------------------------------
Many times during your design, you will allow users to add or delete items to or from a list box. One way you will do this is to create a list of items originating from another list; this allows you to control the items the user can select from a list before continuing with the issue at hand. Borland C++ Builder ships with a dialog box completely configured to handle this transaction. To allow two list boxes to exchange data, you provide the appropriate buttons. If only one list will be used as the source, provide one button that could allow the user to select an item. It is also a good idea to allow the user to select and add all items at once. Also important is the ability for the user to remove mistakenly selected items. Over all, these kinds of list boxes handle their transactions through the use of four buttons: two are used to exchange one item at a time from one list to another, two to exchange all items from one list to another.
Practical Learning: Configuring List Boxes 1.
To start a new project, on the Standard toolbar, click the New button
2.
Make sure the Application icon is selected and click OK
3.
To add Borland C++ Builder’s built-in list box dialog, on the Standard toolbar, click the New button . In the Object Repository, click the Forms property page.
4.
Click the Dual List Box icon:
5.
Click OK.
6.
To remove the starting form, press F12 to display the Code Editor.
7.
Click the Unit1.cpp to select it.
8.
Right-click the Unit1.cpp tab and click Close Page
9.
When asked whether you want to save Unit1, click No.
10. To test the application, press F9. 626
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
11. Use the existing transfer buttons to exchange items between both list boxes. 12. Close the form using its Windows Close button. 13. On the form, click the Source List label. 14. On the Object Inspector, click Caption and type Cities to locate on a map: 15. Click Destination List and type Cities Selected: 16. To remove the items in the list, click the left list to select it 17. On the Object Inspector, click the ellipsis button of the Items field
Delete all items in the String List Editor dialog box and click OK
18. To create our own list, double-click an unoccupied area of the form and implement the OnCreate event as follows: //--------------------------------------------------------------------------void __fastcall TDualListDlg::FormCreate(TObject *Sender) { SrcList->Items->Add("Shangai"); SrcList->Items->Add("Melbourne"); SrcList->Items->Add("Kenitra"); SrcList->Items->Add("London"); SrcList->Items->Add("Valencia"); SrcList->Items->Add("Rio de Oro"); SrcList->Items->Add("Santiago"); SrcList->Items->Add("Dublin"); SrcList->Items->Add("Bamako"); SrcList->Items->Add("New Delhi"); SrcList->Items->Add("Tokyo"); SrcList->Items->Add("Alexandria"); SrcList->Items->Add("Boston"); SrcList->Items->Add("Quebec"); SrcList->Items->Add("Dar-Es-Salam"); SrcList->Items->Add("Munich"); } //---------------------------------------------------------------------------
19. Display the form. 20. Click the Cancel button and press Delete 21. Also delete the Help button 22. Double-click the OK button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TDualListDlg::OKBtnClick(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
23. To test the dialog box, press F9 24. After using the list boxes, click OK to close the form. 25. Press F12 to display the form 26. Click the left list to select it 27. To make sure that double-clicking an item in the left list has the same effect as clicking the Include button, click the Events tab 28. Double-click the event of the OnDblClick field and implement it as follows: void __fastcall TDualListDlg::SrcListDblClick(TObject *Sender)
Copyright © 2003 FunctionX, Inc.
627
Chapter 25: List-Based Controls
Borland C++ Builder Programming
{
IncludeBtnClick(Sender); } //---------------------------------------------------------------------------
29. Display the form and click the right list 30. On the Object Inspector, double-click the event of the OnDblClick field and implement it as follows: //--------------------------------------------------------------------------void __fastcall TDualListDlg::DstListDblClick(TObject *Sender) { ExcludeBtnClick(Sender); } //---------------------------------------------------------------------------
31. To test the application, press F9 32. After using the form, close it 33. Open the Editor1 application Here is the dialog box we are going to design
34. To use one of the template dialog boxes, on the main menu of C++ Builder, click File -> New -> Other... 35. In the New Items dialog box, click Dialogs. In the Dialogs property page, click Standard Dialog (Vertical) and click OK 36. On the Object Inspector, change the Caption to Date and Time 37. Change the Name to dlgDateAndTime 38. To save the new dialog box, on the Standard toolbar of C++ Builder, click the Save All button 39. Type DateAndTime to replace the name of the unit and press Enter 40. On the Standard tab of the Component Palette, click Label and click on the top border of the Bevel on the dialog box. Change its Caption to Select a Format 41. On the Standard tab of the Component Palette, click ListBox and click inside the Bevel on the dialog box 42. Change its Name to lstDateAndTime 43. Design the dialog box as the above picture 44. Click an unoccupied area on the dialog box to select it. On the Object Inspector, click the Events tab 45. Double-click the empty box next to OnActivate and implement its event as follows: 628
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
//--------------------------------------------------------------------void __fastcall TdlgDateAndTime::FormActivate(TObject *Sender) { lstDateAndTime->Clear(); TDateTime TodayDate = Date(); TDateTime RightNow = Time(); lstDateAndTime->Items->Add(TodayDate); ShortDateFormat = "m/d/yy"; lstDateAndTime->Items->Add(TodayDate); ShortDateFormat = "mm/dd/yy"; lstDateAndTime->Items->Add(TodayDate); ShortDateFormat = "yy/m/dd"; lstDateAndTime->Items->Add(TodayDate); ShortDateFormat = "yyyy/m/dd"; lstDateAndTime->Items->Add(TodayDate); DateSeparator = '-'; ShortDateFormat = "yyyy/m/dd"; lstDateAndTime->Items->Add(TodayDate); ShortDateFormat = "dd/mmm/yy"; lstDateAndTime->Items->Add(TodayDate); LongDateFormat = "dddd"; DateSeparator = ','; ShortDateFormat = "dddd/ mmmm dd/ yyyy"; lstDateAndTime->Items->Add(TodayDate); ShortDateFormat = "mmmm dd/ yyyy"; lstDateAndTime->Items->Add(TodayDate); ShortDateFormat = "dddd/ dd mmmm/ yyyy"; lstDateAndTime->Items->Add(TodayDate); ShortDateFormat = "dd mmmm/ yyyy"; lstDateAndTime->Items->Add(TodayDate); ShortTimeFormat = "h:n:s"; lstDateAndTime->Items->Add(RightNow); ShortTimeFormat = "hh:nn:ss"; lstDateAndTime->Items->Add(RightNow); LongTimeFormat = "HH:nn:ss"; lstDateAndTime->Items->Add(RightNow); // To eliminate or reduce the possiblity of an error // make sure a format is always selected lstDateAndTime->ItemIndex = 0; } //---------------------------------------------------------------------------
46. Save your project 47. To display the main form, on the main menu, click View -> Forms... On the View Form dialog box, double-click frmMain 48. On the form, double-click MainMenu1 49. In the Menu Editor, click the box on the right side of Format 50. In the Object Inspector, click Properties and click Caption 51. Type &Insert and press Enter 52. On the Menu Designer, click Insert and click the box under it. Type &Date and Time... and press Enter 53. Set the Hint value to Inserts today's date or the current time 54. Drag the Insert menu and position it between View and Format Copyright © 2003 FunctionX, Inc.
629
Chapter 25: List-Based Controls
Borland C++ Builder Programming
55. Close the Menu Designer 56. On the main menu of C++ Builder, click File -> Include Unit Hdr... 57. In the Use Unit dialog box, make sure DateAndTime is selected and click OK 58. On the main menu of Editor, click Insert -> Date and Time... and implement its event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::DateandTime1Click(TObject *Sender) { dlgDateAndTime->ShowModal(); if( dlgDateAndTime->ModalResult == mrOk ) { TListBox *Lst = dlgDateAndTime->lstDateAndTime; rchEditor->SelText = Lst->Items->Strings[Lst->ItemIndex]; }
} //---------------------------------------------------------------------------
59. Test your project 60. After using the application, close it and return to Bcb 61. Save All
25.2 Check List Boxes 25.2.1 Overview A Check List Box is a List Box whose items are each equipped with a check box:
630
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
Figure 89: The Project Options Dialog Box A Check List Box combines the functionalities of the List Box and the Check Box controls. As a list box, it displays each of its items on a line. If there are too many items than the control can display, it would be equipped with a vertical scroll bar. As described for the list box, if at least of of the items is wider than the control's width, you can make the list box display a horizontal scroll bar. To select an item in the list, the user can click the desired string. Unlike the regular list box, the user cannot select more than one string in the list. For this reason, if you desire a normal list of objects, use the regular List Box control. The most important and obvious characteristic of the Check List Box is that each item displays a check box on its left. This box allows the user to select or deselect each item. To select an item, the user must click its box and not its string, to indicate an explicit selection. This draws a check mark in the box. As described with the Check Box control, the user can deselect a string by removing the check mark. The check mark indicates that an item is selected and the absence of the check mark indicates the contrary. Like the Check Box control, you can allow the user to indicate a "half-checked" item. In this case, a check box can appear unchecked, checked, or grayed.
Practical Learning: Creating a Pizza Application 1.
Start a new project with its default form
2.
Save the project in a new folder named Pizza1
3.
Save the unit as Main and the project as Pizza
4.
Design the form as follows:
Copyright © 2003 FunctionX, Inc.
631
Chapter 25: List-Based Controls
Borland C++ Builder Programming
Control Form GroupBox RadioButton RadioButton RadioButton 5.
Name
Caption
Other Properties BorderStyle: bsDialog ShowHint: true
rdoSmall rdoMedium rdoLarge
Caption: Pizza Size Small Meium Large
Alignment: taLeftJustify Alignment: taLeftJustify Alignment: taLeftJustify
Save All
25.2.2 Characteristics of a Checked List Box To provide a Check List Box, from the Additional tab of the Component Palette, click the CheckListBox button
and click the form or container that would host the control.
Like the regular List Box control, the items of a Check List Box object are AnsiString strings controlled by a TStrings list called Items. At design time, to create a list of items and add it to the control, open the String List Editor dialog box, add the necessary strings, and click OK. Of course, you can add the strings at run time, using the Items::Add() method. After creating the list, each item appears with a flat check box to its left. If you want a 3D check box, you can change the Boolean Flat property from its true default to a false value: Flat = true
632
Flat = false
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
When you create the list, the items are stored in the same order you entered them, if you want, you can rearrange them alphabetically or numerically. This can be done by setting the Boolean value of the Sorted property accordingly. As described for the list box, if you set it to true, the items of the Check List Box would be sorted. If you set it back to false, the items of the list box would not go back to the way they were but new items would be added at the end of the list. If you provide a longer list than the control's height can display, it would have a vertical scroll bar. If just one or a few items are hidden by the scroll bar, you can heighten it if the form provides more space. Alternatively, you can also create the list in various columns. To do this, set the value of the Columns property to a number of your choice. Here is a Check List Box with two columns:
Normally, the number of columns should not exceed 5 or this would indicate that you may simply need more than one Check List Box control: If at least one of the items is wider than the width of the control, you have various alternatives. You can display a horizontal scroll bar by calling the Win32 API SendMessage() function and specifying the LB_SETHORIZONTALEXTENT value as the message to send: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { SendMessage(CheckListBox1->Handle, LB_SETHORIZONTALEXTENT, 140, 0); } //---------------------------------------------------------------------------
You can also resize the control to make it wider to accommodate the large item. After creating the list, sorted or not, each item has a positional index that allows you to access it. The array of this index can have a different name depending on why you want to access it. For example, the items are stored in an array called Header and each item can be accessed using the Header[Index] array. The Header index is used if you want an item to be distinct from the others. To do this, at run time, access that item Header index and set its Header Boolean property to true. To distinguish the header item from the Copyright © 2003 FunctionX, Inc.
633
Chapter 25: List-Based Controls
Borland C++ Builder Programming
others, it uses a different text color and it does not display a check box. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { CheckListBox1->Header[2] = True; } //---------------------------------------------------------------------------
In the same way, you can make more than one item appear as Header. The Header item displays a color represented by the HeaderBackgroundColor property. The text of the Header item displays in a color known as the HeaderColor property. You can set these values to any valid color you want. Also, the items are stored in an array called Checked. The first, top, item has an index of 0 and can be accessed with Checked[0]. The second item has an index of 1 and can be accessed with Checked[0], etc. As stated already, to select an item, the user clicks its check box. To find out if an item has been checked, access its Checked index and check whether it is true or false. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::CheckListBox1Click(TObject *Sender) { if( CheckListBox1->Checked[1] == True ) ShowMessage("Welcome to Montgomery County!"); } //---------------------------------------------------------------------------
To increase the options of the Check List Box, you can let the user "half-Select" an item or indicate when an item is not completely selected and not completely deselected. Such an item is referred to as grayed. To allow this, you must set the AllowGrayed Boolean property to true. If a Check List Box control has this property on, to check the appearance of a check mark or absence of it on an item, use the State property. The value of this property is an enumeration type defined as follows: enum TCheckBoxState {cbUnchecked, cbChecked, cbGrayed};
634
If an item is checked, its State has a value of cbUnchecked
If an item is clearly checked, its State has a value of cbChecked
If an item is undecided, that is, if it is grayed, its Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
State has a value of cbGrayed The check mark appearance of the items of a Check List Box control are stored in an array called State. To find out the check mark or lack of it of an item, call the State property and specify the index of the desired item. The following checks whether a check mark appears on the 5th item of the list: //--------------------------------------------------------------------------void __fastcall TForm1::CheckListBox1Click(TObject *Sender) { if( CheckListBox1->State[4] == cbChecked ) ShowMessage("The Bay Bridge is closed.\nYou can't get to Annapolis!!!"); } //---------------------------------------------------------------------------
If for any reason you do not want the users to be able to select items, only to view the list, you can set the Enabled property of the control to false. On the other hand, if you want to disable only one or a few items, you can do that. The items of a Check List Box are stored in an array called ItemEnabled[Index]. To enable or disable an item, call this property and specify the index of the item you want to enable or disable. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { CheckListBox1->ItemEnabled[2] = False; CheckListBox1->ItemEnabled[4] = False; CheckListBox1->ItemEnabled[5] = False; } //---------------------------------------------------------------------------
Practical Learning: Creating a Check List Box Application 1.
On the Component Palette, click the Additional tab and click the CheckListBox button
2.
Click an empty area on the form
3.
While the CheckListBox control is still selected, on the Object Inspector, click the ellipsis button of the Items
4.
Create the list of items as follows:
Copyright © 2003 FunctionX, Inc.
635
Chapter 25: List-Based Controls
Borland C++ Builder Programming
Figure 90: The String List Editor for a Check List Box 5.
Click OK
6.
Change the Name of the control to lstToppings
7.
Design the rest of the form as follows:
Figure 91: The Pizza Application in Design Control
636
Name
Caption or Text
Other Properties
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Label CheckListBox GroupBox RadioButton RadioButton RadioButton RadioButton Label Edit Label Edit Label Edit BitBtn 8.
Chapter 25: List-Based Controls
Toppings Selection
rdoMixToppings rdoHalfAndHalf rdoThirdAnd2Thirds rdoThirdEach edtPizzaPrice edtPriceToppings edtTotalPrice
Topping Distribution Mix Toppings Half and Half 1/3 and 2/3 1/3 Each Pizza Price 9.25 Price Toppings 0.00 Total Price 0.00 Kind: bkClose
Save All
25.2.3 Methods to Manage a Check List Box A Check List Box control is based on the TCheckListBox class. Therefore, to programmatically create this control, you can use its constructor. Specify the control owner and its parent. All of the functionality of the Check List Box control is provided though its properties. When the user clicks the check box of an item, the Check List Box fires an OnClickCheck event. You can use this event to take some action. The OnClickCheck event of a TNotifyEvent type and therefore specifies only the Sender of the event.
Practical Learning: Using Check List Box Events 1.
On the form, click the Check List Box to select it and, on the Object Inspector, click the Events tab
2.
Double-click the event side of the OnClickCheck field and implement it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::lstToppingsClickCheck(TObject *Sender) { int Pepperoni, Sausage, GroundBeef, Olives, Pineapple, ExtraCheese, Onions, GreenPepper; int NumberOfToppings; double PricePizza, PriceToppings, PriceTotal; const double PriceEachTopping = 0.45; // Get the price of the pizza based on the selected size if( rdoSmall->Checked == True ) PricePizza = 8.65; else if( rdoMedium->Checked == True ) PricePizza = 9.55; else if( rdoLarge->Checked == True ) PricePizza = 10.75; // Check each item. // If a check box is on an item, the item counts as one
Copyright © 2003 FunctionX, Inc.
637
Chapter 25: List-Based Controls
Borland C++ Builder Programming
// If an item is not checked, count it as 0 if( lstToppings->Checked[0] == True ) Pepperoni = 1; else Pepperoni = 0; if( lstToppings->Checked[1] == True ) Sausage = 1; else Sausage = 0; if( lstToppings->Checked[2] == True ) GroundBeef = 1; else GroundBeef = 0; if( lstToppings->Checked[3] == True ) Olives = 1; else Olives = 0; if( lstToppings->Checked[4] == True ) Pineapple = 1; else Pineapple = 0; if( lstToppings->Checked[5] == True ) ExtraCheese = 1; else ExtraCheese = 0; if( lstToppings->Checked[6] == True ) Onions = 1; else Onions = 0; if( lstToppings->Checked[7] == True ) GreenPepper = 1; else GreenPepper = 0; // Calculate the total number of items that have a check mark NumberOfToppings = Pepperoni + Sausage + GroundBeef + Olives + Pineapple + ExtraCheese + Onions + GreenPepper; // Calculate the total price of toppings PriceToppings = NumberOfToppings * PriceEachTopping; // Calculate the total price of the order PriceTotal = PricePizza + PriceToppings; // If only one topping is selected, there is no need to specify // how the toppings would be distributed if( NumberOfToppings == 1 ) { rdoMixToppings->Enabled = False; rdoHalfAndHalf->Enabled = False; rdoThirdAnd2Thirds->Enabled = False; edtThirdEach->Enabled = False; } // If two toppings are selected, give the customer the option of
638
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
// 1) mixing both toppings on the pizza // 2) putting each topping on one 1/2 of the pizza // 3) putting one topping on 1/3 of the pizza and the other topping on 2/3 else if( NumberOfToppings == 2 ) { rdoMixToppings->Checked = True; rdoMixToppings->Enabled = True; rdoHalfAndHalf->Enabled = True; //rdoThirdAnd2Thirds->Checked = False; rdoThirdAnd2Thirds->Enabled = True; edtThirdEach->Checked = False; edtThirdEach->Enabled = False; } // If three toppings are selected, let the user decides // 1) to mix toppings on the pizza // 2) to lay each topping on 1/3 of the pizza area else if( NumberOfToppings == 3 ) { rdoMixToppings->Checked = True; rdoMixToppings->Enabled = True; rdoHalfAndHalf->Enabled = False; rdoThirdAnd2Thirds->Enabled = False; edtThirdEach->Enabled = True; } // If more than three toppings are selected, the only solution is // to distribute them on the whole pizza area else if( NumberOfToppings > 3 ) { rdoMixToppings->Checked = True; rdoMixToppings->Enabled = True; rdoHalfAndHalf->Enabled = False; rdoThirdAnd2Thirds->Enabled = False; edtThirdEach->Enabled = False; } edtPizzaPrice->Text = FloatToStrF(PricePizza, ffFixed, 6, 2); edtPriceToppings->Text = FloatToStrF(PriceToppings, ffFixed, 6, 2); edtTotalPrice->Text = FloatToStrF(PriceTotal, ffFixed, 6, 2); } //---------------------------------------------------------------------------
3.
On the form, double-click the Small, the Medium, and the Large radio buttons
4.
Implement them as follows: //--------------------------------------------------------------------------void __fastcall TForm1::rdoSmallClick(TObject *Sender) { // Let the OnClickCheck of the Check List Box perform the calculations lstToppingsClickCheck(Sender); } //--------------------------------------------------------------------------void __fastcall TForm1::rdoMediumClick(TObject *Sender) { // Let the OnClickCheck of the Check List Box perform the calculations lstToppingsClickCheck(Sender); } //--------------------------------------------------------------------------void __fastcall TForm1::rdoLargeClick(TObject *Sender) {
Copyright © 2003 FunctionX, Inc.
639
Chapter 25: List-Based Controls
Borland C++ Builder Programming
// Let the OnClickCheck of the Check List Box perform the calculations lstToppingsClickCheck(Sender); } //---------------------------------------------------------------------------
5.
Test the application:
Figure 92: The Pizza Application - Result 6.
After using the form, close it and return to Bcb
7.
Save All
25.3 Combo Boxes 25.3.1 Introduction Like a radio button, a combo box allows the user to select one item from a group. Unlike radio buttons, a combo box saves space by using just as much room as an edit control. Like a list box, a combo box displays a list of items to the user. Unlike a list box, there is a version of a combo box that retracts once the user has made his selection; this is useful when space saving is particularly important. To add a combo box to your application, from the Standard tab of the Component Palette, click the ComboBox button and click on the form. You can then resize or reposition the control to the desired location.
640
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
25.3.2 Characteristics of a Combo Box Just like every control of your application, the name is the most important property of the control, for you and the compiler. This name allows you and the compiler to refer to the control. By default, the first combo box you add to your form at design time is called ComBox1, the second would be ComboBox2, etc. To change the name of the control, click the Name field, type a new name and press Enter (or click somewhere else). Probably the first thing the user sees on a combo box is the text it displays. Although the text of an item is an AnsiString, the items are composed from the TStrings class. To create this list, when the control is selected on the form, on the Object Inspector, click the Items field to reveal an ellipsis button. Click the ellipsis button to display the String List Editor. Type each string and press Enter: Once you click OK, the control would be filled with the new items. You can also fill out the control using the methods of the TStrings class. For example, to create a list of items, you could write: //--------------------------------------------------------------------------void __fastcall TForm1::btnFillTheListClick(TObject *Sender) { cbxColors->Items->Add("Old Blue"); cbxColors->Items->Add("Light Blue"); cbxColors->Items->Add("Salmon"); cbxColors->Items->Add("Dark Violet"); } //---------------------------------------------------------------------------
By default, the items you add to the combo box will appear in the order they are supplied. For example the TStrings::Add() method would add the new string at the end of the list. If you want the list of items to be sorted, you can change the value of the Sorted property in the Object Inspector from false (the default) to true. To sort a list programmatically, you can write: //--------------------------------------------------------------------------void __fastcall TForm1::btnSortTheListClick(TObject *Sender) { cbxColors->Sorted = True; } //---------------------------------------------------------------------------
You can un-sort the list by changing the value of the Sorted property. This property works exactly like its equivalent in the TListBox control There are three styles of combo boxes, although all allow the user to make only one selection. These styles are controlled by the TComboBoxStyle enumerator and the Style property of the Object Inspector. A combo box can be configured to allow the user to add items to the list. In this case, if the user does not find the desired item in the list, he can type a new value. To provide this ability, set the Style to csDropDown. If you set the Style to csDropDownList, the user cannot enter a new item in the list but can still select one from the control. A combo box with the csSimple Style permanently displays a combination of an edit box and a list box. The user can scroll in the list box and select an item; after the selection, the control would still display the list. The other two styles, csOwnerDrawFixed and csOwnerDrawVariable, are typically used to display varying objects such as pictures or colors.
Copyright © 2003 FunctionX, Inc.
641
Chapter 25: List-Based Controls
Borland C++ Builder Programming
If the combo box has a style other than csSimple, there is typically a fixed number of items that display when the user clicks the control’s arrow. You can control the number of items that displays using the DropDownCount property. By default, this is set to 8. If the list contains a number of items less than the DropdownCount integer value, all of the items would display fine. If the list contains more than the DropDownCount number of items, when the user clicks the arrow, a scroll box would appear. The control would display DropDownCount number of items; to reveal more, the user would have to scroll in the list. The text that displays on a non-drawn combo box is an AnsiString object. If you want the combo box to display a certain string, use the Text property. At design time, you can set this only if the control’s Style is csDropDown or csSimple. At run time, you can set the text that would display in the combo box at startup using the ItemIndex property. This property represents the ordinal integer item of the combo box. The items are counted from 0, then 1, and so on. The ItemIndex of each item is set depending on the value to the Sorted property. If the list is not sorted, the first item entered in the list has an ItemIndex value of 0. If the list gets sorted, the first item in ascending order has an ItemIndex property set at 0. You can therefore use this property to control what item to display in the list: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { cbxColors->ItemIndex = 0; } //---------------------------------------------------------------------------
You can also use the ItemIndex property to find out what item is selected at a given time.
25.3.3 Methods of Combo Box Management The TComboBox as a class has only its constructor and its destructor as methods. Its other methods are derived from the the parent TCustomComboBox class. The TComboBox constructor is used to dynamically create an instance of the object. If you cannot add the control at design time, declare a pointer to TComboBox and use the new operator to assign the control’s owner for cleaning purposes: //--------------------------------------------------------------------------void __fastcall TForm1::btnCreateComboBoxClick(TObject *Sender) { TComboBox *CarMake = new TComboBox(this); CarMake->Parent = this; } //---------------------------------------------------------------------------
After creating the object, you can manipulate its properties the change the defaults. If you create a local list, you will manipulate it in the same function or event: //--------------------------------------------------------------------------void __fastcall TForm1::btnCreateComboBoxClick(TObject *Sender) { TComboBox *CarMake = new TComboBox(this); CarMake->Parent = this; CarMake->Left = 32; CarMake->Top = 16;
642
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 25: List-Based Controls
CarMake->Items->Add("Ford"); CarMake->Items->Add("Renault"); CarMake->Items->Add("Fiat"); CarMake->Items->Add("Honda"); } //---------------------------------------------------------------------------
25.3.4 Operations on Combo Box Using Events There are two mains operations a user will perform on a ComboBox control: selecting an item from the list or changing the content of the list. Many of the events that occur during the use of a combo box are from other controls or actions that are not programmatically directly related to a combo box. It is usually possible to fill out the list of a combo box at design time; that is, if you know the list of items that will be used. Otherwise, you will use another event or function to provide the items of the list. For example, the OnCreate event of a form is a common place to fill out a list, especially if you want the list to owns its items the first time it appears to the user. If you create a combo box named cbxSports but do not provide its items at design time, you can use the OnCreate event of the hosting form to fill it up as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { cbxMajor->Items->Add("Accounting"); cbxMajor->Items->Add("Medical Assistant"); cbxMajor->Items->Add("Fire Science"); cbxMajor->Items->Add("Computer Sciences"); cbxMajor->Items->Add("Business Administration"); cbxMajor->Items->Add("Hospitality Management"); cbxMajor->Items->Add("Criminal Justice"); cbxMajor->Items->Add("Computer Technician"); cbxMajor->Items->Add("Cartography"); cbxMajor->Items->Add("Music"); cbxMajor->ItemIndex = 3; } //---------------------------------------------------------------------------
The most regular operation a user performs on a combo box is to select an item from the list. This happens when the user clicks the arrow to expand the list and then clicks one item. Once the user clicks one of the items, the list disappears and the combo box becomes a regular edit box, unless the style of the combo box is csSimple. Using this event, you can find out what item the user would have selected and act accordingly. For example, you can transfer the selected item to another control such as an edit box. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::cbxMajorClick(TObject *Sender) { edtMajor->Text = cbxMajor->Items->Strings[cbxMajor->ItemIndex].c_str(); } //---------------------------------------------------------------------------
The OnChange event occurs when the user changes the event that was displaying in the edit box of the combo box. Also this event occurs also in response to the user making a selection, it is more appropriate if you allow the user to edit an item of the list or if you allow the user to add items to the list by typing directly in the edit box. Like the Edit Copyright © 2003 FunctionX, Inc.
643
Chapter 25: List-Based Controls
Borland C++ Builder Programming
control, the OnChange event occurs immediately as the user types anything in the edit box portion of the combo box. The OnClick event cannot respond to these events. You can use the OnChange event to deal with the user trying to modify the item on the edit box. You can also use it to respond to other actions associated with the user making a selection. For example, you can simply display the list the number of items in the list: //--------------------------------------------------------------------------void __fastcall TForm1::cbxMajorChange(TObject *Sender) { edtCount->Text = IntToStr(cbxMajor->Items->Count); } //---------------------------------------------------------------------------
Practical Learning: Configuring List Boxes 1.
Open the Editor2 application from the resources that accompany this book
2.
Make sure the main form is displaying. From the Standard tab of the Component Palette, click ComboBox
3.
On the form, click the bottom (Formatting) toolbar
4.
As the new combo box is still selected, on the Object Inspector, change the following properties: Text = Times New Roman Name = cboFonts Hint = Font|Changes the font of the selection
5.
Add another combo box on the right side of the Font combo box with following properties: Text = 10 Name = cboFontSize Hint = Font Size|Changes the font size of the selection Width = 50
644
6.
Double-click (TStrings) from the Items property
7.
Type 8 and press Enter
8.
Complete the list with 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 32, 36, 48, 54, 72
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
9.
Chapter 25: List-Based Controls
Click OK
10. Drag the Font combo box to the left of the toolbar 11. Also drag the Font Size combo box to the right side of the Font combo box 12. Right-click the Formatting toolbar and click New Separator. Move the new separator to the right side of the Font Size combo box
13. Double-click the Font combo box and implement its OnChange event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::cboFontsChange(TObject *Sender) { rchEditor->SelAttributes->Name = cboFonts->Text; rchEditor->SetFocus(); } //---------------------------------------------------------------------------
14. On the form, double-click the Font Size combo box and implement its OnChange event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::cboFontSizeChange(TObject *Sender) { rchEditor->SelAttributes->Size = StrToInt(cboFontSize->Text); rchEditor->SetFocus(); }
15. While you are in the Code Editor, on the top combo box of the Object Inspector, select frmMain and click the Events tab 16. Double-click FormCreate to access its event. At the end of the event, before the closing bracket, fill the Font combo box with the computer's fonts as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { Application->OnHint = ShowHints; int i; for(i = 0; i < Screen->Fonts->Count; i++) { // Get a pointer of the menu we want to change TMenuItem *FontItem; FontItem = new TMenuItem(ListOfFonts);
Copyright © 2003 FunctionX, Inc.
645
Chapter 25: List-Based Controls
}
Borland C++ Builder Programming
// Get a font from the system and store it as a caption of the menu FontItem->Caption = Screen->Fonts->Strings[i]; // Add the font to the menu item to create a list of fonts ListOfFonts->Add(FontItem); // Eventually, if the user selects a font here, // we will call a function, called Applyfont, to apply that font FontItem->OnClick = ApplyFont;
for(i = 0; i < Screen->Fonts->Count; i++) cboFonts->Items->Add(Screen->Fonts->Strings[i]);
} //---------------------------------------------------------------------------
17. We also need to make sure that when the user clicks anywhere in the text, both combo boxes can display the font name and its size. 18. Click the RichEdit control (the big wide area on the main form). On the Object Inspector, double-click the box on the right side of OnSelectionChange 19. Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::rchEditorSelectionChange(TObject *Sender) { cboFonts->Text = rchEditor->SelAttributes->Name; cboFontSize->Text = IntToStr(rchEditor->SelAttributes->Size); } //---------------------------------------------------------------------------
20. Finally, after the user has used the Font dialog box, if he clicks OK, since we took care of giving focus to the RichEdit control already, we need to make sure the combo boxes display the font and the font size that were selected 21. On the form, double-click ActionList1 22. On the left frame of the ActionList Editor, click Dialog. On the right frame, click FontEdit1 23. In the Object Inspector, click the Event tab if necessary and double-click the right section of OnAccept 24. Add the following two lines to the end of the event: //--------------------------------------------------------------------------void __fastcall TfrmMain::FontEdit1Accept(TObject *Sender) { // Do the inverse of the BeforeExecute event // If the user clicks OK, get the characteristics of the font // Apply them to the selected text of the Rich Edit control rchEditor->SelAttributes->Name = FontEdit1->Dialog->Font->Name; rchEditor->SelAttributes->Style = FontEdit1->Dialog->Font->Style; rchEditor->SelAttributes->Size = FontEdit1->Dialog->Font->Size; rchEditor->SelAttributes->Color = FontEdit1->Dialog->Font->Color; cboFonts->Text = FontEdit1->Dialog->Font->Name; cboFontSize->Text = StrToInt(FontEdit1->Dialog->Font->Size);
} //---------------------------------------------------------------------------
25. Test your project. After using it, click the application 26. Save and close the project.
646
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Chapter 25: List-Based Controls
647
Chapter 26: Grid-Based Controls
648
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
Chapter 26: Grid-Based Controls 26.1 The Win32 Calendar 26.1.1 Introduction The Win32 API provides a control used to select dates on a colorful calendar. The dates used and the way they display are based on the Regional Settings of the Control Panel. It may also depend on the operating system
This convenient control is called Month Calendar. The title bar of the control displays two buttons and two labels. The left button allows the user to select the previous month by clicking the button. The left label displays the currently selected month. The right label displays the year of the displayed date. The right button is used to get to the next month. The calendar can be configured to display more than one month. Here is an example that displays two months:
If the control is displaying more than one month, the buttons would increment or decrement by the previous or next month in the list. For example, if the control is displaying April and May, if the user clicks the left button, the control would display March and April. If the control is displaying April and May and the user clicks the right Copyright © 2003 FunctionX, Inc.
649
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
button, the control would display May and June. Also, to select any month of the current year, the user can click the name of the month, which displays the list of months and then allows the user to click the desired month:
To select a year, the user clicks the year number. This changes the year label into a spin button:
To change the year, the user can click the up or down arrows of the spin button. As the spin button is displaying, the user can also use the arrow keys of the keyboard to increase or decrease the value. Under the title bar, the short names of week days display, using the format set in Control Panel. In US English, the first day is usually Sunday. The first day can be changed by the programmer. On the control, the currently selected date has a circle around. To select a date on the control, the user clicks the desired date, which changes from the previous selection.
650
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
In the main area, the numeric days of the month display on a white background (this color and any color on the control can be changed as we will see in the next section). To select a date, the user clicks it in the list. By default, the calendar opens with today's day circled with a hand-drawn-look-alike ellipse. Using the buttons of the title bar, the month label, and/or the year, the user can change the date. If at one time the calendar is displaying a date other than today, and if the user wants to return to today's date, he can click the bottom label that displays Today (you as the programmer can hide the Today label if you want).
Practical Learning: Creating a Calendar-Based Application 1.
Start Borland C++ Builder or a new project with its default form
2.
Save the project in a new folder called Payroll1
3.
Save the unit as Main and the project as Payroll
4.
Change the Caption of the form to Employees Payroll
5.
Change the name of the form to frmMain
6.
Design the form as follows:
Form BorderStyle: dsDialog Caption: Employees Payroll Name: frmMain ShowHint: true Panel Height: 128 Width: 578 Label Caption: Start Period: Edit Name: edtStartPeriod SpeedButton AllowAllUp: true GroupIndex: 18 Name: btnStartPeriod Glyph: C:\Programs Files\Common Files\Borland Shared\Images\Buttons\calendar.bmp Label Caption: Ending Period Edit Name: edtEndPeriod SpeedButton AllowAllUp: true GroupIndex: 20 Name: btnEndPeriod
Copyright © 2003 FunctionX, Inc.
651
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
Glyph: C:\Programs Files\Common Files\Borland Shared\Images\Buttons\calendar.bmp 7.
Double-click an empty area on the form to access its OnCreate event and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { edtStartPeriod->Text = Date(); edtEndPeriod->Text = Date() + 13; } //---------------------------------------------------------------------------
8.
Save All
26.1.2 Calendar Properties To create a calendar, add the MonthCalendar button to a form or container. The MonthCalendar object is created from the TMonthCalendar class which is indirectly derived from TWinControl. The MonthCalendar control is a rectangular object without a border. After placing it on the form, it displays the current month and only one month. This is because, by default, its width and height are set enough to accommodate only one month. To display more than one month, change the width of the control to provide enough space:
In the same way, you can increase the height to display many months. To make it a highly visual object, a calendar uses different colors to represent the background, week days, the background of the title bar, the text of the title bar, the text of the days of the previous month, and the text of the days of the next month. These colors are controlled by the CalColors property of the control. At design time, you can set these colors to the values of your choice:
652
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
Of course, you can programmatically change these colors. Although any color is allowed in any category, you should make sure that the calendar is still reasonably appealing and usable. Under the title bar, the short names of week days display, using the format set in Control Panel. In US English, the first day is usually Sunday. If you want to start with a different day, set the value using the FirstDayOfWeek property. Under the names of the week and their line separator, the numeric days of the month are listed. The MonthCalendar control is used to let the user know today's date in two ways. On the calendar, today's date is circled by a hand-drawn ellipse. In the bottom section of the calendar, today's date is also displayed as a sentence. If you want to display or hide the bottom label, set the ShowToday Boolean property accordingly. For example, to hide it, set this property to false. At any time, a particular date is selected and has an ellipse with the same color as the background of the title bar. By default, the selected date is today's date. On the Object Inspector, it is represented by the Date property. When the user clicks the calendar, a date is selected. To find out what date the user selected, you can access the TMonthCalendar::Date value. The Date value of the MonthCalendar is a TDateTime type. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::MonthCalendar1Click(TObject *Sender) { Label1->Caption = MonthCalendar1->Date; } //---------------------------------------------------------------------------
When the user clicks the MonthCalendar control, one date is selected. To control whether the user can select one or more dates, set the value of the MultiSelect property accordingly. For example, if you want the user to select a range of dates on the control, set the MuiltiSelect property to true.
Practical Learning: Adding a Calendar Control 1.
Click an empty area on the form, under the panel
2.
On the Component Palette, click the Win32 tab. Double-click the MonthCalendar button
Copyright © 2003 FunctionX, Inc.
653
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
3.
Click an empty area on the form again and, once more, from the Win32 tab of the Component Palette, double-click the Calendar button
4.
Position both calendars as follows:
5.
Change the Name of the left Calendar control to calStartPeriod and change the Name of the right Calendar to calEndPeriod
6.
Change the FirstDayOfWeek of both controls to dowMonday
7.
Set the Visible property of both calendars to false
8.
On the form, double-click the left SpeedButton and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnStartPeriodClick(TObject *Sender) { if( btnStartPeriod->Down == True ) calStartPeriod->Visible = True; else calStartPeriod->Visible = False; } //---------------------------------------------------------------------------
9.
On the form, double-click the right SpeedButton and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnEndPeriodClick(TObject *Sender) { if( btnEndPeriod->Down == True ) calEndPeriod->Visible = True; else calEndPeriod->Visible = False; } //---------------------------------------------------------------------------
10. Test the application and return to Bcb 11. Save All
26.1.3 Calendar Methods and Events The TMonthCalendar class provides a constructor that can be used to programmatically create a calendar and place it on a form or container. To do this, declare a pointer to
654
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
TMonthCalendar and, using the new operator, specify a the container and the parent of the control. Here is an example: //--------------------------------------------------------------------------#include #include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TMonthCalendar *MCal = new TMonthCalendar(this); MCal->Parent = this; } //---------------------------------------------------------------------------
After creating the control, all of the properties we have seen above can be accessed. To accentuate the importance of one or more days of a month, you can call the BoldDays() method. Its syntax is: void __fastcall BoldDays(const unsigned * Days, const int Days_Size, unsigned &MonthBoldInfo);
This method can be used to format some days in bold and it is mostly used in conjunction with the OnGetMonthInfo() event. The only event the MonthCalendar control handles on its own is the OnGetMonthInfo() event. Its syntax is: void __fastcall OnGetMonthInfo(TObject *Sender, DWORD Month, DWORD &MonthBoldInfo)
This event fires as soon as the month of the calendar has been changed. As a descendant of TWinControl, the MonthCalendar control fires the same regular events of a Windows control.
Practical Learning: Adding a Calendar Control 1.
On the top section of the source file of the form, include the DateUtils header file under the vcl header file: #include
2.
On the form, double-click the left Calendar and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::calStartPeriodClick(TObject *Sender) {
Copyright © 2003 FunctionX, Inc.
655
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
// Get the date that the user has just selected TDateTime DateSelected = calStartPeriod->Date; // Find out the numeric "name" of the weekday Word DOTW = DayOfTheWeek(DateSelected); // Make sure the user selected a Monday if( DOTW == 1 ) { // Since the user selected a Monday date, // display it in the Ending Period edit box edtStartPeriod->Text = calStartPeriod->Date;//.FormatString("dddd, M MMM yyyy"); } // Make sure the user can select only a Monday date else if( DOTW != 1 ) { ShowMessage("The selected date is invalid\n" "A time sheet period starts on Monday"); return; }
} //---------------------------------------------------------------------------
3.
On the form, double-click the right Calendar and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::calEndPeriodClick(TObject *Sender) { // Get the current date selected on the End Period Calendar TDateTime DateSelected = calEndPeriod->Date; // Find the numeric "name" of the weekday Word DOTW = DayOfTheWeek(DateSelected); // Get the date of the left calendar TDateTime StartPeriod = calStartPeriod->Date; // Find out if the user selected a Sunday date if( DOTW == 7 ) { // Since the user selected a date on Sunday, // display it in the Ending Period edit box if( DateSelected == StartPeriod + 13 ) edtEndPeriod->Text = calEndPeriod->Date; // Make sure the date the user selected is the week following // the previous else { ShowMessage("Invalid date selection\n" "The time sheet period spans 14 days"); } } // Make sure the user selects a Sunday date. // Otherwise, dismiss it else if( DOTW != 7 ) { ShowMessage("The selected date is invalid\n" "A time sheet period ends on Sunday"); } } //---------------------------------------------------------------------------
656
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
4.
Test the application
5.
Return to Bcb and Save All
Chapter 26: Grid-Based Controls
26.2 The Date and Time Picker 26.2.1 Overview The Date and Time Picker is a control that allows the user to select either a date or a time value. This control provides two objects in one:
Copyright © 2003 FunctionX, Inc.
657
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
One of the advantages of the Date and Time Picker control is that it allows the user to select a time or a date value instead of typing it. This tremendously reduces the likelihood of mistakes. To create a Date or Time Picker control, add a DateTimePicker control or other container.
to a form
Practical Learning: Creating a Date Time Picker Application 1.
Create a new project with its default form
2.
Save the project in a new folder called Payroll2
3.
Save the unit as Main and the project as Payroll
4.
Design the form as follows:
Form BorderStyle: dsDialog Name: frmMain Panel Caption: None 5.
Caption: Employees Payroll ShowHint: true
Save All
26.2.2 The Time Picker After adding a DateTimePicker control to a container, to make it a Timer Picker control, change its Kind property to a dtkTime value. This changes the control into a spin control:
658
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
The Time Picker control is a spin button made of different sections: the hours value, the minutes value, the optional seconds value, and the optional AM/PM string. To change the time, the user clicks a section and uses either the mouse or the keyboard to increase or decrease that particular value. To change another value, the user must first click it and then use the spin button. By default, the time displays using the HH:MM:SS AM/PM format. This means that the time uses two digits for the hours from 00 to 11, two digits for the minutes from 00 to 59, two digits for the seconds from 00 to 59 and the AM or PM for morning or afternoon. To control how the time displays, set the desired value in the Format property. The possible values are: Format
Used For
h
Hour for 12-hour basis
hh
Hour for 12-hour basis
H
Hour for 24-hour basis
HH
Hour for 24-hour basis
m
Minute
mm
Minute
t tt
AM/PM AM/PM
Description Used to display the hour with one digit if the value is less than 10 Used to display the hour with a leading 0 if the value is less than 10 Used to display the hour with one digit if the value is less than 10 Used to display the hour with a leading 0 if the value is less than 10 Used to display the minute with one digit if the value is less than 10 Used to display the minute with a leading 0 if the value is less than 10 Displays the letter A or P for the AM or PM section Displays the letters AM or PM for the last section
You can set the format at design time using the Format field on the Object Inspector. To set the format at run time, assign the desired format to the TDateTimePicker::Format property. By default, after adding the control to the form or container, it assumes the time of the computer when the control was added. If you want to set a different time, apply a Format combination to the Time property. In the same way, at any time you can retrieve the time value on the control by accessing the Time property. Sometimes the user may want to manually edit the time of the control such as typing the hour, the minute, the second, or AM/PM. Fortunately, the Date Picker control is equipped to natively allow or disallow some values. For example, the user cannot type anything else than a digit for the hours, minutes, or second portions and he can type only a, A, p, or P for the AM/PM section. This is the default scenario where you let this object help you control the values that the user can type. If you want to allow the user to type the value of the date or time of the control, set the ParseInput Boolean property to true.
26.2.3 The Date Picker After adding the DateTimePicker control to the form, to allow the user to set the dates and not the times on the control, set its Kind property to dtkDate. This would change the control into a combo box. If you do not like the combo box, you can display a spin button instead. This ability is controlled by the DateMode property. The dmComboBox value is used to display a combo box while the dmUpDown value displays a spin button.
Copyright © 2003 FunctionX, Inc.
659
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
If the control displays a combo box, if the user clicks the arrow on the Date control, a calendar object similar to the MonthCalendar control we saw earlier displays:
This calendar displays to the bottom-left or the bottom-right side of the combo box. To control this alignment, change the value of the CalAlignment property. The displayed MonthCalendar object allows the user to select a date using the same techniques we described for the MonthCalendar control. The MonthCalendar of the DateTimePicker control displays using the same colors and other properties as we saw with the MonthCalendar control. After the user has selected a date, the date value displays in the Edit section of the combo box and the calendar disappears. If the control displays a spin button, the object is divided in different sections that can each be changed individually:
To change either the day, the month, or the year, the user must click the desired section and use either the arrows of the button or the arrow keys on the keyboard to increase or decrease the selected value. If you want to control the range of dates the user can select, use the MinDate and the MaxDate properties. When you add the DateTimePicker control to your form or container, it displays the date of the computer at the time the control was added. If you want the control to display a different date, set the desired value in the Date field using the Object Inspector. At any time, you can find out what value the Date Picker has by retrieving the value of the TDateTimePicker::Date property. By default, the date displays using either the Short Date or the Long Date formats of Control Panel. This display is controlled by the DateFormat field in the Object Inspector. If you want to customize the way the date is displayed, set the desired string using the Format property. The possible formats are as follows:
660
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Format
Chapter 26: Grid-Based Controls
Used For
d
Days
dd
Days
ddd dddd M
Weekdays Weekdays Months
MM
Months
MMM MMMM yy yyyy
Months Months Years Years
Description Displays the day as a number from 1 to 31 Displays the day as a number with a leading 0 if the number is less than 10 Displays a weekday name with 3 letters as Mon, Tue, etc Displays the complete name of a week day as Monday, etc Displays the numeric month from 1 to 12 Displays the numeric month with a leading 0 if the number is less than 10 Displays the short name of the month as Jan, Feb, Mar, etc Displays the complete name of the month as January, etc Displays two digits for the year as 00 for 2000 or 03 for 2003 Displays the numeric year with 4 digits
The user may want to edit the date value of the control, including typing the month, day, year, the name of the month or the name of the weekday. The Date Picker object is equipped to control the types of values that can be entered. For example, the user cannot type the name of a month, only a number and the control would display the corresponding name of the month. This is the default scenario where you let this object help you control the values that the user can type. If you want to take matters into your own hands, that is, if you want to allow the user to type the value of the date or time of the control, set the ParseInput Boolean property to true. If you allow the user to manually enter the date value, if the value is incorrect, when the control looses focus, the compiler would make an attempt to convert the date entered into a valid and true date. If the user enters an invalid value, the compiler would throw an EconvertError error:
This means that you should be reluctant to let the users type whatever they want. The less they type, the less checking you need to do.
Practical Learning: Adding Date Picker Controls 1.
Add two labels
Copyright © 2003 FunctionX, Inc.
and two DateTimePicker controls
to the label as follows:
661
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
2.
Change the Name of the left control to dteStartPeriod
3.
Change the Name of the right control to dteEndPeriod and set its CalAlignment property to dtaRight
4.
Double-click an unoccupied area on the form and implement its OnCreate event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { dteStartPeriod->CalColors->BackColor = TColor(RGB(230, 245, 255)); dteStartPeriod->CalColors->MonthBackColor = TColor(RGB(212, 235, 255)); dteStartPeriod->CalColors->TextColor = clBlue; dteStartPeriod->CalColors->TitleBackColor = TColor(RGB(0, 0, 160)); dteStartPeriod->CalColors->TitleTextColor = clWhite; dteStartPeriod->CalColors->TrailingTextColor = TColor(RGB(190, 125, 255)); dteStartPeriod->Format = "ddd d MMM yyyy"; dteEndPeriod->CalColors->BackColor = TColor(RGB(255, 236, 218)); dteEndPeriod->CalColors->MonthBackColor = TColor(RGB(255, 235, 214)); dteEndPeriod->CalColors->TextColor = TColor(RGB(102, 52, 0)); dteEndPeriod->CalColors->TitleBackColor = TColor(RGB(176, 88, 0)); dteEndPeriod->CalColors->TitleTextColor = TColor(RGB(255, 238, 220)); dteEndPeriod->CalColors->TrailingTextColor = TColor(RGB(230, 115, 0)); dteEndPeriod->Format = "ddd d MMM yyyy"; } //---------------------------------------------------------------------------
5.
Test the application
6.
Close the form and return to Bcb
26.2.4 Date Time Picker Events The Date Time Picker control enjoys some events that the Calendar object does not have. Whenever the user changes the date or time value of the control, an OnChange event fires. You can use this event to take some action such as indicating to the user that the new date is invalid. If you had allowed the user to enter almost any type of value in the control by setting the ParseInput Boolean property to true, and if he decides to edit the value of the control, while the user is doing this, the OnUserInput event fires. This can be a good place to check what the user is doing and take some action. The syntax of the OnUserInput event is: void __fastcall DateTimePickerUserInput(TObject *Sender, const AnsiString UserString, TDateTime &DateAndTime, bool &AllowChange)
The UserString argument is the value that the user is typing. It could be a digit or a letter. The control returns the DateAndTime as the value of the control. If, during your checking, you find out that the user typed a wrong value, you can allow or disallow by setting the value of the AllowChange argument accordingly. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::DateTimePicker1UserInput(TObject *Sender, const AnsiString UserString, TDateTime &DateAndTime,
662
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
{
Chapter 26: Grid-Based Controls
bool &AllowChange) if( TDateTime(UserString) < Date() ) { ShowMessage("The cleaning date you entered, " + UserString + ", is not valid!"); AllowChange = False; } else { Edit1->Text = DateAndTime; AllowChange = True; }
} //---------------------------------------------------------------------------
If the Kind property of the control is set to dtkDate and if the DateMode value is dmComboBox, when the user clicks the arrow of the combo box to display the MonthCalendar, the OnDropDown() event fires. On the other hand, if the user clicks the arrow to retract the Calendar, an OnCloseUp() event fires. Both event are TNotifyEvent type.
Practical Learning: Using Date Time Picker Events 1.
On the form, click the left Date and Time Picker control to select it
2.
On the Object Inspector, click the Events tab and scroll down completely if necessary
3.
Double-click the event side of the OnCloseUp field and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::dteStartPeriodCloseUp(TObject *Sender) { // Add two weeks to the date selected by the user dteEndPeriod->Date = dteStartPeriod->Date + 13; } //---------------------------------------------------------------------------
4.
Test the application by changing the date values of the left control and notice that two weeks are always added to set the value of the right control
Copyright © 2003 FunctionX, Inc.
663
Chapter 26: Grid-Based Controls
5.
Borland C++ Builder Programming
After using the form, close it and return to Bcb
26.3 The String Grid Control 26.3.1 Overview of Grids A grid is a technique of using columns and rows to represent data in a visual format. There is no strict rule as to what a grid control is used for. It can be used to simply display a series of values by categories. It can also be used as a time sheet, which will be the basis of the next exercise. Sometimes it is used as a calendar, similar to the MonthCalendar control and as we will see later on. To organize its content, a grid is made of vertical and horizontal lines. These lines are used as separators. They create vertical entities called columns and horizontal sections called rows:
The intersection of a column and a row is called a cell. The cell is the most important entity and the most used aspect of a grid. It holds the actual values of the grid. The cells of a grid can be used to display data or they can be used to receive data from the user. This means that data of a grid is entered or stored in cells. Because the role of a grid is unpredictable, the most top cell of each column can be used to display a label. By nature, a column specifies a category of value. Therefore, the label of a column signifies the category of values of that column:
To create a series of values for each category, you use a row of data. A row is also called a record. To make a record explicit, the most left row can display a label. The easiest and most basic label consists of a number. In this case, rows can be labeled from top to bottom as 1, 2, 3, 4, etc.
664
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
In most cases, each cell is in fact an Edit control and its content is an AnsiString. This means that a cell can contain a natural number, a floating-point variable, or a string.
26.3.2 String Grid Properties To create a grid of data, the Visual Component Library (VCL) provides various controls. One of these controls is called StringGrid and is implemented by the TStringGrid class. To create a grid, you can add a StringGrid button . Therefore, you can click this button and position it on a form or another control container:
Like many other controls, a grid is represented with a 3-D effect that raises its borders. This effect is controlled by the BorderStyle property. If you do not want to display borders on the control, set the BorderStyle property to bsNone:
A grid is made of vertical divisions called columns and horizontal divisions called rows. Two of the most visual characteristics of a StringGrid control are its number of columns and its number of rows. These two values are set using the ColCount and the RowCount properties. The values are integer type and should be >= 0. If you set either property to a negative value, it would be set to 1. If you do not want to display columns, set the ColCount to 0. In the same way, if you do not want to display rows, set the RowCount value to 0. By default, if a grid contains more columns than its width can show, it would display a vertical scroll bar. In the same way, if there are more rows than the control's height can accommodate, it would be equipped with a horizontal scroll bar. The ability to display scroll bars is controlled by the ScrollBars property. You can use it to display only the vertical scroll bar (ssVertical), only the horizontal scroll bar (ssHorizontal), both scroll bars (ssBoth), or no scroll bar (ssNone) at all. Like most other list-based controls, a grid is used to display data and, in some applications, you may want the users to enter or use values of the grid control. To guide Copyright © 2003 FunctionX, Inc.
665
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
the users with the values in the grid, you can display explicit text in the fixed columns and fixed rows. The top and left cells are qualified as fixed and, by default, they are the most top and the left cells respectively. Besides these ranges of cells, you can add a fixed row of cells and a fixed column of rows. If you want to display only one fixed row, it must be the most top range. This characteristic is controlled by the FixedRows property and, by default, is set to 1. If you want to display an additional range of fixed cells on top, change the value of FixedRows. In the same way, the number of fixed columns on the left side of the object is controlled by the FixedCols property. Setting either of these values to 0 would hide the fixed column or row:
FixedCols=1; FixedRows=1
FixedCols=0; FixedRows=0
FixedCols=1; FixedRows=0
FixedCols=0; FixedRows=1
FixedCols=2; FixedRows=1
FixedCols=1; FixedRows=2
26.3.3 Cells Properties The intersection of a column and a row is called a cell. To distinguish cells that hold indicative values and those that hold usable or modifiable values, cells are divided in two categories distinguished by two colors. The cells on the top section and those on the left, 666
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
when displaying a different color than the cells in the middle-center section of the control, are called fixed cells. To guide the user with the values on the grid, the cells on top and those on the left display the same color as the form, known in the Control Panel as the Button Color. The cells that display usable and modifiable values have a background color known in Control Panel as Window Color. To change the background color of cells that display values, use the Color property of the Object Inspector. Here is a grid with the clSkyBlue color:
To change the colors of the fixed columns and rows, change the color value of the FixedColor property. Here is a grid with the clNavy FixedColor:
You can also change these colors programmatically. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TStringGrid *StatRates = new TStringGrid(this); StatRates->Parent = this; StatRates->Color = TColor(RGB(255, 230, 204)); StatRates->FixedColor = TColor(RGB(255, 128, 0));
} //---------------------------------------------------------------------------
To distinguish cells, they are separated by vertical and horizontal lines known as grid lines. By default, the grid lines have a width of 1 integer. To display a wider line, change the value of the GridLineWidth. A reasonable value should be less than 10. If you do not want to display grid lines, set the GridLineWidth to 0. In order to access all of the cells that are part of a column, you should know the column’s index number. The most left column, which is sometimes the fixed column, unless the FixedCols value is set to 0, has an index of 0. The second column from left has an index of 1, etc. In the same way, rows are presented by an index. The most top row has an index of 0; the second row from top has an index of 1, etc.
Copyright © 2003 FunctionX, Inc.
667
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
By default, all columns have a width of 64 pixels. At design or run time, you can control this by changing the value of the DefaultColWidth property. If you want to control the widths of individual columns, at run time, call the TStringGrid::ColWidths property and specify the index of the column you need. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { StringGrid1->Color = TColor(RGB(255, 230, 204)); StringGrid1->FixedColor = TColor(RGB(255, 128, 0)); StringGrid1->ColWidths[2] = 48; StringGrid1->ColWidths[3] = 22; StringGrid1->ColWidths[4] = 96;
} //---------------------------------------------------------------------------
By default, all rows have a height of 24 pixels. At design or run time, you can control this by changing the value of the DefaultRowHeight property. If you want to control the height of individual rows, at run time, call the TStringGrid::ColHeights property and specify the index of the row you want access to. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { StringGrid1->Color = TColor(RGB(255, 230, 204)); StringGrid1->FixedColor = TColor(RGB(255, 128, 0)); StringGrid1->ColWidths[2] = 48; StringGrid1->ColWidths[3] = 22; StringGrid1->ColWidths[4] = 96; StringGrid1->RowHeights[1] = 18; StringGrid1->RowHeights[2] = 40; StringGrid1->RowHeights[3] = 12;
} //---------------------------------------------------------------------------
668
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
The columns of the grid are stored in a collection or array called Cols. By specifying the index of a column, you can change the label of the column header. In the same way, the rows are grouped in a collection called Rows. This allows you to change the text of a row header based on its index. The cells of a grid are stored in a two-dimensional array called Cells. The TStringGrid class provides Options that allow you to customize the behavior of the StringGrid control. For example, if you want to allow the user to move the position of a column, set the goColMoving option to true. If you want users to be able to move rows, set the goRowMoving option to true. In order to display cells with their default control appearance, the DefaultDrawing property must be set to true, which is the default. If you want to further customize the appearance of cells, you may have to draw them at run time. In this case, you would set the DefaultDrawing value to false.
Practical Learning: Controlling a StringGrid Properties 1.
Open the Payroll1 application you started previously
2.
Continue designing the form as follows:
Label Caption: First Week Label Caption: Second Week StringGrid Hint: Enter hours worked for each day in the appropriate cell ColCount: 7 FixedCols: 0 Height: 78 Name: grdTimeSheet RowCount: 3 Width: 458 Copyright © 2003 FunctionX, Inc.
669
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
Options GoEditing: true Panel BitBtn Glyph C:\Program Files\Common Files\Borland Shared\Images\Buttons\calculat.bmp Caption: &Process It Default: true StringGrid ColCount: 3 Height: 78 RowCount: 3 Width: 185 Label Caption: Hourly Salary: Edit Name: edtHourlySalary Width: 58 Label Caption: Total Earnings: Edit Name: edtTotalEarnings Width: 58 BitBtn Kind: bkClose
GoTabs: true
Name: btnProcessIt Name: grdEarnings
3.
Save All
4.
Double-click an empty area on the form to access its OnCreate() event
5.
To customize the StringGrid's cells, add the following code: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { dteStartPeriod->CalColors->BackColor = TColor(RGB(230, 245, 255)); dteStartPeriod->CalColors->MonthBackColor = TColor(RGB(212, 235, 255)); dteStartPeriod->CalColors->TextColor = clBlue; dteStartPeriod->CalColors->TitleBackColor = TColor(RGB(0, 0, 160)); dteStartPeriod->CalColors->TitleTextColor = clWhite; dteStartPeriod->CalColors->TrailingTextColor = TColor(RGB(190, 125, 255)); dteStartPeriod->Format = "ddd d MMM yyyy"; dteEndPeriod->CalColors->BackColor = TColor(RGB(255, 236, 218)); dteEndPeriod->CalColors->MonthBackColor = TColor(RGB(255, 235, 214)); dteEndPeriod->CalColors->TextColor = TColor(RGB(102, 52, 0)); dteEndPeriod->CalColors->TitleBackColor = TColor(RGB(176, 88, 0)); dteEndPeriod->CalColors->TitleTextColor = TColor(RGB(255, 238, 220)); dteEndPeriod->CalColors->TrailingTextColor = TColor(RGB(230, 115, 0)); dteEndPeriod->Format = "ddd d MMM yyyy"; grdTimeSheet->Cells[0][0] grdTimeSheet->Cells[1][0] grdTimeSheet->Cells[2][0] grdTimeSheet->Cells[3][0] grdTimeSheet->Cells[4][0] grdTimeSheet->Cells[5][0] grdTimeSheet->Cells[6][0]
= = = = = = =
"Monday"; "Tuesday"; "Wednesday"; "Thursday"; "Friday"; "Saturday"; "Sunday";
grdTimeSheet->RowHeights[0] = 18; grdTimeSheet->Cells[0][1] = "0.00";
670
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
grdTimeSheet->Cells[1][1] grdTimeSheet->Cells[2][1] grdTimeSheet->Cells[3][1] grdTimeSheet->Cells[4][1] grdTimeSheet->Cells[5][1] grdTimeSheet->Cells[6][1]
= = = = = =
"0.00"; "0.00"; "0.00"; "0.00"; "0.00"; "0.00";
grdTimeSheet->RowHeights[1] = 18; grdTimeSheet->Cells[0][2] grdTimeSheet->Cells[1][2] grdTimeSheet->Cells[2][2] grdTimeSheet->Cells[3][2] grdTimeSheet->Cells[4][2] grdTimeSheet->Cells[5][2] grdTimeSheet->Cells[6][2]
= = = = = = =
"0.00"; "0.00"; "0.00"; "0.00"; "0.00"; "0.00"; "0.00";
grdTimeSheet->RowHeights[2] = 18; grdEarnings->Cells[0][1] grdEarnings->Cells[0][2] grdEarnings->Cells[1][0] grdEarnings->Cells[2][0]
= = = =
"Regular"; "Overtime"; "Hours"; "Amount";
} //---------------------------------------------------------------------------
6.
Save All and Test the application:
7.
Close the form and return to Bcb
26.3.4 StringGrid Methods To programmatically create a StringGrid control, declare a pointer to TStringGrid class using the new operator. Use its default constructor to specify the container of the control as parent. Here is an example: //--------------------------------------------------------------------------#include #include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------#pragma package(smart_init)
Copyright © 2003 FunctionX, Inc.
671
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
#pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TStringGrid *StatRates = new TStringGrid(this); StatRates->Parent = this; } //---------------------------------------------------------------------------
After creating the control, you can set its properties to the values of your choice. After using the control, you can get rid of it using the delete operator, or you can trust its parent to do it for you. If at any time a cell is selected, you can get the rectangular dimension of that cell using the CellRect() method. Its syntax is: TRect __fastcall CellRect(int ACol, int ARow);
The arguments, ACol and ARow, represent the column index and the row index of the cell that has focus. This method returns the TRect rectangle of the cell. You can call this method when the user clicks a cell in the StringGrid control. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { StringGrid1->ColWidths[0] = 72; StringGrid1->ColWidths[1] = 54; StringGrid1->ColWidths[2] = 35; StringGrid1->ColWidths[3] = 75; StringGrid1->RowHeights[0]= StringGrid1->RowHeights[1]= StringGrid1->RowHeights[2]= StringGrid1->RowHeights[3]=
15; 22; 10; 35;
} //--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1Click(TObject *Sender) { TRect Recto = StringGrid1->CellRect(StringGrid1->Col, StringGrid1->Row); int Area = Recto.Width() * Recto.Height(); Label1->Caption = Area; } //---------------------------------------------------------------------------
672
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
If the mouse is positioned or passing somewhere on or over the StringGrid control and you want to know on what cell the mouse is, you can use the MouseToCell() method. Its syntax is: void __fastcall MouseToCell(int X, int Y, int &ACol, int &ARow);
This method is usually used on a Mouse event such as OnMouseDown(), OnMouseMove(), and OnMouseUp() as these events provide the mouse coordinates. The MouseToCell() method retrieves the horizontal and vertical coordinates of the mouse, translates that position to the column and row indexes of the cell under the mouse and return those values. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { int x, y; StringGrid1->MouseToCell(X, Y, x, y); Label1->Caption = "Col: " + AnsiString(x) + " Row: " + AnsiString(y); } //---------------------------------------------------------------------------
In the same way, sometimes when the user clicks a cell, you may want to find out what cell was clicked. To get this information, you can call the GridCoord() method. Its syntax is: struct TGridCoord { int X; int Y; };
Copyright © 2003 FunctionX, Inc.
673
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
TGridCoord __fastcall MouseCoord(int X, int Y);
This method also is usually used in a mouse event. It takes as arguments the mouse position and returns a TPoint-like object, called TGridCoord. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { TGridCoord GC = StringGrid1->MouseCoord(X, Y); Label1->Caption = "Col: " + AnsiString(GC.X) + " Row: " + AnsiString(GC.Y); } //---------------------------------------------------------------------------
26.3.5 StringGrid Events As a descendant of TWinControl, the TStringGrid class inherits all the usual events that are common to Windows control. It fires the OnClick event when the user clicks anywhere on the control. It sends an OnDblClick event when the user double-click any cell. It uses all mouse events (OnMouseDown, OnMouseMove, OnMouseUp, OnMouseWheelDown, and OnMouseWheelUp) as well as keyboard events (OnKeyDown, OnKeyPress, and OnKeyUp). Besides the regular control events, the StringGrid control fires events that are proper to its functionality. Just before the user selects the content of a cell, the control fires the OnSelectCell() event. Its syntax is: void __fastcall OnSelectCell(TObject *Sender, int ACol, int ARow, bool &CanSelect)
The ACol and ARow arguments represent the cell that is about to be selected. The CanSelect argument allows you to specify whether the user is allowed to select the content of the cell. This event allows you to decide whether the cell can be selected or not. The following event is used to let the user know that a certain cell cannot be accessed: //--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1SelectCell(TObject *Sender, int ACol, int ARow, bool &CanSelect) { if( ACol == 2 && ARow == 4 ) { ShowMessage("The content of this cell is not accessible.\n" "Please select another cell!"); return; } } //---------------------------------------------------------------------------
674
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
If the user has selected a cell and wants to edit its content, you can find out the content of such a cell using the OnGetEdtText() event. This even fires as soon as the user has selected text included in a cell but just before the user has had a chance to edit it. This means that you can determine whether the user is allowed to change the contents of a particular cell. When this even fires, it communicates the grid coordinates of the cell that was clicked, allowing you to retrieve the content of that cell and do what you want. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1GetEditText(TObject *Sender, int ACol, int ARow, AnsiString &Value) { AnsiString Content = StringGrid1->Cells[ACol][ARow]; Label1->Caption = Content; } //---------------------------------------------------------------------------
On the other hand, when the user has changed the content of a cell, the StringGrid control fires an OnSetEditText() event. This is a good place to validate, accept, or reject the changes that the user has performed. This event also provides you with the grid coordinates of the cell whose contents the user has modified. To better control what type of text the user is allowed to enter in a cell, in all cells of a particular row, or in all cells of a particular column, you can use the OnGetEditMask() event. Its syntax is: void __fastcall OnGetEditMask(TObject *Sender, int ACol, int ARow, AnsiString &Value)
The ACol and the ARow parameters represent the grid indexes of the cell. The Value is a string of the same type used for the EditMask property of the MaskEdit control. This event is used to set the EditMask needed for a particular cell. The following event restricts only US Social Security Numbers in all cells of the second column: //--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1GetEditMask(TObject *Sender, int ACol, int ARow, AnsiString &Value) { if( StringGrid1->Col == 2 ) Value = "000-00-0000"; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
675
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
If you had allowed the user to move the columns, whenever a user has performed this operation, the StringGrid control would fire the OnColumnMoved() event. Its syntax is: void __fastcall TStringGrid(TObject* Sender, long FromIndex, long ToIndex);
This event is a good place to decide what to do, if there is anything to do, when the user has moved a column. In the same way, if you had allowed the user to move rows, the StringGrid control sends an OnRowMoved() event immediately after the user has moved a row. If you had let the compiler know that you would set the appearance of cells yourself, which would have been communicated by setting the DefaultDrawing property to false, you can use the OnDrawCell() event to perform this customization. The syntax of this event is: void __fastcall StringGridDrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State)
The cell whose characteristics need to be set is Cell[ACol][ARow]. This means that you can locate any cell in the grid and set its properties as you like and as possible. For example, you can change the individual background color of a cell. The following code changes the background color of Cell[3][2] to blue: //--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) { if( ACol == 3 && ARow == 2 ) { StringGrid1->Canvas->Brush->Color = clBlue; StringGrid1->Canvas->FillRect(Rect); } } //---------------------------------------------------------------------------
In the same way, you can change the text color of any cell of your choice independently of the other cells. The Rect parameter is the location and dimension of the cell whose characteristics you want to change. The State argument is a member of the TGridDrawState set which is defined as follows: enum Grids__3 { gdSelected, gdFocused, gdFixed };
676
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
typedef Set TGridDrawState;
This set allows you to examine the state of a particular cell. Because this value is a set, a particular cell can have more than one of these values. If a cell is selected, which by default gives it a background color different than the others, then its State contains the gdSelected value. If a cell has focus, which could mean that the user has just clicked it, sometimes to edit, the cell has the gdFocused value. Note that a cell can be selected and have focus, which means it would have both gdSelected and gdFocused. If a cell is a fixed cell as we described previously, then the cell has the gdFixed value. Here is an example of using the OnDrawCell() event to customize the appearance of a StringGrid object: //--------------------------------------------------------------------------#include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { StringGrid1->DefaultDrawing = False; } //--------------------------------------------------------------------------void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) { if( State.Contains(gdFixed) ) { StringGrid1->Canvas->Brush->Color = static_cast(RGB(255, 155, 0)); StringGrid1->Canvas->Font->Style = TFontStyles() << fsBold; StringGrid1->Canvas->Font->Color = static_cast(RGB(250, 245, 135)); StringGrid1->Canvas->Rectangle(Rect); } else if( State.Contains(gdSelected) ) { StringGrid1->Canvas->Brush->Color = static_cast(RGB(255, 205, 155)); StringGrid1->Canvas->Font->Style = TFontStyles() >> fsBold; StringGrid1->Canvas->Font->Color = clNavy; StringGrid1->Canvas->FillRect(Rect); } else { StringGrid1->Canvas->Brush->Color = clWhite; StringGrid1->Canvas->Font->Color = clBlue; StringGrid1->Canvas->FillRect(Rect); } StringGrid1->ColWidths[0] StringGrid1->ColWidths[1] StringGrid1->ColWidths[2] StringGrid1->ColWidths[3]
Copyright © 2003 FunctionX, Inc.
= = = =
15; 75; 75; 90;
677
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
StringGrid1->ColWidths[4] = 120; StringGrid1->RowHeights[0] StringGrid1->RowHeights[1] StringGrid1->RowHeights[2] StringGrid1->RowHeights[3] StringGrid1->RowHeights[4]
= = = = =
16; 16; 16; 16; 16;
AnsiString text = StringGrid1->Cells[ACol][ARow]; StringGrid1->Canvas->TextRect(Rect, Rect.Left, Rect.Top, text); } //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { StringGrid1->Cells[0][1] = "1"; StringGrid1->Cells[0][2] = "2"; StringGrid1->Cells[0][3] = "3"; StringGrid1->Cells[0][4] = "4"; StringGrid1->Cells[1][0] StringGrid1->Cells[2][0] StringGrid1->Cells[3][0] StringGrid1->Cells[4][0]
= = = =
"First Name"; "Last Name"; "Phone Number"; "Email Address";
StringGrid1->Cells[1][1] = "Alex"; StringGrid1->Cells[2][1] = "Walters"; StringGrid1->Cells[3][1] = "(202) 133-7402"; StringGrid1->Cells[4][1] = "[email protected]"; StringGrid1->Cells[1][2] = "Bertrand"; StringGrid1->Cells[2][2] = "Kumar"; StringGrid1->Cells[4][2] = "[email protected]"; StringGrid1->Cells[3][3] = "Hermine";
} //---------------------------------------------------------------------------
Practical Learning: Using String Grid Events 1.
In the private section of the form, declare two variables and a method as follows: private: double HoursWeek1, HoursWeek2; double __fastcall EvaluateTime(AnsiString StrTime); // User declarations
2.
In the top section of the source file of the form, include the StrUtils header file: //--------------------------------------------------------------------------#include
678
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
#include #include #pragma hdrstop #include "Main.h" //---------------------------------------------------------------------------
3.
Implement the above method as follows: //--------------------------------------------------------------------------double __fastcall TfrmMain::EvaluateTime(AnsiString StrTime) { //TODO: Add your source code here double dValue; AnsiString StrValue = AnsiReplaceStr(StrTime, " ", ""); if( StrValue.IsEmpty() ) return 0.00; else return StrToFloat(StrValue); } //---------------------------------------------------------------------------
4.
To use of the StringGrid events, on the form, click the top StringGrid control
5.
In the Object Inspector, click the Events tab and double-click the event side of OnSetEditText
6.
Implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::grdTimeSheetSetEditText(TObject *Sender, int ACol, int ARow, const AnsiString Value) { double Monday1 = EvaluateTime(grdTimeSheet->Cells[0][1]); double Tuesday1 = EvaluateTime(grdTimeSheet->Cells[1][1]); double Wednesday1 = EvaluateTime(grdTimeSheet->Cells[2][1]); double Thursday1 = EvaluateTime(grdTimeSheet->Cells[3][1]); double Friday1 = EvaluateTime(grdTimeSheet->Cells[4][1]); double Saturday1 = EvaluateTime(grdTimeSheet->Cells[5][1]); double Sunday1 = EvaluateTime(grdTimeSheet->Cells[6][1]); HoursWeek1 = Monday1 + Tuesday1 + Wednesday1 + Thursday1 + Friday1 + Saturday1 + Sunday1; double Monday2 = EvaluateTime(grdTimeSheet->Cells[0][2]); double Tuesday2 = EvaluateTime(grdTimeSheet->Cells[1][2]); double Wednesday2 = EvaluateTime(grdTimeSheet->Cells[2][2]); double Thursday2 = EvaluateTime(grdTimeSheet->Cells[3][2]); double Friday2 = EvaluateTime(grdTimeSheet->Cells[4][2]); double Saturday2 = EvaluateTime(grdTimeSheet->Cells[5][2]); double Sunday2 = EvaluateTime(grdTimeSheet->Cells[6][2]); HoursWeek2 = Monday2 + Tuesday2 + Wednesday2 + Thursday2 + Friday2 + Saturday2 + Sunday2; } //---------------------------------------------------------------------------
7.
On the form, double-click the Process It button and implement its OnClick event as follows: //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
679
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
void __fastcall TfrmMain::btnProcessItClick(TObject *Sender) { double RegHours1, RegHours2, OvtHours1, OvtHours2; double RegAmount1, RegAmount2, OvtAmount1, OvtAmount2; double RegularHours, OvertimeHours; double RegularAmount, OvertimeAmount, TotalEarnings; double HourlySalary = StrToFloat(edtHourlySalary->Text); double OvtSalary = HourlySalary * 1.5; if( HoursWeek1 < 40 ) { RegHours1 = HoursWeek1; RegAmount1 = HourlySalary * RegHours1; OvtHours1 = 0.00; OvtAmount1 = 0.00; } else if( HoursWeek1 >= 40 ) { RegHours1 = 40; RegAmount1 = HourlySalary * 40; OvtHours1 = HoursWeek1 - 40; OvtAmount1 = OvtHours1 * OvtSalary; } if( HoursWeek2 < 40 ) { RegHours2 = HoursWeek2; RegAmount2 = HourlySalary * RegHours2; OvtHours2 = 0.00; OvtAmount2 = 0.00; } else if( HoursWeek2 >= 40 ) { RegHours2 = 40; RegAmount2 = HourlySalary * 40; OvtHours2 = HoursWeek2 - 40; OvtAmount2 = OvtHours2 * OvtSalary; } RegularHours = RegHours1 + RegHours2; OvertimeHours = OvtHours1 + OvtHours2; RegularAmount = RegAmount1 + RegAmount2; OvertimeAmount = OvtAmount1 + OvtAmount2; TotalEarnings = RegularAmount + OvertimeAmount; grdEarnings->Cells[1][1] grdEarnings->Cells[1][2] grdEarnings->Cells[2][1] grdEarnings->Cells[2][2] edtTotalEarnings->Text
= FloatToStrF(RegularHours, ffFixed, 6, 2); = FloatToStrF(OvertimeHours, ffFixed, 6, 2); = FloatToStrF(RegularAmount, ffFixed, 6, 2); = FloatToStrF(OvertimeAmount, ffFixed, 6, 2); = FloatToStrF(TotalEarnings, ffFixed, 6, 2);
} //---------------------------------------------------------------------------
8.
680
Save All and test the application. Here is an example:
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
9.
Chapter 26: Grid-Based Controls
Close the form and return to Bcb
26.4 The CCalendar Control 26.4.1 Introduction Borland C++ Builder ships with a custom or sample control called CCalendar
This control can be used anywhere you need a flexible calendar other than the Windows MonthCalendar control. You may be wondering why one more calendar, considering that those we reviewed above seemed to be enough. The CCalendar control was configured to only display the days of one month, on a grid. As such, it gives you a lot of customization on the way the control appears. It gives you complete control on the way date-related calculations are performed.
26.4.2 Using a CCalendar To add a CCalendar control to an application, click its button of the Component Palette and click a form or other container.
from the Samples tab
Behind the scenes, the CCalendar control is based on the TCustomGrid class. Therefore, the control is made of a series of 7 columns and 6 rows, separated by grid lines whose width is one pixel. As you would do with the StringGrid object, you can change the Copyright © 2003 FunctionX, Inc.
681
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
background color of the Ccalendar control using the Color property and you can control the width of the grid lines with the GridLineWidth property. Like most other controls, the CCalendar control is surrounded with a border. Unlike most controls, this object uses the same BorderStyle as the Form but with different effects. For example, the default bsSingle value gives it a sunken effect but all the other styles (bsDialog, bsNone, bsSizeable, bsSizeToolWin, and bsToolWindow) remove the borders on the left, the right, and the bottom sides of the control while keeping the names of days raised. The CCalendar control is meant to display one month starting with the first week of the month. If you want the calendar to start on a different week, such as the second, third, or fourth week, change the value of the StartOfWeek property. The days of the month display in cells. When you add the control on a form, it assumes and displays the current date, selecting the current month and year, highlighting the cell that contains the current day, using the current date of the computer. If you do not want to display the current date, you can set the UseCurrentDate Boolean value to false from its true default. You can also specify what day of the month, what month, and what year should be selected when the control appears. These characteristics are respectively controlled by the Day, the Month, and the Year properties. To change the day of the month, the user can click the desired day, which selects its cell and gives it a different background color than the other cells. If you want a calendar to only display a date and you would not like the user to be able to change the date, set the ReadOnly property to true.
26.5 The Color Grid Control 26.5.1 Overview
682
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
The Color Grid is a custom control used to display a series of 16 colors in columns and rows. The colors appear in cells. The grid can be organized to display as 4 rows of 4 columns, 1 row of 16 columns, 16 rows of 1 column, 8 rows of 2 columns, or 2 rows of 8 columns:
To use this control, the user clicks a color in a cell. When using this control, the user can take advantage of the left and the right mouse buttons because the selection of a color depends on what button of the mouse was pressed. If the user clicks a cell with the left mouse button, a color known as Foreground is selected. The right mouse button is used to select a color known as Background. There is no predictable reason why you would implement a Color Grid control on your application except to let the user select colors. This means that it is up to you to decide what this control is used for. Although you can create an object, add 16 buttons, and configure their colors, this control highly simplifies your efforts.
26.5.2 Using a Color Grid Control To use a Color Grid in your application, click a ColorGrid button tab of the Component Palette and click on the form or container.
from the Samples
After adding the control, it display 4 columns and 4 rows of colors. If you want to align cells different, as seen on the above picture, change the value of the GridOrdering property. The options are go16x1, go1x16, go2x8, go4x4 (the default), and go8x2. Their enumerator is defined as: enum TGridOrdering { go16x1, go8x2, go4x4, go2x8, go1x16 };
The Color Grid control is used to specify two colors. The colors are specified by the cells of the grid. These cells are numbered from left to right, then from top to bottom. This numbering allows you to refer to any cell when necessary. When the control has just been added to a form, both the foreground and the background colors are set to the first cell, 0, which is black. When using the control, the user would Copyright © 2003 FunctionX, Inc.
683
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
click a cell on the grid. If you do not want the users to be able to select or change a color, set the Enabled property to false. If the control is enabled and if the user clicks with the left mouse button, a color referred to as Foreground is selected and its cell displays FG. If the user clicks with the right mouse button, a color called Background is selected and its cell would display the GB label. These two labels allow the user to know the current colors that are selected. If you want to hide the indicating labels, use the ForegroundEnabled and the BackgroundEnabled properties. Setting either to false would hide its corresponding label.
26.6 The Value List Editor 26.6.1 Introduction The value list editor is a grid-based control that is used to create an ini list. An ini list is a group of strings in the form Name=Value. Although you can create an ini list manually, the VCL provides a visual mechanism of creating and managing such a list.
26.6.2 The Visual List Editor Control To create a list of items of the form Name=Value, you can use a special control, VisualListEditor Palette.
, which is available from the Additional tab of the Component
After adding the control to a form or to another container such as a frame or a panel, you can create the actual list using the Strings property. To edit the list, type a string under the Key column and type another string under the Value column. You can also edit the list in an event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { // Fill out the Value List Editor with a few strings ValueListEditor1->Strings->Add("Maryland=MD"); ValueListEditor1->Strings->Add("Virginia=VA"); ValueListEditor1->Strings->Add("District of Columbia=DC"); ValueListEditor1->Strings->Add("West Virginia=WV"); ValueListEditor1->Strings->Add("Delaware=DE"); } //---------------------------------------------------------------------------
26.6.3 Combo Boxes on a Value List Editor The reason for using the Value List Editor is to allow the user to create or edit a list of items. The user does this by typing the desired strings in the Key and the Value columns. Alternatively, instead of typing the values, you can let the user select the desired value. To do this, you can provide either a combo box or an external dialog box.
684
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
To provide a combo box on a Value cell, first create a list, and then assign it to the PickList property of the ItemProps member variable of the TValueListEditor class. When doing this, you must specify on the ItemProps which value will use the combo box. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { // Fill out the Value List Editor with a few strings ValueListEditor1->Strings->Add("Maryland=MD"); ValueListEditor1->Strings->Add("Virginia=VA"); ValueListEditor1->Strings->Add("District of Columbia=DC"); ValueListEditor1->Strings->Add("West Virginia=WV"); ValueListEditor1->Strings->Add("Delaware=DE"); // Before creating a combo box, create a list of items TStringList *ListOfStates = new TStringList; ListOfStates->Add("SD"); ListOfStates->Add("ND"); ListOfStates->Add("NV"); ListOfStates->Add("VA"); ListOfStates->Add("WV"); ListOfStates->Add("WA"); ListOfStates->Add("SC"); ListOfStates->Add("NC"); ListOfStates->Add("PA"); ListOfStates->Add("IL"); ListOfStates->Add("LA"); ListOfStates->Add("TX"); ListOfStates->Add("MD"); ListOfStates->Add("DC"); ListOfStates->Add("TN"); ListOfStates->Add("MI"); // Put a combo box in the second item ValueListEditor1->ItemProps[1]->PickList = ListOfStates; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
685
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
Figure 93: The Value List Editor With a Combo Box Using this approach, you can create different lists or different combo boxes for various Value cells. To do this, you should use the OnGetPickList event. In fact, instead of creating the list of items of a combo box in the OnCreate event of the form, which makes you specify what cell would have the combo box, if you want all Value cells to have a combo box, you can create the list in the OnGetPickList event as follows //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { // Fill out the Value List Editor with a few strings ValueListEditor1->Strings->Add("Maryland=MD"); ValueListEditor1->Strings->Add("Virginia=VA"); ValueListEditor1->Strings->Add("District of Columbia=DC"); ValueListEditor1->Strings->Add("West Virginia=WV"); ValueListEditor1->Strings->Add("Delaware=DE"); } //--------------------------------------------------------------------------void __fastcall TForm1::ValueListEditor1GetPickList(TObject *Sender, const AnsiString KeyName, TStrings *Values) { Values->Add("SD"); Values->Add("ND"); Values->Add("NV"); Values->Add("VA"); Values->Add("WV"); Values->Add("WA"); Values->Add("SC"); Values->Add("NC"); Values->Add("PA"); Values->Add("IL"); Values->Add("LA"); Values->Add("TX"); Values->Add("MD"); Values->Add("DC");
686
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
Values->Add("TN"); Values->Add("MI"); } //---------------------------------------------------------------------------
In the same way, you can display a list that depends on the string of the Key cell. This allows you to display a combo box only if the Key cell is not empty. This can be done as follows: //--------------------------------------------------------------------------void __fastcall TForm1::ValueListEditor1GetPickList(TObject *Sender, const AnsiString KeyName, TStrings *Values) { if( KeyName == "CAF" ) { Values->Add("Cameroon"); Values->Add("Senegal"); Values->Add("Lybia"); Values->Add("Ghana"); Values->Add("Tunisia"); } else if( KeyName == "CONCACAF" ) { Values->Add("USA"); Values->Add("Costa Rica"); Values->Add("Canada"); Values->Add("Colombia"); Values->Add("Nicaragua"); Values->Add("Jamaica"); } else if( KeyName == "UEFA" ) { Values->Add("Portugal"); Values->Add("Spain"); Values->Add("Italy"); Values->Add("Holland"); } else { Values->Add("New Zealand"); Values->Add("Greece"); Values->Add("Gabon"); } } //---------------------------------------------------------------------------
26.6.4 Ellipsis Buttons on a Value List Editor To create an ellipsis button, you assign the appropriate style to the EditStyle property of the ItemProps member variable. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { // Fill out the Value List Editor with a few strings ValueListEditor1->Strings->Add("Maryland=MD"); ValueListEditor1->Strings->Add("Virginia=VA");
Copyright © 2003 FunctionX, Inc.
687
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
ValueListEditor1->Strings->Add("District of Columbia=DC"); ValueListEditor1->Strings->Add("West Virginia=WV"); ValueListEditor1->Strings->Add("Delaware=DE"); // Put an ellipsis button in the 4th item ValueListEditor1->ItemProps[3]->EditStyle = esEllipsis; } //---------------------------------------------------------------------------
After adding a button to a Value cell, it is your responsibility to specify what would happen when the user clicks it. For example, imagine you want the user to select a file and provide its path as the value of a cell, you can implement this behavior in the TValueListEditor::OnEditButtonClick() event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::ValueListEditor1EditButtonClick(TObject *Sender) { if( OpenDialog1->Execute() ) ValueListEditor1->Values["West Virginia"] = "Result=" + OpenDialog1->FileName; } //---------------------------------------------------------------------------
Figure 94: The Value List Editor With a Button
688
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
Chapter 27: Lists View Controls 27.1 Tree Views 27.1.1 Overview A TreeView consists of displaying a hierarchical view of items arranged in a parent-child format. It is typically used to show items that belong to interrelated categories, such as parent to child and child to grandchild, etc; or folder to subfolder to file. The starting item of the tree is called the Root and illustrates the beginning of the tree. Each item, including the root, that belongs to the tree is referred to as a node. In the following charts, the down arrow means, "has the following child or children"
A TreeView is not limited to a one-to-one correspondence. Not only can an item have more than one dependency, but also a child can make the tree stop at any category. Categories of a TreeView are organized by levels. The most used trees have one parent called the root; then under the root start the dependents. Here is an example representing the world and some countries:
The children of a parent are recognized by their belonging to the same level but can have different behaviors; for example, while one child might have another child (or other children), an item on the same level does not necessarily abide by a set rule. Everything usually depends on the tree designer.
Copyright © 2003 FunctionX, Inc.
689
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
Consider the following tree:
Practical Learning: Preparing a Tree View 1.
Create a new project with its starting form.
2.
To save the project, on the Standard toolbar, click Save All
3.
Locate the folder where the exercises are selected.
4.
Click the Create New Folder button, type Countries1 and press Enter twice to display the new folder in the Save In combo box.
5.
Click Save twice to save the unit and the project names.
6.
Change the Caption of the form to Countries Statistics
7.
From the Standard tab of the Component Palette, double-click the Panel control .
8.
Set the following properties: Align = alTop Alignment = taLeftJustify BevelInner = bvRaised bvOuter = bvLowered Height = 28
9.
To type the caption, click the Caption field, press Delete, press Space, and type Countries
10. Click an unoccupied area of the form to deselect the panel 11. From the Standard tab, double-click the Panel control
again.
12. Set its Align property to alBottom, its BevelOuter to bvLowered, delete the value of its Caption, and set its Height to 20
27.1.2 Tree View Design C++ Builder provides an exceptional way of creating a tree view at design time; in other words, you can create a complete tree view without writing a single line of code. A member of a TreeView is called a node. Visually, a node on a TreeView displays its text in one of two states: selected or not selected. On another visual front, a node has a child or it is standing alone. When a node has a child, it displays a + (collapsed) or – 690
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
(expanded) buttons. These last two details will be important because at times you will need to know whether a node is selected or not, whether a node has a child or not. The first issue is to know how to create a TreeView and how to assign a child or children to a node.To create a tree view, use the TreeView control from the Win32 tab of the Component Palette. After placing the control on the form, create its children using the Items property. To include pictures on your tree, use an ImageList control.
Practical Learning: Designing a Tree View 1.
Click in the middle of the form. On the Win32 tab of the Component Palette doubleclick the TreeView control
2.
While the new TreeView1 control is still selected, on the Object Inspector, set its Align property to alLeft and its Width to 140
3.
Click the Items field and click its ellipsis button
4.
On the TreeView Items Editor dialog box, click the New Item button
5.
Type World and press Enter
6.
Type Universe
7.
On the dialog box, click Universe and click Delete
8.
Click World and click the New Subitem button.
9.
Type America and press Enter
10. Type Africana and press Enter 11. Type Europe 12. Click Africana and using the Text edit box, edit it to display Africa and press Enter 13. Type Asia 14. Click America and click New Subitem 15. Type Chile and press Enter. 16. Type Canada 17. Click Asia and click New Subitem 18. Type Thailand and press Enter 19. Type Afghanistan
Copyright © 2003 FunctionX, Inc.
691
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
20. Click OK. 21. To test the TreeView, press F9 22. Notice the + button on the World item. 23. Click + and notice the – button 24. Click the other + button to expand the tree items and click the – signs
25. After using the form, close it and save the project 26. To use some images, make sure the Component Palette displays the Win32 tab. Click the ImageList control. 27. Click anywhere on the form. 28. On the form, double-click the ImageList button
29. On the Form1->ImageList1 ImageList dialog box, click the Add button. 30. Locate the folder where the exercises are located and display the Bitmaps folder. 31. Click World and click Open 32. Repeat the same process to add Band, Dans, Category, Insert, and Records
692
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
33. Click OK. 34. On the form, click TreeView1 to select it. 35. On the Object Inspector, set the Images property to ImageList1. 36. While the tree view control is still selected, click the Items field and click its ellipsis button. 37. On the TreeView Items Editor dialog box, click World to select it. Set its Image Index to 0 and its Selected Index to 1 38. Click the + button on World to expand it. 39. Click America to select it. 40. Set its Image Index to 2 and its Selected Index to 3 41. Set the other images according to the following table: Item – Text
Image Index
Selected Index
World America / Africa/ Europe / Asia Chile / Canada / Thailand / Afghanistan
0 2 4
1 3 5
42. Click OK 43. To test the TreeView, press F9 44. Click the various + to expand and click items to select them. Notice the change of graphics
Copyright © 2003 FunctionX, Inc.
693
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
45. After using the form, close it and save the application
27.1.3 Dynamic Tree Views C++ Builder provides three classes equipped with various properties and functions to deal with a tree view. The basic class that manages the TreeView control is the TTreeView. This class controls the visual settings such as background color, borders, dimensions, etc. Each node is managed by the TTreeNode class. The class is used as a descriptor of the control. For example, it can be used to find out what node is selected, whether the node is expanded or collapsed, whether a node has children. The TTreeNodes class is used to manage a TreeView; it combines various operations such as creating the treeview, adding, inserting or deleting nodes, or updating the list of items. To create a tree view, position a TreeView control on the form (although you can still create it completely with code). Once the control is placed, you can customize its window settings derived from the TControl class. The nodes are usually created from the OnCreate event of the host form. To display check boxes on the items of a tree view, call the SetWindowLong() function and specify that you want to add the TVS_CHECKBOXES style. This can be done as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { LONG GWLTreeView = GetWindowLong(TreeView1->Handle, GWL_STYLE); SetWindowLong(TreeView1->Handle, GWL_STYLE, GWLTreeView | TVS_CHECKBOXES); } //---------------------------------------------------------------------------
Practical Learning: Programmatically Creating a Tree View
694
1.
Create a new project with its starting form
2.
Save it in a new folder named Countries2
3.
Save the unit as Main and save the project as Countries2
4.
On the Win32 tab of the Component Palette double-click the TreeView control Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
5.
While the new TreeView control is still selected, set its Align property to alLeft and its width to 175
6.
To create the treeview, double-click an unoccupied area of the form to access the form’s OnCreate event.Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { // The root node TreeView1->Items->Add(NULL, "World"); // First child and its children TreeView1->Items->AddChild(TreeView1->Items->Item[0], "Africa"); TreeView1->Items->AddChild(TreeView1->Items->Item[1], "Botswana"); TreeView1->Items->AddChild(TreeView1->Items->Item[1], "Benin"); // Second child and its children TreeView1->Items->AddChild(TreeView1->Items->Item[0], TreeView1->Items->AddChild(TreeView1->Items->Item[4], TreeView1->Items->AddChild(TreeView1->Items->Item[4], TreeView1->Items->AddChild(TreeView1->Items->Item[4],
"Europe"); "Belgium"); "Denmark"); "Poland");
// Third child and its children TreeView1->Items->AddChild(TreeView1->Items->Item[0], "America"); TreeView1->Items->AddChild(TreeView1->Items->Item[8], "Panama"); TreeView1->Items->AddChild(TreeView1->Items->Item[8], "Colombia"); // Fourth child and its children TreeView1->Items->AddChild(TreeView1->Items->Item[0], "Asia"); TreeView1->Items->AddChild(TreeView1->Items->Item[11], "Bangladesh");
} //---------------------------------------------------------------------------
7.
To test the treeview, press F9
8.
Expand the various nodes to display their children
9.
Close the form
10. To implement a more professional treeview creation, change the listing of the OnCreate event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TTreeNode *World, *Continent; // The root node TreeView1->Items->Add(NULL, "World"); World = TreeView1->Items->Item[0]; // First child of the root TreeView1->Items->AddChild(World, "Africa"); Continent = TreeView1->Items->Item[1]; // Children of first node TreeView1->Items->AddChild(Continent, "Botswana"); TreeView1->Items->AddChild(Continent, "Benin"); // Second child of the root TreeView1->Items->AddChild(World, "Europe");
Copyright © 2003 FunctionX, Inc.
695
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
Continent = TreeView1->Items->Item[4]; // Children of second node TreeView1->Items->AddChild(Continent, "Belgium"); TreeView1->Items->AddChild(Continent, "Denmark"); TreeView1->Items->AddChild(Continent, "Poland"); // Third child of the root TreeView1->Items->AddChild(World, "America"); Continent = TreeView1->Items->Item[8]; // Children of the third node TreeView1->Items->AddChild(Continent, "Panama"); TreeView1->Items->AddChild(Continent, "Colombia"); // Fourth child of the root TreeView1->Items->AddChild(World, "Asia"); Continent = TreeView1->Items->Item[11]; TreeView1->Items->AddChild(Continent, "Asia"); TreeView1->Items->AddChild(Continent, "Bangladesh"); } //---------------------------------------------------------------------------
11. To display the form, press F12.
27.1.4 Adding Images to Nodes We have seen than a tree view's nodes are created from a combination of the TTreeNode and the TTreeNodes classes. The images associated with nodes are handled by the Images property from the TCustomTreeView class. To use images on a treeview, first include an ImageList control to the form. Using the Form ImageList dialog box, add the desired images to the list of images. Note the position of each image in the list because you will need to know the position of each image. To associate an image to a node, first declare a TTreeNode object and set or assign it a position. The TTreeNode object needs to know the position of the node for almost any operation that node will be involved in. The most important property related to an image is the ImageIndex; this is the position of the image on the list created previously. The other image you might be interested in is the Selected index: this informs the TreeNode object about the position of the image the node will display when selected.
Practical Learning: Adding Pictures to a Tree View
696
1.
To add some pictures to the treeview, from the Win32 tab, double-click the ImageList to add it to the form
2.
On the form, double-click the ImageList icon
3.
Click Add.
4.
From the Icons folder, include the following icons: Options, World, Target, Dans, Botswana, Belgium, Denmark, and Columbia.
5.
Click OK Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
6.
On the form, click the TreeView control.
7.
On the Object Inspector, set the Images field to ImageList1
8.
To assign the images to the appropriate nodes, at the end of the OnCreate even, just before the closing curly bracket, type the following: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TTreeNode *World, *Continent; ... TreeView1->Items->Item[2]->ImageIndex = 4; TreeView1->Items->Item[5]->ImageIndex = 5; TreeView1->Items->Item[6]->ImageIndex = 6; TreeView1->Items->Item[10]->ImageIndex = 7; } //---------------------------------------------------------------------------
9.
To test the treeview, press F9
10. After viewing and using the form, close it.
27.2 List Views 27.2.1 Introduction A list view consists of using one of four views to display a list of items. The list is typically equipped with icons that allow the user to verify what view is displaying. There are four views used to display items Large Icons: The view displays a list of items using icons with a 32x32 pixels size of icons. This is the preferred view when the main idea consists of giving an overview of the items Small Icons: Like the other next two views, it uses 16x16 pixel icons to display a simplified list of the items. Once more, no detail is provided about the items of this list. The list is organized in disparate columns with some on top of others. If the list is supposed to be sorted, the alphabetical arrangement is organized from left to right. List: This list, using small icons, is also organized in columns; this time, the columns are arranged so that the first column gets filled before starting the second. If the list is sorted, the sorting is arranged in a top-down manner. Details: This view displays arranged columns of items and provides as much detail as the list developer had arranged it.
27.2.2 List View Design C++ Builder provides a means of creating a list view without any coding, as long as you need just one view to display the list.
Copyright © 2003 FunctionX, Inc.
697
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
from the Win32 tab. If you plan to To create a list view, use the ListView control display a list without columns, you can create the list in a major one-step process. If the list needs a column, its creation would involve a major two-step process. Once the ListView control is placed on the form, create its items using the Items field. The columns are created using the Columns field.
Practical Learning: Designing a List View 1.
Create a new project with its starting form
2.
Save it in a new folder named Statistics1
3.
Save the unit as Main and save the project as Statistics1
4.
Change the Caption of the form to Statistics
5.
Change its dimensions to Height = 195 and Width = 445
6.
To create our listview, on the Component Palette, click the Win32 tab.
7.
Click the ListView control
8.
Click on the form.
9.
Change the position and the dimensions Height = 114 Left = 16 Top = 16 Width = 410
10. While the listview control is still selected on the form, on the Object Inspector, click the Items field and click its ellipsis button 11. On the ListView Items Editor dialog, click New Item 12. Type Yemen and press Enter. 13. Type Botswana and press Enter 14. Type Belgium and press Enter 15. Type Colombia and press Enter 16. Type Denmark and press Enter 17. Type Benin
18. Click OK. 19. To test the list view, press F9. 20. After viewing the listview, close the form 698
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
21. On the form, click the listview. On the Object Inspector, click the Columns field and click its ellipsis button. 22. From the Editing ListView1->Columns window, click Add New Item. 23. While the 0 – TListColumn line is still selected, type Countries to change the Caption of the column header. 24. Click the Width field and type 80 25. Click Add New Item, type Area and change its width to 100 26. Click Add New Item, type Population and change its width to 100 27. Click Add New Item, type Budget and change its width to 50 28. Click Add New Item, type Capital and change its width to 60
29. Close the Editing ListView1->Columns window. 30. On the Object Inspector, change the ViewStyle property to vsReport 31. To add the images to the listview, on the Win32 tab, double-click the ImageList control 32. With the ImageList1 selected, change its Name to imgLarge 33. On the form, double-click the imgLarge control to add the images 34. Click Add 35. Locate the folder where the exercises are located. Access the Flags folder. 36. Click Yemen and click Open. If you receive a message warning that the bitmap is too large…, click No to All 37. Add the other following flags: Botswana, Belgium, Colombia, Denmark, and Benin:
Copyright © 2003 FunctionX, Inc.
699
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
38. Click the list as follows: Item Yemen
700
Image Index 0
Botswana
1
Belgium
2
Colombia
3
Danemark
4
Benin
5
New SubItem 527,970 17,479,206 17B Sanaa 600,370 1,576,470 1.6B Gaborone 30,510 10,241,506 116.5B Brussels 1,138,910 39,685,655 22B Bogota 43,094 5,336,394 59.7B Copenhagen 112,620 6,395,919 299M Cotonou
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
39. Click OK. 40. On the Win32 tab, double-click the ImageList control again. 41. Change its Name to imgSmall 42. Double-click the imgSmall icon 43. Using the Add button, add the following bitmaps: smYemen, smBotswana, smBelgium, smColombia, smDanemark, and smBenin 44. Click OK 45. On the form, click the ListView. 46. On the Object Inspector, set the SmallImages property to imgSmall 47. Change the ViewStyle to vsReport. 48. To test the listview, press F9. 49. After viewing the listview, close the form. 50. On the Component Palette, click the Standard tab. 51. Add four buttons to the form positioned under the list box. 52. Change their captions to &Large Icons, &Small Icons, &List, and &Details respectively: 53. Double-click the Large Icons button to access its OnClick event. 54. Double-click the other buttons and implement their events as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { ListView1->ViewStyle = vsIcon; } //--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender) { ListView1->ViewStyle = vsSmallIcon; } //--------------------------------------------------------------------------void __fastcall TForm1::Button3Click(TObject *Sender) { ListView1->ViewStyle = vsList; } //--------------------------------------------------------------------------void __fastcall TForm1::Button4Click(TObject *Sender) {
Copyright © 2003 FunctionX, Inc.
701
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
ListView1->ViewStyle = vsReport; } //---------------------------------------------------------------------------
55. To test the ListView, press F9. 56. After viewing the ListView, close the form.
27.2.3 Dynamic List Views Creating a ListView from code provides the same flexibility as a ListView object created at design-time, minus the ability to visualize live the list being created. To create a list view, place a ListView control on the form. The columns and the items of the list are created separately. A column is created using the TListColumn class. This allows you to add and format columns. An item of the list is created using the TListItem class. To display check boxes on the list view, send a LVM_SETEXTENDEDLISTVIEWSTYLE message to the control. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { SendMessage((HWND)ListView1->Handle, (UINT)LVM_SETEXTENDEDLISTVIEWSTYLE, (WPARAM)(DWORD) LVS_EX_CHECKBOXES, (LPARAM)LVS_EX_CHECKBOXES); } //---------------------------------------------------------------------------
Practical Learning: Programmatically Creating a List View
702
1.
Start a new application with its default form
2.
Save it in a new folder named Countries3
3.
Save the unit as Main and save the project as Countries3
4.
From the Win32 tab of the Component Palette, double-click the ListView control
5.
Change the Height of the ListView1 control to 200 and its Width to 350
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
6.
Double-click an empty area on the form and implement the OnCreate event of the form as form
7.
At the end of the existing content of the event but before the closing bracket, type: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TListColumn *ListCol; TListItem *ListIt; ListCol = ListView1->Columns->Add(); ListCol->Caption = "Country"; ListCol->Width = 95; ListCol = ListView1->Columns->Add(); ListCol->Caption = "Area (Km2)"; ListCol->Width = 70; ListCol->Alignment = taRightJustify; ListCol = ListView1->Columns->Add(); ListCol->Caption = "Population (M)"; ListCol->Width = 90; ListCol->Alignment = taRightJustify; ListCol = ListView1->Columns->Add(); ListCol->Caption = "Budget"; ListCol->Width = 50; ListCol->Alignment = taRightJustify; ListCol = ListView1->Columns->Add(); ListCol->Caption = "Capital"; ListCol->Width = 85; ListCol = ListView1->Columns->Add(); ListCol->Caption = "@"; ListCol->Width = 30; ListIt = ListView1->Items->Add(); ListIt->Caption = "Belgium"; ListIt->SubItems->Add("30,510"); ListIt->SubItems->Add("10,241,506"); ListIt->SubItems->Add("116.5B"); ListIt->SubItems->Add("Gaborone"); ListIt->SubItems->Add("BC"); ListIt = ListView1->Items->Add(); ListIt->Caption = "Colombia"; ListIt->SubItems->Add("1,138,910"); ListIt->SubItems->Add("39,685,655"); ListIt->SubItems->Add("22B"); ListIt->SubItems->Add("Bogota"); ListIt->SubItems->Add("CO"); ListIt = ListView1->Items->Add(); ListIt->Caption = "Botswana"; ListIt->SubItems->Add("600,370"); ListIt->SubItems->Add("1,576,470"); ListIt->SubItems->Add("1.6B"); ListIt->SubItems->Add("Gaborone");
Copyright © 2003 FunctionX, Inc.
703
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
ListIt->SubItems->Add("BC"); ListIt = ListView1->Items->Add(); ListIt->Caption = "Danemark"; ListIt->SubItems->Add("43,094"); ListIt->SubItems->Add("5,336,394"); ListIt->SubItems->Add("59.7B"); ListIt->SubItems->Add("Copenhagen"); ListIt->SubItems->Add("DA"); ListIt = ListView1->Items->Add(); ListIt->Caption = "Bangladesh"; ListIt->SubItems->Add("144,000"); ListIt->SubItems->Add("129,194,224"); ListIt->SubItems->Add("4.3B"); ListIt->SubItems->Add("Dhaka"); ListIt->SubItems->Add("BG"); ListIt = ListView1->Items->Add(); ListIt->Caption = "Benin"; ListIt->SubItems->Add("112,620"); ListIt->SubItems->Add("6,395,919"); ListIt->SubItems->Add("299M"); ListIt->SubItems->Add("Cotonou"); ListIt->SubItems->Add("BN"); ListView1->ViewStyle = vsReport; } //---------------------------------------------------------------------------
8.
To test the form, press F9
9.
After viewing the form, close it
10. Press F12 to display the form. To add images, from the Win32 tab, double-click the ImageList control. 11. Double-click the ImageList icon on the form. 12. Click Add and add the following images: Map, Belgium, Colombia, Botswana, Denmark, Bangladesh, and Benin
704
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
13. Click OK 14. On the form, click the ListView1 control and, on the Object Inspector, set its Images value to ImageList1 15. Open the FormCreate event again. 16. After the TListItem line, add the following: ListView1->LargeImages = ImageList1; ListView1->SmallImages = ImageList1; 17. After the first ListIt->Caption line, add the following: ListIt->Caption = "Belgium"; ListIt->ImageIndex = 0; 18. In the same way, assign an image to each item list: ListIt->Caption = "Colombia"; ListIt->ImageIndex = 1; ListIt->Caption = "Botswana"; ListIt->ImageIndex = 2; ListIt->Caption = "Danemark"; ListIt->ImageIndex = 3; ListIt->Caption = "Bangladesh"; ListIt->ImageIndex = 4; ListIt->Caption = "Benin"; ListIt->ImageIndex = 5;
19. To test the form, press F9
20. After using the form, close it. 21. On the main menu, click File -> Reopen -> ..\Countries\Project1.bpr. If the project is not on the list, open it. 22. On the form, double-click the TreeView1 control. 23. In the TreeView Item Editor, click each country and click Delete to delete all countries except the continents:
Copyright © 2003 FunctionX, Inc.
705
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
24. Click OK 25. On the form, click an unoccupied area of the form 26. From the Standard tab of the Component Palette, double-click the Panel control 27. Delete its caption and set its Align property to alClient 28. Make sure the latest panel is still selected. 29. From the Standard tab, double-click the Panel control again. 30. Set its Align property to alTop. Set its BevelOuter to bvLowered. Delete its caption and set its Height to 38 31. On the form, click the biggest section of the form, it is occupied by the biggest panel (this should be Panel3). 32. From the Win32 tab of the Component Palette, double-click the ListView control. 33. With the ListView still selected, change its Align property to alClient and its ViewStyle to vsReport. 34. From the Win32 tab of the Component Palette, double-click ImageList. 35. On the form, click the ListView1 control. On the Object Inspector, set both the LargeImages and SmallImages values to ImageList2 36. On the form, double-click ImageList2. Using the Add button and from the Icons folder, add the following icons: Zambia, Guinea, Benin, Austria, Finland, Romania, Monaco, Jamaica, Colombia, Yemen, SouthKorea, and Lebanon 37. Click OK 38. Press F12 to display the Code Editor. In the private section of the TForm1 class, declare the following function: void __fastcall DisplayColumns(TTreeNode *NodeSelected);
39. In the source file, implement the function as follows: //--------------------------------------------------------------------------void __fastcall TForm1::DisplayColumns(TTreeNode *NodeSelected) { TListColumn *Col; // Erase the previous columns so they would not be added to the new ones ListView1->Columns->Clear(); // Find out what node was selected, the root or a continent if( NodeSelected == TreeView1->Items->Item[0] ) {
706
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
Col = ListView1->Columns->Add(); Col->Caption = "Name"; Col->Width = 100; Col = ListView1->Columns->Add(); Col->Caption = "Area (sq km)"; Col->Width = 120; Col->Alignment = taRightJustify; Col = ListView1->Columns->Add(); Col->Caption = "Population"; Col->Width = 120; Col->Alignment = taRightJustify;
} else { Col = ListView1->Columns->Add(); Col->Caption = "Name"; Col->Width = 100; Col = ListView1->Columns->Add(); Col->Caption = "Area (sq km)"; Col->Width = 100; Col->Alignment = taRightJustify; Col = ListView1->Columns->Add(); Col->Caption = "Population"; Col->Width = 100; Col->Alignment = taRightJustify; Col = ListView1->Columns->Add(); Col->Caption = "Capital"; Col->Width = 100; Col = ListView1->Columns->Add(); Col->Caption = "Code"; Col->Width = 40; Col->Alignment = taCenter; } } //---------------------------------------------------------------------------
40. In the header file (Unit1.h), just on top of the class, declare the following twodimensional array: //--------------------------------------------------------------------------#ifndef Unit1H #define Unit1H //--------------------------------------------------------------------------#include #include #include #include #include #include #include const AnsiString CountryStats[12][10] = { // Name Area Population Capital Internet Code { "Zambia", "752,614", "9,959,037", "Lusaka", "zm" }, { "Guinea", "245,857", "7,775,065", "Conakry", "gn" },
Copyright © 2003 FunctionX, Inc.
707
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
{ "Benin", "112,620", "6,787,625", "Cotonou", "bj" }, { "Austria", "83,858", "8,169,929", "Vienna", "at" }, { "Finland", "337,030", "5,183,545", "Helsinki", "fi" }, { "Romania", "237,500", "22,317,730", "Bucharest", "ro" }, { "Monaco", "1.95", "31,987", "Monaco", "mc" }, { "Jamaica", "10,991", "2,680,029", "Kingston", "jm" }, { "Colombia", "1,138,910", "41,008,227", "Bogota", "co" }, { "Yemen", "527,970", "18,701,257", "Sanaa", "ye" }, { "South Korea", "98,480", "48.324 Mlns","Seoul", "kr" }, { "Lebanon", "10,400", "3,677,780", "Beirut", "lb" } }; //--------------------------------------------------------------------------class TForm1 : public TForm
41. Press F12 to display the form. On the form, click the TreeView1 control and, on the Object Inspector, click the Events tab 42. Double-click the event side of OnChange and implement it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::TreeView1Change(TObject *Sender, TTreeNode *Node) { DisplayColumns(Node); ListView1->Items->Clear(); TListItem *lstItem; if( Node->Text == "World" ) { lstItem = ListView1->Items->Add(); lstItem->Caption = "Africa"; lstItem = ListView1->Items->Add(); lstItem->Caption = "Europe"; lstItem = ListView1->Items->Add(); lstItem->Caption = "America"; lstItem = ListView1->Items->Add(); lstItem->Caption = "Asia"; } else if( Node->Text == "Africa" ) { lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[0][0]; lstItem->SubItems->Add(CountryStats[0][1]); lstItem->SubItems->Add(CountryStats[0][2]); lstItem->SubItems->Add(CountryStats[0][3]); lstItem->SubItems->Add(CountryStats[0][4]); lstItem->ImageIndex = 1;
// // // //
Area Population Capital Internet Code
lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[1][0]; lstItem->SubItems->Add(CountryStats[1][1]); lstItem->SubItems->Add(CountryStats[1][2]); lstItem->SubItems->Add(CountryStats[1][3]); lstItem->SubItems->Add(CountryStats[1][4]); lstItem->ImageIndex = 2; lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[2][0]; lstItem->SubItems->Add(CountryStats[2][1]); lstItem->SubItems->Add(CountryStats[2][2]); lstItem->SubItems->Add(CountryStats[2][3]);
708
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
lstItem->SubItems->Add(CountryStats[2][4]); lstItem->ImageIndex = 3; } else if( Node->Text == "Europe" ) { lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[3][0]; lstItem->SubItems->Add(CountryStats[3][1]); lstItem->SubItems->Add(CountryStats[3][2]); lstItem->SubItems->Add(CountryStats[3][3]); lstItem->SubItems->Add(CountryStats[3][4]); lstItem->ImageIndex = 4; lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[4][0]; lstItem->SubItems->Add(CountryStats[4][1]); lstItem->SubItems->Add(CountryStats[4][2]); lstItem->SubItems->Add(CountryStats[4][3]); lstItem->SubItems->Add(CountryStats[4][4]); lstItem->ImageIndex = 5; lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[5][0]; lstItem->SubItems->Add(CountryStats[5][1]); lstItem->SubItems->Add(CountryStats[5][2]); lstItem->SubItems->Add(CountryStats[5][3]); lstItem->SubItems->Add(CountryStats[5][4]); lstItem->ImageIndex = 6; lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[6][0]; lstItem->SubItems->Add(CountryStats[6][1]); lstItem->SubItems->Add(CountryStats[6][2]); lstItem->SubItems->Add(CountryStats[6][3]); lstItem->SubItems->Add(CountryStats[6][4]); lstItem->ImageIndex = 7;
} else if( Node->Text == "America" ) { lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[7][0]; lstItem->SubItems->Add(CountryStats[7][1]); lstItem->SubItems->Add(CountryStats[7][2]); lstItem->SubItems->Add(CountryStats[7][3]); lstItem->SubItems->Add(CountryStats[7][4]); lstItem->ImageIndex = 8; lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[8][0]; lstItem->SubItems->Add(CountryStats[8][1]); lstItem->SubItems->Add(CountryStats[8][2]); lstItem->SubItems->Add(CountryStats[8][3]); lstItem->SubItems->Add(CountryStats[8][4]); lstItem->ImageIndex = 9; } else if( Node->Text == "Asia" ) { lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[9][0];
Copyright © 2003 FunctionX, Inc.
709
Chapter 26: Grid-Based Controls
Borland C++ Builder Programming
lstItem->SubItems->Add(CountryStats[9][1]); lstItem->SubItems->Add(CountryStats[9][2]); lstItem->SubItems->Add(CountryStats[9][3]); lstItem->SubItems->Add(CountryStats[9][4]); lstItem->ImageIndex = 10; lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[10][0]; lstItem->SubItems->Add(CountryStats[10][1]); lstItem->SubItems->Add(CountryStats[10][2]); lstItem->SubItems->Add(CountryStats[10][3]); lstItem->SubItems->Add(CountryStats[10][4]); lstItem->ImageIndex = 11; lstItem = ListView1->Items->Add(); lstItem->Caption = CountryStats[11][0]; lstItem->SubItems->Add(CountryStats[11][1]); lstItem->SubItems->Add(CountryStats[11][2]); lstItem->SubItems->Add(CountryStats[11][3]); lstItem->SubItems->Add(CountryStats[11][4]); lstItem->ImageIndex = 12; } } //---------------------------------------------------------------------------
43. Test the application 44. After using the form, close it and save the project.
27.3 Splitter Bars 27.3.1 Introduction A splitter is a bar that divides a window area in two sections. These sections are referred to as panes or frames.. The splitter bar is used to create independent sections holding various controls. The splitter can divide the sections horizontally or vertically, depending on how you create it. Among other things, the design and implementation allows displaying related data.
27.3.2 Creating a Splitter To create a splitter bar, click the Splitter control from the Additional tab of the Component Palette. The Splitter must be added to a control that has the Align property set to a value other than alNone. If the splitter is embedded to a control that has the alLeft or alRight Align property, it will create two vertical sections. If the Align property of a control is set to alTop or alBottom when receiving the splitter, this creates two horizontal sections. By using a combination of panels and other controls, you can divide the window in as many sections as necessary and in any way.
Practical Learning: Adding a Splitter Bar 1.
710
Display the form. Click the TreeView control on the left side of the form to select it. Make sure its Align property is set to alLeft Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 26: Grid-Based Controls
2.
From the Additional tab of the Component Palette, click the Splitter control
3.
Click the treeview on the left side of the form. Notice that the splitter aligns vertically with the treeview
4.
That’s it. To test the form, press F9:
5.
Drag the splitting line in the middle of the form to resize the sections
6.
After using the form, close it.
Copyright © 2003 FunctionX, Inc.
711
Chapter 28: Creating and Using Lists
712
Borland C++ Builder Programming
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
Chapter 28: Creating and Using Lists 28.1 The TList Class 28.1.1 Introduction We saw how to use the TStrings and its child the TStringList classes to create a list of strings. The role of TStrings is to create a list of single items, such as a list of names or a list of numbers. Sometimes you will want a list that itself is made of objects, an object in this sense being made of its own variables that characterize the object. The Visual Component Library provides various classes adapted for creating a list of objects, such as a list of cars, a list of houses, or a list of countries. As you know, such objects are made of internal parts; for example a car is made of doors, tires, and options (air condition, power steering, cruise control, etc) while a house is made of rooms, furniture, books, electronic devices, etc.
Practical Learning: Creating an Application 1.
Start a new application with its default form.
2.
To save the project, on the Standard toolbar, click the Save All button
3.
Locate the folder that contains your exercises and display it in the Save In combo box.
4.
Click the Create New Folder button. Type Address Book1 and press Enter twice
5.
Click Unit1 to select it, type Main and press Enter.
6.
Type AddressBook1 and press Enter
7.
Change the Caption of the form to Address Book - Contacts List and change its Name to frmMain
28.1.2 List Preparation The TList class is used to create a list of objects, any type of object. In fact, the TList considers that an object is defined as void *ObjectOfTheList[Index];
As you can see from this definition, TList does not care what type of list you want to create as long as you can define it as an entity, an object. The second issue to keep in mind is that, because TList does not care about the kind of list you want to create and does not know in advance the number of objects in your list, it conveniently keeps the objects of your list in an array. This means that, as you create your list, TList adds your objects in an expanding list. Copyright © 2003 FunctionX, Inc.
713
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
To initiate a VCL list, the first thing you must do is to declare an instance of a TList. If the list will be used as a local object, you can declare it in a function or event. If the list will be accessed by more than one event or function, you should declare its instance in the class that will host the list. Because TList is a VCL object, it must be declared as a pointer: TList * ListOfObjects;
In the constructor of the class that will host the list, initialize the pointer by letting the compiler know which class the list instance belongs to. This is done using the new operator. After using the class, you should (must) delete it and recover the memory it was using. This is done using the delete operator. If the list was created globally in a form's class, you can delete it in the OnDestroy event of the form.
Practical Learning: Creating an Object 1.
Press F12 to display the Code Editor. In the public section of the TForm1 class, declare a pointer to TList as follows:
//--------------------------------------------------------------------------class TfrmMain : public TForm { __published: // IDE-managed Components private: // User declarations public: TList * ListOfContacts; // User declarations __fastcall TfrmMain(TComponent* Owner); }; //---------------------------------------------------------------------------
2.
Click the Main.cpp tab to access the source file. In the constructor of the TForm1 class, initialize the TList pointer as follows: //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { ListOfContacts = new TList; } //---------------------------------------------------------------------------
714
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
3.
Chapter 28: Creating and Using Lists
On the Object Inspector, click the Events tab. Double-click the event of OnDestroy and delete the list as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormDestroy(TObject *Sender) { delete ListOfContacts; ListOfContacts = NULL; } //---------------------------------------------------------------------------
4.
Save your project
28.1.3 Preparing an Object for a List Before creating the actual list of objects, you should first define the kind of object will make up the list. One way to do this is to create a class that holds the members or characteristics of the object. For example, for a list of countries, each object can include the country name, its area, its population, its capital, its internet code, etc. The object can be created as follows: class TCountry { public: AnsiString CountryName; Double Area; Extended Population; AnsiString Capital; AnsiString InternetCode; };
To create such an object, you have two options, you can create a fully functional C++ class: class TCountry { private: AnsiString CountryName; Double Area; Extended Population; AnsiString Capital; AnsiString InternetCode; public: __fastcall TCountry(AnsiString cn="", AnsiString a="", Extended p=0, AnsiString c="", AnsiString ic=""); virtual __fastcall ~TCountry(); void __fastcall setCountryName(const AnsiString CName); AnsiString __fastcall getCountryName() const; void __fastcall setArea(const Double A); Double __fastcall getArea() const; void __fastcall setPopulation(const Extended P); Extended __fastcall getPopulation() const; void __fastcall setCapital(const AnsiString C); AnsiString __fastcall getCapital() const; void __fastcall setInternetCode(const AnsiString IC); AnsiString __fastcall getInternetCode() const;
Copyright © 2003 FunctionX, Inc.
715
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
TCountry& __fastcall operator=(const TCountry &Ctry); };
Because your object will only be used as a "frame", it is a traditional shortcut to create it as a simple structure, listing only the members you will need. Such an object can be created as follows: struct TCountry { AnsiString CountryName; Double Area; Extended Population; AnsiString Capital; AnsiString InternetCode; __fastcall TCountry(AnsiString cn="", AnsiString a="", Extended p=0, AnsiString c="", AnsiString ic=""); virtual __fastcall ~TCountry(); TCountry& __fastcall operator=(const TCountry &Ctry); };
This option provides you with three possibilities to create an instance of a country:
you can access each member and assign it an appropriate value
you can use a constructor to initialize a complete object or you can use the constructor to initialize only the members whose value you know
You can use the overloaded assignment operator to make a copy of an instance and assign it to another instance of the object when necessary.
Practical Learning: Creating an Object 1.
To create an object, on the Standard toolbar, click the New button
.
2.
In the New property sheet of the New Items dialog box, click Unit and click OK
3.
Save the unit as Contacts
4.
In the Contacts.h file, type the following: //--------------------------------------------------------------------------#ifndef ContactsH #define ContactsH #include //--------------------------------------------------------------------------struct TContact { // The items that make up a (valid) contact AnsiString FirstName; AnsiString LastName; AnsiString Address; AnsiString City; AnsiString State; AnsiString ZIPCode; AnsiString Country; AnsiString EmailAddress; AnsiString HomePhone; // Default constructor for an empty contact
716
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
__fastcall TContact(); // Contact with only the first and last names __fastcall TContact(AnsiString FN, AnsiString LN); // Complete contact __fastcall TContact(AnsiString FN, AnsiString LN, AnsiString Adr, AnsiString CT, AnsiString St, AnsiString ZIP, AnsiString Ctry, AnsiString Email, AnsiString HP); // Copy constructor __fastcall TContact(const TContact &Cont); // Destructor virtual __fastcall ~TContact(); // This provides the ability to copy a contact TContact& operator=(const TContact& Cont);
}; //--------------------------------------------------------------------------#endif
5.
In the source file of Contacts.cpp, implement the constructors and the operator function as follows: //--------------------------------------------------------------------------#pragma hdrstop #include "Contacts.h" //--------------------------------------------------------------------------#pragma package(smart_init) __fastcall TContact::TContact() : FirstName("John"), LastName("Doe"), Address("123 Main Street"), City("Great City"), State("Big State"), ZIPCode("01234"), Country("USA"), EmailAddress("[email protected]"), HomePhone("(123) 456-7890") { } //--------------------------------------------------------------------------__fastcall TContact::TContact(AnsiString FN, AnsiString LN) : FirstName(FN), LastName(LN), Address(""), City(""), State(""), ZIPCode(""), Country(""), EmailAddress(""), HomePhone("") { // means Undefined } //--------------------------------------------------------------------------__fastcall TContact::TContact(AnsiString FN, AnsiString LN, AnsiString Adr, AnsiString CT, AnsiString St, AnsiString ZIP, AnsiString Ctry, AnsiString Email, AnsiString HP) : FirstName(FN), LastName(LN), Address(Adr), City(CT), State(St), ZIPCode(ZIP), Country(Ctry), EmailAddress(Email), HomePhone(HP) { } //--------------------------------------------------------------------------__fastcall TContact::TContact(const TContact &Cont) : FirstName(Cont.FirstName), LastName(Cont.LastName), Address(Cont.Address), City(Cont.City), State(Cont.State), ZIPCode(Cont.ZIPCode), Country(Cont.Country), EmailAddress(Cont.EmailAddress), HomePhone(Cont.HomePhone) { } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
717
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
__fastcall TContact::~TContact() { } //--------------------------------------------------------------------------TContact& TContact::operator=(const TContact& Cont) { FirstName = Cont.FirstName; LastName = Cont.LastName; Address = Cont.Address; City = Cont.City; State = Cont.State; ZIPCode = Cont.ZIPCode; Country = Cont.Country; EmailAddress = Cont.EmailAddress; HomePhone = Cont.HomePhone; return *this; } //---------------------------------------------------------------------------
6.
Save your project
28.1.4 List Build-Up Once you have an object whose list you want to create, you can create each instance of the object and add the instance to the list. Because the objects are stored in an array, you would repeat this process for each object you want to include in the list. The TList class provides all the necessary operations you would need to perform on a list. Before adding an object to a list, you must "fill" it up first. This can be done by providing a value for each member of the object. Because you are responsible for creating the object, you are also responsible for providing the right value for each member of the object. If you provide a wrong or invalid value, TList does not have any mechanism of checking the internal values of the object. Consequently, you can build the simplest object that contains only one or two members, or you can build the most complex object that resembles a chemical reaction. The choice is yours. Once the object is complete or at least acceptable as far as its structure is defined, you can add it to the list. This can be taken care of by using the TList::Add() method. Its syntax is: int __fastcall Add(void * Item);
The Item to add must be a recognizable and defined object. TList has an internal mechanism of counting the objects that are added to its list. This count is stored in the Count member variable. Therefore, you can call it anytime to find out how many items exist in a list. When using the Add() method, if the Item parameter is the first item to be added to the list, it would receive an index of 0 and the Count would be 1. When you add an object, the Count is incremented. The newly added object receives an index of Count1. This means that there are two main actions the Add() method performs. First it adds an object at the end of the list because the additions of items are incremental. Second, it returns the index of the newly added object. Knowing this index, you can access a specific object in the list array. As you are adding objects to the list, the TList class takes care of incrementing the number of objects in the list. This is taken care of by the Capacity member variable. In 718
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
fact, the Capacity serves two purposes. If you want to specify the number of items in a list, for example imagine you want to create a list of ten countries, you can specify this using the Capacity variable. In the same way, if you want to find out how many items the list can include, you can call the Capacity member variable.
Practical Learning: Creating a List 1.
In the public section of the TfrmMain class, declare a pointer to TContact as follows: //--------------------------------------------------------------------------#ifndef MainH #define MainH //--------------------------------------------------------------------------#include #include #include #include #include "Contacts.h" //--------------------------------------------------------------------------class TfrmMain : public TForm { __published: // IDE-managed Components void __fastcall FormDestroy(TObject *Sender); private: // User declarations public: TList * ListOfContacts; TContact * Contact; // User declarations __fastcall TfrmMain(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TfrmMain *frmMain; //--------------------------------------------------------------------------#endif
2.
To create a list, in the constructor of the TfrmMain class, type the following instances of TContact and add them to the list as follows: __fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { ListOfContacts = new TList; Contact = new TContact; Contact->FirstName = "Hermine"; Contact->LastName = "Toussaint"; Contact->Address = "4088 Patient Rd"; Contact->City = "Silver Spring"; Contact->State = "MD"; Contact->ZIPCode = "20904"; Contact->Country = "USA"; Contact->EmailAddress = "[email protected]"; Contact->HomePhone = "(301) 805-5008"; ListOfContacts->Add(Contact); Contact = new TContact("Bertrand", "Yamaguchi", "7215 16th Street #D14", "Washington", "DC", "20002", "USA", "[email protected]", "(202) 661-5000");
Copyright © 2003 FunctionX, Inc.
719
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
ListOfContacts->Add(Contact); Contact = new TContact; Contact->FirstName = "Lester"; Contact->LastName = "Aarons"; Contact->Address = "10882 Washington Ave"; Contact->City = "Arlington"; Contact->State = "VA"; Contact->ZIPCode = "22231"; Contact->Country = "USA"; Contact->EmailAddress = "[email protected]"; Contact->HomePhone = "(703) 790-4000"; ListOfContacts->Add(Contact); Contact = new TContact; Contact->FirstName = "Charlotte"; Contact->LastName = "Singh"; Contact->Address = "442 Southampton Dr Ste402"; Contact->City = "Rockville"; Contact->State = "MD"; Contact->ZIPCode = "20852"; Contact->Country = "USA"; Contact->EmailAddress = "[email protected]"; Contact->HomePhone = "(301) 667-1437"; ListOfContacts->Add(Contact); } //---------------------------------------------------------------------------
3.
Save your project
28.1.5 List Navigation List navigation consists of moving back and forth among items or locating an item on a list. Once you have added some objects, each one is stored in an array of Items[] defined as follows: void *Items[int Index];
As you can see, the TList::Items member variable takes an index as an integer and returns the object that is stored at that index. You can also use the Items member variable in a for loop to scan the list. To get to the very first item in the list, you have two options. You can call the Items variable with an index of 0 as Items[0]. Alternatively, you can call the TList::First() member variable whose syntax is: void * __fastcall First(void);
In the same way, to access the last item of the list, you can either call Items[TList::Count-1] or use the TList::Last() method. Its syntax is: void * __fastcall Last(void);
On the other hand, if you know the item you want access to, you can call the TList::IndexOf() method. Its syntax is: int __fastcall IndexOf(void * Item);
720
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
To use the IndexOf() method, you must provide the item that you want to locate. If the item exists, this member function returns the index of the item.
Practical Learning: Navigating a List 1.
Display the form and design it as follows:
2.
The form contains three GroupBox controls Named, from top to bottom, grpPersonalInformation, grpResidence, and grpCommunication respectively.
3.
is displayed on the above form. The The Name to give to each Edit control bottom Edit control is named edtRecordNumber.
4.
Set the ReadOnly property value to true for all Edit controls except for the bottom one (the one on the right side of the Record label, leave its ReadOnly value to false).
5.
, from left to right, are Named btnFirst, btnPrevious, The bottom buttons btnNext, btnLast. They use glyphs from the Bitmaps folder. The bitmaps from left to right are First, Previous, Next, and Last
6.
The label with a Caption of 000 is Named lblCount
7.
Save the project.
8.
To keep track of the record that is displaying at all times, we will need a global variable. Therefore, in the private section of the form, declare a variable as:
Copyright © 2003 FunctionX, Inc.
721
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
int CurrentRecord;
9.
In the constructor of the form, just after the opening bracket, initialize the CurrentRecord variable to 0: //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { CurrentRecord = 0; ListOfContacts = new TList; ...
10. Every time we change the displaying record on the form, we will need to update the bottom buttons. For example, when we are on the first record, the user should not be able to use the First and the Previous buttons since this could cause the compiler to throw an error. In the same way, when the last record is displaying, the user would not need the Next and the Last buttons. When displaying each record, we can just update this information. Professionally, it would be nice to have a function that manages these buttons every time a record changes. Object oriented programming means we should divide jobs. 11. In the private section of the form, declare the following method: void __fastcall UpdateButtons();
12. Implement the function as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::UpdateButtons() { if( CurrentRecord <= 1 ) { btnFirst->Enabled = False; btnPrevious->Enabled = False; btnNext->Enabled = True; btnLast->Enabled = True; } else if( CurrentRecord < ListOfContacts->Count ) { btnFirst->Enabled = True; btnPrevious->Enabled = True; btnNext->Enabled = True; btnLast->Enabled = True; } else if( CurrentRecord == ListOfContacts->Count ) { btnFirst->Enabled = True; btnPrevious->Enabled = True; btnNext->Enabled = False; btnLast->Enabled = False; } } //---------------------------------------------------------------------------
13. Also, every time the user clicks a button to navigate the records, the values for that record will need to display. We can handle this update for ever event of the navigation buttons. It appears more professional to have a function that can take care of this so that, whenever a new record needs to be displayed, the function or event that needs this update can just call the function that displays the values for the 722
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
record. The function that makes the call must specify which record needs to be displayed. In the private section of the TForm1 class, declare a function as follows: void __fastcall DisplayRecord(const TContact * Contact);
14. Implement the function as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::DisplayRecord(const Contact * Contact) { // Display or update each Edit control edtFirstName->Text = Contact->FirstName; edtLastName->Text = Contact->LastName; edtAddress->Text = Contact->Address; edtCity->Text = Contact->City; edtState->Text = Contact->State; edtZIPCode->Text = Contact->ZIPCode; edtCountry->Text = Contact->Country; edtEmailAddress->Text = Contact->EmailAddress; edtHomePhone->Text = Contact->HomePhone; } //---------------------------------------------------------------------------
15. On the form, double-click the First button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnFirstClick(TObject *Sender) { // Set the CurrentRecord variable to the first record CurrentRecord = ListOfContacts->IndexOf( ListOfContacts->First() ) + 1; // Retrieve the first record and assign it to a TContact object Contact = reinterpret_cast(ListOfContacts->First()); DisplayRecord(Contact); // Display the current index in the bottom Edit control edtRecordNumber->Text = IntToStr(CurrentRecord); // Call the UpdateButtons() method to decide what to do with the buttons UpdateButtons(); } //---------------------------------------------------------------------------
16. On the form, double-click the Previous button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnPreviousClick(TObject *Sender) { // Decrease the CurrentRecord variable CurrentRecord--; // Get the current record and assign it to a TContact instance Contact = reinterpret_cast(ListOfContacts->Items[CurrentRecord1]); DisplayRecord(Contact); edtRecordNumber->Text = IntToStr(CurrentRecord); UpdateButtons();
} //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
723
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
17. On the form, double-click the Next button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnNextClick(TObject *Sender) { Contact = reinterpret_cast(ListOfContacts->Items[CurrentRecord]); DisplayRecord(Contact); edtRecordNumber->Text = IntToStr(CurrentRecord+1); // Decrease the record numbering CurrentRecord++; UpdateButtons(); } //---------------------------------------------------------------------------
18. On the form, double-click the Last button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnLastClick(TObject *Sender) { CurrentRecord = ListOfContacts->Count; Contact = reinterpret_cast(ListOfContacts->Last()); DisplayRecord(Contact); edtRecordNumber->Text = IntToStr(ListOfContacts->Count); UpdateButtons(); } //---------------------------------------------------------------------------
19. When the form opens, we need to make sure that the first record displays. All we have to do is to call the OnClick event of the Next button. While we are at it, we will display the number of records on the second label of the bottom panel. Find an unoccupied area on the form and double-click it to access the OnCreate event of the form 20. Implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormCreate(TObject *Sender) { // When the form opens, act as if the Next button was clicked btnNextClick(Sender); // Display the total number of records on the navigation bar lblCount->Caption = ListOfContacts->Count;
} //---------------------------------------------------------------------------
21. If the user types a number in the Record Number edit box and press Enter, we will allow the corresponding record to display. The real problem we need to solve is to avoid invalid values. On the top combo box of the Object Inspector, select edtRecordNumber and click the Events tab. 22. Double-click the event of OnKeyDown and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::edtRecordNumberKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { // If the user press Enter while in the Edit control if( Key == VK_RETURN )
724
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
{
// If the Edit control is empty if( edtRecordNumber->Text.IsEmpty() ) return; // Do nothing else if( (edtRecordNumber->Text.ToInt() > 0) && (edtRecordNumber->Text.ToInt() <= ListOfContacts->Count) ) { // Since the Edit control contains CurrentRecord = edtRecordNumber->Text.ToInt(); Contact = reinterpret_cast(ListOfContacts>Items[CurrentRecord-1]); DisplayRecord(Contact); UpdateButtons(); } else if( (edtRecordNumber->Text.ToInt() < 0) || (edtRecordNumber->Text.ToInt() > ListOfContacts->Count) ) { ShowMessage("The record number is not valid"); } } } //---------------------------------------------------------------------------
23. Test the application
Copyright © 2003 FunctionX, Inc.
725
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
24. After using the form, close it and save your project
28.1.6 Operations on a List Operations on items of a list consist of adding, inserting, deleting, or moving items to or from a list. All these operations are already provided by the TList class. The most you have to do is to adapt the class to your goal. Although the operations on a list can be performed in the form layout we have used so far, they can better be viewed and appreciated in a view that displays all records at the same time. That is why sometimes spreadsheet views are used in databases. For this exercise, we will use, we will use a List View control.
Practical Learning: Using a List View 1.
Create a new application with its default form
2.
Save the project in a new folder named AddressBook2
3.
Save Unit1 as Main and save the project as AddressBook2 and
4.
Change its properties of the form as follows: Caption = Address Book - Contacts List Height = 340 Name = frmMain ShowHint = true Width = 456
726
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
5.
From the Win32 tab of the Component Palette, double-click ImageList the form, double-click ImageList1
6.
From the Bitmaps folder, and using the Add button, select the Exit bitmap
7.
From the Standard tab of the Component Palette, double-click MainMenu
8.
While the MainMenu1 icon is still selected, set its Images property to ImageList1
9.
Double-click MainMenu1
and. on
10. While the first menu item is still selected, on the Object Inspector, click Caption, type &File and press Enter 11. Set the Caption of the item under File to E&xit and set its ImageIndex value to 0 12. Close the Menu Designer 13. On the main menu of the form, click File -> Exit and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmListView::Exit1Click(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
14. From the Win32 tab of the Component Palette, click Toolbar unoccupied area on the form
and click an
15. Set the properties of the toolbar as follows: EdgeBorders = [ebTop,ebBottom] Flat = true Height = 24 Images = ImageList1 16. From the Win32 tab of the Component Palette, add a List View control the toolbar on the form
under
17. On the Object Inspector, change the properties as follows: GridLines = true Height = 248 Left = 0 Name = lvwContacts RowSelect = true Top = 26 ViewStyle = vsReport Width = 448 18. On the form, double-click the ListView1 control 19. Using the Add New button, create the columns as follows: Caption First Name MI Last Name Email Address
Copyright © 2003 FunctionX, Inc.
Alignment taLeftJustify taLeftJustify taLeftJustify taLeftJustify
Width 80 24 80 160
727
Chapter 28: Creating and Using Lists
Home Phone
Borland C++ Builder Programming
taCenter
100
20. Add a Panel to the form with the following properties: Align = alBottom BevelOuter = bvLowered Caption = none Height = 20
21. Save your project 22. Create a new Unit and save it as Contacts 23. In the Contacts.h file, create a TContact class as follows: //--------------------------------------------------------------------------#ifndef ContactsH #define ContactsH #include //--------------------------------------------------------------------------class TContact { public: AnsiString FirstName; AnsiString MI; AnsiString LastName; AnsiString Address; AnsiString City; AnsiString State; AnsiString ZIPCode; AnsiString Country; AnsiString EmailAddress; AnsiString HomePhone; __fastcall TContact(AnsiString FN="John", AnsiString M="T", AnsiString LN="Doe", AnsiString Adr="123 Main Street",
728
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
AnsiString CT="USA", AnsiString St="ZZ", AnsiString ZIP="01234", AnsiString Ctry="USA", AnsiString Email="[email protected]", AnsiString HP="(123) 456-7890"); __fastcall TContact(const TContact &Cont); virtual __fastcall ~TContact(); TContact& operator=(const TContact& Cont); }; //--------------------------------------------------------------------------#endif
24. In the Contacts.cpp file, define the object as follows: //--------------------------------------------------------------------------#pragma hdrstop #include "Contacts.h" //--------------------------------------------------------------------------#pragma package(smart_init) //--------------------------------------------------------------------------__fastcall TContact::TContact(AnsiString FN, AnsiString M, AnsiString LN, AnsiString Adr, AnsiString CT, AnsiString St, AnsiString ZIP, AnsiString Ctry, AnsiString Email, AnsiString HP) : FirstName(FN), MI(M), LastName(LN), Address(Adr), City(CT), State(St), ZIPCode(ZIP), Country(Ctry), EmailAddress(Email), HomePhone(HP) { } //--------------------------------------------------------------------------__fastcall TContact::TContact(const TContact &Cont) : FirstName(Cont.FirstName), MI(Cont.MI), LastName(Cont.LastName), Address(Cont.Address), City(Cont.City), State(Cont.State), ZIPCode(Cont.ZIPCode), Country(Cont.Country), EmailAddress(Cont.EmailAddress), HomePhone(Cont.HomePhone) { } //--------------------------------------------------------------------------__fastcall TContact::~TContact() { } //--------------------------------------------------------------------------TContact& TContact::operator=(const TContact& Cont) { FirstName = Cont.FirstName; MI - Cont.MI; LastName = Cont.LastName; Address = Cont.Address; City = Cont.City; State = Cont.State; ZIPCode = Cont.ZIPCode; Country = Cont.Country; EmailAddress = Cont.EmailAddress; HomePhone = Cont.HomePhone;
Copyright © 2003 FunctionX, Inc.
729
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
return *this; } //---------------------------------------------------------------------------
25. In the header file of Main.h, declare a TContacts and a TList instances as follows: //--------------------------------------------------------------------------#ifndef MainH #define MainH //--------------------------------------------------------------------------#include #include #include #include #include #include #include #include #include #include "Contacts.h" //--------------------------------------------------------------------------class TfrmMain : public TForm { __published: // IDE-managed Components TImageList *ImageList1; TMainMenu *MainMenu1; TMenuItem *File1; TMenuItem *Exit1; TToolBar *ToolBar1; TListView *lvwContacts; TPanel *Panel1; void __fastcall Exit1Click(TObject *Sender); private: TContact *Contact; TList *ListOfContacts; // User declarations public: // User declarations __fastcall TfrmMain(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TfrmMain *frmMain; //--------------------------------------------------------------------------#endif
26. In the constructor of the TfrmMain class in the Main.cpp file, initialize the ListOfContacts variable as follows: //--------------------------------------------------------------------------__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { ListOfContacts = new TList; } //---------------------------------------------------------------------------
27. In the OnDestroy event of the the frmMain form, destroy the ListOfContacts variable as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::FormDestroy(TObject *Sender) {
730
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
delete ListOfContacts; ListOfContacts = NULL; } //---------------------------------------------------------------------------
28. Save your project
28.1.7 A List and its New Items In the previous section of this lesson, we learned how to statically create a list. That is, we learned how you, the programmer, can create a list for the user. If you want users to have more control on the list, you should give them the ability to add new items to a list or insert items in the order of her choice. We have already learned that, to add an item to the end of a list, you can use the TList::Add() method. To add a new record, we will provide the user with a dialog box that contains all the fields that compose a complete record. The user can provide the piece of information that are available. Upon clicking OK, we will retrieve the record and add it to the list. Once a few records exist in the list, it is time for maintenance. The ListView we have created will display only a few pieces of information, due to lack of space. That is why we will display only the name, the email address and the telephone number of a contact. If the user wants to see more details about a contact, we will allow him to select a record, either double-click it or click a button. A dialog box will display the whole record. To do this, when the user double-clicks a record, we will retrieve its index and display its details in the same dialog box used to create a new record. Upon closing the dialog box, if the user clicks OK after changing any piece of information on the contact, we will found out if he wants to update the change.
Practical Learning: Adding and Inserting Items to a List 1.
On the View toolbar, click the New Form button
2.
Change its properties as follows: BorderStyle = bsDialog Caption = Address Book - Record Details Height = 360 Name = dlgRecord ShowHint = true Width = 490
3.
Save the form as Record and design it as follows:
Copyright © 2003 FunctionX, Inc.
731
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
4.
The form contains three GroupBox controls Named, from top to bottom, grpPersonalInformation, grpResidence, and grpCommunication respectively.
5.
The Name to give to each Edit control is displayed on the above form. The middle Edit control in the top group is named edtMI
6.
The right buttons are BitBtn
7.
Save the project.
8.
Display the other form (View -> Forms, frmMain) and double-click ImageList1
9.
Using the Add button, add the NewRecord button
type with Kinds mrOk and mrCancel
10. On the form, double-click MainMenu1. 11. Click the blue box under the File menu and click Caption. Type &New Record... and click Name. Type mnuNewRecord 12. Set the ImageIndex value of the New Record menu item to 1 and set its ShortCut to Ctrl+N. Move the new menu item on top of Exit
732
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
13. Close the Menu Designer 14. On the main menu of C++ Builder, click File -> Include Unit Hdr... 15. Record should be selected. Therefore, click OK. 16. Whenever anything changes about the list of records, for example when the user adds or deletes a record, the list of records should be updated. Instead of performing this operation every time, we can use a central function that displays records. When the records need to be displayed or updated, we can just call it. In the private section of the header file of the TfrmMain class, declare a function as follows: private: TContact *Contact; TList *ListOfContacts; void __fastcall ShowContacts(); // User declarations public: // User declarations __fastcall TfrmMain(TComponent* Owner); }; //--------------------------------------------------------------------------extern PACKAGE TfrmMain *frmMain; //--------------------------------------------------------------------------#endif
17. In the source file, implement the function as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::ShowContacts() { // If the list view had some items, dismiss them lvwContacts->Items->Clear(); // For each record, display its values in the list view for( int i = 0; i < ListOfContacts->Count; i++ ) { TListItem * lstItem; lstItem = lvwContacts->Items->Add(); lstItem->Caption = reinterpret_cast(ListOfContacts->Items[i])->FirstName;
Copyright © 2003 FunctionX, Inc.
733
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
lstItem->SubItems->Add(reinterpret_cast(ListOfContacts->Items[i])->MI); lstItem->SubItems->Add(reinterpret_cast(ListOfContacts->Items[i])->LastName); lstItem->SubItems->Add(reinterpret_cast(ListOfContacts->Items[i])->EmailAddress); lstItem->SubItems->Add(reinterpret_cast(ListOfContacts->Items[i])->HomePhone); } } //---------------------------------------------------------------------------
18. To allow the user to create a new contact, on the main menu of the form, click File > New Record and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::mnuNewRecordClick(TObject *Sender) { // Make sure that all edit boxes // from the Record Details dialog boxes are empty dlgRecord->edtFirstName->Text = ""; dlgRecord->edtMI->Text = ""; dlgRecord->edtLastName->Text = ""; dlgRecord->edtAddress->Text = ""; dlgRecord->edtCity->Text = ""; dlgRecord->edtState->Text = ""; dlgRecord->edtZIPCode->Text = ""; dlgRecord->edtCountry->Text = "USA"; dlgRecord->edtEmailAddress->Text = ""; dlgRecord->edtHomePhone->Text = ""; // Display the Record dialog to the user dlgRecord->ShowModal(); // Find out if the user clicked OK if( dlgRecord->ModalResult == mrOk ) { // Create a new record Contact = new TContact; Contact->FirstName = dlgRecord->edtFirstName->Text; Contact->MI = dlgRecord->edtMI->Text; Contact->LastName = dlgRecord->edtLastName->Text; Contact->Address = dlgRecord->edtAddress->Text; Contact->City = dlgRecord->edtCity->Text; Contact->State = dlgRecord->edtState->Text; Contact->ZIPCode = dlgRecord->edtZIPCode->Text; Contact->Country = dlgRecord->edtCountry->Text; Contact->EmailAddress = dlgRecord->edtEmailAddress->Text; Contact->HomePhone = dlgRecord->edtHomePhone->Text; // Add the new record to the list ListOfContacts->Add(Contact); } // Display a condensed list of contacts ShowContacts(); } //---------------------------------------------------------------------------
19. On the form, right-click the toolbar and click New Button. Change the ImageIndex of the new button to 1. 20. Change its Hint to New Record
734
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
21. Click the Events tab of the Object Inspector and on its OnClick right field, select mnuNewRecordClick 22. Test the application. Click File -> New Record...
Copyright © 2003 FunctionX, Inc.
735
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
23. After using the application, close the form and save the project 24. To allow the user to view more details about a record, on the form, click the ListView1 control. 25. On the Object Inspector, click the Events tab. Double-click the empty box on the right side of OnDblClick and implement the event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::lvwContactsDblClick(TObject *Sender) { // Suppose the user double-clicks an item, get its index int ItemSelected = lvwContacts->ItemIndex; // If an item was double-clicked, display its record if( ItemSelected >= 0 ) { Contact = reinterpret_cast(ListOfContacts>Items[ItemSelected]); dlgRecord->edtFirstName->Text = Contact->FirstName; dlgRecord->edtMI->Text = Contact->MI; dlgRecord->edtLastName->Text = Contact->LastName; dlgRecord->edtAddress->Text = Contact->Address; dlgRecord->edtCity->Text = Contact->City; dlgRecord->edtState->Text = Contact->State; dlgRecord->edtZIPCode->Text = Contact->ZIPCode; dlgRecord->edtCountry->Text = Contact->Country; dlgRecord->edtEmailAddress->Text = Contact->EmailAddress; dlgRecord->edtHomePhone->Text = Contact->HomePhone; dlgRecord->ShowModal(); // After viewing the record and upon closing the dialog box // find out if the user clicked OK if( dlgRecord->ModalResult == mrOk ) { // Since the user clicked OK // find out if any value of the record was changed if( (dlgRecord->edtFirstName->Text != Contact->FirstName) || (dlgRecord->edtMI->Text != Contact->MI) || (dlgRecord->edtLastName->Text != Contact->LastName) ||
736
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
{
Chapter 28: Creating and Using Lists
(dlgRecord->edtAddress->Text != Contact->Address) || (dlgRecord->edtCity->Text != Contact->City) || (dlgRecord->edtState->Text != Contact->State) || (dlgRecord->edtZIPCode->Text != Contact->ZIPCode) || (dlgRecord->edtCountry->Text != Contact->Country) || (dlgRecord->edtEmailAddress->Text != Contact->EmailAddress) || (dlgRecord->edtHomePhone->Text != Contact->HomePhone) ) // Since a record has changed, ask a question to the user int Response = Application->MessageBox("The record has changed\n" "Do you want to save it?", "Address Book", MB_YESNO | MB_ICONQUESTION); // If the user wants to update the change on the record if( Response == IDYES ) { // Replace each value of the selected record // with the new value Contact->FirstName = dlgRecord->edtFirstName->Text; Contact->MI = dlgRecord->edtMI->Text; Contact->LastName = dlgRecord->edtLastName->Text; Contact->Address = dlgRecord->edtAddress->Text; Contact->City = dlgRecord->edtCity->Text; Contact->State = dlgRecord->edtState->Text; Contact->ZIPCode = dlgRecord->edtZIPCode->Text; Contact->Country = dlgRecord->edtCountry->Text; Contact->EmailAddress = dlgRecord->edtEmailAddress->Text; Contact->HomePhone = dlgRecord->edtHomePhone->Text; // Redisplay the contacts ShowContacts();
} }
}
}
} //---------------------------------------------------------------------------
26. On the form, double-click ImageList1 and, using the Add button, add the Details bitmap 27. On the form, right-click the toolbar and click New Button. Make sure the ImageIndex of the new button is set to 2 and change its Name to btnRecordDetails 28. Click the Events tab. In its OnClick right box, select lvwContactsDblClick 29. Test the application 30. After using it, close the form and save the project
28.1.8 List Item Insertion Besides the TList::Add() method used to add an item to a list, the TList class also allows you to insert an item in any order inside the list. This operation is handled by the TList::Insert() method. Its syntax is: void __fastcall Insert(int Index, void *Item);
Copyright © 2003 FunctionX, Inc.
737
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
The first argument of this member function, Index, specifies the index that the new item will occupy after being added. Because the list of items is zero-based, to add an item to the first position, specify Index as 0. In the same way, to add an item to the 3rd position, specify Index as 2. The Item argument is the item that you want to insert. There are two main ways you would use the Insert() method. If you know the exact position where you want to insert an object, then supply the known index. By contrast, you can use Insert() to insert a new item before or after one of your choice. To do this, you must first retrieve the index of the item that will succeed the one you want to add. This index would be used as a the Index argument.
Practical Learning: Adding and Inserting Items to a List 1.
On the form, double-click ImageList1 and, using the Add button, add the Insert bitmap
2.
On the form, right-click the toolbar and click New Button
3.
Make sure that the new bitmap is assigned to the new button and change its Name to btnInsertRecord
4.
Change its Hint to Insert Record
5.
Double-click the new button and implement its OnClick event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnInsertRecordClick(TObject *Sender) { // In order to insert a record, we need to find out if a record is selected int ItemSelected = lvwContacts->ItemIndex; // If a record is selected if( ItemSelected >= 0 ) { // Empty all edit boxes of the Record Details dialog box dlgRecord->edtFirstName->Text = ""; dlgRecord->edtMI->Text = ""; dlgRecord->edtLastName->Text = ""; dlgRecord->edtAddress->Text = ""; dlgRecord->edtCity->Text = ""; dlgRecord->edtState->Text = ""; dlgRecord->edtZIPCode->Text = ""; dlgRecord->edtCountry->Text = "USA"; dlgRecord->edtEmailAddress->Text = ""; dlgRecord->edtHomePhone->Text = ""; // Display the Record dialog to the user dlgRecord->ShowModal(); // Find out if the user clicked OK if( dlgRecord->ModalResult == mrOk ) { // Create a new record Contact = new TContact; Contact->FirstName = dlgRecord->edtFirstName->Text; Contact->MI = dlgRecord->edtMI->Text; Contact->LastName = dlgRecord->edtLastName->Text; Contact->Address = dlgRecord->edtAddress->Text;
738
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
Contact->City = dlgRecord->edtCity->Text; Contact->State = dlgRecord->edtState->Text; Contact->ZIPCode = dlgRecord->edtZIPCode->Text; Contact->Country = dlgRecord->edtCountry->Text; Contact->EmailAddress = dlgRecord->edtEmailAddress->Text; Contact->HomePhone = dlgRecord->edtHomePhone->Text;
}
// Insert the new record to the list ListOfContacts->Insert(ItemSelected, Contact);
} // Display an update list of contacts in the ListView ShowContacts(); } //---------------------------------------------------------------------------
6.
If a record is selected and the user presses Insert, we can perform the same task
7.
On the form, click the ListView control and, on the Object Inspector, click the Events tab
8.
Double-clicks the event on the right side of OnKeyDown and implement it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::lvwContactsKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { if( Key == VK_INSERT ) btnInsertRecordClick(Sender); } //---------------------------------------------------------------------------
9.
Test the application
10. After using it, close the form and save the project
28.1.9 Item Removal From a List If you find out that your list includes an item you don't need, you can remove such an item from your list. The deletion of an item from a TList list can be handled by the TList::Delete() method. Its syntax is: void __fastcall Delete(int Index);
To process your request, this method needs the index of the item you want to delete. You can get this index using the TList::Items[] member variable. Besides the Delete() method, the TList class provides a method used to delete a record if you know the value of the record instead of its position. The method is TList::Remove() and its syntax is: int __fastcall Remove(void *Item);
Instead of the index, the Remove() method needs the item itself. If you supply an item that exists in the list, Remove() would delete it. If the operation is successful, Remove() returns the index the item had.
Copyright © 2003 FunctionX, Inc.
739
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
There are two significant differences between the Delete() and the Remove() methods:
Because Delete() takes the index of the item you want to remove, if the index is valid, the operation would be successful and stop. In other words, Delete() only makes sure that the index corresponds to a valid item number in the list: Delete() does not look for it; it only checks that the index is true and then deletes it
Remove() looks for the item you supply to it. If the item exists, Remove() deletes it. If more than one item matches the Item parameter you supply, Remove() deletes the first item that matches Item.
Practical Learning: Deleting Records 1.
On the form, double-click ImageList1 and, using the Add button, add the Delete bitmap
2.
Right-click the toolbar and click New Button. Make sure the Delete bitmap is assigned to the new button and change its Name to btnDeleteRecord
3.
Change its Hint to Delete Record
4.
Double-click the new button and implement its event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnDeleteRecordClick(TObject *Sender) { // Find out if an item is selected int SelectedItem = lvwContacts->ItemIndex; if( SelectedItem >= 0 ) { int Response = Application->MessageBox("Are you sure you want to delete this record?", "Address Book", MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION); if( Response == IDYES ) { // If an item is selected, delete it ListOfContacts->Delete(SelectedItem); // Update and show the list of records ShowContacts(); } } } //---------------------------------------------------------------------------
5.
If the user presses Delete, we can perform the same action
6.
On the form, click the ListView control and, on the Object Inspector, click the Events tab
7.
Double-click the event on the right side of OnKeyDown and change it as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::lvwContactsKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { if( Key == VK_INSERT ) btnInsertRecordClick(Sender); if( Key == VK_DELETE )
740
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
btnDeleteRecordClick(Sender); } //---------------------------------------------------------------------------
28.1.10
8.
Test the application
9.
After using it, close the form and save the project
List Clearance Clearing a list consists of deleting all of its records in one step. This operation can be taken care of by the TList::Clear() method. Its syntax is: void __fastcall Clear();
As you can see, there is no particular information this function needs. If the list is empty, the method would not do anything. If the list contains records, then all of them would be deleted. The only thing you might do is to warn the user especially if the records cannot be recovered.
Practical Learning: Clearing a List 1.
On the form, double-click ImageList1 and, using the Add button, add the Clear bitmap
2.
On the form, right-click the toolbar and click New Button. Make sure the Clear bitmap is assigned to the new button. Change its Name to btnClear and its Hint to Delete All Records
3.
Double-click the new button and implement its event as follows: //--------------------------------------------------------------------------void __fastcall TfrmMain::btnClearClick(TObject *Sender) { int Warning1 = Application->MessageBox("This action will erase all of your contacts" "\nAre you sure you want to erase all contacts?", "Address Book", MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION); if( Warning1 == IDYES ) { int Warning2 = Application->MessageBox( "Are you absolutely sure that you want to erase all contacts?", "Address Book", MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION); if( Warning2 == IDYES ) { ListOfContacts->Clear(); lvwContacts->Items->Clear(); } } } //---------------------------------------------------------------------------
4.
Test the application
5.
After using it, close the form and save the project
Copyright © 2003 FunctionX, Inc.
741
Chapter 28: Creating and Using Lists
Borland C++ Builder Programming
28.2 The VCL's Collection of List 28.2.1 Introduction Besides the TList class that we have studied in this lesson, the VCL provides a wide range of classes that can be used to create lists or maintain them. This means that, although you can create your lists from scratch using C++, there are already classes that can help you accomplish your scenario.
28.2.2
The TOrderedList Class Like TList, the TOrderedList class is derived straight from TObject. The purpose of TOrderedList is to lay a foundation of creating a list that can be ordered. This class itself does not do anything. It gives birth to other classes that take care of the necessary function definitions. This is because C++ Builder implements ordered lists differently.
28.2.3 The TQueue Class A queue is a technique of arranging objects in an order so that items are aligned, like people at the bank: the first person on the line is also the first person to be served and to get out of the bank. This technique is referred to as First-In First-Out (FIFO). To create such a list in a VCL application, you can use the TQueue class. It is derived from the TOrderedList class.
28.2.4 The TStack Class A stack is a technique of arranging objects like putting books in a box. If you decide to remove them, you would first remove the last book you had put in the box. This arrangement is referred to as Last-In First-Out (LIFO). To create such a list, you can use the TStack class which is derived from the TOrderedList. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnStackClick(TObject *Sender) { class TContractor { public: AnsiString FirstName; AnsiString LastName; }; TContractor *Std = new TContractor; TStack *ListOfNumbers = new TStack; Std->FirstName = "James"; Std->LastName = "Waters"; ListOfNumbers->Push(Std); Std = reinterpret_cast(ListOfNumbers->Peek()); edtFirstName1->Text = Std->FirstName; edtLastName1->Text = Std->LastName; Std->FirstName = "Peter"; Std->LastName = "Moon";
742
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Chapter 28: Creating and Using Lists
ListOfNumbers->Push(Std); Std = reinterpret_cast(ListOfNumbers->Peek()); edtFirstName2->Text = Std->FirstName; edtLastName2->Text = Std->LastName; Std->FirstName = "Colette"; Std->LastName = "Arnolds"; ListOfNumbers->Push(Std); Std = reinterpret_cast(ListOfNumbers->Peek()); edtFirstName3->Text = Std->FirstName; edtLastName3->Text = Std->LastName; Std->FirstName = "Harry"; Std->LastName = "Pons"; ListOfNumbers->Push(Std); Std = reinterpret_cast(ListOfNumbers->Peek()); edtFirstName4->Text = Std->FirstName; edtLastName4->Text = Std->LastName; edtCounter->Text = ListOfNumbers->Count(); } //---------------------------------------------------------------------------
There are many other classes the VCL provides for lists.
Copyright © 2003 FunctionX, Inc.
743
Appendix
Borland C++ Builder Programming
Appendices
Computers: An Overview
Creating a Program
Data Output: An Introduction
744
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
A/ Date and Time Functions Dates The date and time are at the core of computer use. The date is a unit that measures the number of years, months, or days elapsed in a specific period. To represent the dates, computers, applications, and compilers are configured with appropriates techniques. The C++ Builder compiler counts dates from 12/30/1899 at midnight. With this compiler, dates and related operations are dealt with using a class called TDateTime. Since this class is not a control, you can use it with any control that displays text. The dates used in your applications will mostly be based on the TDateTime class. To perform most operations in your programs, you will need to know when, how, and where to use this class. As an object, you will need to declare it.
Declaring a Date To declare a date value, use one of the TDateTime constructors. The simplest way is to use the default constructor and provide a valid C++ name. Here is an example: TDateTime Mine;
If not assigned a value, this variable is initialized to 12/30/1899 at midnight. If you know the exact date that you want to initialize, you have two alternatives. You can initialize the variable using a string. Here is an example: TDateTime Mine("12/05/1990");
This could also be written as: AnsiString S = "05/04/1988"; TDateTime Mine = S;
You can also provide the integer values of the year, the mont, and the day respectively. Here is an example: TDateTime Mine(1990, 11, 26); To initialize a date, you can also provide an integer that represents the number of days passed since 1899 to the specified date. Here is and example: TDateTime Mine = 33895;
The Date() Function The simplest way to get the date in your application consists of calling the Date() function. For example, you can use it to display the current date: • In an Edit control: edtToday->Text = Date(); Copyright © 2003 FunctionX, Inc.
745
Appendix
Borland C++ Builder Programming • •
On a label: Label1->Caption = Date(); In a form’s title bar: Form1->Caption = Date();
Date, String, and Numeric Conversions Converting a String to Date Unless you are using a special control that recognizes date values, most of the controls will receive and display text. For this reason, as far as the compiler is concerned, the user or the client of a control would be supplying text derived from an AnsiString class. Therefore, before performing any operation on such a text, you will have to convert the content of the control to a valid date. In the same way, if you want the compiler to consider a date value as a “normal” string, you would convert the date value into a string. If the user types a valid date into an edit box, such as 10/4/1988, this is still considered a string and you would not be able to use it in an operation. To convert a string to a date, you can use the StrToDate() function. The syntax is: TDateTime __fastcall StrToDate(const AnsiString S); This function takes an argument as the string that needs to be converted. The string must be provided in a recognizable format, following the Regional Settings of the Windows Control Panel. For example, in the United States, the components of a date are separated with a forward slash “/”. The typical formats of a date are:
If the string contains an invalid date, the conversion would fail and the program would throw an error. If the conversion is successful, the function would return a valid date. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm2::btnConvertClick(TObject *Sender) { TDateTime Value = StrToDate(edtSource->Text); edtTarget->Text = Value; } //---------------------------------------------------------------------------
746
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
Converting a Date to a String The TDateTime and the AnsiString classes are configured to work in harmony and allow great flexibility. This allows a TDateTime date value to be easily recognized by, and converted to, an AnsiString string. Unless you are performing detailed formatting, a date value can be transparenty identified as a string. For this reason, you can display a date value as a string without any conversion. As an example, when the user clicks a button named Button1 on a form, the date value of the AfewMonthsAgo variable would display in an Edit control named Edit1: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime AFewMonthsAgo("04/28/1998"); Edit1->Text = AFewMonthsAgo;
} //---------------------------------------------------------------------------
An AnsiString string is able to recognize a TDateTime value because the AnsiString is overloaded in the TDateTime class. Its syntax is; __fastcall operator AnsiString() const; The operator AnsiString() function allows any valid TDateTime value to be automatically converted to a string when needed. You can also explicitly use the function if necessary. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDisplayClick(TObject *Sender) { TDateTime AFewMonthsAgo("04/28/1998"); AnsiString Converted = AFewMonthsAgo.operator AnsiString(); edtResult->Text = Converted; } //---------------------------------------------------------------------------
If you have a date value that needs to be converted to a string, you can use the DateToStr() function. Its syntax is: AnsiString __fastcall DateToStr(System::TDateTime Date);
Copyright © 2003 FunctionX, Inc.
747
Appendix
Borland C++ Builder Programming
This function takes one argument, which is the date value that needs to be converted. If the argument is not a valid date, the conversion would fail and the program would throw an error (in simple but practical terms, the program would crash). If the conversion is successful, the function returns a string. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnConvertClick(TObject *Sender) { AnsiString Current = DateToStr(Date()); edtTarget->Text = Current; } //---------------------------------------------------------------------------
Alternatively, you can use the TDateTime::DateString() function to convert a valid date to a string. The syntax of the DateString() method is: AnsiString __fastcall DateString() const; Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime Today = Date(); AnsiString Value = Today.DateString(); Edit1->Text = Value;
} //---------------------------------------------------------------------------
Converting a Date to a Double-Precision Number A TDateTime value can be configured to display as a decimal number. The syntax used is: __fastcall operator double() const; The double data type was overloaded in the TDateTime class to allow converting a TDateTime variable to a fractional equivalent. The number can be made of two parts. If the number is round, that is, without a decimal value, it represents the number of days elapsed since 12/30/1899. If the number has a decimal part, the decimal portion represents a fraction of the day over a 24-hour period. The operator double() function can be used to get the number of days and the amount of time elapsed since 12/30/1899 at midnight and the time of the current date. In the following example, the difference between the current date an time and 12/30/1899 at midnight is displayed as a double-precision number in an Edit control of a form when the form comes up: //--------------------------------------------------------------------------void __fastcall TForm2::FormCreate(TObject *Sender) { TDateTime RightNow = Now();
748
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
double Qty = RightNow.operator double(); Edit1->Text = Qty;
} //---------------------------------------------------------------------------
To convert a TDateTime value to double using the double overloaded operator, declare a double variable and assign the TDateTime variable to it by calling the operator double() function. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); TDateTime Diff = End - Start; double d = Diff.operator double(); edtDifference->Text = d; } //---------------------------------------------------------------------------
Converting a Date to an Integer Like the double data type, the int data type was overloaded in the TDateTime class to convert a TDateTime value to an integer. The syntax of the function is: __fastcall operator int() const; To get the equivalent number of days of a TDateTime value, declare an integer and assign it the desired TDateTime value by calling the operator int() function. If the operator is applied on a variable that holds a particular date, the operator int() function would produce the number of days elapsed since 12/30/1988. In the following example, the number of days since 12/30/1988 to the current date is displayed in an Edit control when the user clicks the button: //--------------------------------------------------------------------------void __fastcall TForm1::btnShowDaysClick(TObject *Sender) { TDateTime Today = Date(); int Days = Today.operator int(); edtDifference->Text = Days; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
749
Appendix
Borland C++ Builder Programming
The Computer’s System of Displaying Dates Your computer and your customer’s uses a set of attributes to display date values. These are provided or configured in the Regional Settings. To access the Regional Settings, in Microsoft Windows XP, click Start Control Panel. From the Control Panel, click Date, Time, Language, and Regional Options:
From the Date, Time, Language, and Regional Options window, click either Change the Format of Numbers, Dates, and Times or Regional and Language Options. From the Regional or Language Options, click the Regional Options property page and click the Customize button:
750
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
The computer uses two main categories of date display. These categories are based on the language used by your computer. For example, most user computers that reside in the United States use a standard known as US English. This commands how the date displays in the continental US. Each category uses specific characters to represent its value. The m or M is used for a month, the d or D is used for a day, and the y or Y is used for the year.
The Short Date Format The Short Date Format is special string that all applications of your computer refer to in order to display a date using the numeric version of a date. The months hold the values 1=January, 2=February, 3=March, 4=April, 5=May, 6=June, 7=July, 8=August, 9=September, 10=October, 11=November, 12=December. Instead on numbers, a month is represented with the letter m; the computer has the responsibility of finding the right month. The computer refers to the following characters and it depends on how these characters are retrieved: Character m or M mm or MM d or D dd or DD y, yy, Y, or YY yyy, YYY, yyyy, YYYY
Meaning The month displays as a single digit if the numeric month is less than 10. The month displays a leading 0 if the numeric month is less than 10. The day displays as a single digit if the day of the month is less than 10. The day displays a leading 0 if the day of the month is less than 10. The year displays with two digits like 88 for 1988 The year displays with 4 digits such as 1988 and not 88
Copyright © 2003 FunctionX, Inc.
Why use it? Can be used to display May as 5 instead of 05 Can be used to display May as 05 instead of 5 Can be used to display the 8th of the month as 8 and not 08 Can be used to display 8th day of the month as 08 and not 8 Used when two digits suffice to display the year Used to display all digits for a year
751
Appendix
Borland C++ Builder Programming
To display a date value, use the syntax: ShortDateFormat = “Format”; To create a format, assign an appropriate string to the ShortDateFormat variable. After creating such a format, you can display a date value in the desirede control the same way you would proceed. Before displaying the date, the compiler would scan the event and find out what format to apply. Here are examples of displaying individual portions of a date value:
//--------------------------------------------------------------------------void __fastcall TfrmDates::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); ShortDateFormat = "d"; edtd->Text = DateValue; ShortDateFormat = "dd"; edtdd->Text = DateValue; ShortDateFormat = "M";
752
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
edtM->Text = DateValue; ShortDateFormat = "MM"; edtMM->Text = DateValue; ShortDateFormat = "y"; edty->Text = DateValue; ShortDateFormat = "yy"; edtyy->Text = DateValue; ShortDateFormat = "yyy"; edtyyy->Text = DateValue; ShortDateFormat = "yyyy"; edtyyyy->Text = DateValue;
} //---------------------------------------------------------------------------
Besides using any of these characters to display their corresponding portion of a date, you can also combine these characters to display a semi or complete date. To do this, you will need a symbol or character that separates the portions of a date. In the US English, the most common character used to separate the portions of a date is the forward slash “/”. Another character used is the dash “-“. Using a set of combinations of the above characters, the operating system proposes a list of possible formats for date display. To access this list, from the Regional Options property page of the Regional and Language Options dialog box, you can click the arrow of the Short Date Format combo box. The combinations are:
Here are examples of displaying the formats specified by the operating system:
//--------------------------------------------------------------------------void __fastcall TfrmMain::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text);
Copyright © 2003 FunctionX, Inc.
753
Appendix
Borland C++ Builder Programming
ShortDateFormat = "M/d/yyyy"; edtMdyyyy->Text = DateValue; ShortDateFormat = "M/d/yy"; edtMdyy->Text = DateValue; ShortDateFormat = "MM/dd/yy"; edtMMddyy->Text = DateValue; ShortDateFormat = "MM/dd/yyyy"; edtMMddyyyy->Text = DateValue; ShortDateFormat = "yy/MM/dd"; edtyyMMdd->Text = DateValue; ShortDateFormat = "yyyy-MM-dd"; edtyyyyMMdd->Text = DateValue; ShortDateFormat = "dd-MMM-yy"; edtddMMMyy->Text = DateValue;
} //---------------------------------------------------------------------------
In the same way, you can create your own formats.
The Long Date Format The operating system provides a more explicit system of displaying date values. This is called the Long Date Format and it uses a global variable called LongDateFormat; this variable is defined in the Regional Options of the Control Panel. When using the LongDateFormat, there are various types of combinations applied to the characters, depending on what you are trying to display. To display the date using the Long Date Format formats, the computer uses the following combinations: d: The single d is used to display the numeric day of a month. Each month starts at 1, then 2, and so on. The month of February can have 28 or 29 days depending on whether the year is a leap year. The other months have 30 or 31 days. If the day occurs before the 10th, the number would display without the leading 0. dd: The double d as dd displays the numeric day of the month, from 1 tom 31 depending on the month and whether it is a leap year for February. If the month occurs before October, it would display with the leading 0. M: The single M is used to display the numeric position of the month. January is 1, February is 2 and December is 12. If the month occurs before October, it would display without the leading 0. MM: The double M as MM displays the numeric position of a month from 1 to 12. If the number is less than 10, it would display with the leading 0. MMM: The triple M as MMM displays the name of the month using three letters. It uses a global variable called ShortMonthNames. This variable is defined by the operating system. By default, the names of the month are Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, and Dec. MMMM: The quadruple M as MMMM displays the complete name of a month as defined as the operating system. The variable used is called LongMonthNames. The names of the months are January, February, March, April, May, June, July, August, September, October, November, and December. Y or YY: The single y or double yy is used to display the numeric year with the last two digits.
754
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
YYY or YYYY: The triple y as yyy or the quadruple one as yyyy is used to display all four digits of a year.
Using Dates Displaying Dates Any control that uses an AnsiString can display a date. From the declarations we have seen, if you create an initialized date, you can use the DateToStr() function to display it. If you use a date variable declared with the default construction, the control would display the first date the compiler can recognize: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime Mine; Edit1->Text = DateToStr(Mine); } //---------------------------------------------------------------------------
If the date is initialized with a valid date value, you can omit the conversion function: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime Mine("12/05/1990"); Edit1->Text = Mine; } //---------------------------------------------------------------------------
If you supply the integer values of the variable, the compiler would take care of displaying the equivalent date: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime Mine(1990, 11, 26); Edit1->Text = Mine; } //---------------------------------------------------------------------------
In the same way, if you initialize the variable with a number of days as an integer, the compiler would calculate and display the corresponding date; //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime Mine = 33895; Edit1->Text = Mine; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
755
Appendix
Borland C++ Builder Programming
Decoding a Date A date variable declared from the TDateTime class is made of a year, a month, and a day values. Decoding a date consists of isolating or retrieving these components of a date value. To perform such an operation, the TDateTime class is equipped with the DecodeDate() method. Its syntax is: void __fastcall DecodeDate(unsigned short* year, unsigned short* month, unsigned short* day) const; Each component is retrieved using a pointer to an unsigned short. The presence of pointers allows you to pass “empty” variables whose value would be altered by the function and returned with new values. In the following example, the current date is stored in a TDateTime variable. Then the year, month, and day of the variable are extracted before constructing a sentence to display on a label: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TDateTime Today = Date(); unsigned short Year, Month, Day; Today.DecodeDate(&Year, &Month, &Day); Label1->Caption = "Today is the " + String(Day) + " of month " + Month + " of the year " + Year + "."; } //---------------------------------------------------------------------------
If you want to display a better English version of the sentence above, you can format the date components to your liking. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { TDateTime Today = Date(); AnsiString Dayth, Monthth; unsigned short Year, Month, Day; Today.DecodeDate(&Year, &Month, &Day); if( Day == 1 ) Dayth = "st"; else if( Day == 2 ) Dayth = "nd"; else if( Day == 3 ) Dayth = "rd"; else Dayth = "th"; if( Month == 1 ) Monthth = "st"; else if( Month == 2 ) Monthth = "nd"; else if( Month == 3 )
756
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
Monthth = "rd"; else Monthth = "th"; Label1->Caption = "Today is the " + String(Day) + Dayth + " of the " + Month + Monthth + " month of the year " + Year + "."; } //---------------------------------------------------------------------------
The DecodeDate() function comes in two versions. Besides the TDateTime’s, the VCL provides another version whose syntax is: void __fastcall DecodeDate(System::TDateTime Date, Word &Year, Word &Month, Word &Day); Since this version is class-independent, the first argument you must supply is a TDateTime value or variable. This time, the year, the month, and the day values are passed by reference, which also allows the function to return them altered. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDecodeClick(TObject *Sender) { TDateTime Typed = StrToDate(edtDate->Text); Word Year, Month, Day; DecodeDate(Typed, Year, Month, Day);
edtDay->Text = Day; edtMonth->Text = Month; edtYear->Text = Year;
} //--------------------------------------------------------------------------void __fastcall TForm1::btnCloseClick(TObject *Sender) { Close();
Copyright © 2003 FunctionX, Inc.
757
Appendix
Borland C++ Builder Programming
} //---------------------------------------------------------------------------
If you want to get or display the English name of the decoded month, you can write a conditional switch whose cases would represent the months by their integral position. You can also declare an AnsiString variable to hold the names of months and retrieve the necessary one when needed. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnDecodeClick(TObject *Sender) { TDateTime Typed = StrToDate(edtDate->Text); Word Year, Month, Day;
AnsiString Months[] = { "", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
DecodeDate(Typed, Year, Month, Day); edtDay->Text = Day; edtMonth->Text = Months[Month]; edtYear->Text = Year; } //---------------------------------------------------------------------------
Encoding a Date Encoding a date consists of supplying the necessary components of a TDateTime to the compiler to create a valid TDateTime value. The function used to perform this operation is: TDateTime __fastcall EncodeDate(Word Year, Word Month, Word Day); This function takes three positive integers (unsigned short) that represent: • • •
the year: valid values range from 0 to 9999; the month: valid values range from 1 to 12; January is 1, February is 2, etc; the day: this could be 28, 29, 30, or 31 depending on the month and whether the year is a leap year, which controls the number of days for the month of February.
The following form is equipped with four Edit controls named edtDay, edtMonth, edtYear and edtDate. When the user clicks the Encode button named btnEncode, the 758
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
OnClick event retrieves the values of the day, the month, and the year from their respective edit box. Then the compiler creates a date from those and displays it in the Date edit box:
//--------------------------------------------------------------------------void __fastcall TForm1::btnEncodeClick(TObject *Sender) { Word Day, Month, Year; Day = edtDay->Text.ToInt(); Month = edtMonth->Text.ToInt(); Year = edtYear->Text.ToInt(); TDateTime Value = EncodeDate(Year, Month, Day); edtDate->Text = Value;
} //---------------------------------------------------------------------------
Finding Out the Leap Year One of the arduous operations performed on dates is to find out out whether the year value of a date is a leap year. Luckily, the IsLeapYear() function can perform it. The syntax of this function is: bool __fastcall IsLeapYear(Word Year); This function takes an unsigned short integer argument variable and examines it. If the argument, which must be a valid year number, is a leap year, the function returns true; otherwise, it would return false. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { if( IsLeapYear(Edit1->Text.ToInt()) ) Edit2->Text = "Leap Year"; else Edit2->Text = "Not"; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
759
Appendix
Borland C++ Builder Programming
The following example starts by requesting a date value from the user using an InputBox() function. Then the date is decoded to retrieve the year value. The year is examined to find out whether it is a leap year, using the IsLeapYear() function. The function displays a message box to show its findings: //--------------------------------------------------------------------------void __fastcall TForm1::btnLeapYearClick(TObject *Sender) { unsigned short Year, Month, Day; AnsiString Value = InputBox("Date and Time", "Enter a date: ", "01/01/1900"); TDateTime Examiner = StrToDate(Value); Examiner.DecodeDate(&Year, &Month, &Day); AnsiString LeapYear = IsLeapYear(Year) ? " is a leap year" : " is not a leap year"; ShowMessage("The date you typed was " + Value + "\n" + AnsiString(Year) + LeapYear); } //---------------------------------------------------------------------------
The Day of the Week The VCL is equipped with a special function that can be used to retrieve the day of the week of a given date. The function used is the DayOfWeek() and its syntax is: int __fastcall DayOfWeek(System::TDateTime Date); To use this function, pass a TDateTime value or variable to the DayOfWeek() function. The TDateTime object passed as argument must hold a valid date value. After execution, the function returns an integer between 1 (included) and 7 (included) that represents the position of the day. Sunday is referred to as the first day of the week and has a value of 1; Monday is 2, etc. Here is an example:
//---------------------------------------------------------------------------
760
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
void __fastcall TForm1::btnGetItClick(TObject *Sender) { TDateTime Value = StrToDate(edtDate->Text); int Day = DayOfWeek(Value); edtDay->Text = Day; } //--------------------------------------------------------------------------void __fastcall TForm1::btnCloseClick(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
Of course, sometimes you will want to get or display the English name of the day. To do this, you can write a switch conditional statement that would display a name accordingly. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnGetItClick(TObject *Sender) { TDateTime Value = StrToDate(edtDate->Text); int Day = DayOfWeek(Value); AnsiString DayName;
switch(Day) { case 1: DayName = "Sunday"; break; case 2: DayName = "Monday"; break; case 3: DayName = "Tuesday"; break; case 4: DayName = "Wednesday"; break; case 5: DayName = "Thursday"; break; case 6: DayName = "Friday"; break; case 7: DayName = "Saturday"; }
edtDay->Text = DayName; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
761
Appendix
Borland C++ Builder Programming
An alternative would be to declare an array of AnsiString strings to hold the names of the week days, then retrieve the necessary one using its corresponding position. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnGetItClick(TObject *Sender) { TDateTime Value = StrToDate(edtDate->Text); int Day = DayOfWeek(Value);
AnsiString DayName[] = { "", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
edtDay->Text = DayName[Day]; } //---------------------------------------------------------------------------
The DayOfWeek() function comes in two versions. Besides the VCL’s, the TDateTime class also is equipped with this method. Its syntax is: int __fastcall DayOfWeek() const; This version does not take an argument. Instead, it is called by a TDateTime variable that needs it. This function returns an integer that represents the weekly position of the day if the execution is successful. The equivalent version of the above program would be: //--------------------------------------------------------------------------void __fastcall TForm1::btnGetItClick(TObject *Sender) { TDateTime Value = StrToDate(edtDate->Text); int Day = Value.DayOfWeek(); edtDay->Text = Day; } //---------------------------------------------------------------------------
Increasing Months on a Date The addition operator of the TDateTime class is used to add a number of days to a date value. If you want to add months to a date value, you can use the IncMonth() function. Its syntax is: TDateTime __fastcall IncMonth(const TDateTime Source, int Months); This function takes two arguments. The first is the date value or variable that serves as the source or reference to work on. This argument must hold a valid TDateTime date 762
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
value; otherwise the execution would fail. The second argument, Months is an integer that represents the number of months to be added to the first argument. If the addition is successful, the IncMonth() function returns a new TDateTime value. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::edtMonthsExit(TObject *Sender) { TDateTime StartDate = edtStartDate->Text; int Months = edtMonths->Text.ToInt(); TDateTime NextPeriod = IncMonth(StartDate, Months);
edtNextPeriod->Text = NextPeriod; } //---------------------------------------------------------------------------
The IncMonth() is used to both add and subtract months from a date. To subtract months, pass the Months argument with a negative value. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::edtMonthsExit(TObject *Sender) { TDateTime StartDate, NextPeriod; int Months; if(edtMonths->Text != "") { StartDate = edtStartDate->Text; Months = edtMonths->Text.ToInt(); NextPeriod = IncMonth(StartDate, Months); edtNextPeriod->Text = NextPeriod; } else edtNextPeriod->Text = edtStartDate->Text; } //---------------------------------------------------------------------------
Replacing a Date void __fastcall ReplaceDate(TDateTime &Target, const TDateTime Source);
Copyright © 2003 FunctionX, Inc.
763
Appendix
Borland C++ Builder Programming
The ReplaceDate() function allows replacing one date with another. On the function, the Target argument is the new date whose value needs to be replaced by that of the Source argument. Since the starting point of the TDateTime class is on 12/30/1899, if the Source argument occurs before that the date, the ReplaceDate() function takes care of reconciling the negative date. Here is an example of using the function:
//--------------------------------------------------------------------------void __fastcall TForm1::btnReplaceClick(TObject *Sender) { TDateTime Original = StrToDate(edtOriginal->Text); TDateTime Replacement; ReplaceDate(Replacement, Original); edtReplacement->Text = DateToStr(Replacement);
} //---------------------------------------------------------------------------
Comparison Operations on Dates The TDateTime class was configured with many arithmetic and comparison operators. These are used on date values allowing you to use TDateTime variables as if they were regular variables.
The Comparison for Equality To find out whether two data values are the same, simply apply the Equality operator on their values. Once you have two TDateTime values or variables, the compiler is configured to perform this comparison. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if(Start == End) pnlComparison->Caption = "Both dates are the same"; else pnlComparison->Caption = "Those are different dates!"; } //---------------------------------------------------------------------------
764
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
This comparison is possible because the Equality operator “==” was overloaded in the TDateTime class. Its syntax is: bool __fastcall operator ==(const TDateTime& Target) const; To compare two dates using the overloaded Equality operator, call the operator==() on the desired date and supply the argument date value that is being compared against ar the Target. The above could have been written: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start.operator ==(End) ) pnlComparison->Caption = "Both dates are the same"; else pnlComparison->Caption = "Those are different dates!"; } //---------------------------------------------------------------------------
The Comparison for Inequality To find out whether two data values are not the same, simply apply the inequality operator on their values. Once you have two TDateTime values or variables, the compiler is configured to perform this comparison. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start != End ) pnlComparison->Caption = "Those are different dates!"; else pnlComparison->Caption = "Both dates are the same"; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
765
Appendix
Borland C++ Builder Programming
This comparison is possible because the inequality operator “!=” is overloaded in the TDateTime class. Its syntax is: bool __fastcall operator !=(const TDateTime& Target) const; To compare two dates using the overloaded inequality operator, call the operator==() on the desired date and supply the argument date value that is being compared against ar the Target. The above could have been written: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start.operator !=(End) ) pnlComparison->Caption = "Those are different dates!"; else pnlComparison->Caption = "Both dates are the same"; } //---------------------------------------------------------------------------
The Comparison for Inferiority To find if Source date occurs prior to a Target date, apply the “less than” comparison operator. To do this, use the operator as if both date values were normal integral or floating-point values. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start < End ) pnlComparison->Caption = DateToStr(Start) + " occurs prior to " + DateToStr(End); else pnlComparison->Caption = "I can't make up my mind"; } //---------------------------------------------------------------------------
766
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
Alternatively, you can use the overloaded “less than” operator to find out when one date is less than another. The syntax used is: bool __fastcall operator <(const TDateTime& Target) const; To perform the above “less than” comparison, you could implement the event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start.operator<(End) ) pnlComparison->Caption = DateToStr(Start) + " occurs prior to " + DateToStr(End); else pnlComparison->Caption = "I can't make up my mind"; } //---------------------------------------------------------------------------
The Comparison for Inferiority or Equality Two TDateTime values can be compared to find out whether they are the same or if a source date occurs prior to a target date. To perform this comparison, apply the “less than or equal” operator as if the values were regular integer or floating-points. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start <= End ) pnlComparison->Caption = "Your film will be ready after 5 O'Clock"; else pnlComparison->Caption = "Wrong date sequence"; } //---------------------------------------------------------------------------
This comparison operator is useful because the “less than or equal to” operator “<=” was overloaded in the TDateTime class. Its syntax is: bool __fastcall operator <=(const TDateTime& Target) const; Copyright © 2003 FunctionX, Inc.
767
Appendix
Borland C++ Builder Programming
Using the <= overloaded operator, The comparison in the above event could have been written as follows: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start.operator <=(End) ) pnlComparison->Caption = "Your film will be ready after 5 O'Clock"; else pnlComparison->Caption = "Wrong date sequence"; } //---------------------------------------------------------------------------
The Comparison for Superiorty To find if a source date occurs after a target date, apply the “greater than” comparison operator. This operator is used the same way you would for a regular integer or a floating-point values. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start > End ) pnlComparison->Caption = DateToStr(Start) + " occurs after " + DateToStr(End); else pnlComparison->Caption = "I can't make up my mind"; } //---------------------------------------------------------------------------
This “greater than” comparison between two date values is possible because its operator was overloaded in the TDateTime class. Its syntax is: bool __fastcall operator >(const TDateTime& Target) const; Using the overloaded operator, the previous event could have been implemented as follows: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender)
768
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
{ TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start.operator >(End) ) pnlComparison->Caption = DateToStr(Start) + " occurs after " + DateToStr(End); else pnlComparison->Caption = "I can't make up my mind"; } //---------------------------------------------------------------------------
The Comparison for Superiority or Equality Two TDateTime values can be compared to find out whether they are the same or if a source date occurs after a target date. To perform this comparison, apply the “greater than or equal” operator as if the values were regular integer or floating-points. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start >= End )
pnlComparison->Caption = "Testing for ""\"Greater Than Or Equal To""\""; else pnlComparison->Caption = "I can't make up my mind"; } //---------------------------------------------------------------------------
Using this comparison, you can validation an intermediary operation. Even after finding out whether the first date is greater than or equal to the second, you can further refine your comparison inside of the comparison. In the following example, a message box displays if the “greater than or equal to” comparison returns false: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start >= End ) { pnlComparison->Caption = "Testing for ""\"Greater Than Or Equal To""\""; if( Start > End ) ShowMessage(DateToStr(Start) + " occurs after " + DateToStr(End)); else if( Start == End ) ShowMessage("Both dates occur at the same time"); } else pnlComparison->Caption = "I can't make up my mind"; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
769
Appendix
Borland C++ Builder Programming
This comparison operator is useful because the “greater than or equal to” operator “<=” was overloaded in the TDateTime class. Its syntax is: bool __fastcall operator >=(const TDateTime& Target) const; To perform the comparison in the above event using the <= overloaded operator, call the operator>=() method on the source date value and include the compared date as the Target argument. The above event could be written as follows: //--------------------------------------------------------------------------void __fastcall TForm1::pnlComparisonClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); if( Start.operator >=(End) ) { pnlComparison->Caption = "Testing for ""\"Greater Than Or Equal To""\""; if( Start > End ) ShowMessage(DateToStr(Start) + " occurs after " + DateToStr(End)); else if( Start == End ) ShowMessage("Both dates occur at the same time"); } else pnlComparison->Caption = "I can't make up my mind"; } //---------------------------------------------------------------------------
Operations on Dates The TDateTime and other VCL functions allow you to perform various types of operations on a date value. The TDateTime class is very sophisticated, especially when copled with the Sysutils functions. Almost all types of operations and all types of comparisons are possible. All arithmetic and all logic comparison operators were overloaded to permit as much flexibility as possible. Some of the operations are possible directly on date values. When not possible or difficult, the decoder and encoder functions can be used to let the compiler work behind the scenes.
Assigning One Date to Another To assign one date to another, simply use the assignment operator “=” as if the variables were regular values. In the following example, the user enters a starting date in an Edit control and finds out the number of days a customer of a car rental wants to keep the car. The clerk enters this value in the # of Days edit box. When the clerk clicks somewhere else, that is, when the edit box loses focus, the content of the edit box is checked. If the number of days is 1 or less, which means the customer wants the car for only one day, the first date is assigned to a second TDateTime variable and displays in the End Date edit box:
770
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
//--------------------------------------------------------------------------void __fastcall TForm1::edtNbrOfDaysExit(TObject *Sender) { TDateTime Start, End; int NbrOfDays; Start = StrToDate(edtStartDate->Text); NbrOfDays = StrToInt(edtNbrOfDays->Text); if(NbrOfDays <= 1) { End = Start; edtEndDate->Text = End; } else { edtEndDate->Text = ""; edtEndDate->SetFocus(); } } //---------------------------------------------------------------------------
Alternatively, the TDateTime has the assignment operator overloaded to allow assigning a date variable to another. The syntaxes of the function are: TDateTime& __fastcall operator =(const TDateTimeBase& rhs); TDateTime& __fastcall operator =(const TDateTime& rhs); TDateTime& __fastcall operator =(const double rhs); TDateTime& __fastcall operator =(const int rhs); To assign one date to another using the operator =() function, use a valid TDateTime value or declare a TDateTime variable and call the operator=() overloaded function by supplying the intended target TDateTime variable. The above event could be rewritten as; //--------------------------------------------------------------------------void __fastcall TForm1::edtNbrOfDaysExit(TObject *Sender) { TDateTime Start, End; int NbrOfDays; Start = StrToDate(edtStartDate->Text); NbrOfDays = StrToInt(edtNbrOfDays->Text); if(NbrOfDays <= 1) { End.operator =(Start); edtEndDate->Text = End;
Copyright © 2003 FunctionX, Inc.
771
Appendix
Borland C++ Builder Programming
} else { edtEndDate->Text = ""; edtEndDate->SetFocus(); }
} //---------------------------------------------------------------------------
Adding Values to a Date To add a number of days to a TDateTime value, simply add an integer to the intended date value. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnAdditionClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime Addition = Start + 5; edtAddition->Text = Addition; } //---------------------------------------------------------------------------
You can also get the number of days from the user by using another control on the application. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnEstimateClick(TObject *Sender) { TDateTime DepositDate = edtDepositDate->Text; int Days = edtDays->Text.ToInt(); TDateTime PickupDate = DepositDate + Days; edtPickupDate->Text = PickupDate; } //--------------------------------------------------------------------------void __fastcall TForm1::btnCloseClick(TObject *Sender) { Close(); }
772
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
//---------------------------------------------------------------------------
The addition operation is possible on a date value because its operator is overloaded in the TDateTime class. The TDateTime class provides a mechanism of adding a number of days to a date value. The syntaxes of the overloaded operator are: TDateTime __fastcall operator +(const TDateTimeBase& rhs) const; TDateTime __fastcall operator +(const TDateTime& rhs) const; TDateTime __fastcall operator +(const double rhs) const; TDateTime __fastcall operator +(const int rhs) const; When applied to a TDateTime value, the addition operator “+” adds a number of days to a date. If the number added exceeds the end of year, the class will calculate and encode a date that corresponds to the date of the subsequent year:
To add a number of months to a date value, decode the date to retrieve its year, month, and day values. Add the intended number of months to your date and re-encode the date. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnAdditionClick(TObject *Sender) { TDateTime Start = StrToDate(edtDate->Text); int Months = StrToInt(edtValue->Text); unsigned short Year, Month, Day; Start.DecodeDate(&Year, &Month, &Day); TDateTime End(Year, Month+Months, Day); edtResult->Text = DateToStr(End);
} //--------------------------------------------------------------------------void __fastcall TForm1::btnExitClick(TObject *Sender) { Close();
Copyright © 2003 FunctionX, Inc.
773
Appendix
Borland C++ Builder Programming
} //---------------------------------------------------------------------------
To add a number of years to a date value, decode it to extract the year, month, and day values. Add the integral number of years to the source year. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnAdditionClick(TObject *Sender) { TDateTime Start = StrToDate(edtDate->Text); int Years = StrToInt(edtValue->Text); unsigned short Year, Month, Day; Start.DecodeDate(&Year, &Month, &Day); TDateTime End(Year+Years, Month, Day); edtResult->Text = DateToStr(End); } //---------------------------------------------------------------------------
Assigning an Added Date You can add a value to a date variable and assign the new value to the original date. This is the same as using the += operator. The syntaxes used for this operator are: TDateTime& __fastcall operator +=(const TDateTimeBase& rhs); TDateTime& __fastcall operator +=(const TDateTime& rhs); TDateTime& __fastcall operator +=(const double rhs); TDateTime& __fastcall operator +=(const int rhs); To add an number of days to an existing date value, you can use the += operator as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnDisplayClick(TObject *Sender) { TDateTime OneDay = StrToDate(edtDate->Text); TDateTime Added; OneDay += 5; edtResult->Text = DateToStr(OneDay); } //---------------------------------------------------------------------------
774
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
Subtracting Dates To get the number of days between two dates, perform the subtraction operation on their values. To do this, you can declare a double precision number or an integer that would store the subtracted number from the later date to the earlier. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); int Diff = End - Start; edtDifference->Text = Diff; } //---------------------------------------------------------------------------
To get the difference of years between two dates, apply the subtraction operator on their values to get the integral number of days. Then divide this number by 365. This difference produces the number of years in ranges of 365 days. Here is an example:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); int Years = End - Start; edtYears->Text = (Years / 365); } //--------------------------------------------------------------------------void __fastcall TForm1::btnCloseClick(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
As an alternative, you can decode both dates and subtract their year values; this would produce the difference of years with regards to the years, not the real dates. For example, Copyright © 2003 FunctionX, Inc.
775
Appendix
Borland C++ Builder Programming
the difference between 12/31/2001 and 01/01/2002 would produce a year. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); unsigned short StartYear, StartMonth, StartDay, EndYear, EndMonth, EndDay; Start.DecodeDate(&StartYear, &StartMonth, &StartDay); End.DecodeDate(&EndYear, &EndMonth, &EndDay); int Years = EndYear - StartYear;
edtYears->Text = Years; } //---------------------------------------------------------------------------
To get the difference of months between two dates, perform the subtraction operator on their values to get the number of days elapsed and divide the result by 30. This would produce a number of months. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text); int Months = End - Start; edtYears->Text = Months / 30; } //---------------------------------------------------------------------------
The subtraction operation is possible on TDateTime values because the subtraction operator “-“ is overloaded in the TDateTime class as follows: TDateTime __fastcall operator -(const TDateTimeBase& rhs) const; TDateTime __fastcall operator -(const TDateTime& rhs) const; TDateTime __fastcall operator -(const double rhs) const; TDateTime __fastcall operator -(const int rhs) const; The subtraction operator “-“ is overloaded in the TDateTime class to allow getting the difference of days, that is, the elapsed number of days between two dates. The subtraction is performed as Date2 – Date1. In this case, if Date2 occurs after Date1, the result would be a positive number; otherwise a negative value would indicate that Date2 occurs before Date1. You can also use the operator int() overloaded function to get the difference of days between two dates. To do this, declare an integer that stores the subtracted number between two TDateTime dates. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnDifferenceClick(TObject *Sender) { TDateTime Start = StrToDate(edtStart->Text); TDateTime End = StrToDate(edtEnd->Text);
776
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
TDateTime Diff = End - Start; int Days = Diff.operator int(); edtDifference->Text = Days; } //---------------------------------------------------------------------------
Assigning a Subtracted Date The -= operator is used to subtract a number of days from a date and assign the new value to the date (whose value has been subtracted). This operation is performed using the overloaded -= operator that uses the following syntaxes: TDateTime& __fastcall operator -=(const TDateTimeBase& rhs); TDateTime& __fastcall operator -=(const TDateTime& rhs); TDateTime& __fastcall operator -=(const double rhs); TDateTime& __fastcall operator -=(const int rhs); To subtract a number of days from a date, you can use the subtraction operator. To assign the subtracted value to the original date, use the -= operator. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnDisplayClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); int IntValue = StrToInt(edtSubtract->Text); DateValue -= IntValue; edtResult->Text = DateToStr(DateValue);
} //---------------------------------------------------------------------------
Decrementing a Date To decrement a date value, declare a TDateTime variable and apply the – operator on its value. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { static TDateTime Start = StrToDate(edtStartDate->Text); edtStartDate->Text = Start--; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
777
Appendix
Borland C++ Builder Programming
The TDateTime class allows subtracting one day from a TDateTime value. This is done using the overloaded decrement operator whose syntaxes are: TDateTime& operator --(); TDateTime operator --(int); To decrement a date value using the -- overloaded operator, you have two options. To use the pre-decrement operator, as if you were using “--Value”, call the operator--() function. This would apply the operator before recalling the variable. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { static TDateTime Start = StrToDate(edtStartDate->Text); edtStartDate->Text = Start.operator --(); } //---------------------------------------------------------------------------
To use the post-increment operator, which is the same as “Value--“, in which case the statement is called before being incremented, use the operator--(int) method. The int argument is not specific but you must supply it. Therefore, type any integer number between the parentheses. Remember that the argument supplied is not the decrementing value; it is only a “witness” but it is necessary. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { static TDateTime Start = StrToDate(edtStartDate->Text); edtStartDate->Text = Start.operator --(2); } //---------------------------------------------------------------------------
Incrementing a Date To increment a date value, declare a TDateTime variable and use the ++ operator on its value. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnIncrementClick(TObject *Sender) { static TDateTime Original = StrToDate(edtDate->Text); edtIncremented->Text = Original++; } //---------------------------------------------------------------------------
The TDateTime class allows you to add one day from a TDateTime value. This is done using the overloaded increment operator with the following syntaxes:
778
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
TDateTime& operator ++(); TDateTime operator ++(int); To apply the pre-increment operator, as if you were using “++Value”, call the operator++() function. This would apply the operator before recalling the variable. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnIncrementClick(TObject *Sender) { static TDateTime Original = StrToDate(edtDate->Text); edtIncremented->Text = Original.operator ++(); } //---------------------------------------------------------------------------
To use the post-increment operator, which is the same as “Value++“, in which case the statement is called before being incremented, use the operator++(int). The int argument is not specific but you must supply it. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnIncrementClick(TObject *Sender) { static TDateTime Original = StrToDate(edtDate->Text); edtIncremented->Text = Original.operator ++(12); } //---------------------------------------------------------------------------
This was the opening dialog box:
This was performed after the first click:
Formatting and Controlling the Display of Dates The TDateTime class and the VCL provide special functions that can be used to control how the date values display in your application. Calling one of these functions, a date can use almost any format you see fit or any that makes your application user-friendly. To control how a date value displays, the TDateTime class uses the FormatString() method. Its syntax is: AnsiString __fastcall FormatString(const AnsiString& format); The TDateTime::FormatString() method takes a string argument that specifies what format to apply. After formatting the date value, the method returns an AnsiString string. Alternatively, the VCL has its own function that performs the same operation. Its syntax is:
Copyright © 2003 FunctionX, Inc.
779
Appendix
Borland C++ Builder Programming
AnsiString __fastcall FormatDateTime(const AnsiString Format, System::TDateTime DateValue); The FormatDateTime() function takes two arguments. The Format argument is a string that specifies how the date should be formatted. Since this function is not part of the TDateTime class, it needs a valid TDateTime date value to work on; that is the role of the DateValue argument.
The Default Display The default format used by both the TDateTime::FormatString() and the FormatDateTime() functions conforms to the ShortDateFormat of the Windows Control Panel. For these functions, that format is represented by the “c” string. You can still use it explicitly. For the TDateTime::FormatString() method, an example would be: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime DateValue("10/22/2001"); Edit1->Text = DateValue.FormatString("c"); } //---------------------------------------------------------------------------
A similar example applied on the FormatDateTime() function would be: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime DateValue("10/22/2001"); Edit1->Text = FormatDateTime("c", DateValue); } //---------------------------------------------------------------------------
Displaying the Numeric Day The days of months are numbered from 1 to 31, depending on the month. The formatting functions represent each of these days with the “d” format. Here is an example used for the TDateTime::FormatString() method: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatStringClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("d"); } //---------------------------------------------------------------------------
This would produce:
780
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
The implementation of this format using the FormatDateTime() function is: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatDateTimeClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatDateTime->Text = FormatDateTime("d", DateValue); } //---------------------------------------------------------------------------
When the day has a numeric value that is less than 10, the default “c” and the “d” formats display its value without the leading 0. To display the leading 0 as in 02 or 08, use the “dd” format. Here is an example implemented using the TDateTime::FormatString() method: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("dd"); } //---------------------------------------------------------------------------
Using the FormatDateTime() function, you could have written the same event as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = FormatDateTime("dd", DateValue); } //---------------------------------------------------------------------------
Displaying Weekday Names The names of the week use two formats: 3 letters or full name. To display a name with 3 letters, use the “ddd” format. The names will be Sun, Mon, Tue, Wed, Thu, Fri, or Sat. Here is an example using the TDateTime::FormatString() method: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text);
Copyright © 2003 FunctionX, Inc.
781
Appendix
Borland C++ Builder Programming
edtFormatString->Text = DateValue.FormatString("ddd"); } //---------------------------------------------------------------------------
The same event using the FormatDateTime() function would be written as: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = FormatDateTime("ddd", DateValue); } //---------------------------------------------------------------------------
To display the weekday and the numeric day of the month, you can create a format that combines both strings. When creating this string, the format must be separated inside the string so the compiler would know which format to apply and where. To separate the formats, you can use (almost) any character but you should conform to those used in your regional settings. One of the most regularly used separators on dates is the comma but the simplest separator is an empty space. Here is an example (using the TDateTime::FormatString() method): //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("ddd dd"); } //---------------------------------------------------------------------------
To display the complete name of a weekday, use the “dddd” format string. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("dddd"); } //---------------------------------------------------------------------------
You can also display the weekday followed by the numeric day of the month. Here is an example that uses the FormatDateTime() function: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text);
782
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
edtFormatString->Text = FormatDateTime("dddd dd", DateValue); } //---------------------------------------------------------------------------
Displaying Numeric Months Months are displayed using two categories: a number or a name. To display the numeric range of a month, use the “m” format. The months are numbered as follows: 1=January, 2=February, 3=March, 4=April, 5=May, 6=June, 7=July, 8=August, 9=September, 10=October, 11=November, and 12=December. Here is an example that displays the numeric month of a date, using the TDateTime::FormatString() method: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("m"); } //---------------------------------------------------------------------------
Following the rules we applied to display a combination of a weekday and a month’s numeric day, you can display a month day and the month’s numeric value. This time, instead of an empty space, you should use a character that would indicate that the date is displaying a combination of month and day (or day and month). The best character to use is the one that conforms to the regional settings of your computer. In the United States’ English, this would be the forward slash “/”. Here is an example that uses the FormatDateTime() function: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = FormatDateTime("m/dd", DateValue); } //---------------------------------------------------------------------------
In the same way you can combine a weekday short name followed by the combination of day/month (or month/day) as you see fit. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = FormatDateTime("ddd m/dd", DateValue); } //---------------------------------------------------------------------------
This would produce:
Copyright © 2003 FunctionX, Inc.
783
Appendix
Borland C++ Builder Programming
When using the “m” format, if the number of the month is less than 10, the compiler would display it as 1, 2, 3, 4, 5, 6, 7, 8 or 9, without the leading 0. If you want to display the leading zero for a month between 1 and 9, as 01 or 07, use the “mm” format. Here is an example with the TDateTime::FormatString() method: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("mm"); } //---------------------------------------------------------------------------
You can also use this format when constructing a combined date: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("dddd, mm/dd"); } //---------------------------------------------------------------------------
Displaying Months Names You can display a month by its name using one of two formats: short or long name. The short names of months are: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, and Dec. To display a month with one of these names, use the “mmm” format. The following example uses the TDateTime::FormatString() method: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("mmm"); } //---------------------------------------------------------------------------
This time, the name of the month would become more explicit in a combined format, allowing the application to be more explicit. To create such a combined date, apply the rules we have reviewed so far. The following TDateTime::FormatString() implementation displays a date as short weekday-day-short month name combination: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) {
784
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("ddd dd mmm");
} //---------------------------------------------------------------------------
You can also use a comma and space to separate the name of the weekday from the other components. The following event uses the FormatDateTime() function: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = FormatDateTime("dddd, dd mmm", DateValue); } //---------------------------------------------------------------------------
To display a complete name of a month, use the “mmmm” format. The name would display as one of the following: January, February, March, April, May, June, July, August, September, October, November, and December; confirming to the Regional Settings of your computer. Here is an example that uses the TDateTime::FormatString() method: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); AnsiString Formatter = "ddd, d mmmm"; edtFormatString->Text = DateValue.FormatString(Formatter); } //---------------------------------------------------------------------------
Another implementation that uses the FormatDateTime() function can display the weekday-day-month combination with an empty space as the separator: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); AnsiString Formatter = "dddd, dd mmmm"; edtFormatString->Text = FormatDateTime(Formatter, DateValue);
Copyright © 2003 FunctionX, Inc.
785
Appendix
Borland C++ Builder Programming
} //---------------------------------------------------------------------------
Displaying the Year A year value can be displayed using 2 or 4 digits. To display a year value as a number between 00 and 99, use the “y” or the “yy” formats as follows (this event uses the TDateTime::FormatString() method): //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("y"); } //---------------------------------------------------------------------------
To make the displays we have used so far a little more explicit, you can include the year value in a combined date string, as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("ddd dd mmm yy"); } //---------------------------------------------------------------------------
Therefore, you can apply any combination of the formats we have used so far to display a date, as illustrated in the following FormatDateTime() function call: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); AnsiString Formatter = "dddd dd mmmm yy"; edtFormatString->Text = FormatDateTime(Formatter, DateValue); } //---------------------------------------------------------------------------
A year value represented with two digits is hardly explicit, unless you have a good reason for using it. The alternative is to use all four digits to display a year. This format is created with the “yyy” or the “yyyy” strings. Here is an example with the TDateTime::FormatString() method: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); edtFormatString->Text = DateValue.FormatString("yyy"); } //---------------------------------------------------------------------------
Since this format would be the only one with four digits in a combined string, it makes a date easier to read. Once again, you can apply the rules we have used so far, to create and display a combined date. The default format used by Microsoft Windows for the English 786
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
language is as Sunday, January 27, 2002. You can use the TDateTime::FormatString() method to create such a format as follows: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); AnsiString Formatter = "dddd, mmmm dd, yyyy"; edtFormatString->Text = DateValue.FormatString(Formatter); } //---------------------------------------------------------------------------
Using these rules, you can display a date as you wish. The following FormatDateTime() function display a date differently than the event above: //--------------------------------------------------------------------------void __fastcall TForm1::btnFormatClick(TObject *Sender) { TDateTime DateValue = StrToDate(edtDate->Text); AnsiString Formatter = "dddd d mmmm yyyy"; edtFormatString->Text = FormatDateTime(Formatter, DateValue); } //---------------------------------------------------------------------------
Doing Time The time is a unit that measures the number of hours, minutes, or seconds that divide a day. A day is made of 24 non-spatial divisions called hours. An hour is made of 60 parts called minutes and a minute is made of 60 seconds. As done with dates, most of the operations performed on time values are centered around the TDateTime class. This class is based on a double-precision number initialized at 0.00. The constant 0.00 corresponds to 12/30/1899 at midnight. A double-precision number is made of two sections: an integer part and a decimal side. The integral part is a natural number with no decimal value, such as 8, 1450, or 32. For the TDateTime class, the integral section represents the number of days that have elapsed since 12/30/1899. On a double-precision number, such as 204.58, the decimal part starts with a period “.” and is made of all digits on the right side of the period. For the TDateTime class, the decimal part represents the number of seconds that have elapsed since midnight. By default, the compiler refers to the Regional Settings of your computer to display the time, separating it in hour, minute, second, and AM/PM. The default symbol to separate the hour and the minute, or the minute and the second is “:”. To separate the seconds and the AM/PM, the compiler leaves a one-character empty space between them.
Copyright © 2003 FunctionX, Inc.
787
Appendix
Borland C++ Builder Programming
Declaring Time Variables The time portion of a TDateTime object can be declared and manipulated as a value. To declare time variables, you will use one of the constructors of the TDateTime class. If you declare a variable using the default constructor, as TDateTime TimeValue, the time will be initialized to midnight or 12:00:00 AM. The time in regular value is a floating number that ranges from 0 included to 1 excluded. More precisely, the time is a value defined as follows: 0 >= Time >= 0.99999 The 0 constant represents midnight while the 0.99999 double-precision number represents 12:59:59 PM. In between, 0.50 represents 12:00:00 PM (noon time), 0.325 represents 7:48:00 AM, and 0.738 represents 5:42:43 PM. To declare a time variable, use the TDateTime class and specify the name for the variable. Here is an example: TDateTime Mine;
If not assigned a valued, this variable is initialized at midnight or 12:00:00 AM. You can display its value in an Edit control as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime TimeValue; Edit1->Text = TimeValue; } //---------------------------------------------------------------------------
You can initialize a time value with a double-precision number between 0.00000 and 0.99999. Here is an example: TDateTime Value = 0.2185;
Such an initialization is the same as: TDateTime Value(0.2185);
You can also get the value from an intermediary action or request it from the user. This allows you, if necessary, to convert any floating-point number to a time value, as follows:
You can also use an independent floating-point number to initialize a time variable. Here is an example: 788
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
//--------------------------------------------------------------------------void __fastcall TForm1::btnTimeClick(TObject *Sender) { double Number = edtNumber->Text.ToDouble(); TDateTime TimeValue = Number; edtTime->Text = TimeValue; } //--------------------------------------------------------------------------void __fastcall TForm1::btnCloseClick(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
Still using the default constructor, if you know the time you want to initialze a variable with, you can provide it. To do that, declare an instance of the TDateTime constructor and type the time value between the double-quotes of the parentheses. If the time is known only for the hour(s) and the minute(s), you can initialize it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime TimeValue("08:22"); Label3->Caption = TimeValue; } //---------------------------------------------------------------------------
When using this formula, the hour value must be between 0 and 23. Any other value outside of this range will cause an error. The minute value must range from 0 to 59; otherwise, an error would be thrown. If the hour portion has a value between 0 and 11:59, the time is set in the morning with the AM in the AM/PM section. If the hour portion is between 12 and 23, the time is set in the afternoon. When displaying it, the compiler, by default, calculates and displays the 0 to 12 portion and then displays PM in the AM/PM section. You can also initialize a time value using the Hour:Minute:Second formula as a string. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime TimeValue("20:22:52"); Label3->Caption = TimeValue; } //---------------------------------------------------------------------------
Once again, in the absence of an AM/PM section, the compiler would consider the hour portion to evaluate whether the time occurs in the morning or in the afternoon. The value of the seconds must be between 0 and 59; otherwise, an error will be thrown. You can also initialize a time value by specifying the AM/PM portion as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) {
Copyright © 2003 FunctionX, Inc.
789
Appendix
Borland C++ Builder Programming
TDateTime TimeValue("10:22:52 AM"); Label3->Caption = TimeValue;
} //---------------------------------------------------------------------------
The AM and the PM can be in uppercase or lowercase. In otherwords the AM/PM portion can be represented as AM, Am, aM, am, PM, Pm, pM, or pm. Only the characters A and P (uppercase or lowercase) are accepted as the first character. Only the M or m characters are accepted as the second character. Any other combination or other character will cause an error. If you know the values of the hour, the minute, the second, and the millisecond, you can use them to initialize a time variable. To do this, you must supply the arguments in order following the constructor: __fastcall TDateTime(unsigned short Hour, unsigned short Minute, unsigned short Second, unsigned short Millisecond); The hour value must be between 0 and 23. The minutes must be between 0 and 59. The Second argument must have a value between 0 and 59. Whenever the seconds are not important to represent the time, provide their value as 0. The milliseconds must range from 0 to 999. If you do not know the millisecond value, provide it as 0. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime TimeValue(8, 20, 42, 605); ShowMessage("The time considered is " + TimeValue); } //---------------------------------------------------------------------------
Since a double-precision number has a decimal section that represents the time of the day, you can assign such a value to a TDateTime variable to initialize a time value. If the integral part of the value is greater than 0, it would represents the number of days. If it is 0, only the time would be recognized as a fraction of the day. Using this, you can initia The integral part is section y number
The Time() Function To get or display the time on your application, you can use the Time() function. You can display it on: • An edit box: edtTime->Text = Time(); • A label: lblCurrent->Caption = “Now is the time: “ + Time(); • A panel: pnlTime->Caption = “At this time, we have “ + Time();
Converting a String to Time To convert a string to a time value, use the StrToTime() function whose syntax is: TDateTime __fastcall StrToTime(const AnsiString S); This function takes a string as argument. The string must be in a in a valid time format, following the Regional Rettings of the Control Panel. For example, in the United States, 790
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
the components of a time are separated with a colon “:”. The typical formats of a time are:
If the string contains an invalid date, the conversion would fail and the program would throw an error. If the conversion is successful, the function returns a valid time. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm2::btnConvertClick(TObject *Sender) { TDateTime Value = StrToTime(edtSource->Text); edtTarget->Text = Value; } //---------------------------------------------------------------------------
Converting a Time Value to a String A time value is, default, recongnized and treated as a string whenever necessary. This flexibility allows you to perform transparent conversions from a time variable to a string value. Therefore, it perfectly legimate to write the following assignment: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime TimeValue("22:32:58"); AnsiString Right = TimeValue; Edit1->Text = Right; } //---------------------------------------------------------------------------
This conversion is possible because the AnsiString constructor is overloaded in the TDateTime class. Its syntax is: __fastcall operator AnsiString() const; Therefore, to convert a time value to a string, you can simply assign the time to a string variable. You can also explicity call the overloaded AnsiString as follows: //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
791
Appendix
Borland C++ Builder Programming
void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime StartTime("9:05:00 AM"); AnsiString ToDisplay = StartTime.operator AnsiString(); Edit1->Text = ToDisplay; } //---------------------------------------------------------------------------
The TDateTime class is also equipped with a method that can be used to convert a time value to a string when necessary. The syntax used is: AnsiString __fastcall TimeString() const; To convert a time value to a string, declare a TDateTime variable and call the TimeString() method. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime TimeValue("22:32:58"); AnsiString ToDisplay = TimeValue.TimeString(); Edit1->Text = ToDisplay; } //---------------------------------------------------------------------------
Alternatively, to convert a time value to a string, you can use the TimeToStr() function. Its syntax is: AnsiString __fastcall TimeToStr(System::TDateTime Date); This function takes one argument, which is the time value that needs to be converted. If the argument is not a valid time value, the conversion would fail and the program would throw an error. If the conversion is successful, the function returns an AnsiString value. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnConvertClick(TObject *Sender) { AnsiString Current = TimeToStr(Time()); edtTarget->Text = Current; } //---------------------------------------------------------------------------
Converting a Time Value to a Double-Precision Number A TDateTime time value is a number that represents a fraction of the day on a 24-hour basis. This is quite helpful in algebraic operation related assignments. To convert a time value to a double-precision number, simply cast the time value to a double. In the following example, the current time is converted to double and displayed in an edit box when the user clicks a button on a form: //---------------------------------------------------------------------------
792
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime TimeX = Time(); double Value = double(TimeX); Edit1->Text = Value; } //---------------------------------------------------------------------------
The conversion of a time value to a double-precision number is possible because the double data type was overloaded in the TDateTime to allow this conversion. The syntax of the function used is: __fastcall operator double() const; You can use this function transparently as done in the previous example. You can also call it explicitly call it as follows: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime TimeX("09:42:18 AM"); double Value = TimeX.operator double(); Edit1->Text = Value; } //---------------------------------------------------------------------------
Doing Time Displaying the Time The AnsiString class is highly compatible with the TDateTime class. This flexibility allows any text-based control to be able to display a time value. Thanks to this feature, you do not have to convert a time value in order to display. For example, to show the current time on the caption of a form, you can just write: //--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender) { Caption = Time(); } //---------------------------------------------------------------------------
This ability is independent of the format of the time; that is, it independent of the form of initialization or source of the time value.
Decoding a Time A time variable declared with the TDateTime class is made of an hour, a minute, a second, and a millisecond portions. Decoding a time consists of isolating these components from a valid time value. To perform such an operation, the TDateTime class is equipped with the DecodeTime() method. Its syntax is: void __fastcall DecodeTime(unsigned short* hour, unsigned short* min, unsigned short* sec, unsigned short* msec) const;
Copyright © 2003 FunctionX, Inc.
793
Appendix
Borland C++ Builder Programming
Each component is retrieved using a pointer to an unsigned short. The presence of pointers allows you to pass “empty” variables whose values would be altered by the function and returned with new values. In the following example, the current time is stored in a TDateTime variable named RightNow. A form is equipped with four Edit controls named edtTime edtHours, edtMinutes, and edtSeconds. Although the mSec argument is required to decode a time value, it was used only during the decoding operation. After calling the function, the hour, the minute, and the second are retrieved and displayed in the corresponding edit boxes:
//--------------------------------------------------------------------------void __fastcall TForm1::btnDecodeClick(TObject *Sender) { TDateTime RightNow = Time(); unsigned short Hours, Minutes, Seconds, Milliseconds; RightNow.DecodeTime(&Hours, &Minutes, &Seconds, &Milliseconds); edtTime->Text = RightNow; edtHours->Text = Hours; edtMinutes->Text = Minutes; edtSeconds->Text = Seconds;
} //--------------------------------------------------------------------------void __fastcall TForm1::btnExitClick(TObject *Sender) { Close(); } //---------------------------------------------------------------------------
794
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
Besides the TDateTime::DecodeTime() method, the VCL provides a function that can be used to decode a time value. Its syntax is: void __fastcall DecodeTime(System::TDateTime Time, Word &Hour, Word &Min, Word &Sec, Word &MSec); The global DecodeTime() function is called in the same circumstances as the TDateTime::DecodeTime() method except that it takes five arguments. The first is and must be a valid TDateTime time value; this could be a time value in a recognizable format or an already initialized variable. If this argument does not carry a valid time, the function will fail and throw an error. The other four arguments are positive integers (unsigned short) passed as reference. This allows the function to alter them and return their changed values.
Encoding a Time Encoding a time consists of supplying the necessary components of a TDateTime to the compiler to create a valid time value. The function used to perform this operation is: System::TDateTime __fastcall EncodeTime(Word Hour, Word Min, Word Sec, Word MSec); This function takes four positive integers (unsigned short) that represent: • • • •
the hours: the values range from 0 to 23; midnight is 0 while 3 PM is 15 the minutres: the values range from 0 to 59; the seconds: the values range from 0 to 59; the milliseconds: the values range from 0 to 999.
Example: the following form is equipped with five Edit controls named edtHours, edtMinutes, edtSeconds, edtMilliseconds, and edtTime. When the user clicks the Encode button named btnEncode, the OnClick event retrieves the of the hour, the minute, the second, and the millisecond values from their respective edit boxes. The the compiler creates a time from those valules and displays the result in the Time edit box:
Copyright © 2003 FunctionX, Inc.
795
Appendix
Borland C++ Builder Programming
//--------------------------------------------------------------------------void __fastcall TForm1::btnEncodeClick(TObject *Sender) { Word Hours, Minutes, Seconds, Milliseconds; Hours = edtHours->Text.ToInt(); Minutes = edtMinutes->Text.ToInt(); Seconds = edtSeconds->Text.ToInt(); Milliseconds = edtMilliseconds->Text.ToInt(); TDateTime Value = EncodeTime(Hours, Minutes, Seconds, Milliseconds); edtTime->Text = Value;
} //---------------------------------------------------------------------------
Replacing a Time Value To change the time of a TDateTime value, you can use the ReplaceTime() function. Its syntax is: void __fastcall ReplaceTime(TDateTime &TimeTarget, const TDateTime TimeSource); The ReplaceTime() function takes two arguments. The second argument is the original time that is used as the time reference. The first argument is the time value that needs to be changed or replaced. The TimeSource argument must be a valid time variable or in a recognizable format; otherwise, the function would fail and throw an error. The TimeTarget argument can be an existing time value of a TDateTime variable. After the function has executed, if successful, the time portions of both arguments would be the same.
Comparison Operations On Time Values The TDateTime class provides various functions used to perform any type of comparisons between time values. By default, these operatiorns can be applied the same way as done on regular variables. They can also be customized for more detailed comparisons.
796
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
The Comparison for Equality To find out whether two values carry the same time formats, apply the equality comparison, exactly the same way you would proceed for regular variables. In the following example, two time values are retrieved from two edit boxes for comparison. If both times are the same, a message displays accordingly on a panel: //--------------------------------------------------------------------------void __fastcall TForm1::Panel1Click(TObject *Sender) { TDateTime StartTime = StrToTime(edtStart->Text); TDateTime EndTime = StrToTime(edtEnd->Text); if( StartTime == EndTime ) Panel1->Caption = "Same Time"; else Panel1->Caption = ""; } //---------------------------------------------------------------------------
The equality comparison works on time values thanks the overloaded equality operator on the TDateTime class: bool __fastcall operator ==(const TDateTime& rhs) const; The equality comparison works on all components of a time value. If either the hour, the minute, the second, or the AM/PM is not the same the operation renders false. If you want to compare just the hours, you should decode the time values and then perform the comparison on the hours.
The Comparison for Difference As opposed to the equality operation, you can use the inequality operator to find out whether two dates are different. To do this, simply apply the operator the same way you would do on regular variables. Here is an example following the same logic as above: //--------------------------------------------------------------------------void __fastcall TForm1::Panel1Click(TObject *Sender) { TDateTime StartTime = StrToTime(edtStart->Text); TDateTime EndTime = StrToTime(edtEnd->Text); if( StartTime != EndTime ) Panel1->Caption = "Different Times"; else Panel1->Caption = ""; } //---------------------------------------------------------------------------
The difference comparison works on all four entities of a time value. It examines the hours, minutes, seconds, and AM/PM of the values provides on both sides of the operator. If any of both similar components are different, the operation produces a true result. This operation is based on the overloaded != operator whose syntax is: bool __fastcall operator !=(const TDateTime& rhs) const;
Copyright © 2003 FunctionX, Inc.
797
Appendix
Borland C++ Builder Programming
The Comparison for Previous Occurrence To find out whether a certain time occurs before another, use the “less than” operator “<” the same way you would proceed for a regular variable. In the following example, when the user click the Compare button on a form, the time values on two edit boxes and retrieved and examined. If the time displayed in the Start Shift edit box occurs before the time in the End Shift edit box, a dialog box displays a message accordingly: //--------------------------------------------------------------------------void __fastcall TForm1::btnCompareClick(TObject *Sender) { TDateTime Start = StrToTime(edtStart->Text); TDateTime End = StrToTime(edtEnd->Text); if( End < Start ) ShowMessage("Invalid Time Sheet. Please verify accuracy."); } //---------------------------------------------------------------------------
The “less than” comparison is based on its overloaded operator in the TDateTime class using the following syntax: bool __fastcall operator <(const TDateTime& rhs) const;
The Comparison for Previous or Equal Occurrence The “less than or equal to” operator “<=” is used to check whether a time occurs previous to, or in concordance with, another time. To use this operator on time values, proceed as if you were dealing with regular variables. Here is an example based on the above event: //--------------------------------------------------------------------------void __fastcall TForm1::btnCompareClick(TObject *Sender) { TDateTime Start = StrToTime(edtStart->Text); TDateTime End = StrToTime(edtEnd->Text); if( End <= Start ) ShowMessage("Make sure your time sheet is correct.\n" "If you didn't work on this day, " "you don't have to sign the time sheet." "\n\t\t\t\t\t\tThanks");
} //---------------------------------------------------------------------------
The operator that allows this comparison uses the following syntax: bool __fastcall operator <=(const TDateTime& rhs) const;
The Comparison for Subsequent Occurrence To find out whether one time occurs after another, use the “greate than” operator “>”. This operator works the same way it would for a regular variable. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnCompareClick(TObject *Sender)
798
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
{ TDateTime Start = StrToTime(edtStart->Text); TDateTime End = StrToTime(edtEnd->Text); if( Start > End ) { lblMessage->Caption = "Your time sheet is not correct."; edtStart->SetFocus(); } } //---------------------------------------------------------------------------
The “greater than” operator compare the hours, minutes, seconds, and AM/PM portions of two dates and evaluates if the left date occurred after the right date; in this case, the operator would produce a true Boolean value. This operation is based on its overloaded operator from the TDateTime class. Its syntax is: bool __fastcall operator >(const TDateTime& rhs) const;
The Comparison for Latter or Same Time If a time occurs after or in concordance with another time, you can find this out using the “greater than or equal operator. This operator is applied on two valid time values the same way it would be used on regular variables. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnCompareClick(TObject *Sender) { TDateTime Start = StrToTime(edtStart->Text); TDateTime End = StrToTime(edtEnd->Text); if( Start >= End ) { lblMessage->Caption = "Your time sheet is not correct."; edtStart->SetFocus(); } } //---------------------------------------------------------------------------
This comparison is possible thanks to the following overloaded operator in the TDateTime class: bool __fastcall operator >=(const TDateTime& rhs) const;
Controlling Time Display The TDateTime class and the systdate.h file provide various techniques to control how the time displays. Fundamentally the time displays according to the settings of the Control Panel. There are two main functions for this purpose. The TDateTime class is equipped with the FormatString() function whose syntax is: AnsiString __fastcall FormatString(const AnsiString& Format);
Copyright © 2003 FunctionX, Inc.
799
Appendix
Borland C++ Builder Programming
This function takes, as an argument, a string that specifies how the components of the time value should display. The VCL provides an alternative function to apply the same technique.; Its syntax is: extern PACKAGE AnsiString __fastcall FormatDateTime(const AnsiString Format, System::TDateTime Time); When calling the FormatDateTime() function, you must pass two arguments. The Time argument represents a time value or a variable that holds a valid time value. The Format argument is a string that specifies how the time of the Time argument should display.
Displaying in Default Format By default, the FormatDateTime() and the TDateTime::FormatString() functions follow the format set by the computer’s Regional Settings of the Control Panel, which usually ignores the 0 for hours less than 10. To display the time using the default format, call the TDateTime::FormatString() method and provide the Format argument as “c”. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime RightNow = Time(); RightNow.FormatString("c"); Edit1->Text = RightNow; } //---------------------------------------------------------------------------
Displaying the Leading Zero When the hour portion of a time value is less than 10, you can control whether to display the leading zero. This also applies to the minutes and the seconds. Each of these entities provide two formats to take care of this. The syntax used to display the time is hh:nn:ss AM/PM The hh, nn, and ss portions are not case sensitive. To ignore a leading zero when an hour less than 10 displays, use only h for the hour portion. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime RightNow = Time(); Edit1->Text = RightNow.FormatString("h:nn:ss AM/PM"); } //---------------------------------------------------------------------------
800
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
To display the leading zero, use the hh format for the hour portion. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime RightNow = Time(); Edit1->Text = RightNow.FormatString("hh:nn:ss AM/PM"); } //---------------------------------------------------------------------------
The rule to display or not display the leading zero for the minutes is the same. To avoid the leading zero, use only one n when displaying the time. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnTimeClick(TObject *Sender) { TDateTime TimeX("08:05:52 AM"); // Displaying the minutes without the leading zero edtStart->Text = TimeX.FormatString("HH:n:ss AM/PM"); // Displaying the minutes with the leading zero edtEnd->Text = TimeX.FormatString("hh:nn:ss AM/PM"); } //---------------------------------------------------------------------------
These same rules apply for the seconds portions of the time display. The AM/PM portion is case sensitive. This allows you display it in uppercase or in lowercase. To display the AM/PM section in lowercase, type am/pm or ampm in the AM/PM section. On the other hand, to display it in uppercase, type it as AM/PM or AMPM. Here is an example: //--------------------------------------------------------------------------void __fastcall TForm1::btnTimeClick(TObject *Sender) { TDateTime TimeX("18:05:52"); // Displaying the AM/PM portion in lowercase
Copyright © 2003 FunctionX, Inc.
801
Appendix
Borland C++ Builder Programming
edtStart->Text = TimeX.FormatString("hh:n:ss am/pm"); // Displaying the AMPM portion in uppercase edtEnd->Text = TimeX.FormatString("hh:nn:ss AM/PM");
} //---------------------------------------------------------------------------
Combining Date and Time Fundamental Functions: Now() C++ Builder provides a function that combines date and time. This function, called Now(), can be used to display its value on: • An edit box: edtNow->Text = “Date and Time: “ + Now(); • A label: lblNow->Caption = “Now: “ + Now(); • A form’s caption: Caption = Now();
Converting a String to Date and Time To convert a string to a valid date and time, use the StrToDateTime() function. This function follows the same rules as the StrToDate() function. Its syntax is: TDateTime __fastcall StrToDateTime(const AnsiString S); This function takes a string as argument. The string must have a valid TDateTime format; otherwise, the operation would faild. If everything works fine, after converting the string, the function returns a valid TDateTime value. In the following example, a form is created with a MaskEdit control named edtSource, an Edit control named edtTarget, two Button controls named btnConvert and btnExit. The EditMask of the MaskEdit control is configured as follows: !99/99/0000 !90:00:00 >LL;1;_
Here is the OnClick event of the Convert button: //--------------------------------------------------------------------------void __fastcall TForm1::btnConvertClick(TObject *Sender) { TDateTime Current = StrToDateTime(edtSource->Text); edtTarget->Text = Current; } //--------------------------------------------------------------------------void __fastcall TForm1::btnExitClick(TObject *Sender)
802
Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Appendix
{ Close(); } //---------------------------------------------------------------------------
Converting a Date and Time to a String To convert a date and time value to a string, use the DateTimeToStr(). Its syntax is: AnsiString __fastcall DateTimeToStr(System::TDateTime Date); This function takes, as argument, a valid TDateTime variable or value. If the argument is not a valid date and time value, an error would result. After performing the conversion on the date and time value, the function returns the value as a string. In the following example, when the user clicks a button named btnConvert, the event uses the Now() funtion to retrieve the current date and time then converts it to string and displays the string in an Edit control named edtTarget: //--------------------------------------------------------------------------void __fastcall TForm1::btnConvertClick(TObject *Sender) { AnsiString Current = DateTimeToStr(Now()); edtTarget->Text = Current; } //---------------------------------------------------------------------------
An alternative method to converting a date and time value to a string consists of using the TDateTime::DateTimeString() method. Its syntax: AnsiString __fastcall DateTimeString() const; This function does not need an argument. To use it, declare a TDateTime date variable and call the DateTimeString() method. Here is an example; //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { TDateTime Current = Now(); AnsiString Right = Current.DateTimeString(); Edit1->Text = Right; } //---------------------------------------------------------------------------
Copyright © 2003 FunctionX, Inc.
803
Appendix
Borland C++ Builder Programming
Index A abs() ................................................................. 95 Addition ......................................................... 590 alBottom ........................................................ 461 alClient................................................... 500, 502 algebra.............................................................. 91 Align .............................................................. 362 alLeft.......................................................... 691 TreeView ................................................... 691 AlignButton ................................................... 537 Alignment ...................................................... 502 AllCaps .......................................................... 524 AllowUp ........................................................ 438 alNone............................................................ 363 alTop .............................................................. 458 AM/PM .......................................................... 659 angle............................................................... 120 ANSI_CHARSET .......................................... 252 AnsiCompareStr() ............................................ 67 AnsiExtractQuotedStr() ................................... 72 AnsiLowerCase() ............................................. 63 AnsiQuotedStr()............................................... 71 AnsiSameCaption().......................................... 68 AnsiSameStr().................................................. 68 AnsiSameText() ............................................... 68 AnsiString ........................................................ 55 AnsiCompare()............................................. 66 AnsiCompareIC()......................................... 66 AnsiLastChar()............................................. 69 AppendStr() ................................................. 64 c_str()........................................................... 61 Delete() ........................................................ 70 IsEmpty() ..................................................... 56 Length() ....................................................... 57 LowerCase()................................................. 63 Pos()............................................................. 70 SetLength() .................................................. 57 sprintf() ........................................................ 93 SubString()................................................... 70 ToInt().......................................................... 91 ToIntDef().................................................... 92 Trim()........................................................... 59 TrimLeft() .................................................... 58 TrimRight().................................................. 59 UpperCase() ................................................. 61 AnsiUpperCase() ............................................. 62 ANTIALIASED_QUALITY ......................... 253 appearance ....................................................... 36 Apply ............................................................. 441 804
arc .................................................................. 120 arithmetic ......................................................... 91 ArrowKeys..................................................... 537 ASCII ............................................................. 483 Assign().......................................................... 256 atan() .............................................................. 132 AutoResize..................................................... 473 AutoSelect...................................................... 483 B Back ............................................................... 443 Background .................................................... 683 BackgroundEnabled ....................................... 684 BALTIC_CHARSET ..................................... 252 BEGIN_MESSAGE_MAP ............................ 345 behavior ........................................................... 36 BevelInner...................................................... 553 BevelOuter ..................................................... 444 Bits ................................................................. 235 bitwise............................................................ 253 bkAbort .......................................................... 433 bkAll .............................................................. 433 bkCancel ........................................................ 433 bkClose .................................................. 433, 548 bkCustom ....................................................... 433 bkHelp............................................................ 433 bkIgnore ......................................................... 433 bkNo............................................................... 433 bkOK.............................................................. 433 bkRetry........................................................... 433 blGlyphBottom .............................................. 434 blGlyphLeft.................................................... 434 blGlyphRight.................................................. 434 blGlyphTop .................................................... 434 blinking cursor ............................................... 342 Blue................................................................ 235 BorderStyle ............................................ 481, 557 Borland C++ Builder........................................ 19 BoundsRect .................................................... 340 bsDialog ......................................................... 557 bsNone ........................................................... 599 bsSolid ........................................................... 553 bsTopLine ...................................................... 445 btNext............................................................. 539 btPrev ............................................................. 539 BUTTON ....................................................... 325 Buttons New Form .................................................... 34 Save All........................................................ 27 bvNone................................................... 444, 553 Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
C CalAlignment................................................. 660 CALLBACK.................................................. 343 Cartesian ................................................ 332, 358 case-sensitivity................................................. 65 cbChecked...................................................... 634 cbGrayed................................................ 609, 634 cbUnchecked.................................................. 634 ceil()................................................................. 96 Ceil() ................................................................ 96 CellRect()....................................................... 672 CharCase........................................................ 483 CHARFORMAT............................................ 523 CHARFORMAT2.......................................... 523 Checked ......................................................... 590 CHINESEBIG5_CHARSET.......................... 252 choice............................................................. 605 circle .............................................................. 120 circumference ................................................ 120 Civic............................................................... 546 cl3DDkShadow.............................................. 239 cl3DLight....................................................... 239 clActiveBorder............................................... 239 clActiveCaption ............................................. 239 clAppWorkSpace ........................................... 239 clAqua.................................................... 240, 327 Class Explorer.................................................. 26 Classes AnsiString .................................................... 55 TApplication ................................................ 32 TCheckListBox .......................................... 637 TControl..................................................... 258 TCSpinButton ............................................ 542 TCSpinEdit ................................................ 545 TCustomComboBox .................................. 642 TCustomGrid ............................................. 681 TCustomLabel ........................................... 473 TCustomListBox........................................ 619 TCustomMemo .......................................... 499 TCustomTreeView..................................... 696 TDateTime ................................................. 653 TFindDialog............................................... 528 TFont.......................................................... 248 TFontDialog............................................... 257 TGraphicControl........................................ 473 TGraphicsObject........................................ 256 TLabel........................................................ 473 TList........................................................... 713 TListColumn.............................................. 702 TListItem ................................................... 702 TMainMenu ............................................... 452 TMaskEdit ................................................. 491 TMemo .............................................. 499, 502 TMonthCalendar ........................................ 652 TOrderedList.............................................. 742 Copyright © 2003 FunctionX, Inc.
Appendix
TParaAttributes .......................................... 519 TProgressBar.............................................. 566 TQueue....................................................... 742 TRadioGroup ............................................. 595 TReplaceDialog ......................................... 530 TScreen ........................................................ 32 TStack ........................................................ 742 TStringGrid ........................................ 665, 669 TStrings...................................................... 519 TTextAttributes.......................................... 520 TTimer ....................................................... 554 TTreeNode ................................................. 694 TTreeNodes................................................ 694 TTreeView ................................................. 694 TUpDown .................................................. 538 TValueListEditor ....................................... 685 TWinControl .............................................. 432 classes.hpp ..................................................... 346 clBackground ......................................... 239, 246 clBlack ................................................... 240, 553 clBlue ............................................................. 240 clBtnFace ....................................................... 238 clBtnHighlight................................................ 239 clBtnShadow.................................................. 239 clBtnText ....................................................... 239 clCaptionText................................................. 239 clCream.......................................................... 240 clDefault......................................................... 240 clDkGray........................................................ 240 clFushsia ........................................................ 240 clGradientActiveCaption ............................... 239 clGradientInactiveCaption ............................. 239 clGray............................................. 240, 477, 553 clGreen........................................................... 240 clHighlightText .............................................. 239 clHotLight ...................................................... 239 Click() ............................................................ 355 ClientRect ...................................................... 362 clInactiveBorder............................................. 239 clInactiveCaption ........................................... 239 clInactiveCaptionText.................................... 239 clInfoBk ......................................................... 239 clInfoText....................................................... 239 CLIP_CHARACTER_PRECIS ..................... 253 CLIP_DEFAULT_PRECIS ........................... 253 CLIP_EMBEDED.......................................... 253 CLIP_LH_ANGLES...................................... 253 CLIP_MASK ................................................. 253 CLIP_STROKE_PRECIS.............................. 253 CLIP_TT_ALWAYS ..................................... 253 clLime ............................................................ 240 clLtGray ......................................................... 240 clMaroon........................................................ 240 clMedGray ..................................................... 240 clMenu ........................................................... 239 clMenuBar ..................................................... 239 805
Appendix
clMenuHighlight............................................ 239 clMenuText.................................................... 239 clMoneyGreen ............................................... 240 clNavy............................................................ 240 clNone............................................................ 240 clOlive............................................................ 240 Close ................................................................ 21 clPurple .......................................................... 240 clRed ...................................................... 240, 478 clScrollBar ..................................................... 239 clSilver ........................................................... 240 clSkyBlue............................................... 240, 667 clTeal ............................................................. 240 clWhite........................................................... 240 clWindow....................................................... 239 clWindowFrame............................................. 239 clWindowText ............................................... 239 clYellow......................................................... 240 Code Editor...................................................... 25 ColHeights ..................................................... 668 Color CCalendar .................................................. 682 StringGrid .................................................. 667 COLOR_3DDKSHADOW............................ 239 COLOR_3DFACE......................................... 238 COLOR_3DHILIGHT................................... 239 COLOR_3DLIGHT ....................................... 239 COLOR_3DSHADOW ................................. 239 COLOR_ACTIVEBORDER ......................... 239 COLOR_ACTIVECAPTION ........................ 239 COLOR_APPWORKSPACE ........................ 239 COLOR_BACKGROUND............................ 239 COLOR_BTNHIGHLIGHT .......................... 239 COLOR_BTNHILIGHT................................ 239 COLOR_BTNTEXT...................................... 239 COLOR_CAPTIONTEXT ............................ 239 COLOR_DESKTOP...................................... 239 COLOR_GRADIENTACTIVECAPTION.... 239 COLOR_GRADIENTINACTIVECAPTION 239 COLOR_HIGHLIGHT .................................. 239 COLOR_HIGHLIGHTTEXT........................ 239 COLOR_HOTLIGHT.................................... 239 COLOR_INACTIVEBORDER ..................... 239 COLOR_INACTIVECAPTION.................... 239 COLOR_INACTIVECAPTIONTEXT.......... 239 COLOR_INFOBK ......................................... 239 COLOR_INFOTEXT .................................... 239 COLOR_MENU ............................................ 239 COLOR_MENUTEXT .................................. 239 COLOR_SCROLLBAR ................................ 239 COLOR_WINDOW ...................................... 239 COLOR_WINDOWFRAME......................... 239 COLOR_WINDOWTEXT ............................ 239 colorful........................................................... 649 COLORREF .................................................. 236 Columns......................................................... 596 806
Borland C++ Builder Programming
ColWidths ...................................................... 668 COMBOBOX ................................................ 325 Command Button ........................................... 423 Common Controls ............................................ 37 CompareStr().................................................... 67 CompareText()................................................. 66 Component Palette ........................................... 24 computer programs .......................................... 20 Constants INF ............................................................. 101 Pi 121 sLineBreak ................................................... 76 container........................................................... 37 Containers Frame ......................................................... 395 Control Panel ................................................. 649 Controls CheckListBox ............................................ 635 DateTimePicker ......................................... 661 Edit............................................................. 721 GroupBox................................................... 721 MonthCalendar .......................................... 653 RadioGroup................................................ 596 ScrollBar .................................................... 582 Shape.......................................................... 553 Timer.......................................................... 554 TrackBar .................................................... 548 TreeView.................................................... 691 Controls Styles TVS_CHECKBOXES ............................... 694 CreateFont() ........................................... 251, 254 CreateFontIndirect()....................................... 254 CreateForm().................................................... 34 CreateMessageDialog().................................... 86 csDropDown .................................................. 641 csDropDownList ............................................ 641 csOwnerDrawFixed ....................................... 641 csOwnerDrawVariable................................... 641 CSpinButton................................................... 541 csSimple......................................................... 641 cycle ............................................................... 121 CycleToRad()................................................. 121 D DateFormat .................................................... 660 DateMode....................................................... 659 DateTimePicker OnChange()................................................ 662 OnUserInput() ............................................ 662 DateUtils.hpp ................................................. 655 Default ................................................... 441, 501 DEFAULT_CHARSET ................................. 252 DEFAULT_PITCH........................................ 253 DEFAULT_QUALITY.................................. 253 DefaultColWidth............................................ 668 DefaultDrawing.............................................. 669 Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
DefaultRowHeight ......................................... 668 Definitions Action List ................................................. 465 Angle.......................................................... 120 arc .............................................................. 120 Button ........................................................ 423 CCalendar .................................................. 681 Check Box ................................................. 605 Check List Box .......................................... 630 Child .......................................................... 321 circle .......................................................... 120 circumference............................................. 120 Class Explorer.............................................. 26 Code Editor .................................................. 25 Color Grid .................................................. 683 Combo Box ................................................ 640 Component Palette ....................................... 24 CSpinEdit................................................... 544 Cycle .......................................................... 121 Date and Time Picker................................. 657 degree......................................................... 120 diameter ..................................................... 120 Edit Box ..................................................... 482 Focus.......................................................... 342 Frame ......................................................... 395 Grid ............................................................ 664 Hint ............................................................ 326 INI List....................................................... 684 Instance ........................................................ 32 Interest Rate ............................................... 110 IP Address.................................................. 492 Label .......................................................... 473 Library ......................................................... 31 List Box ..................................................... 619 list view...................................................... 697 Main Menu................................................. 451 MaskEdit.................................................... 486 Menu .......................................................... 451 Message ..................................................... 343 Method ....................................................... 340 Month Calendar ......................................... 649 Node........................................................... 689 Object........................................................... 26 Object Inspector........................................... 27 Parent ......................................................... 321 Payment ..................................................... 110 Periods ....................................................... 110 Present Value ............................................. 109 Progress Bar............................................... 560 project .......................................................... 27 Property........................................................ 51 Radian ........................................................ 120 Radio Button .............................................. 589 RadioGroup................................................ 594 Root............................................................ 689 Scroll Bar ................................................... 574 Copyright © 2003 FunctionX, Inc.
Appendix
speed button ............................................... 437 Spin Button ................................................ 540 Splitter........................................................ 710 Status Bar ................................................... 461 StringGrid .................................................. 665 Timer.......................................................... 553 tool tip ........................................................ 326 toolbar .......................................................... 22 Toolbar....................................................... 458 Toolbar Separator....................................... 458 Track Bar ................................................... 545 TreeView.................................................... 689 UpDown..................................................... 533 Win32........................................................... 31 Windows Control ......................................... 36 degree............................................................. 120 DegToRad() ................................................... 122 delete.............................................................. 255 Design Time..................................................... 36 DesktopHeight ............................................... 361 DesktopLeft ................................................... 361 DesktopRect................................................... 361 DesktopTop.................................................... 361 DesktopWidth ................................................ 361 Details ............................................................ 697 Dialog Boxes Add Method ............................................... 615 Align ............................................................ 46 Alignment .................................................... 46 Color .......................................................... 243 Colors......................................................... 440 Console Wizard............................................ 33 Edit Tab Order ........................................... 526 Find ............................................................ 527 Font ............................................................ 257 New Items .................................................... 33 Project Options........................................... 631 Replace....................................................... 529 Size............................................................... 46 String List Editor........................ 245, 595, 620 Tab Order ................................................... 331 diameter ......................................................... 120 dividend ......................................................... 108 Division.......................................................... 591 dkDock........................................................... 621 dmComboBox ................................................ 659 dmUpDown.................................................... 659 DockSite......................................................... 621 DoubleDecliningBalance()............................. 106 dowMonday ................................................... 654 Down.............................................................. 438 DRAFT_QUALITY....................................... 253 DragKind ....................................................... 621 DragMode ...................................................... 621 DropDownCount............................................ 642 dtkDate........................................................... 659 807
Appendix
dwExStyle...................................................... 323 DWORD ........................................................ 323 dwStyle .......................................................... 323 E E350............................................................... 546 EASTEUROPE_CHARSET.......................... 252 ecLowerCase.................................................. 483 ecNormal........................................................ 483 EConvertError................................................ 661 ecUpperCase .................................................. 483 EDIT .............................................................. 325 EditStyle ........................................................ 687 Elantra............................................................ 546 EM_REDO .................................................... 522 EN_SETFOCUS ............................................ 497 Enabled .......................................................... 331 TColorGrid ................................................ 684 END_MESSAGE_MAP ................................ 345 Enumerator TEditCharCase........................................... 483 Enumerators TAlignment........................................ 474, 502 TButtonLayout........................................... 434 TCheckBoxState ........................................ 609 TComboBoxStyle ...................................... 641 TGridOrdering ........................................... 683 TProgressBarOrientation ........................... 561 TScrollBarKind.......................................... 580 TScrollCode............................................... 584 TTextLayout .............................................. 474 TTickMark ................................................. 550 TTrackBarOrientation................................ 548 TUDBtnType ............................................. 539 Escort ............................................................. 546 ETO_CLIPPED ............................................. 242 ETO_GLYPH_INDEX.................................. 242 ETO_NUMERICSLATIN ............................. 242 ETO_NUMERICSLOCAL............................ 242 ETO_OPAQUE ............................................. 242 ETO_PDY...................................................... 242 ETO_RTLREADING .................................... 242 Events OnChange .......................................... 256, 497 OnClick.............................................. 427, 447 OnClickCheck............................................ 637 OnCreate ............................................ 355, 447 OnDblClick................................................ 247 OnDownClick ............................................ 542 OnExit() ..................................................... 497 OnGetMonthInfo ....................................... 655 OnKeyUp ................................................... 349 OnMouseEnter() ........................................ 480 OnMouseLeave .......................................... 480 OnMouseUp............................................... 352 OnUpClick ................................................. 542 808
Borland C++ Builder Programming
Execute() ........................................................ 245 existence......................................................... 321 exp() ............................................................... 101 expl().............................................................. 102 exponent........................................................... 98 ExtendedSelect............................................... 621 ExtTextOut() .................................................. 242 F FF_DECORATIVE........................................ 253 FF_DONTCARE ........................................... 253 FF_MODERN................................................ 253 FF_ROMAN .................................................. 253 FF_SCRIPT ................................................... 253 FF_SWISS ..................................................... 253 FG .................................................................. 684 finance.............................................................. 91 FIXED_PITCH .............................................. 253 FixedCols ....................................................... 666 FixedRows ..................................................... 666 floor() ............................................................... 97 Floor() .............................................................. 98 focus............................................................... 342 Focus.............................................................. 546 FocusControl.................................................. 483 Foreground..................................................... 683 ForegroundEnabled........................................ 684 Format DateTimePicker ......................................... 659 frame .............................................................. 321 frexp() .............................................................. 98 Frexp() ............................................................. 99 frexpl() ............................................................. 98 fsBold............................................................. 478 fsUnderline..................................................... 478 Functions abs ................................................................ 95 AnsiCompareStr........................................... 67 AnsiExtractQuotedStr .................................. 72 AnsiLowerCase............................................ 63 AnsiQuotedStr.............................................. 71 AnsiSameCaption......................................... 68 AnsiSameStr ................................................ 68 AnsiSameText.............................................. 68 AnsiUpperCase ............................................ 62 atan............................................................. 132 ceil................................................................ 96 Ceil............................................................... 96 CompareStr .................................................. 67 CompareText................................................ 66 CreateFont.......................................... 251, 254 CreateFontIndirect ..................................... 254 CreateMessageDialog .................................. 86 CreateWindow ............................................. 50 CreateWindowEx ......................................... 50 CycleToRad ............................................... 121 Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
DegToRad.................................................. 122 DoubleDecliningBalance ........................... 106 exp.............................................................. 101 expl ............................................................ 102 ExtTextOut................................................. 242 floor.............................................................. 97 Floor............................................................. 98 frexp ............................................................. 98 Frexp ............................................................ 99 frexpl............................................................ 98 FutureValue ............................................... 110 GetBkColor................................................ 241 GetBkMode................................................ 242 GetDesktopWindow................................... 357 GetParent ................................................... 329 GetSysColor............................................... 238 GetTickCount............................................. 556 GetWindowRect......................................... 340 InputBox ...................................................... 88 InputQuery ................................................... 89 InterestPayment ......................................... 113 InterestRate ................................................ 116 IntPower..................................................... 100 labs............................................................... 95 ldexp .......................................................... 102 Ldexp ......................................................... 102 ldexpl ......................................................... 102 LnXP1........................................................ 103 Log10 ......................................................... 103 Log2 ........................................................... 104 LogN .......................................................... 104 LowerCase ................................................... 63 MessageBox................................................. 78 MessageDlg ................................................. 83 MessageDlgPos............................................ 85 NetPresentValue ........................................ 119 NumberOfPeriods ...................................... 111 Payment ..................................................... 112 PostQuitMessage ....................................... 427 pow ............................................................ 100 Power ......................................................... 101 powl ........................................................... 100 PresentValue .............................................. 114 QuotedStr ..................................................... 72 RadToCycle ............................................... 123 RadToDeg.................................................. 123 SameText ..................................................... 65 SendMessage ............................................. 353 SendMessage()........................................... 493 SetBkColor ................................................ 241 SetBkMode ................................................ 241 SetParent .................................................... 329 SetTextColor...................................... 240, 251 ShellExecute()............................................ 479 ShowMessage .............................................. 75 ShowWindow............................................. 341 Copyright © 2003 FunctionX, Inc.
Appendix
SLNDepreciation ....................................... 107 sqrt ............................................................. 105 sqrtl ............................................................ 105 StringReplace ............................................... 71 strlen............................................................. 57 StrToFloat .................................................... 93 StrToInt........................................................ 92 SYDDepreciation ....................................... 108 SystemParametersInfo ............................... 357 UpperCase.................................................... 62 WinMain().................................................... 31 FutureValue()................................................. 110 FW_BLACK .................................................. 252 FW_BOLD..................................................... 252 FW_DEMIBOLD .......................................... 252 FW_DONTCARE.......................................... 252 FW_EXTRABOLD ....................................... 252 FW_EXTRALIGHT ...................................... 252 FW_HEAVY.................................................. 252 FW_LIGHT ................................................... 252 FW_MEDIUM............................................... 252 FW_REGULAR............................................. 252 FW_THIN...................................................... 252 FW_ULTRABOLD ....................................... 252 FW_ULTRALIGHT ...................................... 252 G Garamond....................................................... 478 GB.................................................................. 684 GB2312_CHARSET...................................... 252 gdFixed .......................................................... 677 gdFocused ...................................................... 677 GdMarquis ..................................................... 546 gdSelected ...................................................... 677 geometry .......................................................... 91 GetBkColor() ................................................. 241 GetBkMode() ................................................. 242 GetBValue()................................................... 237 GetDesktopWindow() .................................... 357 GetGValue()................................................... 237 GetParent()..................................................... 329 GetRValue()................................................... 237 GetSysColor() ................................................ 238 GetTickCount() .............................................. 556 GetWindowRect() .......................................... 340 Glyph ..................................................... 433, 439 go16x1 ........................................................... 683 go1x16 ........................................................... 683 go2x8 ............................................................. 683 go4x4 ............................................................. 683 go8x2 ............................................................. 683 goColMoving ................................................. 669 goRowMoving ............................................... 669 graphical user interface (GUI) ......................... 37 GREEK_CHARSET ...................................... 252 Green.............................................................. 235 809
Appendix
GridCoord() ................................................... 673 GridLineWidth............................................... 667 GridOrdering.................................................. 683 GroupIndex .................................................... 438 H Handle............................................................ 251 HANGUL_CHARSET .................................. 252 HBRUSH ......................................................... 32 HCURSOR ...................................................... 32 Header............................................................ 633 HeaderBackgroundColor ............................... 634 HEBREW_CHARSET .................................. 252 Height .................................................... 333, 439 HFONT .......................................... 248, 251, 253 HH:MM:SS AM/PM...................................... 659 HICON............................................................. 32 Hide()............................................................. 342 hInstance ........................................................ 323 HInstance ......................................................... 32 HINSTANCE................................................... 32 hint ................................................................... 23 hMenu ............................................................ 323 host................................................................. 395 HWND........................................................... 324 hWndParent ................................................... 323 I idAbort........................................................... 425 IDABORT........................................................ 81 idCancel ......................................................... 425 IDCANCEL ..................................................... 81 IDIGNORE ...................................................... 81 idNo ............................................................... 425 IDNO ............................................................... 81 idOK .............................................................. 425 IDOK ............................................................... 81 idRetry ........................................................... 425 IDRETRY ........................................................ 81 idYes .............................................................. 425 IDYES.............................................................. 81 Increment ....................................................... 544 INF................................................................. 101 INI list............................................................ 684 Initialize() ........................................................ 32 InputBox()........................................................ 88 InputQuery() .................................................... 89 instance ............................................................ 32 int ..................................................................... 32 INT_MAX ..................................................... 549 Integrated Development Environment (IDE)... 20 InterestPayment()................................... 113, 115 InterestRate() ................................................. 116 InternalRateOfReturn() .................................. 117 Interval........................................................... 555 IntPower() ...................................................... 100 810
Borland C++ Builder Programming
IPM_GETADDRESS .................................... 495 IPM_ISBLANK ............................................. 493 IPM_SETADDRESS ..................................... 494 IPM_SETFOCUS .......................................... 496 IPM_SETRANGE.......................................... 496 IPN_FIELDCHANGED ................................ 497 IsMasked ........................................................ 489 ItemIndex ....................................................... 595 ItemProps ....................................................... 685 Items TList........................................................... 720 TListView .................................................. 698 TreeView.................................................... 691 K Kind ............................................................... 579 DateTimePicker ................................. 658, 663 dtkTime ...................................................... 658 L labs() ................................................................ 95 lapse ............................................................... 553 Large Icons .................................................... 697 LargeChange .................................................. 581 Layout ............................................................ 439 LB_SETHORIZONTALEXTENT ........ 622, 633 lbOwnerDrawFixed........................................ 621 lbOwnerDrawVariable ................................... 621 lbStandard ...................................................... 621 ldexp()............................................................ 102 Ldexp()........................................................... 102 ldexpl()........................................................... 102 Left......................................................... 332, 358 library............................................................... 31 Lines............................................................... 500 List ................................................................. 697 LISTBOX....................................................... 325 LnXP1() ......................................................... 103 Log10() .......................................................... 103 Log2 ............................................................... 104 LOGFONT..................................................... 254 LogN() ........................................................... 104 long .................................................................. 93 LongTimeFormat ........................................... 628 lowercase ......................................................... 61 LP_ADDSTRING.......................................... 620 lpClassName .................................................. 323 LPCTSTR ................................................ 32, 323 lpfnWndProc.................................................... 32 lpParam .......................................................... 323 lpszClassName ............................................... 325 lpWindowName ............................................. 323 M MAC_CHARSET .......................................... 252 Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Macros GetBValue()............................................... 237 GetGValue()............................................... 237 GetRValue()............................................... 237 MAKEIPADDRESS .................................. 494 MAKEIPRANGE ...................................... 496 USEFORM .................................................. 34 MAKEIPADDRESS ...................................... 494 MAKEIPRANGE .......................................... 496 mantissa ........................................................... 98 Margin............................................................ 439 math.hpp .......................................................... 96 Max ................................................................ 580 MaxDate ........................................................ 660 Maximize ......................................................... 21 MaxLength............................................. 489, 501 MB_DEFBUTTON1 ......................................... 80 MB_DEFBUTTON2 ......................................... 80 MB_DEFBUTTON3 ......................................... 80 MB_DEFBUTTON4 ......................................... 80 MB_ICONASTERISK .................................... 79 MB_ICONERROR .......................................... 80 MB_ICONEXCLAMATION .......................... 79 MB_ICONHAND............................................ 80 MB_ICONINFORMATION............................ 79 MB_ICONQUESTION ................................... 80 MB_ICONSTOP.............................................. 80 MB_ICONWARNING .................................... 79 mbAbort........................................................... 84 mbAll ............................................................... 84 mbCancel ......................................................... 84 mbHelp ............................................................ 84 mbIgnore.......................................................... 84 mbLeft............................................................ 351 mbMiddle....................................................... 351 mbNo ............................................................... 84 mbNoToAll...................................................... 84 mbOK .............................................................. 84 mbRetry ........................................................... 84 mbRight ......................................................... 351 mbYes .............................................................. 84 mbYesToAll .................................................... 84 MDICLIENT ................................................. 325 menu template................................................ 452 message.......................................................... 343 MESSAGE_HANDLER................................ 345 MessageBox() .................................................. 78 MessageDlg()................................................... 83 MessageDlgPos() ............................................. 85 Messages EM_REDO ................................................ 522 EN_SETFOCUS ........................................ 497 IPM_CLEARADDRESS ........................... 494 IPM_GETADDRESS ................................ 495 IPM_SETADDRESS ................................. 494 IPM_SETFOCUS ...................................... 496 Copyright © 2003 FunctionX, Inc.
Appendix
IPM_SETRANGE...................................... 496 IPN_FIELDCHANGED ............................ 497 LB_SETHORIZONTALEXTENT .... 622, 633 LP_ADDSTRING...................................... 620 PBM_SETBARCOLOR ............................ 562 PBM_SETRANGE .................................... 562 PBM_SETRANGE32 ................................ 562 WM_MOVE ...................................... 344, 354 WM_QUIT................................................. 427 WM_SETTEXT......................................... 353 WMKeyDown............................................ 348 WMKeyPress ............................................. 349 WMKeyUp................................................. 349 Method ........................................................... 340 Microsoft.......................................................... 19 Microsoft Windows ......................................... 31 Microsoft Word.............................................. 440 milliseconds ................................................... 556 Min................................................................. 580 MinDate ......................................................... 660 Minimize .......................................................... 20 Modified......................................................... 501 MouseToCell()............................................... 673 mrAbort.......................................... 425, 428, 433 mrAll.............................................................. 425 mrCancel ................................................ 425, 433 mrIgnore................................................. 425, 433 mrNo ...................................................... 425, 433 mrNone .................................................. 425, 433 mrNoToAll............................................. 425, 433 mrOk ...................................................... 425, 433 mrRetry .................................................. 425, 433 mrYes..................................................... 425, 433 mrYesToAll ........................................... 425, 433 MSG............................................................... 343 Multiple Document Interface (MDI)................ 37 Multiplication................................................. 591 MultiSelect..................................................... 621 mutual-exclusive ............................................ 437 N Name.............................................................. 324 Napierian........................................................ 103 natural logarithm ............................................ 103 Navigator ....................................................... 546 NetPresentValue().......................................... 119 new................................................................. 255 Next................................................................ 443 nHeight........................................................... 323 NONANTIALIASED_QUALITY................. 253 non-exclusive ................................................. 605 Notice3........................................................... 499 NULL............................................................. 251 Numbering ..................................................... 520 NumberOfPeriods()........................................ 111 nWidth ........................................................... 323 811
Appendix
O Object Inspector............................................... 27 object-oriented programming (OOP) ............. 577 OEM_CHARSET .......................................... 252 OnAccept() ............................................ 508, 646 OnChange().................................... 256, 497, 643 OnClick.......................................................... 427 OnClickCheck() ............................................. 637 OnColumnMoved()........................................ 676 OnCreate() ............................................. 355, 643 OnDblClick............................................ 247, 674 OnDestroy() ................................................... 715 OnDrawCell() ........................................ 676, 677 OnEditButtonClick()...................................... 688 OnExit() ......................................................... 497 OnGetEditMask()........................................... 675 OnGetEdtText................................................ 675 OnKeyDown .......................................... 348, 674 OnKeyPress ................................................... 674 OnKeyUp ............................................... 349, 674 OnMouseDown.............................. 351, 352, 674 OnMouseMove .............................................. 674 OnMouseUp........................................... 352, 674 OnMouseWheelDown ................................... 674 OnRowMoved() ............................................. 676 OnScroll() ...................................................... 584 OnSelectCell() ............................................... 674 OnSelectionChange ....................................... 646 OnSetEditText()..................................... 675, 679 OnUserInput()................................................ 662 operating system ............................................ 649 Operating Systems ........................................... 19 Operators : 347 + 64 <<............................................................... 250 ==................................................................. 56 delete.................................................. 255, 714 new............................................. 255, 341, 671 OR | ............................................................ 253 Orientation ............................................. 548, 561 OUT_CHARACTER_PRECIS...................... 253 OUT_DEFAULT_PRECIS............................ 253 OUT_DEVICE_PRECIS ............................... 253 OUT_OUTLINE_PRECIS ............................ 253 OUT_RASTER_PRECIS .............................. 253 OUT_STRING_PRECIS ............................... 253 OUT_STROKE_PRECIS .............................. 253 OUT_TT_ONLY_PRECIS............................ 253 OUT_TT_PRECIS......................................... 253 overflow......................................................... 101 P PageSize......................................................... 581 PARAFORMAT ............................................ 524 812
Borland C++ Builder Programming
PARAFORMAT2 .......................................... 524 Parent ............................................................. 321 ParseInput ...................................................... 659 PasswordChar ................................................ 483 Payment()....................................................... 112 pbLowered ..................................................... 465 PBM_SETBARCOLOR ................................ 562 PBM_SETRANGE ........................................ 562 PBM_SETRANGE32 .................................... 562 pbNone........................................................... 465 pbRaised......................................................... 465 PBS_SMOOTH.............................................. 562 PBS_VERTICAL........................................... 561 Perform()........................................................ 353 Pi 121 pitch ............................................................... 253 POINT............................................................ 335 poScreenCenter .............................................. 561 Position .......................................... 362, 536, 580 PostQuitMessage() ......................................... 427 pow().............................................................. 100 Power()........................................................... 101 powl()............................................................. 100 PresentValue()................................................ 114 Print() ............................................................. 522 PrintDlg.......................................................... 523 printf() .............................................................. 93 procedure ....................................................... 343 programmer.................................................... 473 project .............................................................. 27 Projects AddressBook1............................................ 713 AddressBook2............................................ 726 BitmapButtons ........................................... 435 BodyMonitor1............................................ 560 BodyTag1........................................... 578, 594 CarInventory1 ............................................ 546 CDPublisher1 ............................................. 541 ColorPreview ............................................. 533 CompTicks1 ............................................... 557 Countries1 .................................................. 690 Countries2 .................................................. 694 Editor1 ............................................... 504, 628 Editor2 ....................................................... 644 Employment............................................... 475 FastFood1................................................... 606 Notice......................................................... 466 Operations .................................................. 590 Payroll1 .............................................. 651, 669 Payroll2 ...................................................... 658 Pizza1......................................................... 631 TrafficLight1.............................................. 553 PROOF_QUALITY....................................... 253 Properties ActiveControl............................................. 424 Align .......................................................... 362 Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
BorderIcons.................................................. 53 BoundsRect................................................ 340 Caption............................................... 326, 424 ClientRect .................................................. 362 Enabled ...................................................... 331 Height ........................................................ 333 Hint ............................................................ 327 Left............................................................. 332 ModalResult............................................... 425 Position ...................................................... 362 ShowHint ................................................... 327 Showing ..................................................... 330 TabOrder.................................................... 331 TabStop...................................................... 331 Text ............................................................ 326 Top............................................................. 332 Visible........................................................ 330 Width ......................................................... 333 property page ................................................. 439 Q QuotedStr() ...................................................... 72 R radian ............................................................. 120 RadToCycle() ................................................ 123 RadToDeg() ................................................... 123 random ............................................................. 91 Rapid Application Development (RAD).......... 31 ratio ................................................................ 120 ReadOnly ....................................................... 500 RECT ............................................................. 337 Red................................................................. 235 reference .......................................................... 31 Regional Settings ..................................... 68, 649 Restore ............................................................. 21 RichEdit ......................................................... 325 RICHEDIT_CLASS ...................................... 325 Rio ................................................................. 546 rotation........................................................... 121 Run Time ......................................................... 36 RUSSIAN_CHARSET .................................. 252 S SameText() ...................................................... 65 Save All ........................................................... 27 sbHorizontal................................................... 579 sbVertical....................................................... 579 SCROLLBAR........................................ 325, 577 ScrollBars ...................................................... 665 SelCount ........................................................ 621 SelectAll()...................................................... 485 SendMessage()............................................... 353 Sentra ............................................................. 546 SetBkColor().................................................. 241 Copyright © 2003 FunctionX, Inc.
Appendix
SetBkMode().................................................. 241 SetFocus() ...................................................... 342 SetParams() .................................................... 583 SetParent() ..................................................... 329 Sets TGridDrawState ......................................... 676 SetTextColor() ....................................... 240, 251 SetWindowLong().......................................... 694 Shape.............................................................. 445 SHIFTJIS_CHARSET ................................... 252 shortcut........................................................... 453 ShortDateFormat............................................ 628 ShowAccelChar ............................................. 483 Showing ......................................................... 330 ShowMessage .................................................. 75 ShowToday .................................................... 653 ShowWindow() .............................................. 341 Single Document Interface (SDI)..................... 37 SIZE ............................................................... 336 sLineBreak ....................................................... 76 SLNDepreciation()......................................... 107 Small Icons .................................................... 697 SmallChange .................................................. 581 Spacing........................................................... 439 SpeedButton................................................... 437 spin button...................................................... 533 sqrt()............................................................... 105 sqrtl().............................................................. 105 ssAlt ............................................................... 351 ssBoth............................................................. 576 ssCtrl .............................................................. 351 ssDouble......................................................... 351 ssHorizontal ................................................... 576 ssLeft.............................................................. 351 ssMiddle......................................................... 351 ssNone............................................................ 576 ssRight ........................................................... 351 ssShift..................................................... 348, 351 ssVertical ....................................................... 576 Standard Controls............................................. 37 StartOfWeek .................................................. 682 STATIC ................................................. 325, 481 static text ........................................................ 473 StaticText ....................................................... 481 statistics............................................................ 91 Statistics1 ....................................................... 698 status bar ........................................................ 328 Step ................................................................ 563 StepBy() ......................................................... 566 StepIt() ........................................................... 566 StringGrid BorderStyle ................................................ 665 StrToFloat() ..................................................... 93 StrToInt() ......................................................... 92 Structures TCarInventory............................................ 546 813
Appendix
TGridCoord................................................ 674 Subtraction ..................................................... 591 Sunday ........................................................... 653 SYDDepreciation() ........................................ 108 SYMBOL_CHARSET................................... 252 Syntaxes InputBox().................................................... 88 USEFORM .................................................. 34 WinMain() ................................................... 31 WNDCLASS ............................................... 32 WNDCLASSEX .......................................... 32 system .............................................................. 20 SystemParametersInfo()................................. 357 T TabOrder................................................ 331, 501 TabStop.......................................................... 331 taCenter.......................................................... 465 TActionEvent................................................. 471 TActionList.................................................... 466 Execute().................................................... 471 OnChange .................................................. 471 OnUpdate() ................................................ 471 taLeftJustify ........................................... 465, 590 TApplication .................................................... 32 CreateForm()................................................ 34 Hint ............................................................ 328 HintColor ................................................... 327 Initialize() .................................................... 32 MessageBox() .............................................. 80 Run() ............................................................ 35 taRightJustify ......................................... 465, 590 Taskbar ............................................................ 20 TBasicAction ................................................. 471 TBevel bsTopLine .................................................. 445 TBitBtn Enabled ...................................................... 722 Glyph ......................................................... 433 Kind ................................................... 432, 548 Layout ........................................................ 434 Margin........................................................ 434 NumGlyphs................................................ 434 OnClick() ................................................... 723 Spacing ...................................................... 434 TButton Cancel ........................................................ 424 Default ............................................... 424, 501 ModalResult............................................... 425 OnClick() ................................................... 611 TButtonLayout............................................... 434 TCanvas ......................................................... 240 Font .................................................... 248, 253 TCCalendar BorderStyle ................................................ 682 Color .......................................................... 682 814
Borland C++ Builder Programming
Day............................................................. 682 GridLineWidth ........................................... 682 Month......................................................... 682 ReadOnly ................................................... 682 StartOfWeek .............................................. 682 UseCurrentDate.......................................... 682 Year............................................................ 682 TCheckBox .................................................... 611 Alignment .................................................. 608 AllowGrayed.............................................. 609 Caption....................................................... 608 Checked ..................................................... 607 GetControlsAlignment() ............................ 612 State ........................................................... 609 TCheckBoxState ............................................ 609 TCheckListBox AllowGrayed.............................................. 634 Checked ..................................................... 634 Columns ..................................................... 633 Enabled ...................................................... 635 Header ........................................................ 633 HeaderBackgroundColor ........................... 634 HeaderColor............................................... 634 ItemEnabled ............................................... 635 Sorted ......................................................... 633 State ........................................................... 634 TColor............................................................ 235 TColorDialog Color .......................................................... 244 TColorDialogOption ...................................... 245 TColorGrid Background ................................................ 683 BackgroundEnabled ................................... 684 Enabled ...................................................... 684 Foreground ................................................. 683 ForegroundEnabled .................................... 684 GridOrdering.............................................. 683 TColorSelect .................................................. 507 TComboBox................................................... 642 DropDownCount........................................ 642 ItemIndex ................................................... 642 Items........................................................... 641 Name .......................................................... 641 Sorted ................................................. 641, 642 Style ........................................................... 641 TComboBoxStyle .......................................... 641 TControl................................................. 258, 612 Caption....................................................... 424 Hide() ......................................................... 342 Perform().................................................... 353 Show()........................................................ 341 WndProc().................................................. 354 TCSpinButton OnDownClick().......................................... 542 OnUpClick() .............................................. 542 TCSpinEdit .................................................... 545 Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
Increment ................................................... 544 MaxValue .................................................. 544 MinValue ................................................... 544 TCustomEdit.................................................. 485 TCustomForm Close()........................................................ 427 TDateTimePicker CalAlignment............................................. 660 Date............................................................ 660 DateFormat ................................................ 660 DateMode .................................................. 659 Format........................................................ 659 Kind ........................................................... 658 MaxDate .................................................... 660 MinDate ..................................................... 660 Name.......................................................... 662 OnDropDown() .......................................... 663 ParseInput .................................................. 659 Time........................................................... 659 TEdit .............................................................. 485 BorderStyle ................................................ 599 CharCase.................................................... 483 Clear() ........................................................ 485 ClearSelection() ......................................... 485 FocusControl.............................................. 483 OnChange()................................................ 485 OnEnter() ................................................... 485 PasswordChar ............................................ 483 ReadOnly ................................................... 721 SelLength ................................................... 484 SelStart....................................................... 484 SelText....................................................... 484 ShowAccelChar ......................................... 483 Text ............................................................ 482 TFileExit ........................................................ 507 TFileOpen ...................................................... 507 TFilePrintSetup.............................................. 507 TFileSaveAs .................................................. 507 TFindDialog FindText..................................................... 528 Position ...................................................... 528 Top............................................................. 529 TFont ..................................................... 248, 429 Assign()...................................................... 256 Color .......................................................... 251 Height ........................................................ 249 Name.......................................................... 249 Size .................................................... 250, 476 Style ........................................... 250, 258, 476 TFontDialog................................................... 257 TFontEdit....................................................... 507 TFontStyles.................................................... 250 TForm ActiveControl............................................. 424 BorderStyle ................................................ 557 Font ............................................................ 476 Copyright © 2003 FunctionX, Inc.
Appendix
OnCreate().................................................. 686 OnDestroy() ............................................... 715 Position poScreenCenter ...................................... 561 ShowHint ................................... 328, 463, 466 ShowModal() ..................................... 425, 611 TGraphicsObject ............................................ 256 THAI_CHARSET.......................................... 252 thumb ............................................................. 575 TImageList Name .................................................. 699, 701 Times New Roman ........................................ 429 title bar ............................................................. 20 TKeyEvent ..................................................... 348 TKeyPress ...................................................... 348 TLabel AutoResize................................................. 473 Caption....................................................... 473 Layout ........................................................ 474 Left............................................................. 477 Name .......................................................... 477 Top ............................................................. 477 Transparent ........................................ 474, 477 WordWrap.................................................. 474 tlBottom ......................................................... 474 tlCenter........................................................... 474 TList Add() .................................................. 718, 737 Capacity ..................................................... 718 Clear() ........................................................ 741 Count.......................................................... 718 Delete() ...................................................... 739 First() ......................................................... 720 IndexOf().................................................... 720 Insert()........................................................ 737 Items................................................... 720, 739 Last() .......................................................... 720 Remove().................................................... 739 TListBox ........................................................ 622 Clear() ........................................................ 625 Columns ..................................................... 621 DockSite..................................................... 621 DragMode .................................................. 621 ExtendedSelect........................................... 621 Height......................................................... 622 ItemIndex ................................................... 621 Items........................................................... 620 MultiSelect................................................. 621 SelCount..................................................... 621 Selected ...................................................... 621 TListView Height......................................................... 702 Items........................................................... 698 LargeImages............................................... 706 OnKeyDown()............................................ 739 SmallImages............................................... 706 815
Appendix
ViewStyle .................................................. 701 Width ......................................................... 702 tlTop............................................................... 474 TMainMenu Caption....................................................... 453 Images................................................ 499, 727 Items .......................................................... 452 TMaskEdit EditMask.................................................... 675 Text ............................................................ 492 tmBoth ........................................................... 550 tmBottomRight .............................................. 550 TMemo .......................................................... 499 Align .......................................................... 500 Lines .......................................................... 500 MaxLength................................................. 501 Modified .................................................... 501 ReadOnly ................................................... 500 TabOrder.................................................... 501 WantReturns .............................................. 501 WantTabs................................................... 501 WordWrap ................................................. 502 TMessage....................................................... 344 TModalResult ................................................ 425 TMonthCalendar BoldDays()................................................. 655 CalColors ................................................... 652 Date............................................................ 653 FirstDayOfWeek ........................................ 653 MultiSelect................................................. 653 OnClick() ................................................... 655 OnGetMonthInfo()..................................... 655 ShowToday ................................................ 653 Visible........................................................ 654 TMouseButton ............................................... 351 TMouseEvent................................................. 351 TMouseMoveEvent ............................... 351, 352 TMsgDlgButtons ............................................. 84 tmTopLeft ...................................................... 550 TNotifyEvent ................. 256, 346, 471, 480, 637 OnChange .................................................. 471 TNotifyType .................................................. 480 TObject .................................................... 55, 347 toolbar ...................................................... 22, 321 ToolBar Align .......................................................... 458 Buttons DropDownMenu .................................... 460 OnClick() ............................................... 460 Style ....................................................... 460 tbsDropDown......................................... 460 Style ........................................................... 458 Top................................................................. 332 TParaAttributes FirstIndent.................................................. 520 LeftIndent .................................................. 520 816
Borland C++ Builder Programming
Numbering ................................................. 520 RightIndent ................................................ 520 TPoint............................................................. 335 TPopupMenu.................................................. 457 Caption....................................................... 457 OnClick() ................................................... 457 TPrintDlg ....................................................... 507 TProgressBar.................................................. 566 Max ............................................................ 562 Min............................................................. 562 Orientation ................................................. 561 Position ...................................................... 562 Smooth ....................................................... 561 Step ............................................................ 563 StepBy() ..................................................... 566 StepIt() ....................................................... 566 TProgressBarOrientation pbHorizontal .............................................. 561 pbVertical................................................... 561 TRadioButton................................................. 592 Alignment .................................................. 590 Caption....................................................... 589 Checked ..................................................... 590 Left............................................................. 590 Name .......................................................... 589 OnClick() ................................................... 592 Top ............................................................. 590 TRadioGroup Columns ..................................................... 596 ItemIndex ................................................... 595 Transparent .................................................... 474 TRect.............................................................. 338 TRichEdit HideSelection ............................................. 521 Lines........................................................... 519 Paragraph ................................................... 519 PopupMenu ................................................ 521 Print() ......................................................... 522 SelAttributes .............................................. 521 TRichEditAlignCenter ................................... 507 trigonometry..................................................... 91 TScreen ............................................................ 32 HintFont ..................................................... 327 TScrollBar...................................................... 582 Kind ........................................................... 579 LargeChange .............................................. 581 Max ............................................................ 580 Min............................................................. 580 OnChange()................................................ 583 OnScroll() .................................................. 584 PageSize..................................................... 581 Position ...................................................... 580 SetParams() ................................................ 583 SmallChange .............................................. 581 TScrollBarKind.............................................. 580 TScrollCode ................................................... 584 Copyright © 2003 FunctionX, Inc.
Borland C++ Builder Programming
TScrollEvent .................................................. 584 TScrollingWinControl ................................... 576 TSearchFind................................................... 507 TSearchFindFirst ........................................... 507 TSearchFindNext ........................................... 507 TSearchReplace ............................................. 507 TSize.............................................................. 336 TSpeedButton AllowUp .................................................... 438 Caption....................................................... 439 Down.......................................................... 438 Flat ............................................................. 438 Glyph ......................................................... 439 GroupIndex ................................................ 438 Height ........................................................ 439 Layout ........................................................ 439 Margin........................................................ 439 NumGlyphs................................................ 439 OnClick() ................................................... 654 Spacing ...................................................... 439 Width ......................................................... 439 TStaticBorderStyle......................................... 481 TStatusBar ..................................................... 461 Align .......................................................... 461 Panels......................................................... 464 Alignment .............................................. 465 Bevel ...................................................... 465 Style ....................................................... 465 psOwnerDraw .................................... 465 psText ................................................ 465 Text ........................................................ 465 Width ..................................................... 465 SimplePanel ............................................... 461 SimpleText................................................. 462 TStringGrid CellRect()................................................... 672 Cells ........................................................... 669 ColCount.................................................... 665 ColHeights ................................................. 668 Color .......................................................... 667 Cols ............................................................ 669 ColWidths .................................................. 668 DefaultDrawing ......................................... 669 DefaultRowHeight ..................................... 668 FixedCols ................................................... 666 FixedRows ................................................. 666 GridCoord() ............................................... 673 GridLineWidth........................................... 667 MouseToCell()........................................... 673 OnColumnMoved().................................... 676 OnDrawCell() .................................... 676, 677 OnGetEditMask()....................................... 675 OnGetEdtText() ......................................... 675 OnRowMoved() ......................................... 676 OnSelectCell() ........................................... 674 OnSetEditText()................................. 675, 679 Copyright © 2003 FunctionX, Inc.
Appendix
Options....................................................... 669 goColMoving ......................................... 669 goRowMoving ....................................... 669 RowCount .................................................. 665 Rows .......................................................... 669 ScrollBars................................................... 665 TStringList ..................................................... 713 TStrings.................................................. 620, 713 Add() .......................................... 595, 620, 641 Delete() ...................................................... 624 TTextAttributes Bold............................................................ 520 TTickMark ..................................................... 550 TTimer Enabled ...................................................... 555 Interval ....................................................... 555 TToolBar........................................................ 458 EdgeBorders............................................... 727 Flat ............................................................. 727 Height......................................................... 727 Images ........................................................ 727 TTrackBar Max ............................................................ 549 Min............................................................. 549 OnChange()................................................ 551 Orientation ................................................. 548 SliderVisible .............................................. 549 ThumbLength............................................. 549 TickMarks .................................................. 550 TTrackBarOrientation trHorizontal ................................................ 548 TTreeView Align .......................................................... 691 Items........................................................... 691 TUDBtnType ................................................. 539 TUpDown AlignButton................................................ 537 ArrowKeys................................................. 537 Associate .................................................... 537 Increment ................................................... 536 Max ............................................................ 536 Min............................................................. 536 OnChanging() ............................................ 539 OnChangingEx() ........................................ 539 OnClick() ................................................... 539 OnMouseUp() ............................................ 539 Orientation ................................................. 535 Position ...................................................... 536 Thousands .................................................. 536 Wrap........................................................... 536 TURKISH_CHARSET .................................. 252 TValueListEditor ItemProps ................................................... 685 OnEditButtonClick() .................................. 688 OnGetPickList() ......................................... 686 TVisualListEditor 817
Appendix
Strings ........................................................ 684 TVS_CHECKBOXES ................................... 694 TWinControl.......................................... 612, 652 Handle........................................................ 481 Parent ......................................................... 329 SetFocus() .................................................. 342 U udRight........................................................... 537 UINT................................................................ 32 underflow....................................................... 101 uppercase ......................................................... 61 UpperCase() ..................................................... 62 V VARIABLE_PITCH...................................... 253 VCL_MESSAGE_HANDLER...................... 345 visibility ......................................................... 321 Visible............................................................ 330 Visual Component Library (VCL) ................... 31 W WantReturns .................................................. 501 WantTabs....................................................... 501 WC_IPADDRESS ......................................... 492 Width ..................................................... 333, 439 Win32 .............................................................. 31 Win32 API ..................................................... 649 Win32 Functions SetWindowLong() ..................................... 694 Win32 Structures CHARFORMAT........................................ 523
818
Borland C++ Builder Programming
CHARFORMAT2...................................... 523 PARAFORMAT ........................................ 524 PARAFORMAT2 ...................................... 525 WindowProc .................................................. 355 Windows .......................................................... 19 Menu Designer........................................... 453 Windows 95 ..................................................... 37 Windows Styles WS_DISABLED........................................ 331 WS_TABSTOP.......................................... 332 WS_VISIBLE ............................................ 331 WinMain() ....................................................... 31 WM_DESTROY............................................ 427 WM_MOVE .......................................... 344, 354 WM_QUIT..................................................... 427 WM_SETTEXT..................................... 326, 353 WMKeyDown................................................ 348 WMKeyPress ................................................. 349 WMKeyUp..................................................... 349 WNDCLASS.................................................... 32 WNDCLASSEX .............................................. 32 WNDPROC ..................................................... 32 WndProc()...................................................... 354 WordPerfect ................................................... 451 WordWrap ............................................. 474, 502 WorkAreaHeight............................................ 361 WorkAreaRect ............................................... 361 Wrap............................................................... 536 WS_CHILD ................................................... 329 WS_DISABLED............................................ 331 WS_TABSTOP.............................................. 332 WS_VISIBLE ................................................ 331
Copyright © 2003 FunctionX, Inc.
Copyright © 2003 FunctionX, Inc.
819