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

📄 ch15.htm

📁 好书《C++ Builder高级编程技术》
💻 HTM
📖 第 1 页 / 共 5 页
字号:
#include <typinfo.hpp>

#include <sysutils.hpp>

#pragma hdrstop

#include "Main.h"

#pragma resource "*.dfm"

TForm1 *Form1;

__fastcall 
TForm1::TForm1(TComponent* Owner)

  : TForm(Owner)

{

}

void __fastcall TForm1::ApplyBtnClick(TObject *Sender)

{

  OrdersTable->ApplyUpdates();

}

void __fastcall TForm1::RevertBtnClick(TObject *Sender)

{

  OrdersTable->RevertRecord();


}



void __fastcall TForm1::CancelClick(TObject *Sender)

{

  OrdersTable->CancelUpdates();

}

void __fastcall TForm1::OkBtnClick(TObject *Sender)

{

  Close();

}

void __fastcall TForm1::OrdersTableUpdateError(TDataSet *DataSet,

  
EDatabaseError *E, TUpdateKind UpdateKind, TUpdateAction &UpdateAction)

{

  TTypeInfo TypeInfo;

  AnsiString UpdateKindStr[] = {"Modified", "Inserted", "Deleted"};

  AnsiString S(UpdateKindStr[UpdateKind]);

  S 
+= ": " + E->Message;

  AnsiString Temp = DataSet->Fields[0]->OldValue;

  Temp = + ": " + S;

  ListBox1->Items->Add(Temp);

  UpdateAction = uaSkip;

}

void __fastcall TForm1::DataSource1DataChange(TObject 
*Sender,

  TField *Field)

{

  AnsiString UpdateStat[] = {"Unmodified", "Modified", "Inserted", "usDeleted"};

  Panel1->Caption = UpdateStat[OrdersTable->UpdateStatus()];

  if 
(OrdersTable->UpdateStatus() == usModified)

  {

    Edit1->Text = OrdersTable->Fields[0]->OldValue;

    Edit2->Text = OrdersTable->Fields[0]->NewValue;

  }

  else

  {

    Edit1->Text = "Unmodified";

    
Edit2->Text = "Unmodified";

  };

}

void __fastcall TForm1::ListBox1DblClick(TObject *Sender)

{

  AnsiString S(ListBox1->Items->Strings[ListBox1->ItemIndex]);

  if (S.Length() > 0)

    ShowMessage(S);

}

</FONT></PRE>

<P>The first thing to notice about the CachedUpdates program is that it tracks which
records have been modified. For example, change the <TT>OrderNo</TT> field of the
first two records to the values <TT>1</TT> and <TT>2</TT>. If you now select one
of 
these records, you will see that the small panel in the lower left corner of the
screen gets set to <TT>Modified</TT>. This means that the update status for this
field has been set to modified.</P>
<P>Here is the <TT>TUpdateStatus</TT> type:</P>

<PRE><FONT COLOR="#0066FF">TUpdateStatus = (usUnmodified, usModified, usInserted, usDeleted);

</FONT></PRE>
<P>Any particular record in a database is going to be set to one of these values.</P>
<P>Here is the code that sets the value in the 
<TT>TPanel</TT> object:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::DataSource1DataChange(TObject *Sender,

  TField *Field)

{

  AnsiString UpdateStat[] = {&quot;Unmodified&quot;, &quot;Modified&quot;, &quot;Inserted&quot;, 
&quot;usDeleted&quot;};

  Panel1-&gt;Caption = UpdateStat[OrdersTable-&gt;UpdateStatus()];

  if (OrdersTable-&gt;UpdateStatus() == usModified)

  {

    Edit1-&gt;Text = OrdersTable-&gt;Fields[0]-&gt;OldValue;

    Edit2-&gt;Text = 
OrdersTable-&gt;Fields[0]-&gt;NewValue;

  }

  else

  {

    Edit1-&gt;Text = &quot;Unmodified&quot;;

    Edit2-&gt;Text = &quot;Unmodified&quot;;

  };

}

</FONT></PRE>
<P>The relevant line in this case is the second in the body of the function. 
In particular,
notice that it reports on the value of <TT>OrdersTable-&gt;UpdateStatus</TT>. This
value will change to reflect the update status of the currently selected record.

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



<BLOCKQUOTE>
	<P>
<HR>
<FONT 
COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Notice that in this case, I explicitly
	type out the names associated with the elements of the <TT>TUpdateStatus</TT> type.
	In some cases, you can use an alternative means to accomplish the same end without
	
explicitly typing out the strings. This second technique involves using the advanced
	RTTI supported by BCB. To show this value to the user, the code could call the <TT>GetEnumName</TT>
	routine from the <TT>TypInfo</TT> unit. This routine retrieves 
the name of an enumerated
	value. To use this routine, pass in the type that you want to examine, as well as
	the ordinal value of the element in that type whose name you want to see:</P>
	<PRE><FONT COLOR="#0066FF">PPropInfo PropInfo =

  
GetPropInfo(PTypeInfo(ClassInfo(__classid(TForm1))), &quot;Borderstyle&quot;);

ShowMessage(GetEnumName(PropInfo-&gt;PropType, int(bsDisabled)));</FONT></PRE>
	<P>Unfortunately, this type of code will work only for VCL-style classes and for
	
properties of VCL-style classes. Because a <TT>TDataSet</TT> does not have an <TT>UpdateStatus</TT>
	property, the code in the <TT>DataSource1DataChange</TT> method must use the more
	pedantic method outlined previously. 
<HR>


</BLOCKQUOTE>

<P>At 
the same time that the CachedUpdates program reports that a record has been
modified, it also reports on the old and new value of the <TT>OrderNo</TT> field
for that record. In particular, if you change the first record's <TT>OrderNo</TT>
field to 
<TT>1</TT>, it will report that the old value for the field was <TT>1003</TT>,
and the new value is <TT>1</TT>. (This assumes that you have the original data as
it shipped with BCB. Remember that if you end up ruining one of these tables performing

these kinds of experiments, you can always copy the table over again from the CD.)</P>
<P>In the code that reports on the old and new value of the <TT>OrderNo</TT> field,
you should examine these lines in particular:</P>
<PRE><FONT 
COLOR="#0066FF">Edit1-&gt;Text = OrdersTable-&gt;Fields[0]-&gt;OldValue;

Edit2-&gt;Text = OrdersTable-&gt;Fields[0]-&gt;NewValue;

</FONT></PRE>
<P>As you can see, this information is easy enough to come by--you just have to know
where to look.</P>

<P>If you enter the values <TT>1</TT> and <TT>2</TT> into the <TT>OrderNo</TT> fields
for the first two records, you will encounter errors when you try to commit the data.
In particular, if you try to apply the data, the built-in referential integrity 
will
complain that there is no way to link the <TT>Orders</TT> and <TT>Items</TT> table
on the new <TT>OrderNo</TT> you have created. As a result, committing the records
is not possible. The code then rolls back the erroneous records to their original

state.</P>
<P>When viewing these kinds of errors, choose Options | Environment | Preferences
and then turn off the Break on Exception option. The issue here is that you want
the exception to occur, but you don't want to be taken to the line in your 
program
where the exception surfaced. You don't need to view the actual source code because
these exceptions are not the result of errors in your code. In fact, these exceptions
are of the kind you want and need to produce and which appear to the user 
in an orderly
fashion via the program's list box.

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



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Referential integrity is a means
	of enforcing the rules in a database. This subject is discussed in 
some detail in
	Chapter 16 and also in Chapter 12, &quot;Understanding Relational Databases.&quot;
	For now, you should not be concerned with the details of how referential integrity
	works. The key point is simply that some tables have to obey rules, 
and the BDE will
	not let users enter invalid data that violates these rules. 
<HR>


</BLOCKQUOTE>

<P>Here is the code that reports on the errors in the <TT>OrderNo</TT> field and
rolls back the data to its original state:</P>
<PRE><FONT 
COLOR="#0066FF">void __fastcall TForm1::OrdersTableUpdateError(TDataSet *DataSet,

  EDatabaseError *E, TUpdateKind UpdateKind, TUpdateAction &amp;UpdateAction)

{

  TTypeInfo TypeInfo;

  AnsiString UpdateKindStr[] = {&quot;Modified&quot;, 
&quot;Inserted&quot;, &quot;Deleted&quot;};

  AnsiString S(UpdateKindStr[UpdateKind]);

  S += &quot;: &quot; + E-&gt;Message;

  AnsiString Temp = DataSet-&gt;Fields[0]-&gt;OldValue;

  Temp = + &quot;: &quot; + S;

  
ListBox1-&gt;Items-&gt;Add(Temp);

  UpdateAction = uaSkip;

}

</FONT></PRE>
<P>This particular routine is an event handler for the <TT>OnUpdateError</TT> event
for the <TT>Table1</TT> object. To create the routine, click once on the <TT>Table1</TT>

object, select its Events page in the Object Inspector, and then double-click the
<TT>OnUpdateError</TT> entry.</P>
<P>The <TT>OrdersTableUpdateError</TT> method will get called only if an error occurs
in attempting to update records. It will get 
called at the time the error is detected
and before BCB tries to commit the next record.</P>
<P><TT>OrdersTableUpdateError</TT> gets passed four parameters. The most important
is the last, which is a <TT>var</TT> parameter. You can set this parameter 
to one
of the following values:</P>
<PRE><FONT COLOR="#0066FF">TUpdateAction = (uaAbort, uaSkip, uaRetry, uaApplied);

</FONT></PRE>
<P>If you set the <TT>UpdateAction</TT> variable to <TT>uaAbort</TT>, the entire
attempt to commit the updated data 
will be aborted. None of your changes will take
place, and you will return to edit mode as if you had never attempted to commit the
data. The changes you have made so far will not be undone, but neither will they
be committed. You are aborting the 
attempt to commit the data, but you are not rolling
it back to its previous state.</P>
<P>If you choose <TT>uaSkip</TT>, the data for the whole table will still be committed,

⌨️ 快捷键说明

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