Teach Yourself Visual C++ 6 in 21 Days
- 15 -
Updating and Adding Database Records Through ADO
Now that you've gotten your feet wet with an ODBC database application, one of the
oldest Microsoft database access technologies, it's time to turn your attention to
the newest Microsoft database access technology, ActiveX Data Objects (ADO). Designed
for use with all of Microsoft's programming and scripting technologies, ADO presents
the Visual C++ programmer with new challenges in database programming, while still
keeping the functionality familiar. Today, you will learn
- How ADO works and how it uses the OLE DB technology for providing simple database
access.
- How you can build a simple ADO application in a couple of minutes using ActiveX
controls.
- How you can build a complete database application using regular forms controls.
- How you can use special ADO macros to build a custom record set class for use
in your database applications.
CAUTION: This chapter works with some features that may not be included
in all versions of Visual C++. Although ADO is an important new area of pro-gramming
with Microsoft data access technologies, this chapter discusses some things that
you may not have the ability to do with your version of Visual C++.
What Is ADO?
A couple years ago, Microsoft designed a new data access technology called OLE
DB. This data access technology was intended to be much more than simply a way of
getting data into and out of databases. This technology was intended to be the means
of accessing data, regardless of where that data may be located. Through the OLE
DB technology, you could access mail messages, spreadsheets, files, and so on. Anything
that might have data could possibly be accessed through the OLE DB technology. This
was one of the first technologies to be produced from the research and development
of the object- oriented file system at the heart of what Microsoft has been calling
"Cairo" for the past few years.
NOTE: Many of the technologies bundled under the product name of Cairo
will be released some time next year in the Windows NT 5.0 operating system.
As you can imagine, with the range of functionality that OLE DB must have to access
data in all of those different sources, it might be quite complex to work with this
technology. Well, it is. This is where ActiveX Data Objects come into play. ADO was
designed as another layer on top of OLE DB, specifically for providing database access.
One of the goals in designing ADO was to create a control that could be used to
provide data access and control in Web pages, caching the data records on the client.
Part of the reason for this goal was to allow a Web browser user to access an entire
set of data records, without having to pull down each individual record, one at a
time, to navigate and make changes to the records. Because of this capability with
ADO, the ADO control is distributed with Microsoft's Internet Explorer Web browser
(version 4.0 and above).
ADO Objects
To make ADO as easily usable in scripting languages such as VBScript as it is
in programming environments such as Visual Basic, Microsoft tried to keep the number
of objects to a minimum. As a result, you have a small number of basic objects:
- Connection
- Error
- Command
- Parameter
- Recordset
- Field
Along with these objects, you have collection objects for containing collections
of Error, Parameter, and Field objects.
The Connection Object
The Connection object is used for establishing and maintaining a connection to
a database. This object is configured with the connection information, including
database location, user ID, and password, before opening the connection. Once all
of this information is appropriately configured, the connection object should have
its Open method called to open the connection. Once the Connection object goes out
of scope, the connection is automatically closed. If you want more control over closing
and opening the database connection, you can call the Connection object's Close method
to close the connection.
The Connection object is also the object through which any high-level connection
functionality is controlled. This includes all transaction control, through the Connection
object's BeginTrans, CommitTrans, and RollbackTrans methods.
The Error Object
Whenever a database error occurs, the error information from the database is placed
into an ADO Error object. The error information in the error object is the database
error information, not ADO error information. Whenever you encounter an error and
need to look up the error information to determine what went wrong, you'll need to
examine the database error codes and descriptions, not the ADO error codes.
The Command Object
The Command object is used to execute commands in the database. You can use this
object to run SQL statements or call stored procedures (SQL functions that are stored
in the database). Any time that a command returns rows of data, you need to attach
the Command object to a Recordset object for the returned data to be stored in.
When you call a stored procedure, as with functions in any other programming language,
you'll often need to pass parameters to the stored procedure. To pass these parameters,
you'll attach a series of Parameter objects to the Command object. Each of the Parameter
objects will have the name of the parameter that it holds the value for, along with
the value that should be passed to the database for that particular parameter.
The Parameter Object
The Parameter object is used for passing variables and for calling stored procedures
or parameterized queries. These are attached to a Command object for use in calling
the command that has been programmed into the Command object.
The Recordset Object
The Recordset object contains a set of records from the database. The set of records
is the result of a command being sent to the database that results in a set of records
being returned. You can navigate through the Recordset, much like you do with the
Recordset objects for other database access technologies. You can also access the
fields in each record in the Recordset through the Field objects that are associated
with the Recordset. You can update the records in the Recordset, and then use the
Recordset to update the database. You can also insert new records into the Recordset,
or delete records and have those changes made in the database.
The Field Object
The Field object represents a single column in the Recordset. Each Field object
contains the column name, data value, and how the data value should be represented.
Because ADO was designed to be used in Microsoft's scripting languages, and the only
data type available in these scripting languages is the Variant data type, the Field
objects always contain a Variant data value. The data value is automatically converted
to the correct data type when updating to the database. As the programmer working
with the ADO objects, you will have to convert the value from a Variant to whatever
data type you need it to be, as well as convert it back to a Variant when updating
the value.
Using the ADO ActiveX Control
There are two different ways in which you can use the ADO control in your Visual
C++ applications. The simple way to incorporate ADO into your application is through
the use of ActiveX controls. You can add the ADO data control to your Visual C++
project, just like any other ActiveX control, as shown in Figure 15.1.
FIGURE 15.1. Adding
the ADO ActiveX control to a project.
Once you add the ADO control to your project, and place it on a window, you'll
need to specify the data connection in the control properties, as shown in Figure
15.2. You'll also need to specify the source for the records that will be retrieved
by the control, as shown in Figure 15.3.
FIGURE 15.2. Specifying
the database connection.
FIGURE 15.3. Specifying
the record source.
To use the ADO control efficiently, you'll also want to use data-bound controls
that are ADO-enabled, such as the Microsoft DataGrid control. When you add these
controls to the window with the ADO control, you'll specify the ADO control as the
data source for the control, as shown in Figure 15.4. If the control is designed
to only provide access to a single field in a record set, you'll also need to specify
which field is to be used for the control.
FIGURE 15.4. Specifying
the data source.
Once you add all these controls to the window and configure them, you can run
your application and have full database access through ADO without having written
a single line of code, as shown in Figure 15.5.
FIGURE 15.5. A
running ADO control database application.
This is such a simple way to build database applications: Just place controls
on a window and configure the properties to tell it where to get the data. What's
the downside of building ADO applications this way? First, using this approach involves
a lot of unnecessary overhead in building ADO applications. For each SQL query or
table that you want to pull in a separate record set, you have to add a separate
ADO control. Each of these ADO controls will establish a separate connection to the
database, which could cause problems with databases that have a limited number of
connections available (not to mention the additional overhead on the application).
Finally, not all data-bound controls are ADO enabled. ADO is such a new technology
that there are few controls that you can use with it at this time. You may find some
controls that allow you to retrieve and display data for the user, but do not allow
the user to change and edit the data. Others may not even provide you with that much
functionality.
Importing the ADO DLL
If you look around in the MFC class hierarchy, you'll find that there are no classes
for use with ADO. If you don't want to use the controls approach, then what are your
options? Do you have to create the classes yourself? No, Microsoft has provided other
means for you to create and use classes for each of the objects in ADO, through the
use of a new C++ precompiler directive called #import.
The #import precompiler directive was first added to Visual C++ with the 5.0 release.
You can use this directive to import an ActiveX DLL that has been built with the
IDispatch interface description included in the DLL. This directive tells the Visual
C++ compiler to import the DLL specified by the #import directive and to extract
the object information from the DLL, creating a couple of header files that are automatically
included in your project. These header files have the filename extensions .TLH and
.TLI and are in the output directory for your project (the Debug or Release directory,
the same directory where you'll find the executable application after you've compiled
your project). These two files contain definitions of classes for each of the objects
in the DLL that you can use in your code. The #import directive also tells the compiler
to include the DLL as part of the project, eliminating the need to include the .LIB
file for the DLL in your project.
You can import the ADO DLL by placing the following code at the beginning of the
header file in which you are defining any database objects:
#define INITGUID
#import "C:\Program Files\Common Files\System\ADO\msado15.dll"
Ârename_namespace("ADOCG") rename("EOF", "EndOfFile")
using namespace ADOCG;
#include "icrsint.h"
In these four lines of directives, the first line defines a constant that needs
to be defined for ADO. The second imports the ADO DLL, creating the two header files
mentioned earlier. After the filename to be imported, this directive includes two
attributes to the #import directive. The first, rename_namespace, renames the namespace
into which the DLL has been imported. This is followed with the line following the
#import, where the renamed namespace is specified as the one used. The second attribute,
rename, renames an element in the header files that are created using the #import
directive. The reason you rename elements in these header files is to prevent conflicts
with another element named elsewhere. If you examine the header file, the element
specified is not renamed in the file, but when the compiler reads the file, the element
is renamed. The final line includes the ADO header file, which contains the definition
of some macros that you will use when writing your ADO applications.
Connecting to a Database
Before you can use any of the ADO objects, you need to initialize the COM environment
for your application. You can do this by calling the CoInitialize API function, passing
NULL as the only parameter, as follows:
::CoInitialize(NULL);
This enables you to make calls to ActiveX objects. If you leave out this one line
of code from your application, or don't put it before you begin interacting with
the objects, you get an COM error whenever you run your application.
When you are finished with all ADO activity, you also need to shut down the COM
environment by calling the CoUninitialize function, as follows:
CoUninitialize();
This function cleans up the COM environment and prepares your application for
shutting down.
Once you initialize the COM environment, you can create a connection to the database.
The best way to do this is not to declare a Connection object variable, but to declare
a Connection object pointer, _ConnectionPtr, and use it for all your interaction
with the Connection object. Once you declare a Connection object pointer, you can
initialize it by creating an instance of the Connection object, calling the CreateInstance
function, passing it the UUID of the Connection object as its only parameter, as
follows:
_ConnectionPtr pConn;
pConn.CreateInstance(__uuidof(Connection));
TIP: When you work with these objects and functions, you need to use the
correct number of underscore characters in front of the various object and function
names. The _ConnectionPtr object has only a single underscore character, whereas
the __uuidof function has two.
Once you create the object, you can call the Open function to establish the connection
to the database. This function takes four parameters. The first parameter is the
connection definition string. This string defines the OLE DB data source for the
database. It may be an ODBC OLE DB driver, where OLE DB is sitting on top of an ODBC
data source, as you'll use in your sample application. If you are using SQL Server
or Oracle databases, it may be a direct connection to the OLE DB interface provided
by the database itself. The second parameter is the user ID for connecting to the
database. The third parameter is the password for connecting to the database. The
fourth parameter is the cursor type to use with the database. These types are defined
in the msado15.tlh header file that is created by the #import directive. A typical
use of the Open function to connect to an ODBC data source that doesn't need a user
ID or password is like the following:
pConn->Open(L"Provider=MSDASQL.1;Data Source=TYVCDB", L"", L"",
ÂadOpenUnspecified);
Executing Commands and Retrieving Data
Once you have the connection open, you can use a Command object to pass SQL commands
to the database. This is the normal method of executing SQL commands with ADO. To
create a Command object, follow the same process that you used to create a Connection
object. Declare a Command object pointer, _CommandPtr, and then create an instance
of it using the UUID of the Command object, as follows:
_CommandPtr pCmd;
pCmd.CreateInstance(__uuidof(Command));
Once you create your Command object, assuming that you have already established
the connection to the database, set the active connection property of the Command
object to the open Connection object pointer, as follows:
pCmd->ActiveConnection = pConn;
Next, specify the SQL command to be executed by setting the CommandText property
of the Command object, as follows:
pCmd->CommandText = "Select * from Addresses";
At this point, you have two options for how you execute this command and retrieve
the records. The first is to call the Command object's Execute method, which will
return a new Recordset object, which you'll want to set to a Recordset object pointer,
as follows:
_RecordsetPtr pRs;
pRs = pCmd->Execute();
The other approach to running the command and retrieving the records is to specify
that the Command object is the source for the records in the Recordset. This requires
creating the Recordset object as follows:
_RecordsetPtr pRs;
pRs.CreateInstance(__uuidof(Recordset));
pRs->PutRefSource(pCmd);
Now, you'll need to create two NULL variant values to pass as the first two parameters
to the Recordset's Open method. The third parameter will be the cursor type to use,
followed by the locking method to use. Finally, the fifth parameter to the Recordset's
Open method is an options flag that indicates how the database should evaluate the
command being passed in. You do this with the following code:
// Create the variant NULL
_variant_t vNull;
vNull.vt = VT_ERROR;
vNull.scode = DISP_E_PARAMNOTFOUND;
// Open the recordset
pRs->Open(vNull, vNull, adOpenDynamic, adLockOptimistic, adCmdUnknown);
You could take another approach to accomplish all of the preceding tasks with
only a few lines of code. Skip the use of the Command and Connection objects altogether,
placing all the necessary connection information in the Recordset's Open function.
You can specify the SQL command as the first parameter and the connection information
as the second parameter, instead of the two NULLs that you passed previously. This
method reduces all of the preceding code to the following few lines:
_RecordsetPtr pRs;
pRs.CreateInstance(__uuidof(Recordset));
pRs->Open(_T("Provider=MSDASQL.1;Data Source=TYVCDB"),
_T("select * from Addresses"), adOpenDynamic,
adLockOptimistic, adCmdUnknown);
TIP: Although placing all of the command and connection information into
the Recordset Open function is fine for a simple application, such as the one that
you will build today, you are better off using the Connection object with any application
that has more than a couple of database queries. This allows you to make a single
connection to the database and use that one connection for all interaction with the
database.az
Navigating the Recordset
Once you've retrieved a set of records from the database, and you are holding
them in a Recordset object, you'll need to navigate the set of records. This functionality
is available, just as you would expect, through the MoveFirst, MoveLast, MovePrevious,
and MoveNext functions. None of these functions take any parameters because they
perform the functions that you would expect them to perform.
Along with these functions, the Recordset object also has two properties, BOF
and EOF (which you should normally rename to prevent a collision with the default
definition of EOF), which can be checked to determine if the current record in the
set is beyond either end of the set of records.
Accessing Field Values
When you need to begin accessing the data values in each of the fields is where
working with ADO in Visual C++ begins to get interesting. Because ADO is intended
to be easy to use in Microsoft's scripting languages, VBScript and JScript, which
only have variant data types, all data elements that you'll retrieve from fields
in the ADO Recordset are variant values. They have to be converted into the data
types that you need them to be. There are two ways of doing this. The first way is
the straight-forward way of retrieving the values into a variant and then converting
them, as in the following code:
_variant_t vFirstName;
CString strFirstName;
vFirstName = pRs->GetCollect(_variant_t("FirstName"));
vFirstName.ChangeType(VT_BSTR);
strFirstName = vFirstName.bstrVal;
The not-so-straight-forward way to do this is actually the better way, and in
the long run, is a lot easier to work with. Microsoft has created a series of macros
that perform the conversion for you and that maintain a set of variables of the records
in the set. To do this, you'll define a new class to use as the interface for your
record set. This class will be a descendent of the CADORecordBinding class, which
is defined in the icrsint.h header file, which you included just after the #import
directive. This class will not have any constructor or destructor but will have a
series of macros, along with a number of variables. Each field in the set of records
has two variables, an unsigned long, which is used to maintain the status of the
variable, and the field variable itself. These variables must be regular C variables,
and they cannot be C++ classes such as CString. A simple example of this class declaration
is the following:
class CCustomRs :
public CADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs)
ADO_FIXED_LENGTH_ENTRY(1, adInteger, m_lAddressID, lAddressIDStatus, ÂFALSE)
ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szFirstName,
Âsizeof(m_szFirstName), lFirstNameStatus, TRUE)
ADO_FIXED_LENGTH_ENTRY(3, adDate, m_dtBirthdate, lBirthdateStatus, ÂTRUE)
ADO_FIXED_LENGTH_ENTRY(4, adBoolean, m_bSendCard, lSendCardStatus, ÂTRUE)
END_ADO_BINDING()
public:
LONG m_lAddressID;
ULONG lAddressIDStatus;
CHAR m_szFirstName[51];
ULONG lFirstNameStatus;
DATE m_dtBirthdate;
ULONG lBirthdateStatus;
VARIANT_BOOL m_bSendCard;
ULONG lSendCardStatus;
};
Once you define this record layout class to match the record layout that will
be returned by your database query, you can declare a variable of this class for
use in your application, as follows:
CCustomRs m_rsRecSet;
Next, you need to create a pointer to an IADORecordBinding interface, as follows:
IADORecordBinding *picRs = NULL;
This is a pointer to a COM interface that is part of the ADO Recordset object.
Once you retrieve the set of records, you need to retrieve the pointer to the IADORecordBinding
interface and bind the custom record set class to the Recordset object, as in the
following code:
if (FAILED(pRs->QueryInterface(__uuidof(IADORecordBinding), (LPVOID Â*)&picRs)))
_com_issue_error(E_NOINTERFACE);
picRs->BindToRecordset(&m_rsRecSet);
Now, as you navigate the records in the set, you just need to access the member
variables of your custom record class to retrieve the current value for each field.
The BEGIN_ADO_BINDING and END_ADO_BINDING Macros
The key to the second method of accessing the data values in the record set is
in the macros that are used in defining the record class. The set of macros start
with the BEGIN_ADO_BINDING macro, which takes the class name as its only parameter.
This macro sets up the structure definition that is created with the rest of the
macros that follow.
The set of macros is closed by the END_ADO_BINDING macro. This macro doesn't take
any parameters, and it wraps up the definition of the record binding structure that
is created in the class. It is in the rest of the macros, which are used between
these two, where the real work is done.
The ADO_FIXED_LENGTH_ENTRY Macros
The ADO_FIXED_LENGTH_ENTRY macro is used for any database fields that are fixed
in size. It can be used with a date or boolean field, or even a text field that is
a fixed size, with no option for any variation in the database. There are two versions
of this macro; you add a 2 to the end of the name of the second version (ADO_FIXED_LENGTH_ENTRY2).
Both versions require the same first three and last parameters. The first version
requires an additional parameter that is not required in the second version. The
first parameter is the ordinal number of the field in the record set. This is the
position in the field order as returned by the SQL query that is run to populate
the record set. The second parameter is the data type of the field; the available
data types are defined in the header file created by the #import directive. The third
parameter is the variable into which the data value is to be copied. For the first
version of the macro, the fourth parameter is the variable for the field status (the
unsigned long that you defined with the variable for the actual value). The last
variable is a boolean that specifies whether this field can be modified.
The ADO_NUMERIC_ENTRY Macros
You use the ADO_NUMERIC_ENTRY macros with numeric fields only. They are similar
to the ADO_FIXED_LENGTH_ENTRY macros in that there are two different versions of
the macro, named in the same way. In these macros, the first five parameters are
the same in both versions, along with the final parameter. Like with the ADO_FIXED_LENGTH_ENTRY
macros, the first version has an additional parameter that is not used in the second
version.
The first three parameters for the ADO_NUMERIC_ENTRY macros are the same as those
for the ADO_FIXED_LENGTH_ENTRY macros, as are the last parameter and the next to
last parameter for the first version. It is the fourth and fifth parameters that
are unique to these macros. The fourth parameter specifies the precision of the value
in this field of the record set. The fifth parameter specifies the scale of the value.
Both of these parameters are crucial in correctly converting the value to and from
a variant data type.
The ADO_VARIABLE_LENGTH_ENTRY Macros
The final series of macros is the ADO_VARIABLE_LENGTH_ENTRY macros. You use this
series of macros with database fields that are likely to vary in length. With a SQL-based
database, you want to use this series of macros with any varchar (variable-length
character string) columns. There are three versions of this macro. In all three versions,
the first four parameters are the same, and the final parameter is the same. It is
the parameters between them that vary.
The first parameter is the ordinal position of the column in the record set as
returned by the SQL query. The second parameter is the data type. The third parameter
is the variable in which the data value should be placed. The fourth parameter for
all versions of the macro is the size of the variable into which the value is to
be placed. This prevents the data from being written past the end of the variable
that you defined for it to be placed in. As with the previous macros, the final parameter
specifies whether the field is updateable.
In the first version of this macro, there are two parameters between the fourth
and final parameters. The second version of this macro only has the first of these
two parameters, and the third version only has the second of these two parameters.
The first of these two parameters is the status variable for use with this field.
The second of these two parameters is the length of the field in the database. The
preceding example used the second version of this macro.
Updating Records
When you need to update values in a record in the recordset, how you handle it
depends on which of the two methods you used to retrieve the data elements from the
recordset. If you retrieved each field and converted it from a variant yourself,
you need to update each individual field that has been changed. The update is done
using the Recordset object's Update method, which takes two variables, the field
being updated and the new value for the field. You could make this update using the
following code:
_variant_t vName, vValue;
vName.SetString("FirstName");
vValue.SetString("John");
pRs->Update(vName, vValue);
If you created your record class and bound it to the recordset, updating the record
is a little simpler. Once you have copied the new values into the variables in the
record class, you can call the record-bound version of the Update function, as in
the following:
picRs->Update(&m_rsRecSet);
This updates the record in the Recordset object to be updated with the values
in the record class that you have bound to the set.
Adding and Deleting
Adding and deleting records from an ADO recordset is similar to how you accomplish
it in other database access technologies. However, there are some slight subtleties
to how you perform the addition of new records.
For deleting the current record, you can call the Recordset object's Delete method.
This method requires a single parameter that specifies how the delete is supposed
to be done. Most likely, you'll pass the adAffectCurrent value so that only the current
record in the recordset is deleted, as in the following code:
pRs->Delete(adAffectCurrent);
pRs->MovePrevious();
As with any other database access technology, once you've deleted the current
record, there is no current record, so you need to navigate to another record before
allowing the user to do anything else.
When you are adding a new record, you can call the Recordset object's AddNew method.
Once you have added a new record, the new record is the current record in the record
set. If you check the variables in the record class that you created, you'll find
that they are all empty. However, you cannot just begin entering data values into
these fields. To allow the user to immediately enter the various data elements in
the new record, you'll blank out the values in the record class and pass this variable
as the only parameter to the Add New class. You need to call it through the record-binding
interface pointer, as in the following example:
CString strBlank = " ";
COleDateTime dtBlank;
m_rsRecSet.m_lAddressID = 0;
strcpy(m_rsRecSet.m_szFirstName, (LPCTSTR)strBlank);
m_rsRecSet.m_dtBirthdate = (DATE)dtBlank;
m_rsRecSet.m_bSendCard = VARIANT_FALSE;
picRs->AddNew(&m_rsRecSet);
This allows you to provide the user with a blank record, ready for editing. Once
the user has entered all the various values in the record, copy all these values
back to the record variable. Then, call the Update method to save the record.
Closing the Recordset and Connection Objects
Once you finish working with a record set, you'll close the record set by calling
the Close method, as follows:
pRs->Close();
Once you finish all database interaction for the entire application, you'll also
close the connection to the database by calling the Connection object's Close method:
pConn->Close();
Building a Database Application Using ADO
The sample application that you will build today is another simple database application,
basically the same as the one you built yesterday. You'll use ADO to retrieve a set
of records from an Access database, providing functionality to navigate the record
set. The user will be able to make changes to the data in the record set, and those
changes will be reflected in the database as well. The user will also be able to
add new records to the record set and delete records as desired. You will accomplish
all of this using ADO as the means of accessing the database, which will go through
the ODBC driver that was configured yesterday.
Creating the Application Shell
The application that you will build today will be an SDI-style application. As
with sev-eral other sample applications that you build in the course of reading this
book, everything that you do in today's application is just as applicable to an MDI
or dialog-style application. To start the application, you'll use the MFC AppWizard
to build the application shell, using most of the SDI-style application default settings.
To start your application, create a new AppWizard project, naming the project
something appropriate, such as DbAdo. Specify on the first panel of the AppWizard
that you are building an SDI-style application. Accept all the default settings for
steps 2 through 5, being sure to leave the second step stating that you want no database
support included in the application. On the final AppWizard step, specify that the
view class should be inherited from the CFormView class.
Once you finish creating your application shell, design the main dialog form for
use in your application. Add the standard controls for each of the fields in the
Addresses table from the database you used yesterday (or if you used a different
database yesterday, add controls for all the fields in the table that you used),
as shown in Figure 15.6. Configure the controls using the properties listed in Table
15.1.
TIP: If you want to save a little time when building the example, you can
leave out most of the controls and database fields from the application. The key
fields that you'll need to include on the screen are ID, First and Last Names, Birthdate,
and Send Card. If you want to leave out the other fields from the application, that's
fine. You will need to include these fields in the CCustomRs class that you create
in this chapter.
FIGURE 15.6. The
main form layout.
TABLE 15.1. CONTROL PROPERTY SETTINGS.
Object |
Property |
Setting |
Static Text |
ID |
IDC_STATIC |
|
Caption |
Address ID |
Edit Box |
ID |
IDC_EDIT_ADDRESSID |
Static Text |
ID |
IDC_STATIC |
|
Caption |
First Name |
Edit Box |
ID |
IDC_EDIT_FIRSTNAME |
Static Text |
ID |
IDC_STATIC |
|
Caption |
Last Name |
Edit Box |
ID |
IDC_EDIT_LASTNAME |
Static Text |
ID |
IDC_STATIC |
|
Caption |
Spouse Name |
Edit Box |
ID |
IDC_EDIT_SPOUSENAME |
Static Text |
ID |
IDC_STATIC |
|
Caption |
Address |
Edit Box |
ID |
IDC_EDIT_ADDRESS |
Static Text |
ID |
IDC_STATIC |
|
Caption |
City |
Edit Box |
ID |
IDC_EDIT_CITY |
Static Text |
ID |
IDC_STATIC |
|
Caption |
State Or Province |
Edit Box |
ID |
IDC_EDIT_STATEORPROVINCE |
Static Text |
ID |
IDC_STATIC |
|
Caption |
Postal Code |
Edit Box |
ID |
IDC_EDIT_POSTALCODE |
Static Text |
ID |
IDC_STATIC |
|
Caption |
Country |
|