home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 31 / CDASC_31_1996_juillet_aout.iso / vrac / del2faq.zip / ALLFAQ.ZIP / DELSEC06.FAQ < prev    next >
Text File  |  1996-02-07  |  25KB  |  600 lines

  1. SECTION 6 - Delphi Database
  2.  
  3. This document contains information that is most often provided  to users of this
  4. section.  There is a listing of common Technical Information Documents that can
  5. be downloaded from the libraries, and a listing of the ***thirty two*** most
  6. frequently asked questions and their answers.
  7.  
  8. NOTE: The term dataset is used to reference TTable, TQuery, or TStoredProc
  9.       components.
  10.  
  11. --------------------------------------------------------------------------------
  12. Q:   "Where can I obtain help on ReportSmith, InterBase and SQL Links/ODBC
  13.      connectivity?"
  14.  
  15. A:   Go to Borland's Development Tools forum (BDEVTOOLS).  There are sections
  16.      for ReportSmith, InterBase, Borland Database Engine, SQL Links/ODBC
  17.      connectivity, etc.
  18.  
  19. --------------------------------------------------------------------------------
  20. Q:   "I have a TQuery and TDataSource.  In the TStrings property of the TQueryI
  21.      have 'SELECT * FROM dbo.AnyTable' where dbo is the database on my SQL
  22.      server.  When I set active to TRUE I get the error: 'Token not found.
  23.      Token :dbo. line number:1'.  What's wrong?"
  24.  
  25. A:   If the RequestLive property is set to true, then enclose the owner and
  26.      tables names in quotes:
  27.  
  28.      Ex:
  29.      SELECT * FROM "dbo.table"
  30.  
  31.      If request live is false, don't use quotes.
  32.  
  33.      Ex:
  34.      SELECT * FROM dbo.table
  35.  
  36. --------------------------------------------------------------------------------
  37. Q:   "When I try to run a database application from Delphi, I get an exception
  38.      EDatabaseError with message 'An error occurred while attempting to
  39.      initialize the Borland Database Engine (Error $2C09)'."
  40.  
  41. A:   Add SHARE.EXE to your AUTOEXEC.BAT file or add DEVICE=VSHARE.386 to the
  42.      [386Enh] section of your SYSTEM.INI file and reboot.
  43.  
  44. --------------------------------------------------------------------------------
  45. Q:   "I have Quattro Pro 6.0 and IDAPI is on the network.  After I've installed
  46.      Delphi and the new IDAPI over the network IDAPI and run Quattro Pro from
  47.      another machine, I get an error that it could not load Language Driver."
  48.  
  49. A:   Add [Borland Language Drivers] section to the WIN.INI file to point to the
  50.      IDAPI/LANGDRV directory.  For example:
  51.  
  52.      [Borland Language Drivers]
  53.      LDPATH=C:\IDAPI\LANGDRV
  54.  
  55. --------------------------------------------------------------------------------
  56. Q:   "What does IDAPI error $2C08 mean?"
  57.  
  58. A:   "Cannot load IDAPI01.DLL". Make sure you have in your WIN.INI file the
  59.      following section with DLLPATH pointing to the correct location:
  60.  
  61.      [IDAPI]
  62.      DLLPATH=C:\IDAPI
  63.      CONFIGFILE01=C:\IDAPI\IDAPI.CFG
  64.  
  65. --------------------------------------------------------------------------------
  66. Q:   "Why do I get 'Index out of range' when use tTable.FindNearest and
  67.      tTable.FindKey on a dBASE table with an expression index?"
  68.  
  69. A:   FindKey and FindNearest are not meant to work with dBASE expression indexes.
  70.      Use the tTable's GoToKey and GotoNearest which will work fine with dBASE
  71.      expression indexes.
  72.  
  73. --------------------------------------------------------------------------------
  74. Q:   "What is the equivalent in Delphi of Paradox's TCursor?"
  75.  
  76. A:   The TTable component.
  77.  
  78. --------------------------------------------------------------------------------
  79. Q:   "How to I create a Paradox table with an Auto Increment type field
  80.      programatically?  I'm using TTable.CreateTable, but TFieldType doesn't
  81.      include this type."
  82.  
  83. A:   Use a TQuery and SQL CREATE TABLE statement.  For example:
  84.  
  85.      procedure TForm1.Button1Click(Sender: TObject);
  86.      begin
  87.        with Query1 do
  88.          begin
  89.            DatabaseName := 'DBDemos';
  90.            with SQL do
  91.              begin
  92.              Clear;
  93.              Add('CREATE TABLE "PDoxTbl.db" (ID AUTOINC,');
  94.              Add('Name CHAR(255),');
  95.              Add('PRIMARY KEY(ID))');
  96.              ExecSQL;
  97.              Clear;
  98.              Add('CREATE INDEX ByName ON "PDoxTbl.db" (Name)');
  99.              ExecSQL;
  100.            end;
  101.          end;
  102.      end;
  103.  
  104. --------------------------------------------------------------------------------
  105. Q:   "How do you tell which record and which field of a TDBGrid is current?"
  106.  
  107. A:   Here is a method to keep track of the current column and row. The following
  108.      code in the method MyDBGridDrawDataCell updates the variables Col and Row
  109.      (which must not be local to the method) every time the grid is redrawn.
  110.      Using this code you can assume that Col and Row point to the current column
  111.      and row respectively.
  112.  
  113.        var
  114.          Col, Row: Integer;
  115.  
  116.        procedure TForm1.MyDBGridDrawDataCell(Sender: TObject; const Rect: TRect;
  117.          Field: TField; State: TGridDrawState);
  118.        var
  119.          RowHeight: Integer;
  120.        begin
  121.          if gdFocused in State then
  122.          begin
  123.            RowHeight := Rect.Bottom - Rect.Top;
  124.            Row := (Rect.Top div RowHeight) - 1;
  125.            Col := Field.Index;
  126.          end;
  127.        end;
  128.  
  129. --------------------------------------------------------------------------------
  130. Q:   "How do I highlight the current row in a TDBGrid?"
  131.  
  132. A:   In the TDBGrid's Options property enable the dgRowSelect item.
  133.  
  134. --------------------------------------------------------------------------------
  135. Q:   "How to I create a mask for a TDBEdit control?"
  136.  
  137. A:   Edit masks are applied to the fields in the table (TField components) and
  138.      not in the data controls themselves.  Double-click on the TTable icon and
  139.      add all the fields you want from your table.  When a field is highlighted,
  140.      its properties show up in the object inspector, including an edit mask.
  141.      Linking the TDBEdit and any of the other data controls to this dataset will
  142.      follow the edit mask rules for the fields set this way.
  143.  
  144. --------------------------------------------------------------------------------
  145. Q:   "Is there a simple way to catch exceptions in the control's events?"
  146.  
  147. A:   Create a method of the form to trap for exceptions.  This method will be
  148.      called on the OnException method of the application.  In your method, check
  149.      for the exception you're looking for, ie EDatabaseError.  Check the on-line
  150.      help for the OnException event.  It has info on how to call your own method
  151.      for the event.  For example:
  152.  
  153.      Procedure TForm1.MyExcept(Sender:TObject; E:Exception);
  154.      {Don't forget to do a forward declaration for this in the class definition}
  155.      begin
  156.        If E is EDatabaseError then
  157.          MessageDlg('Trapped exception', mtInformation, [mbOk], 0)
  158.        else
  159.          { it's not the error you're looking for, raise it }
  160.      end;
  161.  
  162.      procedure TForm1.FormCreate(Sender: TObject);
  163.      begin
  164.         Application.OnException := MyExcept;
  165.         { Here is how you assign the OnException event to your handler }
  166.      end;
  167.  
  168. --------------------------------------------------------------------------------
  169. Q:   "What versions of Informix (Online, I-NET) do the SQL Links support?"
  170.  
  171. A:   SQL Links will work fine with all versions of the Informix sever, including
  172.      5.01.  However, we are not compatible with the new version of their client
  173.      (5.0 Windows based client).  You should use the 4.2 (DOS based) client.
  174.  
  175. --------------------------------------------------------------------------------
  176. Q:   "What is the definition of 'IDAPI'? What is 'SQL Links'?"
  177.  
  178. A:   IDAPI is the Integrated Database Application Program Interface.  BDE is a
  179.      way to access multiple data sources with a consistent API.  IDAPI is just
  180.      the API for the BDE. It includes all the functions necessary to access,
  181.      manipulate, etc. the datA:  Delphi, dBASE for Windows, and Paradox for
  182.      Windows use these functions to access datA:  You can use them yourself in
  183.      your programs.  You get the docs if you purchase the BDE.  It lists all the
  184.      available functions and what they do.  If you look at the Delphi source you
  185.      will see these functions used. They are prefaced with "Dbi" (e.g.
  186.      DbiCreateTable).
  187.  
  188.      SQL Links is a collection of native drivers that enable you to connect to
  189.      remote database servers.
  190.  
  191. --------------------------------------------------------------------------------
  192. Q:   "Is IDAPI necessary for data access in Delphi?  Can you 'bundle' IDAPI
  193.      inside of a Delphi EXE so that your distributed application does not need
  194.      to install IDAPI on your user's computers?"
  195.  
  196. A:   IDAPI is necessary for data access in Delphi.  Delphi comes with the BDE
  197.      redistributable diskette that installs IDAPI.
  198.  
  199. --------------------------------------------------------------------------------
  200. Q:   "How do I change the color of a grid cell in a TDBGrid?"
  201.  
  202. A:   Enter the following code in the TDBGrid's OnDrawDataCell event:
  203.  
  204.      Procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
  205.        Field: TField; State: TGridDrawState);
  206.      begin
  207.         If gdFocused in State then
  208.            with (Sender as TDBGrid).Canvas do
  209.            begin
  210.               Brush.Color := clRed;
  211.               FillRect(Rect);
  212.               TextOut(Rect.Left, Rect.Top, Field.AsString);
  213.            end;
  214.      end;
  215.  
  216.      Set the Default drawing to true.  With this, it only has to draw the
  217.      highlighted cell.  If you set DefaultDrawing to false, you must draw all
  218.      the cells yourself with the canvas properties.
  219.  
  220. --------------------------------------------------------------------------------
  221. Q:   "How do I get the password diaglog box to suppress when I open a password
  222.      protected table?"
  223.  
  224. A:   Simply supply the Session object with the password you want to add before
  225.      you open the table:
  226.  
  227.      Session.AddPassword ('PASSWORD');
  228.  
  229.      Once you close the table, you can remove the password with
  230.      RemovePassword('PASSWORD'), or you can remove all current passwords with
  231.      RemoveAllPasswords.  (Note: This is for Paradox tables only)
  232.  
  233. --------------------------------------------------------------------------------
  234. Q:   "Where do I find a listing and description of the BDE functions and data
  235.      types?"
  236.  
  237. A:   DBIPROCS.INT in your DELPHI\DOC\ directory contains a listing of BDE
  238.      functions, expected parameters, return values and a brief description of
  239.      each.  DBITYPES.INT is a listing of types used with BDE functions.  For any
  240.      BDE function call add the following units to your USES clause:  DBITYPES,
  241.      DBIPROCS and DBIERRS.  For more detailed information on the use of the
  242.      IDAPI functions, obtain the Database Engine User's guide from Customer
  243.      Service.
  244.  
  245. --------------------------------------------------------------------------------
  246. Q:   "Is there a BDE API or a DLL available for rebuilding crashed indexes (like
  247.      the TUTILITY.EXE shipped with Pdoxwin)?"
  248.  
  249. A:   The BDE includes a function to rebuild indexes, called DbiRegenIndexes().
  250.  
  251.      Add the following units to your USES clause: DBITYPES, DBIPROCS and
  252.      DBIERRS.  Then call the BDE function as follows:
  253.  
  254.      DBIRegenIndexes(Table1.Handle);
  255.  
  256.      Note: The table must be opened in exclusive mode and the index must already
  257.      exist.
  258.  
  259. --------------------------------------------------------------------------------
  260. Q:   "Is there a BDE API or a DLL available to pack a dBASE table?"
  261.  
  262. A:   The BDE includes a function to pack dBASE tables, called DbiPackTable().
  263.  
  264.      Add the following units to your USES clause: DBITYPES, DBIPROCS and
  265.      DBIERRS.  Then call the BDE function as follows:
  266.  
  267.      DBIPackTable(Table1.DbHandle, Table1.Handle, 'TABLENAME.DBF', szDBASE,
  268.        TRUE);
  269.  
  270.      Note: The table must be opened in exclusive mode.
  271.  
  272. --------------------------------------------------------------------------------
  273. Q:   "Is there a programmatic way to add an alias to the IDAPI.CFG file?"
  274.  
  275. A:   The BDE includes a function called DbiAddAlias().  The Specifications are
  276.      available in the Section 6 (Database) library, file AddAlias.txt.  Also, a
  277.      Delphi component called AliasManager is available in the Section 6
  278.      (Database) library.  This allows you to create, delete and modify aliases.
  279.  
  280. --------------------------------------------------------------------------------
  281. Q:   "How can I view dBASE records marked for deletion?"
  282.  
  283. A:   Call the following function on the AfterOpen event of the table. You Must
  284.      include DBITYPES, DBIERRS, DBIPROCS in the uses clause.  To call, send as
  285.      arguments name of TTable and TRUE/FALSE depending to show/not show deleted
  286.      records. Ex:
  287.  
  288.      procedure TForm1.Table1AfterOpen(DataSet: TDataset);
  289.      begin
  290.        SetDelete(Table1, TRUE);
  291.      end;
  292.  
  293.      procedure SetDelete(oTable:TTable; Value: Boolean);
  294.      var
  295.        rslt: DBIResult;
  296.        szErrMsg: DBIMSG;
  297.      begin
  298.        try
  299.          oTable.DisableControls;
  300.            try
  301.              rslt := DbiSetProp(hDBIObj(oTable.Handle), curSOFTDELETEON,
  302.              LongInt(Value));
  303.              if rslt <> DBIERR_NONE then
  304.                begin
  305.                  DbiGetErrorString(rslt, szErrMsg);
  306.                  raise Exception.Create(StrPas(szErrMsg));
  307.                end;
  308.            except
  309.              on E: EDBEngineError do ShowMessage(E.Message);
  310.              on E: Exception do ShowMessage(E.Message);
  311.            end;
  312.        finally
  313.          oTable.Refresh;
  314.          oTable.EnableControls;
  315.        end;
  316.      end;
  317.  
  318. --------------------------------------------------------------------------------
  319. Q:   "How can I create a column in the grid to which records in a dBASE table
  320.      are marked for deletion?"
  321.  
  322. A:   Create a calculated field, then for the OnCalcField event of the table
  323.      replace the calculated field you've created like so:
  324.  
  325.      procedure TForm1.Table1CalcFields(DataSet: TDataset);
  326.      var
  327.        RCProps : RecProps;
  328.        Result : DBIResult;
  329.      begin
  330.        Result := DbiGetRecord(Table1.Handle, dbiNoLock, Nil, @RCProps);
  331.        If RCProps.bDeleteFlag then Table1Del.Value := 'X' else
  332.        Table1Del.Value := '';
  333.      end;
  334.  
  335.      Note: You must first call the SetDelete(TTable,TRUE) function from the
  336.      previous FAQ:
  337.  
  338. --------------------------------------------------------------------------------
  339. Q:   "How can I determine the actual size of a blob field as stored in
  340.      the table?"
  341.  
  342. A:   Here is a function GetBlobSize that returns the size of a given blob, memo,
  343.      or graphic field.  An example of calling it follows.
  344.  
  345.      Function GetBlobSize(Field: TBlobField): Longint;
  346.      begin
  347.        with TBlobStream.Create(Field, bmRead) do
  348.        try
  349.          Result := Seek(0, 2);
  350.        finally
  351.          Free;
  352.        end;
  353.      end;
  354.  
  355.      procedure TForm1.Button1Click(Sender: TObject);
  356.      begin
  357.        { This sets the Edit1 edit box to display the size of }
  358.        { a memo field named Notes.                           }
  359.        Edit1.Text := IntToStr(GetBlobSize(Notes));
  360.      end;
  361.  
  362. --------------------------------------------------------------------------------
  363. Q:  "How do I show the contents of a memo field in a DBGrid?"
  364.  
  365. A:  Use the following code for the OnDrawDataCell event of the DBGrid.  Note:
  366.     before running create a TMemoField object for the memo field by double
  367.     clicking on the TTable component and adding the memo field.
  368.  
  369.     procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
  370.       Field: TField; State: TGridDrawState);
  371.     var
  372.       P  : array [0..50] of char;   {array size is number of characters needed}
  373.       BS : tBlobStream;             {from the memo field}
  374.       S  : String;
  375.     begin
  376.       If Field is TMemoField then begin
  377.       with (Sender as TDBGrid).Canvas do
  378.         begin
  379.           {Table1Notes is the TMemoField}
  380.           BS := tBlobStream.Create(Table1Notes, bmRead);
  381.           FillChar(P,SizeOf(P),#0);               {terminate the null string}
  382.           BS.Read(P, 50);           {read 50 chars from memo into blobStream}
  383.           BS.Free;
  384.           S := StrPas(P);
  385.           while Pos(#13, S) > 0 do              {remove carriage returns and}
  386.             S[Pos(#13, S)] := ' ';              {line feeds}
  387.           While Pos(#10, S) > 0 do
  388.             S[Pos(#10, S)] := ' ';
  389.           FillRect(Rect);                          {clear the cell}
  390.           TextOut(Rect.Left, Rect.Top, S);         {fill cell with memo data}
  391.         end;
  392.       end;
  393.     end;
  394.  
  395. --------------------------------------------------------------------------------
  396. Q:  "Is there a way to use the return key for data entry, instead of tab or the
  397.     mouse?"
  398.  
  399. A:  Use this code for an Edit's OnKeyPress event.
  400.  
  401.     procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
  402.     begin
  403.       If Key = #13 Then
  404.       Begin
  405.         SelectNext(Sender as tWinControl, True, True );
  406.         Key := #0;
  407.       end;
  408.     end;
  409.  
  410.     This causes Enter to behave like tab.  Now, select all controls on the form
  411.     you'd like to exhibit this behavior (not Buttons) and go to the Object
  412.     Inspector and set their OnKeyPress handler to EditKeyPress.  Now, each
  413.     control you selected will process Enter as Tab.  If you'd like to handle
  414.     this at the form (as opposed to control) level, reset all the controls
  415.     OnKeyPress properties to blank, and set the _form_'s OnKeyPress property to
  416.     EditKeyPress.  Then, change Sender to ActiveControl and set the form's
  417.     KeyPreview property to true:
  418.  
  419.     procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
  420.     begin
  421.       If Key = #13 Then
  422.       begin
  423.         SelectNext(ActiveControl as tWinControl, True, True );
  424.         Key := #0;
  425.       end;
  426.     end;
  427.  
  428.     This will cause each control on the form (that can) to process Enter as Tab.
  429.  
  430. --------------------------------------------------------------------------------
  431. Q:  "How do I do a locate on a non-indexed field?"
  432.  
  433. A:  The following function can be added to your to your unit and called as
  434.     follows:
  435.  
  436.       Locate(Table1, Table1LName, 'Beman');
  437.  
  438.     Table1 is your table component, Table1LName is TField you've add with the
  439.     fields editor (double click on the table component) and 'Beman' is the name
  440.     you want to find.
  441.  
  442.     (* Locate will find SValue in a non-indexed table *)
  443.     Function Locate( const oTable: TTable; const oField: TField;
  444.     const sValue: String): Boolean;
  445.     var
  446.       bmPos  : TBookMark;
  447.       bFound : Boolean;
  448.     begin
  449.       Locate := False;
  450.       bFound := False;
  451.       If not oTable.Active then Exit;
  452.       If oTable.FieldDefs.IndexOf( oField.FieldName ) < 0 then Exit;
  453.       bmPos := oTable.GetBookMark;
  454.       With oTable do
  455.       begin
  456.         DisableControls;
  457.         First;
  458.         While not EOF do
  459.           if oField.AsString = sValue then
  460.           begin
  461.             Locate := True;
  462.             bFound := True;
  463.             Break;
  464.           end
  465.           else Next;
  466.       end ;
  467.       If (Not bFound) then oTable.GotoBookMark( bmPos);
  468.       oTable.FreeBookMark( bmPos );
  469.       oTable.EnableControls;
  470.     end;
  471.  
  472. --------------------------------------------------------------------------------
  473. Q:  "Why can't I use the ixUnique option when creating indexes for Paradox
  474.     tables with the AddIndex method of the TTable component?"
  475.  
  476. A:  The index options used in the AddIndex method of the TTable component are
  477.     table specific.  For example, the ixUnique option works with dBASE tables
  478.     but not Paradox.  The following table shows how these options apply to dBASE
  479.     and Paradox tables.
  480.  
  481.     Index Options         dBASE    Paradox
  482.     ---------------------------------------
  483.     ixUnique               *
  484.     ixDescending           *        *
  485.     ixNonMaintained        *        *
  486.     ixPrimary                       *
  487.     ixCaseInsensitive               *
  488.  
  489. --------------------------------------------------------------------------------
  490. Q:  "How can I determine the current record number for a dataset?"
  491.  
  492. A:  If the dataset is based upon a Paradox or dBASE table then the record number
  493.     can be determined with a couple of calls to the BDE (as shown below).  The
  494.     BDE doesn't support record numbering for datasets based upon SQL tables, so
  495.     if your server supports record numbering you will need to refer to its
  496.     documentation.
  497.  
  498.     The following function takes as its parameter any component derived from
  499.     TDataset (i.e. TTable, TQuery, TStoredProc) and returns the current record
  500.     number (greater than zero) if it is a Paradox or dBASE table.  Otherwise,
  501.     the function returns zero.
  502.  
  503.     NOTE: for dBASE tables the record number returned is always the physical
  504.     record number.  So, if your dataset is a TQuery or you have a range set
  505.     on your dataset then the number returned won't necessarily be relative to
  506.     the dataset being viewed, rather it will be based on the record's physical
  507.     position in the underlying dBASE table.
  508.  
  509.  
  510.     uses DbiProcs, DbiTypes, DBConsts;
  511.  
  512.     function RecordNumber(Dataset: TDataset): Longint;
  513.     var
  514.       CursorProps: CurProps;
  515.       RecordProps: RECProps;
  516.     begin
  517.       { Return 0 if dataset is not Paradox or dBASE }
  518.       Result := 0;
  519.  
  520.       with Dataset do
  521.       begin
  522.         { Is the dataset active? }
  523.         if State = dsInactive then DBError(SDataSetClosed);
  524.  
  525.         { We need to make this call to grab the cursor's iSeqNums }
  526.         Check(DbiGetCursorProps(Handle, CursorProps));
  527.  
  528.         { Synchronize the BDE cursor with the Dataset's cursor }
  529.         UpdateCursorPos;
  530.  
  531.         { Fill RecordProps with the current record's properties }
  532.         Check(DbiGetRecord(Handle, dbiNOLOCK, nil, @RecordProps));
  533.  
  534.         { What kind of dataset are we looking at? }
  535.         case CursorProps.iSeqNums of
  536.           0: Result := RecordProps.iPhyRecNum;  { dBASE   }
  537.           1: Result := RecordProps.iSeqNum;     { Paradox }
  538.         end;
  539.       end;
  540.     end;
  541.  
  542. --------------------------------------------------------------------------------
  543. Q:  "I am getting a DBEngine message when editing a record that says "Multiple
  544.     records found but only one expected".  What does this mean?
  545.  
  546. A:  You may need to create a unique index on the table so that each row can be
  547.     uniquely identified.  That *may* first require altering the table and adding
  548.     a column to be populated with unique values.
  549.  
  550. --------------------------------------------------------------------------------
  551. Q:  "I have had no success getting at Microsoft Access data using Delphi other
  552.     than a simple TTable view.  Using TQuery I can get a read-only view to work,
  553.     but I cannot a read/write view to work.  After the login screen I am
  554.     presented with an exception message like 'Passthrough SQL connection must
  555.     be shared'."
  556.  
  557. A:  Use the Database Engine Configuration to change the 'SQLPASSTHRU MODE'
  558.     option in the alias associated with your Access database from its default
  559.     blank value to 'SHARED AUTOCOMMIT' (without the quotes).
  560.  
  561. --------------------------------------------------------------------------------
  562. Q:  "How can I determine when the current record in a dataset has changed?"
  563.  
  564. A:  Check the DataSource's State property in the OnDataChanged event.  The State
  565.     property will be set to dsBrowse if the record position has changed.  The
  566.     following example will display a message box every time the record position
  567.     has changed in MyDataSource:
  568.  
  569.     procedure TMyForm.MyDataSourceDataChange(Sender: TObject; Field: TField);
  570.     begin
  571.       if (Sender as TDataSource).State = dsBrowse then
  572.         ShowMessage('Record Position Changed');
  573.     end;
  574.  
  575. --------------------------------------------------------------------------------
  576. Q:  Why is it that when I create a table using the TTable component's
  577.     CreateTable method it creates the fields correctly but does not create
  578.     the indexes even though I do a
  579.  
  580.     NewTable.IndexDefs.Assign(Table1.IndexDefs)?
  581.  
  582. A:  This is the correct way to transfer the index definition to NewTable,
  583.     however, the IndexDefs property of Table1 may not be up-to-date so
  584.     you need to call the Update method of Table1's IndexDefs property prior
  585.     to its assignment to NewTable like this example shows:
  586.  
  587.     with NewTable do begin
  588.       Active := False;
  589.       DatabaseName := 'DBDEMOS';
  590.       TableName := 'Temp';
  591.       TableType := ttParadox;
  592.       FieldDefs.Assign(Table1.FieldDefs);
  593.       Table1.IndexDefs.Update;             { Do an update first }
  594.       IndexDefs.Assign(Table1.IndexDefs);
  595.       CreateTable;
  596.     end;
  597.  
  598. --------------------------------------------------------------------------------
  599.  
  600.