C++Builder and the VCL  - Part 02

 

 

Is to forget the right way

 

And forget that the going is easy.

 

This program declares this string as an AnsiString, and then passes it to a procedure that can break it down into a series of words. These words are passed back one at a time and added into an array. The array is then displayed in a series of one word lines in a memo control. The program appears in Listings 3.1 and 3.2.

Listing 3.1. The header for the ChuangTzu program.

 

 

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

 

#ifndef MainH

 

#define MainH

 

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

 

#include <Classes.hpp>

 

#include <Controls.hpp>

 

#include <StdCtrls.hpp>

 

#include <Forms.hpp>

 

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

 

 

 

class TChuangTzu

 

{

 

private:

 

  int FCount;

 

  AnsiString FWords[40];

 

  AnsiString GetWords(int Index);

 

  void SetWords(int Index, AnsiString S);

 

public:

 

  TChuangTzu(){ FCount = 0; }

 

  __property AnsiString Words[int Index] = {read=GetWords, write=SetWords};

 

  __property int Count={read=FCount, write=FCount};

 

};

 

 

 

class TForm1 : public TForm

 

{

 

published:

 

  TButton *Button1;

 

  TMemo *Memo1;

 

  void __fastcall Button1Click(

 

  TObject *Sender);

 

private:

 

public:

 

  virtual __fastcall TForm1(TComponent* Owner);

 

};

 

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

 

extern TForm1 *Form1;

 

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

 

#endif

Listing 3.2. The main module of the ChuangTzu program.

 

 

#include <vcl.h>

 

#pragma hdrstop

 

#include "Main.h"

 

#include "codebox.h"

 

#pragma resource "*.dfm"

 

TForm1 *Form1;

 

 

 

///////////////////////////////////////

 

// ChuangTzu //////////////////////////

 

///////////////////////////////////////

 

AnsiString TChuangTzu::GetWords(int i)

 

{

 

  return FWords[i];

 

}

 

 

 

void TChuangTzu::SetWords(int i,const AnsiString s)

 

 

 

{

 

   FCount++;

 

   FWords[i]=s;

 

}

 

 

 

AnsiString StripWords(AnsiString &S, char Token)

 

{

 

  AnsiString TokenStr(Token);

 

  AnsiString Temp1;

 

  AnsiString Temp2 = S + ` `;

 

 

 

  Temp1 = strtok(S.c_str(), TokenStr.c_str());

 

  Temp2 = strrev(Temp2.c_str());

 

  Temp2.SetLength(Temp2.Length() - (Temp1.Length() + 1));

 

  S = strrev(Temp2.c_str());

 

  return Temp1;

 

}

 

 

 

///////////////////////////////////////

 

// Form1 //////////////////////////////

 

///////////////////////////////////////

 

 

 

__fastcall TForm1::TForm1(TComponent* Owner)

 

  : TForm(Owner)

 

{

 

}

 

 

 

void __fastcall TForm1::Button1Click(

 

  TObject *Sender)

 

{

 

  TChuangTzu C;

 

  AnsiString S1;

 

  int i = 0;

 

  AnsiString S =

 

    "Easy is right. Begin right ";

 

    "And you are easy. "

 

    "Continue easy and you are right. "

 

    "The right way to go easy "

 

    "Is to forget the right way "

 

    "And forget that the going is easy.";

 

 

 

  do

 

  {

 

    S1 = StripWords(S, ` `);

 

    C.Words[i] = S1;

 

    i++;

 

  } while (S1 != "");

 

 

 

 

 

  for (i = 0; i < C.Count; i++)

 

  {

 

    Memo1->Lines->Add(C.Words[i]);

 

  }

 

 

 

}

 

The core of this program is the ChuangTzu class definition:

class TChuangTzu

 

{

 

private:

 

  int FCount;

 

  AnsiString FWords[40];

 

  AnsiString GetWords(int Index);

 

  void SetWords(int Index, AnsiString S);

 

public:

 

  TChuangTzu(){ FCount = 0; }

 

  __property AnsiString Words[int Index] = {read=GetWords, write=SetWords};

 

  __property int Count={read=FCount, write=FCount};

 

};

At the heart of the object is the declaration for an array of 40
AnsiStrings:

AnsiString FWords[40];

 

You can access the second member of this array with code that looks like this:

FWords[1] = "Let some sad trumpeter stand on the empty streets at dawn."

 

AnsiString S = FWords[1];

 

The ChuangTzu object provides an array property that gives you access to this private data. There is, of course, no reason why you can't declare an array of AnsiStrings on their own without a supporting object. However, I thought it might be helpful to see how you can use an array in your program without breaking the rule about keeping the raw data of your objects private.

Here is the declaration for the array property:

__property AnsiString Words[int Index] = {read=GetWords, write=SetWords};

 

As you can see, it returns an AnsiString and can be indexed with an integer value. (There is nothing to keep you from declaring array properties that are indexed with AnsiStrings!)

Here is the get method for the array:

AnsiString TChuangTzu::GetWords(int i)

 

{

 

  return FWords[i];

 

 

 

}

 

Notice that it takes a single integer as a parameter and returns an AnsiString.

Here is the set method for the array:

void TChuangTzu::SetWords(int i,const AnsiString s)

 

{

 

  FCount++;

 

  FWords[i]=s;

 

}

 

This function takes both an integer and a string as parameters. In order to call this method, you write

ChuangTzu.Words[i] = "peace proclaims olives of endless age."

 

Here you can see the integer that is passed to SetWords, as well as a quote from Shakespeare's 107 sonnet that is passed in the second parameter. The FCount variable is used to track the number of entries in the array. Note that properties provide an improvement over get and set methods by letting you use simple array syntax.

Here is an example of how to declare a large AnsiString that extends over several lines:

AnsiString S =

 

    "Easy is right. Begin right ";

 

    "And you are easy. "

 

    "Continue easy and you are right. "

 

    "The right way to go easy "

 

    "Is to forget the right way "

 

    "And forget that the going is easy.";

 

The StripWords function is used to peel off words from this quote one at a time, starting at the front:

AnsiString StripWords(AnsiString &S, char Token)

 

{

 

  AnsiString TokenStr(Token);

 

  AnsiString Temp1;

 

  AnsiString Temp2 = S + ` `;

 

 

 

  Temp1 = strtok(S.c_str(), TokenStr.c_str());

 

  Temp2 = strrev(Temp2.c_str());

 

  Temp2.SetLength(Temp2.Length() - (Temp1.Length() + 1));

 

  S = strrev(Temp2.c_str());

 

  return Temp1;

 

}

 

There are two interesting things about this function. The first shows how to concatenate strings with the plus operator, and the second shows how to use standard C string functions on an AnsiString.

Notice the bit of syntax that adds a space onto the end of the string passed to the function:

AnsiString Temp2 = S + ` `;

 

You can always use the plus operator to append or prefix information to an AnsiString. This eliminates any need for you to use the strcat function, though strcat is a likely candidate for the routine inside the AnsiString implementation that actually performs the concatenation.

 


NOTE: The reason I append a space onto the end of this string is to ensure that the AnsiString object that underlies this C string makes a unique copy of it. If I simply assign the two strings without changing them, the call to strrev or strtok will affect both strings!

Note also that
AnsiStrings support the += operator. Therefore you don't have to write S = S + "Sam", but can write S += "Sam".


I do, however, use standard C functions to tokenize and reverse the strings:

Temp1 = strtok(S.c_str(), TokenStr.c_str());

 

Temp2 = strrev(Temp2.c_str());

 

Temp2.SetLength(Temp2.Length() - (Temp1.Length() + 1));

 

What the function does here is not important. Just notice that I can get at the underlying null-terminated string with the c_str() method of the AnsiString class. Once I have access to the underlying string, I can pass it to any of the standard C functions such as strrev, strcat, strtok, and so on.

To get the length of a string, call the Length method of the AnsiString class:

int I = MyString.Length();

 

I have gone on at some length about the AnsiString class because it is used extensively throughout this book and throughout most BCB programs. The key fact to remember is that AnsiStrings are compatible with the VCL. For instance, you can assign an AnsiString directly to a VCL control:

AnsiString S("Doubting the filching age will steal his treasure");

 

Edit1->Text = S;

 

You can also assign a regular C string to a VCL control, but that is because the VCL string is represented on the C++ side as an AnsiString, which can have its constructor called by a null-terminated string:

char S[] = {"Till either gorge be stuffed or prey be gone"};

 

Memo1->Text = S;

 

 


NOTE: This time the quote is from Shakespeare's Venus and Adonis, with this seemingly gruesome line appearing in the following context:

Till either gorge be stuffed or prey be gone
Even so she kiss'd his brow, his cheek, his chin,
And where she ends she doth anew begin.

The bard's point being, I suppose, that her appetites were large.


In fact, you can take advantage of these constructors and assign an integer directly to a VCL control:

Memo1->Text = 2;

 

This is considerably more freedom than the strongly typed Object Pascal language would ever give you liberty to pursue.

Exploring SYSDEFS.H

This chapter is largely about the places where C++ and the VCL are woven together. Most of the code that is used to accomplish this end is found in SYSDEFS.H. SYSDEFS.H is the place where the developers put most of the core system code needed to allow BCB to use the VCL native types. In particular, if there was some element of Object Pascal not supported by C++ that played a role in the VCL, solutions to that problem were mapped out in this file. The only major exception to this rule is DSTRINGS.H, where the AnsiString class is declared.

 


NOTE: SYSDEFS.H is to some degree a testament to the power of the C++ language. It features classes and templates that emulate features of Object Pascal that Delphi relies on the compiler to implement. The advantage of having the compiler implement these features is that it is possible to pick and choose exactly what kind of syntax you want to use. Thus the elegance and simplicity of the Object Pascal language. On the other hand, the extraordinary richness and flexibility of the C++ object model is highlighted beautifully by the work done in SYSDEFS.H.


The following classes appear in SYSDEFS.H:

class __declspec(delphireturn) Currency : public CurrencyBase

 

class __declspec(delphireturn) TDateTime : public TDateTimeBase

 

class __declspec(delphiclass) TObject

 

class TMetaClass

 

class __declspec(delphireturn) Set

 

class TVarArray

 

class TVarData

 

class TVarRec

 

template<class T> class OpenArray

 

class __declspec(delphireturn) Variant: public TVarData

 

class AutoCmd

 

class NamedParm

 

class Procedure: public AutoCmd

 

class Function: public AutoCmd

 

class PropertySet: public AutoCmd

 

class PropertyGet: public AutoCmd

 

typedef class TMemoryManager *PMemoryManager;

 

class THeapStatus;

 

Some of these classes, such as TObject and TMetaClass, have already been discussed at length. Other classes such as Procedure, Function, PropertyGet, and PropertySet are part of the guts of the BCB-VCL interface, and are not worth discussing in this context. But a few of these classes, notably Set, Currency, TDataTime, Variant, and TMemoryManager play an important role in standard BCB programming, and will therefore be covered in the next few pages. I will also take a look at TVarRec and OpenArray, because they play an important supporting role in the object model.

Formatting Text

In the next three sections, called TDateTime, TCurrency, and OpenArrays, I explore some of the ways you can format text before presenting it to the user. You should read through all three sections to get a feeling for some of the options supplied by BCB.

Many tools for formatting text have been contributed by third-party VCL vendors. There are many sites on the Web that provide links to third-party toolmakers. To get started, visit the Link section of my Web site: users.aol.com/charliecal, or go to www.borland.com.

TDateTime

The VCL comes equipped with a number of functions for working with the current date or time, as shown by this excerpt from the online help:

Date

Returns the current date

DateTimeToStr

Converts a value from time format to a string

DateTimeToString

Converts a value from time format to a string

DateToStr

Converts a value from date format to a string

DayOfWeek

Returns the current day of the week

DecodeDate

Decodes the specified date

DecodeTime

Decodes the specifies time

EncodeDate

Returns values specified in date format

EncodeTime

Returns values specified in time format

FormatDateTime

Formats a date and time using the specified format

Now

Returns the current date and time

StrToDate

Coverts a string to a date format

StrToDateTime

Converts a string to a date/time format

StrToTime

Converts a string to a time format

Time

Returns the current time

TimeToStr

Converts a time format to a string

 

There is also a TDateTimeField that is used in database programming.

Many of these functions work with an object type called TDateTime that is found in the SYSDEFS.H unit. Most of the rest of this section is dedicated to exploring the uses of the TDateTime type. This type exists in order to provide compatibility with the VCL routines listed at the beginning of this section.

The following method shows two different ways of outputting the current date:

void __fastcall TForm1::CurrentDate1Click(TObject *Sender)

 

{

 

  TDateTime Date1, Date2 = Now();

 

  AnsiString S;

 

 

 

  DateTimeToString(S, "dddd, mmmm dd, yyyy", Date1.CurrentDate());

 

  S += "\r" + Date2.FormatString("dddd, mmmm dd, yyyy");

 

 

 

  ShowMessage(S);

 

 

 

}

 

The code shown in the CurrentDate1Click method outputs the following text inside a RichEdit control:

Tuesday, January 07, 1997

 

Tuesday, January 07, 1997

 

 


NOTE: The TRichEdit control from the Win95 page of the Component Palette provides supports for the RTF format. There is no set limit on the amount of text you can display in a TRichEdit control, and it supports the use of multiple fonts and colors inside a single control. There are also routines for saving the loading and saving the contents of the control, and for printing.


I initialize the first of the two TDateTime objects with the default constructor, which automatically sets the date to Saturday, December 30, 1899. Then set asks this object to retrieve the current date (not the default date), which it passes to me in the form of a TDateTime object:

static TDateTime __fastcall CurrentDate();

 

A raw TDateTime object is obviously not something I can show to the user, so I use the VCL DateTimeToString routine to convert it into a string:

DateTimeToString(S, "dddd, mmmm dd, yyyy", Date1.CurrentDate());

 

This routine takes a string in the first parameter, a format string in the second parameter, and a TDateTime record in the third parameter.

 


NOTE: If you look in SYSDEFS.H, you will see that TDateTime is declared delphireturn. That is because this object needs to conform to the expectations of VCL routines such as DateTimeToString. In other words, this is a classic example of what use the BCB team made of delphireturn.


The second TDateTime object shown in this example is set equal to the current date during its construction:

TDateTime Date2(Now());

 

Notice that I pass the VCL routine called Now to the TDateTime constructor. Now returns a variable of type TDateTime that is initialized to the current date and time. You can then use the FormatString method of the TDateTime object to retrieve the current string:

S += "\n" + Date2.FormatString("dddd, mmmm dd, yyyy");

 

The FormatString method takes the same type of format string passed to DateTimeToString.

Format strings are documented in the online help, but the basic idea behind them is that if you pass in two letters, you get a number back; pass in three letters, and you get an abbreviation back; and pass in four letters, and you get a whole word back:

dd => 01

 

ddd => Sun

 

dddd => Sunday

 

The following method displays the current time in a specified format:

void __fastcall TForm1::CurrentTime1Click(TObject *Sender)

 

{

 

  TDateTime Date(Now());

 

  RichEdit1->Text = Date.FormatString("h:nn:ss am/PM");

 

 

 

}

 

The result will look something like this:

12:27:56 PM

 

In this case, the PM is in large letters because I wrote it that way in the format specifier. Had I written am/pm, the resulting string would have looked like this: 12:27:56 pm. The rest of the format specifiers works like this:

h => No leading zero

 

hh => Prefix a leading zero

 

In short, if you pass in one letter, you get back a string with no leading zero; if you pass in two, a leading zero will be added automatically. Notice that you pass in an n to specify the format for minutes. This is because m has already been used for months.

Here is a table listing the various specifiers you can use:

 

 

 

 

 

Format specifier

Item

s

Seconds

m

Minutes

h

Hour

d

Day

m

Month

y

Year

am/pm

Format for time

c

Uses ShortDateFormat and LongTimeFormat

t

Uses ShortTimeFormat

tt

Uses LongTimeFormat

ddddd

Display date using ShortDateFormat

dddddd

Display date using LongDateFormat

 

The various date and time formats shown in the last items will be explained in just one moment.

The DateTimeString., DateString, and TimeString methods of the TDateTime object do not require that you provide format specifiers. Here, for instance, is an example of how to use the DateTimeString method:

void __fastcall TForm1::DateandTime1Click(TObject *Sender)

 

{

 

  TDateTime Date(Now());

 

  RichEdit1->Lines->Add(Date.DateTimeString());

 

 

 

}

 

On my system, the code shown here produces the following result:

1/7/97 12:31:24 PM

 

The format used in this case is global to the system. You can change these settings by working with a series of global variables found in SYSUTILS.HPP:

extern System::AnsiString CurrencyString;

 

extern unsigned char CurrencyFormat;

 

extern unsigned char NegCurrFormat;

 

extern char ThousandSeparator;

 

extern char DecimalSeparator;

 

extern unsigned char CurrencyDecimals;

 

extern char DateSeparator;

 

extern System::AnsiString ShortDateFormat;

 

extern System::AnsiString LongDateFormat;

 

extern char TimeSeparator;

 

extern System::AnsiString TimeAMString;

 

extern System::AnsiString TimePMString;

 

extern System::AnsiString ShortTimeFormat;

 

extern System::AnsiString LongTimeFormat;

 

extern System::AnsiString ShortMonthNames[12];

 

extern System::AnsiString LongMonthNames[12];

 

extern System::AnsiString ShortDayNames[7];

 

 

 

extern System::AnsiString LongDayNames[7];

 

On my system, these values are preset as follows:

 

 

CurrencyString: $

 

CurrencyFormat: 0

 

NegCurrFormat: 0

 

ThousandSeparator: ,

 

DecimalSeparator: .

 

CurrencyDecimals: 2

 

DateSeparator: /

 

ShortDateFormat: M/d/yy

 

LongDateFormat: dddd, MMMM dd, yyyy

 

TimeSeparator: :

 

TimeAMString: AM

 

TimePMString: PM

 

ShortTimeFormat: h:mm AMPM

 

LongTimeFormat: h:mm:ss AMPM

 

ShortMonthNames: Jan

 

ShortMonthNames: Feb

 

ShortMonthNames: Mar

 

ShortMonthNames: Apr

 

ShortMonthNames: May

 

ShortMonthNames: Jun

 

ShortMonthNames: Jul

 

ShortMonthNames: Aug

 

ShortMonthNames: Sep

 

ShortMonthNames: Oct

 

ShortMonthNames: Nov

 

ShortMonthNames: Dec

 

LongMonthNames: January

 

LongMonthNames: February

 

LongMonthNames: March

 

LongMonthNames: April

 

LongMonthNames: May

 

LongMonthNames: June

 

LongMonthNames: July

 

LongMonthNames: August

 

LongMonthNames: September

 

LongMonthNames: October

 

LongMonthNames: November

 

LongMonthNames: December

 

ShortDayNames: Sun

 

ShortDayNames: Mon

 

ShortDayNames: Tue

 

ShortDayNames: Wed

 

ShortDayNames: Thu

 

ShortDayNames: Fri

 

ShortDayNames: Sat

 

LongDayNames: Sunday

 

LongDayNames: Monday

 

LongDayNames: Tuesday

 

LongDayNames: Wednesday

 

LongDayNames: Thursday

 

LongDayNames: Friday

 

LongDayNames: Saturday

 

For instance, the following code changes the nature of the current ShortDateFormatString:

void __fastcall TForm1::SetShortDateFormattoMMMMDDDDYYYY1Click(TObject *Sender)

 

{

 

  RichEdit1->Text = Now();

 

  ShortDateFormat = "MMMM/DDDD/YYYY";

 

  RichEdit1->Lines->Add(Now());

 

 

 

}

 

The text displayed by calling this function is as follows:

1/7/97 1:30:21 PM

 

January/Tuesday/1997 1:30:21 PM

 

The first block of text shows the default behavior, and the second block shows what happened after I made a few subtle changes to the system.

The system initializes the date and time strings to the choices you make in Windows. You can see the current Windows settings by calling GetLocaleInfo. Changes you make are written to the system registry if you send a WM_INICHANGE message to your own nonconsole mode application.

The following lines of code show how you can compare two times using the greater than operator:

void __fastcall TForm1::SetTimeOne1Click(TObject *Sender)

 

{

 

  FTimeOne = Now();

 

  RichEdit1->Text = FTimeOne.TimeString();

 

}

 

 

 

void __fastcall TForm1::SetTimeTwo1Click(TObject *Sender)

 

{

 

  FTimeTwo = Now();

 

  RichEdit1->Lines->Add(FTimeTwo.TimeString());

 

}

 

 

 

void __fastcall TForm1::CompareOnetoTwo1Click(TObject *Sender)

 

{

 

  AnsiString S;

 

 

 

  if (FTimeOne > FTimeTwo)

 

    S = FTimeOne.TimeString() + " > " + FTimeTwo.TimeString();

 

  else

 

    S = FTimeTwo.TimeString() + " > " + FTimeOne.TimeString();

 

  RichEdit1->Lines->Add(S);

 

 

 

}

 

The first method shown here sets a global object called TimeOne to the current time. The second method sets a second global object to the current time. After calling the two functions at a two-second interval, you can end up with the objects initialized to two different times:

2:10:29 PM

 

2:10:31 PM

 

You can then use the third function to compare them using the > operator:

2:10:31 PM > 2:10:29 PM

 

You can also use the ++ operator on the current time to increment the day by one:

void __fastcall TForm1::CompareTwotoOne1Click(TObject *Sender)

 

{

 

  int i;

 

 

 

  RichEdit1->Lines->Add(FTimeOne.DateTimeString());

 

  FTimeOne++;

 

  RichEdit1->Lines->Add(FTimeOne.DateTimeString());

 

 

 

}

 

Here is the output in the RichEdit control after a run off all four functions:

2:13:03 PM

 

2:13:05 PM

 

2:13:05 PM > 2:13:03 PM    // First call to compare

 

1/7/97 2:13:03 PM

 

1/8/97 2:13:03 PM

 

2:13:03 PM > 2:13:05 PM    // Second call to compare

 

As you can see, calling the ++ operator incremented the day by one. After incrementing the values, a second call to the compare function showed that FTimeOne is now larger than FTimeTwo. Even though it is still two seconds smaller than date two, it is now a day later, and hence larger.

This should give you some sense of what you can do with the TDateTime type. This has not been an exhaustive investigation of what can be done, but if you now open up SysDefs.h, you should have no trouble following the drift of the declaration for the other methods in the TDateTime object. The code samples shown in this section are from a program found on disk called, in a flight of poetic fancy, DateTime1.

TCurrency

I will now spend a few minutes looking at TCurrency. After the rather lengthy investigation of TDateTime that you just saw, there should not be much need to examine this object in-depth, because it follows the same general patterns of logic you saw in the previous section.

When following this discussion, remember that the primary reason the type exists is for compatibility with the VCL. In particular, the TCurrency type, like the TDateTime type, is used in database programming. For instance, there is a TCurrencyField and a TDateTimeField available for database programmers. The principles behind the field types are examined in detail throughout the lengthy database section of this book, Chapters 8 through 18, and particularly in Chapter 11, "Working with Field Objects."

The Currency type is a wrapper around the built in __int64 type. The compiler probably will not support assigning large numbers directly to a variable of type Currency:

Currency C = 5000000000000; // This probably won't work

 

You can, however, pass in a string that holds a large number. Consider the following method:

Currency C = void __fastcall TForm1::Button1Click(TObject *Sender)

 

{

 

  Currency C("500000000000000");

 

 

 

  AnsiString S = C;

 

  Edit1->Text = S;

 

  Edit2->Text = Format("%m", OPENARRAY(TVarRec, (C)));

 

 

 

}

 

This method initializes a variable of type currency to a large number with a string. Most of the time large numbers will be entered by the user in an edit control, so this is a reasonable way to proceed.

Once you have an instance of a Currency type, you can display it directly to the user or assign it to a TCurrencyField from a database table. Notice that you can assign a string directly to a Currency field, and vice versa. When translating a currency type to a string, the result does not produce a formatted result string. Instead, you see a plain number:

500000000000000

 

If you want to display the string with proper formatting, use the Format function, as shown in the Button1Click method:

$500,000,000,000,000.00

 

I will describe the Format function, and OpenArrays, in the next section of this chapter, called "Working with OpenArrays." Note that some of the database controls will automatically display a currency type with formatting, depending in part on their attributes and the contents of the Data Dictionary.

The currency type will handle numbers in the following range:

-922337203685477.5808..922337203685477.5807

 

That is, it can handle between 19 and 20 digits.

There are many operators in the Currency class for performing mathematical operations. For instance, the following syntax is valid:

Currency C1 = 25000, C2 = 5000, C3;

 

C1 += C2;

 

C1 = C2 / 9;

 

C3 = (C1 % C2);

 

C3 = (C1 + C2) / C1 + (C1 % C2);

 

It's interesting to note that these operations are performed on objects, and not on simple types. This is an excellent example of the power of C++ and the amazing things you can do with operator overloading. If you are not used to C++, you should spend a few moments contemplating the fact that C1, C2, and C3 are objects, with methods and functions, and not just simple built-in types like integers or floats. You should open up SysDefs.h to see a list of all the supported syntax used by the Currency object.

MaskEdits and Other Issues

The native VCL TMaskEdit control deserves at least a few sentences of comment during this discussion of formatting strings. This component is a descendant of TEdit that enables you to control the kinds of characters that are entered into the control.

The key to the TMaskEdit component is the EditMask property, which enables you to enter a string that uses several special characters to define what the user can legally enter into the edit area.

There is a property editor associated with the EditMask property. In this editor, you can find a number of default masks, as shown in Figure 3.1. However, on many occasions, none of these masks will suit your purpose.

Figure 3.1. The property editor for the EditMask property.

I could, for instance, enter the following string as an EditMask:

######;0;_

 

The purpose of this string is to ensure that the user can enter only numeric values. In particular, these numeric values represent the number of widgets that the user wants to sell.

To understand the EditMask property, you need to study the entry associated with it in the online help. Here, you see all the characters that can be used to define a mask, along with a description of the effects associated with each character. I quote most of this list later in this section.

In the online help for the EditMask property, you will find that a pound sign (#) enables you to enter only numbers, spaces, and plus and minus characters. Notice, however, that there is also a 0 character, which requires that you enter a number in each location marked with that character. The question, then, is which special character should you use: # or 0?

In some cases, the 0 character is not what you want, because it forces the user to enter values. For instance, the following code forces the user to enter a six-digit number, one for each of the zeroes you place in the mask:

000000;0;_

 

The preceding mask would enable you to enter the number 123456, but it would reject 123. If you want to give the user the freedom to enter a number of any size up to 999,999, you should use the # character rather than the 0 character. One way to sum this matter up is as follows: The 0 character means that the user has to fill in that place with a number, while the # character enables the user to fill in that space with either a number or a blank space.

The zero that appears after the semicolon in the previous string specifies that the mask entered is not saved as part of the data. If I had placed a 1 here, the mask would have been saved. In this particular case, there are no mask characters to either save or discard, so it doesn't matter what I placed in this location. The phone mask, shown previously in Figure 3.1, contains two parentheses that would be affected by this value.

Finally, the very end of the previous mask includes an underscore. This value is used to specify what will be used to designate the space character.

Notice that each field of the mask is separated from the last by a semicolon. Each mask consists of three fields, which were described earlier.

The MaskEdit component is available from the Additional page of the Component palette. Notice that you can click a button in the EditMask dialog found in the EditMask property editor in order to load country-specific strings.

 


NOTE: The following lists format specifiers you can use in the TMaskEdit control:
! If an ! character appears in the mask, leading blanks don't appear in the data. If an ! character is not present, trailing blanks don't appear in the data.

> If a > character appears in the mask, all characters that follow are in uppercase until the end of the mask or until a < character is encountered.

< If a < character appears in the mask, all characters that follow are in lowercase until the end of the mask or until a > character is encountered.

<> If these two characters appear together in a mask, no case checking is done and the data is formatted with the case the user uses to enter the data.
\ The character that follows a \ character is a literal character. Use this character when you want to allow any of the mask special characters as a literal in the data.

L The L character requires only an alphabetic character only in this position. For the US, this is A-Z, a-z.

l The l character permits only an alphabetic character in this position, but doesn't require it.

A The A character requires an alphanumeric character only in this position. For the US, this is A-Z, a-z, and 0-9.

a The a character permits an alphanumeric character in this position, but doesn't require it.

C The C character requires a character in this position.

c The c character permits a character in this position, but doesn't require it.

0 The 0 character requires a numeric character only in this position.

9 The 9 character permits a numeric character in this position, but doesn't require it.

# The # character permits a numeric character or a plus or minus sign in this position, but doesn't require it.

: The : character is used to separate hours, minutes, and seconds in times. If the character that separates hours, minutes, and seconds is different in the International settings of the Control Panel utility on your computer system, that character is used instead of :.

/ The / character is used to separate months, days, and years in dates. If the character that separates months, days, and years is different in the International settings of the Control Panel utility on your computer system, that character is used instead of /.

; The ; character is used to separate masks.

_ The _ character automatically inserts a blank the edit box. When the user enters characters in the field, the cursor skips the blank character. When using the EditMask property editor, you can change the character used to represent blanks. You can also change this value programmatically. See the following table.


Remember that many of the database objects such as the TDateTime field provide automatic formatting for you.

This is also an area where you might consider turning to third-party products. For instance, Turbo Power Software has an excellent library called Orpheus that provides many fine controls and routines for automatically formatting strings. You should, however, explore the market thoroughly, because there are many fine products out there for handling this type of problem. In particular, many controls are found on the Web. You can visit my Web site to find links to sites that specialize in components. (users.aol.com/charliecal).

Working with OpenArrays

The VCL supports a concept called an array of const. This type will enable you to pass in a variable number of parameters. Think for a moment of the sprintf function:

int sprintf(char *buffer, const char *format[, argument, ...]);

 

The last argument of this function can contain multiple parameters. OpenArrays provide the same functionality in Object Pascal. In short, they enable you to pass a variable number of parameters to a function.

Here is an Object Pascal code segment declared to accept an array of const:

function Format(const Format: string; const Args: array of const): string;

 

The Format function acts almost exactly like sprintf. Its first parameter contains a string with various format specifiers in it, and its second parameter consists of an array containing multiple values to be formatted. The format specifiers used by Format are essentially the same as those you would use in sprintf. After all, the whole point of adding the function to the Object Pascal language was simply to duplicate the functionality of sprintf inside Object Pascal. Here is the way a call to Format looks in Object Pascal:

var

 

  S, Name: string;

 

  Age: Integer;

 

begin

 

  Name = GetName;

 

  Age = GetAge;

 

  S :=  Format("My name is %s and I am %d years old", [Name, Age]);

 

 

 

end;

 

All this is good and well, but you might object that C++ already has support for this kind of syntax through sprintf. That is correct, but you will find many VCL database routines that use arrays of const for various purposes. There are no standard C++ equivalents of these routines, as they exist only in the VCL. In other words, the C++ and Object Pascal implementations of this functionality differ in their details, even though the interface to the user is similar. As a result, special C++ classes need to be created to duplicate this built-in Object Pascal type.

While you may or may not find the Format routine itself useful, you will definitely need to know about arrays of const if you want to work with the VCL, and particularly if you want to do any database programming. For instance, the following database routines all use arrays of const: FinkKey, FindNearest, SetRange, AddRecord, SetKeyFields, AppendRecord, InsertRecord, SetFields, AssignValue, and DBErrorFmt. Clearly, this is a subject that you need to understand.

Here is the declaration for the Format function as it appears in SYSUTILS.HPP:

extern System::AnsiString __fastcall Format(const System::AnsiString Format,

 

  const System::TVarRec *Args, const int Args_Size);

 

As you can see, this function takes not two, but three parameters. You will not, however, pass all three parameters into this function, nor will you pass them into any of the database routines shown previously. Instead, you use a macro called OPENARRAY, which enables you to call VCL functions that use arrays of const with a reasonable degree of ease.

 


NOTE: As I am sure you have noticed already, this particular subject is not exactly the garden spot of C++Builder. We

 

<<Back (Part 01)  

Next (Part 03) >>

 

VMS Desenvolvimentos

Diversas Dicas, Apostilas, Arquivos Fontes, Tutoriais, Vídeo Aula, Download de Arquivos Relacionado a Programação em C++ Builder.

Voltar ao Site  

Voltar ao Index