📄 ch11.htm
字号:
AnimalsQueryMass->AsInteger =
AnimalsQuerySIZE->AsInteger * AnimalsQueryWEIGHT->AsInteger;
}
</FONT></PRE>
<P>The code shown here assigns the value of the <TT>AnimalsQueryMass</TT> object
to the product of the
<TT>AnimalsQuerySIZE</TT> and <TT>sqlWeightWEIGHT</TT> fields.
This kind of multiplication is legal to do because all of the fields are of the same
type. Furthermore, you could have used the <TT>Value</TT> property instead of <TT>AsInteger</TT>.
I
explicitly declared the type in this example to help illustrate precisely what
is going on.</P>
<P><TT>OnCalcField</TT> methods are called each time a record is displayed to the
user. As a result, all of the <TT>Mass</TT> fields displayed in the grid
are properly
filled in, as shown in Figure 11.4.<BR>
<BR>
<A NAME="Heading11"></A><A HREF="11ebu04.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/11/11ebu04.jpg">FIGURE 11.4.</A><FONT COLOR="#000077">
</FONT><I>The <TT>MASS</TT> field contains the product of the <TT>WEIGHT</TT> and
<TT>SIZE</TT>
fields. </I><BR>
<BR>
A <TT>TDBImage</TT> control contains a bitmap from the <TT>BMP</TT> field of the
table.</P>
<P>To get the screen shot shown in Figure 11.4, I opened the <TT>Column</TT> property
in the <TT>TDBGrid</TT> object and selected Add All
Fields. I then deleted the <TT>Area</TT>
and <TT>BMP</TT> fields and closed the <TT>Column</TT> property editor. I will talk
more about the grid object later in this chapter.</P>
<P>If you choose to never instantiate a particular field in the Fields
Editor, the
current dataset you are working with no longer contains that field. It can't be accessed
programmatically or visually at runtime. Usually, this is exactly the effect you
want to achieve, and so this trait will generally be perceived as a
strong benefit.
However, there are times when it might not serve your purposes, and in those cases
you should either create an object for all the fields in a table or stay away from
the Fields Editor altogether. Remember that you can hide fields
inside a grid by
using the <TT>Column</TT> property, as shown previously. That way, you create objects
for all fields, but show only certain ones to the user.
<H3><A NAME="Heading12"></A><FONT COLOR="#000077">Lookup Fields</FONT></H3>
<P>You can use
lookup fields to look up a value in one table that you want to use
in a second table. For instance, suppose you had two tables, one of which contained
a list of books, and the other contained a list of authors. It would be nice if you
could
automatically view a list of the existing authors whenever you needed to add
a new book to the <TT>Books</TT> table. That way, you could enter the book's name,
look up the author in a drop-down list, and presto, you would be done. The <TT>Books</TT>
table would then automatically contain a reference to the appropriate author in the
<TT>Authors</TT> table. That is, the author number from the <TT>Authors</TT> table
would automatically be inserted in the <TT>Books</TT> table.</P>
<P>Another way to
think about lookup fields is that they provide the ability to perform
a powerful kind of pseudo-join using the <TT>TTable</TT> object. Suppose two tables
called <TT>Authors</TT> and <TT>Books</TT> are related on a field called <TT>AuthNo</TT>.
<TT>AuthNo</TT> is the primary key of the <TT>Authors</TT> table, and it is a foreign
key in the <TT>Books</TT> table. When you are looking at the <TT>Books</TT> table,
sometimes you would like to be able to include the name of the author of each book
inside the book table. That is, you would like to perform a join on the book and
author table. You can't actually perform a join, however, because you are using the
<TT>TTable</TT> object, and not <TT>TQuery</TT>. The solution to this dilemma is
the
lookup field. It will use the foreign key in the <TT>Books</TT> table to reference
the name of the author from the <TT>Author</TT> table. This technique does join one
better, however, because it will let you not only view a field from the
<TT>Authors</TT>
table as if it were part of the <TT>Books</TT> table, but also enables you to drop
down a list of all the authors in the <TT>Authors</TT> table while you are still
viewing the <TT>Books</TT> table, as shown in Figure 11.5.</P>
<P>In
short, lookup fields give you the same type of benefits you derive from performing
a join between two tables. In particular, they let you combine the fields of two
tables so that you can create one dataset with fields from multiple tables.
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Lookup fields are a bit like a combination
of a one-to-many relationship and a calculated field. The techniques used to actually
implement them, however, have
more in common with calculated fields than they do
with one-to-many relationships. There are significant differences between the three
technologies, but I still tend to think of calculated fields, lookup fields, and
one-to-many relationships as
being interrelated concepts. <BR>
<BR>
Because lookup fields are so much like one-to-many relationships, it is usually not
a good idea to use both techniques simultaneously with the same two <TT>TTable</TT>
objects. For instance, if you have the
<TT>Authors</TT> table related to the books
table in a one-to-many, you wouldn't want to simultaneously do a lookup from the
<TT>Books</TT> table to the author table. This problem, and its solution, are addressed
in the <TT>Lookup</TT> example on
the CD-ROM that accompanies this book. That program
will be discussed throughout the rest of this section of the chapter. <BR>
<BR>
I should perhaps add that lookup fields are a great technique to use with relatively
small datasets. If you are
from the world of big iron, and work with tables that
contain tens of thousands of records or more, you will probably find lookup fields
are of only limited use to you.
<HR>
</BLOCKQUOTE>
<P>Needless to say, BCB gives good support for using
lookup fields. You can now perform
automatic lookups inside grids, list boxes, and combo boxes. In particular, the following
controls support lookups: <TT>TDBGrid</TT>, <TT>TDBCtrlGrid</TT>, <TT>TDBLookupListBox</TT>,
and
<TT>TDBLookupComboBox</TT>.</P>
<P>The Lookup program shows how to proceed. The code for this application is shown
in Listings 11.1 through 11.3. Two views of the program are shown in Figures 11.5
and 11.6.<BR>
<BR>
<A NAME="Heading14"></A><A
HREF="11ebu05.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/11/11ebu05.jpg">FIGURE 11.5.</A><FONT COLOR="#000077">
</FONT><I>The main form for the Lookup program.</I>
<H6></H6>
<P><A NAME="Heading15"></A><A HREF="11ebu06.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/11/11ebu06.jpg">FIGURE 11.6.</A><FONT COLOR="#000077">
</FONT><I>This form features
a combo box that lets you perform lookups from the <TT>Books</TT>
table into the <TT>Authors</TT> table.</I>
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>The first version of Delphi had
a
<TT>TDBLookupCombo</TT> control and a <TT>TDBLookupList</TT> control that had certain
limited capabilities. Both of these controls are still present in some versions of
BCB, but they have been moved off the Data Controls page onto the Win 3.1 page.
They
are being kept around solely for compatibility with legacy Pascal code, and you should
not use them in new programs. <BR>
<BR>
The <TT>TDBLookupComboBox</TT> control and the <TT>TDBLookupListBox</TT> control
now replace the old 16-bit
controls, and they outperform them on several fronts.
In particular, the <TT>TDBLookupComboBox</TT> and <TT>TDBLookupListBox</TT> will
be filled up automatically with the data from the lookup table. Don't confuse the
old control with the new ones!
<TT>TDBLookupComboBox</TT> is the fancy one; the <TT>TDBLookupCombo</TT>
is the old-fashioned one. You might use the following somewhat whimsical mnemonic:
The <TT>TDBLookupListBox</TT> has a bigger name than the <TT>TDBLookupList</TT> because
it
has "bigger" capabilities. <BR>
<BR>
By the way, this is a classic example of why it is important to get things right
the first time. In particular, it shows why it is sometimes better to cut a feature
rather than trying to put in a hack
that you will want to improve in later versions.
In particular, the <TT>TDBLookupCombo</TT> was poorly implemented in the first version
of Delphi, which was a 16-bit program. Because Delphi 2.0 promised to compile all
your 16-bit programs, this
component had to be left in the product even though it
was replaced with a far superior tool. Now, this old nemesis lives on even in the
C++ version of the product, because BCB advertises the fact that it supports all
the legacy Pascal code you
might want to bring into a project. <BR>
<BR>
Here's the summary: The original error was made back in Delphi 1.0, but the repercussions
still echo even when the 32-bit version of the Delphi is ported to C++! Clearly,
it is worthwhile making sure
that things are designed right the first time, or else
they should be left out of the product altogether. Of course, this is a rule that
can be stated fairly easily, but is difficult to live up to.
<HR>
</BLOCKQUOTE>
<P><A
NAME="Heading17"></A><FONT COLOR="#000077"><B>Listing 11.1. The core functionality
for the Lookup program is done in the Object Inspector for the TDMod object and not
here in the code for Dmod1.cpp.</B></FONT></P>
<PRE><FONT
COLOR="#0066FF">///////////////////////////////////////
// File: DMod1.cpp
// Project: Lookup
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include "DMod1.h"
#pragma resource "*.dfm"
TDMod *DMod;
__fastcall TDMod::TDMod(TComponent* Owner)
: TDataModule(Owner)
{
AuthorTable->Open();
BookDetailTable->Open();
BookLookupTable->Open();
}
void __fastcall TDMod::AuthorTableCalcFields(TDataSet *DataSet)
{
AuthorTableFirstLast->AsString =
AuthorTableFirst->AsString + " " + AuthorTableLast->AsString;
}
void TDMod::RefreshBookDetail()
{
BookDetailTable->Refresh();
}
AnsiString TDMod::GetCurBook()
{
return
BookDetailTable->FieldByName("Title")->AsString;
}
AnsiString TDMod::GetCurAuthor(void)
{
return AuthorTable->FieldByName("FirstLast")->AsString;
}
void TDMod::FindAuthor(AnsiString S)
{
AuthorTable->FindNearest(OPENARRAY(TVarRec, (S)));
}
void TDMod::FindTitle(AnsiString S)
{
AnsiString Temp(BookLookupTable->IndexName);
BookLookupTable->IndexName = "idxTitle";
BookLookupTable->FindNearest(OPENARRAY(TVarRec, (S)));
BookLookupTable->IndexName = Temp;
}
void TDMod::BookLookupInsert()
{
BookLookupTable->Insert();
}
void TDMod::BookLookupPost()
{
if ((BookLookupTable->State ==
dsEdit)||(BookLookupTable->State == dsInsert))
BookLookupTable->Post();
}
void TDMod::BookLookupCancel()
{
if ((BookLookupTable->State == dsEdit)||(BookLookupTable->State == dsInsert))
BookLookupTable->Cancel();
}
void
TDMod::BookLookupDelete()
{
BookLookupTable->Delete();
}
</FONT></PRE>
<P><A NAME="Heading18"></A><FONT COLOR="#000077"><B>Listing 11.2. Form1 gives you
a look at both the Authors table and the Books table. A drop-down in dbGrid2 lets
you
view the lookup field.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
// File: InsertEdit.cpp
// Project: Lookup
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include
"Main.h"
#include "DMod1.h"
#include "InsertEdit.h"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::Exit1Click(TObject
*Sender)
{
Close();
}
void __fastcall TForm1::EditBook1Click(TObject *Sender)
{
InsertEditForm->ShowEdit(DMod->CurBook);
DMod->RefreshBookDetail();
}
void __fastcall TForm1::NewBook1Click(TObject *Sender)
{
InsertEditForm->ShowInsert();
DMod->RefreshBookDetail();
}
</FONT></PRE>
<P><A NAME="Heading19"></A><FONT COLOR="#000077"><B>Listing 11.3. The InsertEditForm
shows how to use DBLookupComboBoxes.</B></FONT></P>
<PRE><FONT
COLOR="#0066FF">///////////////////////////////////////
// File: InsertEdit.cpp
// Project: Lookup
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -