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

📄 suggest.htm

📁 C++builder学习资料C++builder
💻 HTM
📖 第 1 页 / 共 4 页
字号:
</P> 

<pre>

    Table1<b>-&gt;</b>FieldByName<b>(</b>seq_no<b>)</b><b>-&gt;</b>AsVariant <b>=</b> Table2seq_no<b>-&gt;</b>AsVariant<b>;</b>

    Table1<b>-&gt;</b>FieldByName<b>(</b>date<b>)</b>  <b>-&gt;</b>AsVariant <b>=</b> Table2date  <b>-&gt;</b>AsVariant<b>;</b>

</pre> 

 

 

 

<BR> 

<H3> 

<A NAME="currency">Don't use TCurrencyField unless you have to</A> 

</H3> 

<P> 

<TT>TCurrencyField</TT> is a poorly named class. This field type is used to represent decimal numbers in a database. 

Since the class name is <TT>TCurrencyField</TT>, you might expect that the class uses the <TT>Currency</TT> datatype 

internally. This is not the case. <TT>TCurrencyField</TT> uses floating point numbers internally to store data. 

</P> 

<P> 

Unfortunately, floating point numbers can lead to problems in database applications, particularly when dealing with 

fields that represent dollar amounts. Your dollar amounts may, at times, appear to be off by a penny, and to some 

bankers, this is a big deal! To see what I mean, run a program that displays a <TT>TCurrencyField</TT> in a 

<TT>DBEdit</TT>. Edit the field and set the amount to $12.1351. When the value is written, the half penny causes the 

.1351 to round up to .14. This is correct. Now try to enter 12.1350. Once again, the amount should be rounded up. When 

I test this on the orders database that comes with C++Builder, I see that the amount rounds down to $12.13. This may 

not seem like a big deal, but the problem gets worse when you start connecting to SQL servers, such as MS SQL 

Server and Oracle, where the database's internal format of a number may be different than the BDE's representation of 

the value. 

</P> 

<P> 

The solution to this problem is to switch to the <TT>TBCDField</TT> class whenever possible. <TT>TBCDField</TT> 

represents its data internally using the <TT>Currency</TT> datatype. The <TT>Currency</TT> datatype doesn't suffer 

from the same formatting and rounding problems that plague floating point numbers. You can tell the BDE to use 

<TT>TBCDField</TT>'s by default. To do so, run the BDE administrator, select the configuration tab, find the driver 

that you are using, and set the value of <TT>Enable BCD</TT> to <TT>true</TT>. The <TT>Enable BCD</TT> parameter can 

also be added to the <TT>Params</TT> property of <TT>TDatabase</TT>. 

</P> 

 

 

 

<BR> 

<H3> 

<A NAME="cachedfilter">Don't turn on CachedUpdates when the Filtered property of dataset is on</A> 

</H3> 

<P> 

When you turn on <TT>CachedUpdates</TT>, only the records that are active in the filter are cached. 

If this is the behavior that you expect, then it's no big deal. This presents a problem if you were 

expecting that all records would get cached, regardless of whether they meet the current filter requirements. 

For example: 

</P> 

<pre>

    <font color="navy">// assume that filters and cachedupdates are already off</font>

    <font color="navy">// filter out all records where the stat is not iowa</font>

    Table1<b>-&gt;</b>Filter <b>=</b> <font color="blue">&quot;state = 'IA'&quot;</font><b>;</b>

    Table1<b>-&gt;</b>Filtered <b>=</b> <b>true</b><b>;</b>

    Table1<b>-&gt;</b>CachedUpdates <b>=</b> <b>true</b><b>;</b>

</pre> 

<P> 

In this code example, only records where the state is Iowa will be cached. 

</P> 

 

 

 

<BR> 

<H3> 

<A NAME="updatefilter">Don't call ApplyUpdates when the Filtered property of dataset is on</A> 

</H3> 

<P> 

If you call <TT>ApplyUpdates</TT> on a filtered dataset, only the active records that meed the filter requirements 

will be updated. Records that are hidden because of the filter will not be updated. This isn't so bad if you want 

this behavior. However, you will notice subtle data loss problems if you expect all records to get updated. Inserted 

records that are hidden because of the filter won't be inserted, and modified records that are hidden won't be updated. 

</P> 

<P> 

To ensure that all records get applied, turn off filters while applying the updates. 

</P> 

<pre>

    Query1<b>-&gt;</b>Filtered <b>=</b> <b>false</b><b>;</b>

    Query1<b>-&gt;</b>ApplyUpdates<b>(</b><b>)</b><b>;</b>

    Query1<b>-&gt;</b>CommitUpdates<b>(</b><b>)</b><b>;</b>

    Query1<b>-&gt;</b>Filtered <b>=</b> <b>true</b><b>;</b>

</pre> 

 

 

 

<BR> 

<H3> 

<A NAME="dataaware">Don't use the VCL's data aware controls</A> 

</H3> 

<P> 

The data aware controls that come with C++Builder are extremely inflexible to change. Deriving a new control from 

<TT>TDBEdit</TT> or <TT>TDBCheckBox</TT> doesn't buy you much because just about everything in the base class is 

private. If you want to modify a data aware control, you essentially have to create a new control from scratch and 

duplicate the existing functionality that exists in the VCL class. Duplicating functionality is bad practice. 

</P> 

Take the <TT>TDBCheckBox</TT> class for example. I recently wanted to derive a new control that modified the behavior 

of the default <TT>DBCheckBox</TT>. All I wanted to do was to update the record as soon as the box was checked, 

instead of waiting until the user tabbed to a new control. In order to modify the control, I needed access to the 

<TT>TDataLink</TT> member of the base class. Unfortunately it is declared private, so I could't access it my derived 

class. Because the <TT>TDataLink</TT> and its associated methods were declared private, I could not modify the behavior 

of the control without resorting to hacks (and yes, the <TT>CM_GETDATALINK</TT> message is a hack). 

<P> 

Before you begin a large database project with C++Builder, I suggest that you copy all of the code from DBCTRLS.PAS 

into a new unit. This way, if you need to make a change or derive a new class, you can do so without hacking your way 

into the default VCL controls. If you find that you don't need to modify the default functionality, then you haven't lost 

anything. 

</P> 

<P><B>Note:</B> In order to avoid confusion with the existing VCL controls, I rename my altered controls. For example, 

<TT>TDBEdit</TT> becomes <TT>TXDBEdit</TT>, and <TT>TDBCheckBox</TT> becomes <TT>TXDBCheckBox</TT>. I then add these 

controls to my user package. 

</P> 

 

 

<BR> 

<H3> 

<A NAME="lookupcontrols">Don't use TDBLookupComboBox or TDBLookupListBox</A> 

</H3> 

<P> 

<B>Update:</B> This suggestion applies to BCB3 and BCB4. The bug has been fixed in BCB5. 

</P> 

<P> 

<TT>TDBLookupComboBox</TT> and <TT>TDBLookupListBox</TT> both derive from <TT>TDBLookupControl</TT>. Unfortunately, as 

of version 4.0 of the VCL, <TT>TDBLookupControl</TT> has a nasty bug that can cause access violations whenever a lookup 

object is destroyed. 

</P> 

<P> 

The problem resides in the <TT>Destroy</TT> method of <TT>TDBLookupControl</TT>. 

</P> 

<PRE>

<B>destructor</B> TDBLookupControl.Destroy;

<B>begin</B>

  FListFields.Free;

  FListLink.FDBLookupControl := <B>nil</B>;

  FListLink.Free;

  FDataLink.FDBLookupControl := <B>nil</B>;

  FDataLink.Free;

  inherited Destroy;

<B>end</B>;

</PRE> 

<P> 

<TT>Destroy</TT> is responsible for deleting two datalink objects called <TT>FListLink</TT> and <TT>FDataLink</TT>. Notice 

that neither object is set to <TT>nil</TT> after being deleting. This causes problems in the <TT>Notification</TT> method, 

which gets called when control passes to the base class <TT>Destroy</TT> methods via the <TT>inherited</TT> call. The 

<TT>Notification</TT> method looks like this. 

</P> 

<PRE>

<B>procedure</B> TDBLookupControl.Notification(AComponent: TComponent;

  Operation: TOperation);

<B>begin</B>

  <B>inherited</B> Notification(AComponent, Operation);

  <B>if</B> Operation = opRemove <B>then</B>

  begin

    <B>if</B> (FDataLink &lt;&gt; <B>nil</B>) <B>and</B> (AComponent = DataSource) <B>then</B> DataSource := nil;

    <B>if</B> (FListLink &lt;&gt; <B>nil</B>) <B>and</B> (AComponent = ListSource) <B>then</B> ListSource := nil;

  <B>end</B>;

<B>end</B>;

</PRE> 

<P> 

Observe how the <TT>Notification</TT> method first checks to see if the datalink objects are <TT>nil</TT> before it 

attempts to dereference them. The problem is that the destructor does not set the datalink objects to <TT>nil</TT> 

after it destroys them. This causes the <TT>if</TT> statement to pass even after the datalink objects have been deleted. 

As a result, the <TT>Notification</TT> method makes an assigment to deleted memory, which can cause access violations. 

</P> 

<P> 

To work around this bug, you must either not use the lookup controls, or you will have to find a way to recode 

<TT>TLookupControl::Destroy</TT> so that it sets the datalink objects to <TT>nil</TT> after deleting them. If you 

heeded the advice about <A HREF="#dataaware">not using the built in data aware controls</A>, then you can fix the 

<TT>Destroy</TT> function by changing it as shown below. 

</P> 

<PRE>

<B>destructor</B> TDBLookupControl.Destroy;

<B>begin</B>

  FListFields.Free;

  FListLink.FDBLookupControl := <B>nil</B>;

  FListLink.Free;

  FListLink := <B>nil</B>;



  FDataLink.FDBLookupControl := <B>nil</B>;

  FDataLink.Free;

  FDataLink := <B>nil</B>;



  inherited Destroy;

<B>end</B>;

</PRE> 

<P><B>Note:</B> Borland is aware of this bug.</P> 

 

<BR> 

<H3> 

<A NAME="active">Don't set the Active property of a dataset to true at design time</A> 

</H3> 

In my database projects, I have a large number of <TT>TQuery</TT> controls that function as lookup datasets for 

combo boxes and lookup fields. These controls usually perform a <TT>select *</TT> out of some lookup table. For these 

lookup datasets, it is often convenient to set <TT>Active</TT> to true at design time. However, you should avoid this 

temptation. 

<P> 

Here is the problem that I have witnessed. Whenever I open a data module in the IDE that contains a dataset where 

<TT>Active</TT> is true, the dataset attempts to perform the query against that database. What happens if the server is 

down, or a connection cannot be made to the database? If the dataset cannot be opened, C++Builder silently sets the 

<TT>Active</TT> property of the control back to false. This is bad. No warning is given that the control is being 

modified, and you won't notice the problem until you run your program. Lookup fields will appear to be blank, and 

lookup combo boxes will be empty. 

</P> 

<P> 

To avoid this problem, especially in a large project, make the assignment to <TT>Active</TT> in code. The constructor 

of a data module is a good place to do this. 

</P> 

 

 

 

<BR> 

<H3> 

<A NAME="filteredit">Don't change the Filter property of a dataset while the dataset is in edit mode</A> 

</H3> 

<P> 

When you change the <TT>Filter</TT> or <TT>Filtered</TT> property of a dataset, the VCL calls the 

<TT>CheckBrowseMode</TT> function. This function posts any changes if the dataset was in insert or edit mode. 

This may or may not cause problems in your projects, but it is something to be aware of. Messing with the filter 

of a dataset takes the dataset out of edit mode. 

</P> 

 

 

 

<BR> 

<H3> 

<A NAME="updatestatus">Don't look at the value of UpdateStatus in the OnUpdateRecord handler of a dataset</A> 

</H3> 

<P> 

<TT>UpdateStatus</TT> returns the cached update record status of the current record. When you apply cached updates, 

the VCL loops through and calls your <TT>OnUpdateRecord</TT> handler for each record that was modified, inserted, or 

deleted. However, the VCL does not navigate from record to record as it calls your handler. The record pointer, or 

cursor, does not move. As such, <TT>UpdateStatus</TT> does not change to reflect the status of the record being 

updated. It reflects the status of the current record, and not the record that is being updated. 

</P> 

 

 

  

<H3> 

<A NAME="disablecontrols">Don't call Post after calling DisableControls on a dataset</A> 

</H3> 

<P> 

The dataset classes in BCB provide two methods called <TT>DisableControls</TT> and <TT>EnableControls</TT> that come in 

handy when you have to perform some processing on the dataset. Calling <TT>DisableControls</TT> effectively disconnects the dataset 

from all of its data aware controls. The benefit of doing this is that you can work with a dataset without having the data-aware controls 

refreshing themselves all of the time. 

</P> 

<P> 

There is something you should be aware of though. You never want to call the <TT>Post</TT> method after calling <TT>DisableControls</TT>. 

Why is this? Well, let's say that a user has entered something into a <TT>DBEdit</TT> control. A <TT>DBEdit</tt> control updates its field 

whenever you tab to a new control, or if you call the <TT>Post</TT> method. However, if you call <TT>DisableControls</TT> before 

calling <TT>Post</TT>, the changes that were made in the <TT>DBEdit</TT> control do not get posted to the dataset. 

</P> 

<P> 

<B>Note:</B> This bug will only show up if the <TT>DBEdit</TT> retains the input focus during the entire time that <TT>DisableControls</TT> 

and <TT>Post</TT> are called. Well, how likely is that? More likely than you might think. If you call <TT>Post</TT> from a menu click event, 

or from the <TT>OnClick</TT> event of a toolbar button, then the <TT>DBEdit</TT> will keep the focus. If you call <TT>DisableControls</TT> 

before calling post, then changes that were made to the <TT>DBEdit</tt> control will be lost. 

</P> 

 

 

</TD> </TR> 

 

</TABLE> 

</CENTER> 

</BODY> 

</HTML> 

⌨️ 快捷键说明

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