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