⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch14.htm

📁 好书《C++ Builder高级编程技术》
💻 HTM
📖 第 1 页 / 共 5 页
字号:
AnsiString __fastcall TDMod::GetAddress()

{

  return 
DMod->AddressTableAddress1->AsString + `\r' +

    DMod->AddressTableAddress2->AsString + `\r' +

    DMod->AddressTableCity->AsString + `\r' +

    DMod->AddressTableState->AsString + `\r' +

    
DMod->AddressTableZip->AsString;

}

AnsiString __fastcall TDMod::GetPhone()

{

  return DMod->PhoneTableDescription->AsString + `\r' +

    DMod->PhoneTableNumber->AsString + `\r' +

    DMod->PhoneTableExt->AsString;

}


AnsiString __fastcall TDMod::GetEMail()

{

  return DMod->EMailTableAddress->AsString + `\r' +

    DMod->EMailTableDescription->AsString + `\r' +

    DMod->EMailTableService->AsString;

}

</FONT></PRE>
<P><A 
NAME="Heading14"></A><FONT COLOR="#000077"><B>Listing 14.5. The header for
the Globals unit.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////

// File: Globals.h

// Project: KdAdd

// Copyright (c) 1997 by Charlie 
Calvert

//

#ifndef GlobalsH

#define GlobalsH

AnsiString GetError(int ErrNo, AnsiString &amp;S);

#endif

</FONT></PRE>
<P><A NAME="Heading15"></A><FONT COLOR="#000077"><B>Listing 14.6. The main module
for the Globals unit.</B></FONT></P>

<PRE><FONT COLOR="#0066FF">///////////////////////////////////////

// File: Globals.cpp

// Project: KdAdd

// Copyright (c) 1997 by Charlie Calvert

//

#include &lt;vcl\vcl.h&gt;

#pragma hdrstop

#include &quot;Globals.h&quot;

#define 
ERR_STRING_SIZE 255

AnsiString GetError(int ErrNo, AnsiString &amp;S)

{

  S.SetLength(ERR_STRING_SIZE);

  LoadString(HINSTANCE(HInstance), 1, S.c_str(), ERR_STRING_SIZE);

  return S;

}

</FONT></PRE>
<P><A NAME="Heading16"></A><FONT 
COLOR="#000077"><B>Listing 14.7. The custom RC file
for the project. This is a stub to be filled out later.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">#include &quot;kderrs.inc&quot;

STRINGTABLE

{

  KDERR_CASESTATEMENT, &quot;Command fell through 
case statement&quot;

}

</FONT></PRE>
<P><A NAME="Heading17"></A><FONT COLOR="#000077"><B>Listing 14.8. The include file
for the project has only one entry. This is a stub to be filled out later.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">#define 
KDERR_CASESTATEMENT 1

</FONT></PRE>
<P>The pages in the <TT>TPageControl</TT> are hidden from view in Figure 14.1. In
Figure 14.3 through Figure 14.6, you can see the remaining <TT>TTabSheet</TT> objects.<BR>
<BR>
<A NAME="Heading18"></A><A 
HREF="14ebu03.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/14/14ebu03.jpg">FIGURE 14.3.</A><FONT COLOR="#000077">
</FONT><I>The tab sheet for the Address table.<BR>
<BR>
<A NAME="Heading19"></A></I><A HREF="14ebu04.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/14/14ebu04.jpg">FIGURE 14.4.</A><FONT COLOR="#000077">
</FONT><I>The tab sheet for the 
Phone table.<BR>
<BR>
<A NAME="Heading20"></A></I><A HREF="14ebu05.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/14/14ebu05.jpg">FIGURE 14.5.</A><FONT COLOR="#000077">
</FONT><I>The tab sheet for the E-mail table.</I>
<H4 ALIGN="CENTER"></H4>
<H4><A NAME="Heading21"></A><FONT COLOR="#000077">Using 
the kdAdd Program</FONT></H4>
<P>The kdAdd program has the minimal functionality needed to support the user's needs.
For example, you can perform Insert, Post, Delete, and Cancel operations on all the
tables. Access to these features is provided 
through both the menus and a speedbar.
You can also set the index to the <TT>Company</TT>, <TT>First</TT>, or <TT>Last</TT>
fields of the <TT>kdNames</TT> table. Finally, you can search on either the <TT>Company</TT>,
<TT>First</TT>, or <TT>Last</TT> 
fields.<BR>
<BR>
<A NAME="Heading22"></A><A HREF="14ebu06.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/14/14ebu06.jpg">FIGURE 14.6.</A><FONT COLOR="#000077">
</FONT><I>The tab sheet for Memo.</I>
<H4 ALIGN="CENTER"></H4>
<H4><A NAME="Heading23"></A><FONT COLOR="#000077">Setting Up the Index for 
kdAdd</FONT></H4>
<P>One of the hubs around which the kdAdd program revolves involves the code that
controls the index for the program. This code is called from several different places
in the program. The obvious place to start studying it, however, 
is in the response
method for the menu items that let the user change the index:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::SetupIndex(TObject *Sender)

{

  switch (dynamic_cast&lt;TComponent *&gt;(Sender)-&gt;Tag)

  {

    case 100:

      
DMod-&gt;NamesTable-&gt;IndexName = &quot;idxLastName&quot;;

      break;

    case 101:

      DMod-&gt;NamesTable-&gt;IndexName = &quot;idxFirstName&quot;;

      break;

    case 102:

      DMod-&gt;NamesTable-&gt;IndexName = 
&quot;idxCompany&quot;;

      break;

    case 103:

      DMod-&gt;NamesTable-&gt;IndexName = &quot;&quot;;

      break;

  }

}

void __fastcall TForm1::IndexClick(TObject *Sender)

{

  SetupIndex(Sender);

  
DMod-&gt;NamesTable-&gt;FindNearest(OPENARRAY(TVarRec, (&quot;AAAA&quot;)));

}

</FONT></PRE>
<P>The code has three menu choices for changing the index. The first lets the user
set the index to the last name; the second, to the first name; and the 
third, to
the company name. All three menu items are attached to the <TT>IndexClick</TT> method
shown here.</P>
<P><TT>IndexClick</TT> calls <TT>SetupIndex</TT> to do the real work. You use the
tag property of the <TT>TMenuItem</TT> that is clicked to 
decide which index to choose:</P>
<PRE><FONT COLOR="#0066FF">switch (dynamic_cast&lt;TComponent *&gt;(Sender)-&gt;Tag)

</FONT></PRE>
<P>This way, you can call the function with a simple one-line command:</P>
<PRE><FONT 
COLOR="#0066FF">SetupIndex(Sender);

</FONT></PRE>
<P>After the index has been set up properly, you can search for the first relevant
record in the database:</P>
<PRE><FONT COLOR="#0066FF">DMod-&gt;NamesTable-&gt;FindNearest(OPENARRAY(TVarRec, 
(&quot;AAAA&quot;)));

</FONT></PRE>
<P>The goal of this line is to skip over all the records that contain blanks in the
field on which you're searching. For example, if you switch to the company index,
you might find 20, 100, or even 5,000 records in 
the table that have no information
in the <TT>Company</TT> field. To skip over these records, you can search for the
first row that begins with the letter A.
<H4><A NAME="Heading24"></A><FONT COLOR="#000077">Searching for Records</FONT></H4>
<P>The 
program also uses the <TT>SetupIndex</TT> method when it is conducting searches.
As I stated previously, you can use three possible menu items to start a search.
The first searches on last names; the second, on first names; and the third, on a
company 
name. I have assigned the same values to the <TT>Tag</TT> fields of these
<TT>TMenuItems</TT> that I did to the <TT>Tag</TT> fields of the <TT>TMenuItems</TT>
concerned with switching indexes. That way, I can set up the index properly with
a simple 
call to <TT>SetupIndex</TT>:</P>
<PRE><FONT COLOR="#0066FF">  AnsiString IndexName, S;

  if (InputQuery(&quot;Search for Name&quot;, &quot;Enter Name: &quot;, S))

  {

    IndexName = DMod-&gt;NamesTable-&gt;IndexName;

    SetupIndex(Sender);

    
DMod-&gt;NamesTable-&gt;FindNearest(OPENARRAY(TVarRec, (S)));

    DMod-&gt;NamesTable-&gt;IndexName = IndexName;

  }

</FONT></PRE>
<P>As you can see, the code also saves the current index so that the current state
of the index can be restored after 
the search:</P>
<PRE><FONT COLOR="#0066FF">  AnsiString IndexName;

  IndexName = DMod-&gt;NamesTable-&gt;IndexName;

  ... // Code omitted here

  DMod-&gt;NamesTable-&gt;IndexName = IndexName;

</FONT></PRE>
<P>The big point to notice here is how 
easily you can take care of these chores by
using the VCL. BCB makes database programming easy, even when you're working with
a fairly complex program.
<H4><A NAME="Heading25"></A><FONT COLOR="#000077">Inserting Data, Canceling Operations</FONT></H4>

<P>Because this database has five tables, you have to devise a technique for specifying
the name of the table on which you want to perform an insertion, deletion, or post.
I use the <TT>TPageControl</TT> to handle these chores. In particular, I assume 
that
if the user is looking at the Address page, then he or she wants to perform an action
on the <TT>kdAdds</TT> table, and if the user is looking at the first page, then
he or she wants to perform an operation on the <TT>kdNames</TT> table, and so 
on:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::Insert1Click(TObject *Sender)

{

  switch (dynamic_cast&lt;TComponent&amp;&gt;(*PageControl1-&gt;ActivePage).Tag)

  {

    case 1:

      DMod-&gt;NamesTable-&gt;Insert();

      break;

    
case 2:

      DMod-&gt;AddressTable-&gt;Insert();

      break;

    case 3:

      DMod-&gt;PhoneTable-&gt;Insert();

      break;

    case 4:

      DMod-&gt;EMailTable-&gt;Insert();

      break;

  }

}

</FONT></PRE>
<P>As you can see, I have 
set the <TT>Tag</TT> field for each of the pages to a unique
value so that I can easily determine the current page:</P>
<PRE><FONT COLOR="#0066FF">switch (dynamic_cast&lt;TComponent&amp;&gt;(*PageControl1-&gt;ActivePage).Tag)

</FONT></PRE>
<P>If the 
user accidentally makes a wrong decision, he or she can undo the most recent
operation on the currently selected table by clicking Cancel:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::Cancel1Click(TObject *Sender)

{

  AnsiString S;

  
switch (dynamic_cast&lt;TComponent&amp;&gt;(*PageControl1-&gt;ActivePage).Tag)

  {

    case 1: DMod-&gt;NamesTable-&gt;Cancel(); break;

    case 2: DMod-&gt;AddressTable-&gt;Cancel(); break;

    case 3: DMod-&gt;PhoneTable-&gt;Cancel(); break;

    
case 4: DMod-&gt;EMailTable-&gt;Cancel(); break;

    default:

      ShowMessage(GetError(1, S));

  }

}

</FONT></PRE>
<P>This system is easy to implement, but it can be a bit confusing to the user when
he or she is looking at the first page, which 
holds information about not only the
<TT>kdNames</TT> table, but also the <TT>kdAdds</TT> and <TT>kdPhone</TT> tables.
The issue here is that the database itself won't be much fun if you have to flip
pages to get even the most basic information about 
a name. To remedy this problem,
I put the Name, Address, and Phone information on the first page but really expect
the user to perform edits on these fields by turning to the Address or Phone page.</P>
<P>To enforce this rule, you can set up an 
options menu that can be turned on and
off:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::SetAddressPhoneClick(TObject *Sender)

{

  SetAddressPhone-&gt;Checked = !SetAddressPhone-&gt;Checked;

  DBEdit6-&gt;ReadOnly = 
SetAddressPhone-&gt;Checked;

  DBEdit17-&gt;ReadOnly = SetAddressPhone-&gt;Checked;

  DBEdit18-&gt;ReadOnly = SetAddressPhone-&gt;Checked;

  DBEdit19-&gt;ReadOnly = SetAddressPhone-&gt;Checked;

  DBEdit20-&gt;ReadOnly = 
SetAddressPhone-&gt;Checked;

  DBEdit8-&gt;ReadOnly = SetAddressPhone-&gt;Checked;

  DBEdit17-&gt;ReadOnly = SetAddressPhone-&gt;Checked;

  DBEdit7-&gt;ReadOnly = SetAddressPhone-&gt;Checked;

}

</FONT></PRE>
<P>If you include this code in the 
program as a response to a <TT>TMenuItem</TT>
called <TT>SetAddressPhone</TT>, then the user can decide whether the views on the
<TT>kdAdds</TT> and <TT>kdPhones</TT> table will be editable. By default, this function
should be turned off, because it 
really is better that the user does not input information
in these fields.</P>
<P>I should perhaps add that the user does not have to understand that he or she
is entering data in separate tables. For example, the user doesn't have to know that
the 
<TT>kdAdds</TT> and <TT>kdPhones</TT> tables exist. All he or she has to understand
is that inputting information about phones is a separate operation from entering
data about addresses or names. This much conceptual background the user must have;

otherwise, he or she will not be able to use the tool at all.</P>
<P>The design of the program helps the user by putting each table on a separate page.
That way, the user can rely on the page metaphor when thinking about the underlying
structure of 
the database. Providing metaphors for the user is a useful way to simplify
the operation of an application.

<DL>
	<DT></DT>
</DL>



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>As you might recall, when I first
	talked 
about the Address2 program, I said that for many users, a simple flat-file
	database is best. When I said that, I was thinking particularly about the kinds of
	problems currently under discussion. Using a relational database takes a certain
	amount of 
conceptual ability that some users may not have the patience to master.
	The actual ideas involved are simple, but many users are still so overwhelmed by
	the very idea of computers that they can't clear their heads sufficiently to grasp
	concepts 
that would be easy for them to assimilate in some other field with which
	they are more familiar. <BR>
	<BR>
	It may sound as though I am being overly polite in this note, but I'm trying to state
	the facts as I see them. Many intelligent people's 
minds really do become inexplicably
	opaque when it comes to thinking about computers. This problem will disappear as
	more and more children grow up using these machines, but for now programmers have
	to think seriously every time they add any level 
of complexity to their programs.
	Programmers will understand relational databases, and so will the small subset of
	users that are targeted by a program of this type; but it is important to understand
	that at this point in history, many users will 
find relational databases perhaps
	too difficult to understand. <BR>
	<BR>
	My point is simply that not everyone is ready to work with relational databases.
	Some people need them, but others will be confused by them. It is good to use relational
	
databases when necessary, but programmers should also be aware that some users might
	not properly understand them. 
<HR>


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -