📄 cb199910hh_f.asp.htm
字号:
One</A> and <A
href="cb199910hh_f.asp.htm#ListingTwo">Listing
Two</A> show the code for this example, which is responsible for three
things. First, it must create the two indexes that we need to sort the
data. Second, it must give users a way to open and close the dataset when
users click the Open and Close buttons. Third, the code must select the
correct index when users click one of the radio buttons. </P>
<P class=BodyText> </P>
<P class=BodyText>The creation of the indexes is handled by a member
function named <I style="mso-bidi-font-style: normal">CreateIndices</I>.
<I>CreateIndices</I> uses the <I>IndexDefs</I> property of ClientDataSet
to create two <I>TIndexDef</I> objects. The first object is the index for
the ItemsTotal field, and the second object is for the PaymentMethod
field: </P>
<P class=BodyText> </P>
<P class=Code><B>void</B> TForm1::CreateIndices()</P>
<P class=Code>{</P>
<P class=Code> TIndexDef
*ItemsIndex=ClientDataSet1->IndexDefs->AddIndexDef();</P>
<P class=Code> ItemsIndex->Name =
"ItemsTotal"; </P>
<P class=Code> ItemsIndex->Fields = "ItemsTotal"; </P>
<P class=Code> </P>
<P class=Code> TIndexDef
*MethodIndex=ClientDataSet1->IndexDefs->AddIndexDef();</P>
<P class=Code> MethodIndex->Name =
"PaymentMethod"; </P>
<P class=Code> MethodIndex->Fields = "PaymentMethod"; </P>
<P class=Code>}</P>
<P class=BodyText> </P>
<P class=BodyText>One of these indexes must be activated when users click
a radio button. The <I style="mso-bidi-font-style: normal">selectIndex</I>
member function performs this task. The function looks at the selected
radio button and activates the corresponding index: </P>
<P class=BodyText> </P>
<P class=Code><B>void</B> TForm1::SelectIndex()</P>
<P class=Code>{</P>
<P class=Code> <B> if</B> (btnItemsTotal->Checked) </P>
<P class=Code> ClientDataSet1->IndexName =
"ItemsTotal"; </P>
<P class=Code> <B> else</B></P>
<P class=Code> ClientDataSet1->IndexName =
"PaymentMethod"; </P>
<P class=Code>}</P>
<P class=BodyText> </P>
<P class=BodyText>When the program starts, the two index objects need to
be created, and the current index for the ClientDataSet must be
initialized. This is done from the constructor of the main form. The
constructor first calls the <I>CreateIndices</I> method, which creates the
index objects. Next, the constructor calls the <I>SelectIndex</I> member
function to initialize the <I>IndexName</I> property of the ClientDataSet:
</P>
<P class=BodyText> </P>
<P class=Code><B>__fastcall</B> TForm1::TForm1(TComponent* Owner) </P>
<P class=Code> : TForm(Owner) </P>
<P class=Code>{</P>
<P class=Code> CreateIndices();</P>
<P class=Code> SelectIndex();</P>
<P class=Code>}</P>
<P class=BodyText> </P>
<P class=BodyText>The last piece of the puzzle is the <I>OnClick</I> event
handlers for the radio buttons. When users click a radio button, the
program must sort the ClientDataSet based on the user's selections. This
is done through the <I>OnClick</I> event handlers for the radio buttons.
The <I>SortingChanged</I> method serves as the <I>OnClick</I> event
handler for both radio buttons. <I
style="mso-bidi-font-style: normal">SortingChanged</I> simply calls the <I
style="mso-bidi-font-style: normal">SelectIndex</I> method to activate the
correct index: </P>
<P class=BodyText> </P>
<P class=Code><B>void</B> <B
style="mso-bidi-font-weight: normal">__fastcall</B>
TForm1::SortingChanged(TObject *Sender) </P>
<P class=Code>{</P>
<P class=Code> SelectIndex();</P>
<P class=Code>}</P>
<P class=BodyText> </P>
<P class=BodyText>Run the program and click the Open button to load the
data. Then click on one of the radio buttons to verify that the data is
sorted according to the radio button that you selected. Toggle the radio
buttons, and watch the data sorting change from one column to the next.
One of our original reasons for using an index to sort the data was to
make sure that users didn't lose their places in the grid when the sorting
order changed. Scroll through the grid and then toggle the radio button.
You should see that the grid doesn't lose its place when you change the
sort field. </P>
<P class=BodyText> </P>
<P class=Subheads>Example Two: Sorting a DBGrid</P>
<P class=BodyText>Example one demonstrated how to sort a dataset without
re-querying the database. Unfortunately, it only allows users to sort on
two of the columns in the dataset. It would be better if users could sort
based on any of the columns in the grid. Furthermore, the sorting should
occur when users click on one of the column headers instead of when they
click on a radio button. </P>
<P class=BodyText> </P>
<P class=BodyText>In this example, we'll expand the first example to allow
sorting by any column in the dataset. To do this, we need to create one
index for every field in the dataset. We'll also nuke the radio buttons.
We'll sort the dataset when users click on the column header of the grid.
</P>
<P class=BodyText> </P>
<P class=BodyText><A
href="cb199910hh_f.asp.htm#ListingThree">Listing
Three</A> and <A
href="cb199910hh_f.asp.htm#ListingFour">Listing
Four</A> shows the code for this example. It's similar to the code for
example one; the main form still has a member function named
<I>CreateIndices</I>. Instead of just creating two <I>IndexDef</I>
objects, however, the new version of <I>CreateIndices</I> loops through
all the fields in the ClientDataSet and creates an <I
style="mso-bidi-font-style: normal">IndexDef</I> object for each of them
(see Figure 7). </P>
<P class=BodyText> </P>
<P class=Code><B>void</B> TForm1::CreateIndices(TClientDataSet *dataset)
</P>
<P class=Code>{</P>
<P class=Code> <I> <SPAN class=CodeBlue>// Loop through the
fields in the dataset and create one</SPAN></I></P>
<P class=Code><I><SPAN class=CodeBlue> // index for each field.
The index name will match the</SPAN></I></P>
<P class=Code><I><SPAN class=CodeBlue> // field name. Note that
we skip</SPAN></I><I> <SPAN class=CodeBlue>calculated fields
because</SPAN></I></P>
<P class=Code><I><SPAN class=CodeBlue> // calculated fields are
not stored in the dataset and</SPAN></I></P>
<P class=Code><I><SPAN class=CodeBlue> // cannot be the basis
of an index. </SPAN></I></P>
<P class=Code> <B> int</B> nFieldCount =
dataset->FieldCount; </P>
<P class=Code> </P>
<P class=Code> <B> for</B> (<B>int</B> j=0; j < nFieldCount;
++j) </P>
<P class=Code> { </P>
<P class=Code> TField *field =
dataset->Fields->Fields[j]; </P>
<P class=Code> AnsiString indexName =
field->FieldName; </P>
<P class=Code><I><SPAN class=CodeBlue> // Make sure
the index name doesn't already exist, and</SPAN></I></P>
<P class=Code><I><SPAN class=CodeBlue> // check if
the field is a calculated field. If the index</SPAN></I></P>
<P class=Code><I><SPAN class=CodeBlue> // doesn't
already exist, and the field isn't a</SPAN></I></P>
<P class=Code><I><SPAN class=CodeBlue> //
calculated field, then create a new index. </SPAN></I></P>
<P class=Code> <B> if </B>(
(dataset->IndexDefs->IndexOf(indexName) == -1) && </P>
<P class=Code>
(field->FieldKind != fkCalculated) ) </P>
<P class=Code> { </P>
<P class=Code> TIndexDef * index =
dataset->IndexDefs->AddIndexDef();</P>
<P
class=Code> index->Name =
indexName; </P>
<P class=Code> index->Fields =
field->FieldName; </P>
<P class=Code> } </P>
<P class=Code> } </P>
<P class=Code>}</P>
<P class=Captions><B>Figure 7:</B> <I>CreateIndices</I> creating an
<I>IndexDef</I> object for ClientDataSet fields. </P>
<P class=BodyText> </P>
<P class=BodyText><I>CreateIndices</I> loops through the <I
style="mso-bidi-font-style: normal">Fields</I> property of
<I>ClientDataSet1</I>. The function creates a <I>TIndexDef</I> object for
each field in the dataset. The name of the index is set to the
<I>FieldName</I> of the field. This naming convention will play a role
when we start working with the grid. </P>
<P class=BodyText> </P>
<P class=BodyText>Note: The <I>CreateIndices</I> function loops through
the fields array before the dataset is opened. For this to work, you must
create persistent fields for the ClientDataSet. If you forget to add
persistent fields to the fields editor, then no fields will exist in the
fields array, and <I>CreateIndices</I> will fail to create the indexes. To
create persistent fields, double-click on the client dataset to bring up
the fields editor. Right-click on the fields editor to bring up a context
menu, and select the Add all fields menu option.</P>
<P class=BodyText> </P>
<P class=BodyText>Notice the second condition in the <B>if</B> statement.
We must ensure we don't attempt to create an index for calculated fields.
Calculated fields aren't stored in the dataset, so there's no way to sort
based on a calculated field. </P>
<P class=BodyText> </P>
<P class=BodyText>Note: ClientDataSet supports a new type of calculated
field called an InternalCalc field. InternalCalc fields are stored with
the dataset, and you can create an index on an InternalCalc field. For
this reason, you should use InternalCalc fields instead of regular
calculated fields whenever you work with ClientDataSet. InternalCalc
fields aren't available in Table and Query components. </P>
<P class=BodyText> </P>
<P class=BodyText>Once the indexes are created, the foundation for sorting
the data is in place. Now all we need is code that sets the
<I>IndexName</I> property of <I>ClientDataSet1</I> based on the grid
column that users click. The best way to handle this task is to create an
<I>OnTitleClick</I> event for the DBGrid control. We set the index based
on the column that users clicked inside this event handler (see Figure 8).
</P>
<P class=BodyText> </P>
<P class=Code><B>void</B> <B
style="mso-bidi-font-weight: normal">__fastcall</B>
TForm1::DBGrid1TitleClick(TColumn *Column) </P>
<P class=Code>{</P>
<P class=Code><I><SPAN class=CodeBlue> // The Column object is
the column the user clicked. </SPAN></I></P>
<P class=Code><I><SPAN class=CodeBlue> // TColumn provides a
Field property that gives us access</SPAN></I></P>
<P class=Code><I><SPAN class=CodeBlue> // to the TField for the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -