Delphi RSS Resources, Delphi Components, Delphi Sites, Articles, Dynamic XML Feeds, Tutorials, Sources
 
 
 
Google
 
Web delphirss.com
| Server-Scripts.com | Informations for JAVA | Informations for PHP | SEO Web Links | Borland Delphi
 

Database

No showing of P_RECNO
One table and database information on two physical forms
Stop the DBGrid control from auto-appending a new entry
Passwords on dBase-files
Create Paradox-Tables
Write a stream into a BLOB-field
Copy a DBMemo contents to another DBMemo field
Commands of the TDBNavigator in code
Search text in a DBMemo
Getting Field Information for a table
Reorder the columns in a TDBGrid control
Create new tables with a structure of an existing one
Look for an existing Record in database before it will be inserted
Display certain database fields in columns
Reorder DBGrid fields programmatically at runtime
Multiple tables in one DBGrid
Loading a TMemoField into a TMemo component
DBGrid location
Case insenstive search on a Paradox primary index
Refreshing a grid after editing in a single record edit form
Floating point numbers in DBGrid
Sorted DBLookupCombobox by secondary index
TQuery and TDBGrid
Descanding indexes
Cancel on related tables
From database to variables
Duplicating a set of records
DBGrid MoveToNextField
Creating a table in a unit
Make index files in delphi
Changing the dir from an Alias at run time
Pre-setting TDBLookupCombo boxes
Variable length record and phrase search
Tables in memory
Put a variable in a memo field
Table locking
Refreshing database data
Data aware outline control
Code for age
ASCII delimited file memo field into a DB table memo
Problems with AddIndex
Master-Detail form
Create a Paradox table
How to retrieve the path from an Alias or TDatabase
Value of TDBLookupCombo
Properly Closing Delphi Database Application
Default key field when inserting new record
Quickie DB searcher
DBGRID saving the user configuration
DBGrid resize
Getting Deletes to cascade across linked tables
dbGrid and Memo Fields
Popup menu that construct from DB
DBGrid as Navigator
Display a customer database entry form from a work order form
StringGrid
Two tables in one DBGrid
Text File Line Counts
FindKey on multiple fields
Creating database aliases in code
Creating and deleting TFields at run-time
Iterating through the fields of a table
Loading bitmaps into dBase/Paradox BLOB fields
Extracting a bitmap from a BLOB field
Searching through query result sets
Cascading deletes with Paradox referential integrity
dBase expression indexes: a primer
Interbase BLOB Fields: a primer
Automate logon for Paradox tables
Working with auto-increment field types
Showing deleted record in a DBF file
Recalling deleted records in DBF files
Rebuilding Indices with TBatchMove
Referential Integrity
Compressing Paradox Files
KEY VIOLATION


No showing of P_RECNO

Question

I have a Master/Detail Form and would like the linked field P_RECNO not to
be displayed in the DBGrid as it is unnecessary information. Can this be
done using DBGrid or do I have to find a thrid party component?

Answer

A:
You can do either

1. edit TTable to exclude P_RECNO

or

2. set

TableX.FieldbyName('P_RECNO).Visible := False;

A:
It can be done via the Fields Editor, which is associated not with the
DBGrid but rather with the Table component in question.  To get to this,
right-click on the appropriate Table component and select the top option.
You should then add all fields to the field list, and highlight the one you
wish to suppress from the DBgrid.  Look for its Visible property and set
that to False.

A:
If you have a TTable component, double click on the component icon (on =
the form), you then get a list of the fields related to the table in a =
dialog, click on one of them and check the object inspector =
properties....make visible false.


One table and database information on two physical forms

Question

I have a requirement to create a delphi app that uses only one table but has
database information on two physical forms.

Answer

A:
1. Add a TTable-component on form2
2. Just for designtime give this table the same values as the table on form1
3. Add the following codefragment in the IMPLEMENTATION-section of
   form2 so that it looks like this:

   unit form2;

   interface
   {...}
   implementation
   uses
       form1;
   {...}

4. Connect a procedure to the OnCreate-event of form2 (with the
   objectinspector)

5. Add the following line to this procedure:

   table1 := form1.table1;

After this you can all the components you connected with table1 at
designtime will work with the Table of form1.

A:
It appears as if the problem you are experiencing is one of synchronization.
Try the following:

  - On Form1
	place Table1
	place DataSource1
		set DataSource1.DataSet :=  Table1
	place DataGrid
		set DataSource := DataSource1

  Up until now, this has been straight forward.  Now do:

  - On Form2
	place DataSource1 (#1 for this form)
	place whatever other components you need
		in any DB components; point the datasource at DataSource1
	In the OnCreate event for this form (e.g. FormCreate) put the
	following code.

		With Form1 do
		 begin
		  Form2.DataSource1.DataSet := Table1;
		 end;

	This code attaches the Table1 on Form1 to the DataSource
	on Form2.  This forces the data displayed on Form2 to be in synch
	with the data displayed on Form1.  Since you are really only using
	one Table.

The only caveat of here is if you are really using TDatabase then this might
not be what you want. The TDatabase component is not required for database
access, however, it supplies you with additional control for client/server
applications.

Thus, if the application is not running in a client/server environment then
the use of the TDatabase is not needed. All you need is the TDataSource and
TTable and any TDB components.


Stop the DBGrid control from auto-appending a new entry

Question

How to stop the dbgrid control from auto-appending a new entry when you move
down after the last record in a table.
It creates a new blank line / record in the table. Can this be stopped?

Answer

A:
Add to your TTables's "BeforeInsert"  event the following line:

procedure TForm1.Tbable1BeforeInsert(DataSet: TDataset);
begin
  Abort;  <<---this line
end;

A:
It traps the down key and checks for end-of-file.

procedure TForm8.DBGrid1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
     if (Key = VK_DOWN) then
	begin
	     TTable1.DisableControls ;
	     TTable1Next ;
	     if TTable1.EOF then
		Key := 0
	     else
		TTable1.Prior ;
	     TTable1.EnableControls ;
	end ;
end;


Passwords on dBase-files

Question

i wrote a program with delphi, that uses a dbase-table. Now
I want to allow the users to create, change and remove passwords to protect their data.

Answer

A:
dBase files do not support passwords. You can of course create your
own password routines. These will however only work within your app.
I am afraid that there are literally thousands of dBase readers /
converters around.


Create Paradox-Tables

Question

How to create Paradox-Tables?

Answer

A:
Here's a bit of code for creating paradox tables:

with TTable.create(self) do begin
     DatabaseName := 'C:\temp';
     TableName := 'FOO';
     TableType := ttParadox;
     with FieldDefs do Begin
	  Add('Age', ftInteger, 0, True);
	  Add('Name', ftString, 25, False);
	  Add('Weight', ftFloat, 0, False);
     End;
     IndexDefs.Add('MainIndex','IntField', [ixPrimary, ixUnique]);
     CreateTable;
End;


Write a stream into a BLOB-field

Question

How can I write a Stream into a BLOB-Field?

Answer

A:
The trick is in the StrPcopy to put your String into a PChar and in the
writing of the buffer to the stream. You can't pass it a PChar because it
needs the buffer, so use [0] and use StrLen() to get the buffer
size which also needs to be passed.

Here is quick example of using a TMemoryStream and writing it to a Blob Field;

var
  cString: String;
  oMemory: TMemoryStream;
  Buffer: PChar;
begin

  cString := 'This is a String Darnit!';

  { Create a new Memory Stream }
  oMemory := TMemoryStream.Create;

  {!! Copy String to a PChar }
  StrPCopy( Buffer, cString );

  { Write the =buffer= and it's size to the stream }
  oMemory.Write( Buffer[0], StrLen( Buffer ) );

  {Write that sucker to the field}
  .LoadFromStream( oMemory );

  { No leaky Apps}
  oMemory.Free;

end;


Copy a DBMemo contents to another DBMemo field

Question

I want to copy a DBMemo contents to another DBMemo field.
I have used copyfrom and pastefrom commands but I was getting errors.
I also used Read and write commmands as shown below:
			  DBMemo6.Read(Mybuf, 4096);
			  DBMemo5.Write(buf, 4096);
I am trying to copy the contents of DBMemo6 to DBMemo5 but I was getting an
error "Field identifer expected".

Answer

Try DBMemo6.Lines:=DBMemo5.Lines.Assign;


Commands of the TDBNavigator in code

Question

Can anyone give me the commands to do in code for the buttons on the DBNavigator?

Answer

A:
I think the example below (lifted out of DELPHI online help) will
sort out the navigator. I have seen a couple of messages about
needing to trap for explicit record posts - like when you scroll over
the record that you have changed. If you intend having data
validation checks then you will need to do this extra trapping.
Can't recall exactly HOW - maybe someone else can help us both.

Before you do any changes / posting you probably should make sure the
table is in edit mode. Check out the state property in DELPHI help.  
It will show you what to do.

The following code determines which database navigator button was clicked and displays a message identifying the name of the button.

procedure TForm1.DBNavigator1Click(Sender: TObject; Button: TNavigateBtn);
var
  BtnName: string;
begin
  case Button of
    nbFirst  : BtnName := 'nbFirst';
    nbPrior  : BtnName := 'nbPrior';
    nbNext   : BtnName := 'nbNext';
    nbLast   : BtnName := 'nbLast';
    nbInsert : BtnName := 'nbInsert';
    nbDelete : BtnName := 'nbDelete';
    nbEdit   : BtnName := 'nbEdit';
    nbPost   : BtnName := 'nbPost';
    nbCancel : BtnName := 'nbCancel';
    nbRefresh: BtnName := 'nbRefresh';
  end;
  MessageDlg(BtnName + ' button clicked.', mtInformation, [mbOK], 0);
end;


Search text in a DBMemo

Question

What I want to do is search the Text in the TDBMemo, just like a Word Processor searches for a particular word. I suspect that because the TDBMemo is attached to the Paradox Table that searching this way is out of the
Question.

Answer

A:
Try this procedure: Attach it to your OnFind Event for the FindDialog. The
only problem is that I can't seem to get the found text to highlight in the
DBMemo, although it works fine in a standard text Memo.

procedure TMainForm.FindDialog1Find(Sender: TObject);
var Buff, P, FT : PChar;
    BuffLen     : Word;
begin
   With Sender as TFindDialog do
   begin
      GetMem(FT, Length(FindText) + 1);
      StrPCopy(FT, FindText);
      BuffLen:= DBMemo1.GetTextLen + 1;
      GetMem(Buff,BuffLen);
      DBMemo1.GetTextBuf(Buff,BuffLen);
      P:= Buff + DBMemo1.SelStart + DBMemo1.SelLength;
      P:= StrPos(P, FT);
      if P = NIL then MessageBeep(0)
      else
      begin
	 DBMemo1.SelStart:= P - Buff;
	 DBMemo1.SelLength:= Length(FindText);
      end;
   FreeMem(FT, Length(FindText) + 1);
   FreeMem(Buff,BuffLen);
  end;
end;

To highlight the found text:

begin
	 DBMemo1.SelStart:= P - Buff;
	 DBMemo1.SelLength:= Length(FindText);
      end;
   FreeMem(FT, Length(FindText) + 1);
   FreeMem(Buff,BuffLen);
   DBMemo1.SetFocus;
  end;


Getting Field Information for a table

Question

I'm trying to get field information for database tables. I can get the database name and the table names but I can't get all the fields for a given table. I tried declaring a ttable component and assigning the database name and table name to the object.

Answer

A:
You need to use the FieldDefs property. The following example will add the
list of fields and their respective sizes to a TMemo component named Memo1
on the form:

procedure TForm1.ShowFields;
var
   i : Word;
begin
   Memo1.Lines.Clear;
   Table1.FieldDefs.Update;                     { must call in case Table1
is not active }
   for i := 0 to Table1.FieldDefs.Count - 1 do
      With Table1.FieldDefs.Items[i] do
	 Memo1.Lines.Add(Name + ' - ' + IntToStr(Size));
end;

A:
If you just wan the names then use the GetFieldNames Method of TTable to get
the FieldNames:
GetIndexNames to get Index Names:

var FldNames, IdxNames : TStringList

begin

  FldNames := TStringList.Create;
 IdxNames := TStringList.Create;

  If Table1.State = dsInactive then Table1.Open;
  Table1.GetFieldNames(FldNames);
  Table1.GetIndexNames(IdxNames);

  {...... do whatever the next bit is ......}

  FldNames.Free; {release the stringlist}
  IdxNames.Free;
end;

To get specific field info, you will have to use FieldDef.


Reorder the columns in a TDBGrid control

Question

I need to allow the end user to reorder the columns in a TDBGrid control and select an index based upon the new first column.

Answer

A:
Var
     i          : Integer ;
    fName       : string ;
............
{ Detect that columns reordered }
............
	with DBGrid1.DataSource.DataSet as TTable do
	for i := 0 to IndexDefs.Count - 1 do begin
		       fName := DBGrid1.Fields[0].FieldName ;
	       if Copy( IndexDefs[i].Fields, 1, Length( fName ) ) = fName then
			IndexName := IndexDefs[i].Name
	end ;


Create new tables with a structure of an existing one

Question

I must create new tables with the same structure of an already existing one.

Answer

A:
I keep forgetting that when a property is assigned a value (ie on the left
side of the ':='), Delphi actually calls the 'write' method and passes
whatever is on the right side of the assignment as the lone parameter.
If a property does not have a write method, it is read only.
This is the FieldDefs property definition for the TDataSet object in the
DB.PAS file:

    property FieldDefs: TFieldDefs read FFieldDefs write SetFieldDefs;

As you can see, it has a write method. Therefore when you code:

   Destination.FieldDefs := Source.FieldDefs;

What actually is done is this:

   Destination.SetFieldDefs(Source.FieldDefs);

(Except you can't code this line because SetFieldDefs is Private.)

This is the IndexDefs property for the TTable object in the DBTABLES.PAS file:

    property IndexDefs: TIndexDefs read FIndexDefs;

As you can see this time, it does not have a write method, therefore it is
read only. However, there is an Assign method for the TIndexDefs object
itself. Therefore the following line should work.

Source.IndexDefs.Update;
Destination.IndexDefs.Assign(Source.IndexDefs);

Call the Update method for the Source.IndexDefs prior to the Assign to be
sure you get what you expect.

The SetFieldDefs method is a one line procedure that calls the FieldDefs'
Assign method.

You may want to check that there is actually an index defined; because
if not, you will probably get an exception like "List Index Out Of Bounds"
(or something like that) when doing the IndexDefs.Assign. ie: if
Source.IndexDefs.Count > 0 then ...
You need to do this because the TIndexDefs.Assign method does not check this
before copying the Index info.  Also, you do not need to Clear the Destination
IndexDefs prior to the call because the Assign method does it for you.


Look for an existing Record in database before it will be inserted

Question

I'm trying to delete the record inserted or not insert it at all if this
new record has a key field that already exists in the database.

Answer

A:
If you are already in the Edit mode or Insert mode, when you change states,
you will automatically attempt to post the record.
And of course, a key violation will cause an error. A way around it is to use
another TTable component mapped to the same table, and do the search on that
one. That way, the table being edited remains unaffected.

A:
Use two TTable components (both pointing to the same table).  Use
one for the search and the other for the editing.

A:
If you "key" the table the BDE will automatically generate an exception
when the user tries to post that create a duplicate key. Use the
Database Desktop to set up the table.

A:
Can you make that field a Primary Index. Then create some DB exception
handling for that key violation.

A:
What I did was prompt the user, with a different form, for the portion of
the record which makes up the unique key (usually just a single entry). I
then did a FindKey to see if it already exists.  If it does, the user is
informed via a MessageDlg, and then returned to the edit form without
creating a new record. Remember that if FindKey fails, the dbCursor is not
moved; no need to bookmark. If it works, then the found record is now
displayed on the edit form and the user can see the contents right away.
Otherwise the following occurs:

        Table.Append;
        Table.FieldByName('KeyField').AsString := UserEntry;
        { ... allow the user to edit all other fields for the record ... }
        { a Cancel button is made active during this time so that if the
          user presses it, the entire new record is canceled. }

In my edit forms, the Unique key field is disabled and shown with a
different color.  This way the user cannot flub up the referential integrity
of the RDB.


Display certain database fields in columns

Question

Is there a way to only display certain database fields in the columns?
i.e., if I have a table with 20 columns, but, I want to display the 2, 4,
5, 10, 11 and 16th in the grid.

Answer

A:
Here is a runtime solution:

Table1.FieldByName(RemovedFieldName).Visible :=  False;

or
Table1.Field[removedFieldNumber-1].Visible := false;


Reorder DBGrid fields programmatically at runtime

Question

At design time you can use the fields editor, and at run time you can use
the mouse to reorder database fields in a DBGrid.  How do you do this
programmatically?

Answer

A:
uses .index := 


Multiple tables in one DBGrid

Question

Anyone know of a way to have several fields from multiple tables in one
DBGrid?

Answer

A:
The only easy way to do it I know is Calculated fields.

A:
As far as I have been able to determine, the only way to put data from
multiple tables into a DBGrid is to use a TQuery object. The catch is:
TQuery is read-only unless you meet some pretty rigid guidelines, one of
which is the data can only come from one table.

Loading a TMemoField into a TMemo component

Question

I am looking for a good way to load a TMemoField from a DBase table into a 
TMemo component. I do not want to use a DBMemo since I want to control when 
and how edits are saved, etc.
The only way that seems to work consistently is to load it into a DBMemo
component and then assign the lines property of the TMemo to the lines property 
of the DBMemo. This is not what I want to do.

Answer

A:
Procedure TMemoToTMemoField;
begin
   TMemoField.Assign( TMemo.Lines );
end;

Procedure TMemoFieldToTMemo;
VAR aBlobStream : TBlobStream;
begin
   aBlobStream := TBlobStream.Create(TMemoField, bmRead);
   TMemo.Lines.LoadFromStream( aBlobStream );
   aBlobStream.Free;
end;

DBGrid location

Question

How can I find know the current record which I selected if I use DBGRID and
TQuery?

Answer

A:
In design mode, double click on the TQuery component, and select all the fields
that you want to use in the DBGrid. Then in the doubleclick event procedure of
DBGrid, check to see what the DBGrid.SelectedIndex is. If it is < 0, there are
no items selected.  Else, the current record of TQuery will point to the same
row selected in the DBGrid.  Thus, you will be able to use something like
requiredvalue :=  Query1Field1.AsString;   etc.....
Of course, the TQuery component and DBGrid component must be connected to each
other.

Case insenstive search on a Paradox primary index

Question

How do you do a case insenstive search on a Paradox primary index in
Delphi?

Answer

A:
Unfortunately, it can't be done. You can create another (secondary)
index, a case-insensitive one, on the same field (or fields) that make up
the primary index, but as you can guess, this will result in more overhead.

Refreshing a grid after editing in a single record edit form

Question

Refreshing a grid after editing in a single record edit form.

Answer

A:
Are you posting the record before you close the form? Closing the
detail form will not post the changes by itself. You must either
post the changes using a dbnavigator component or put in a line of code
that posts the underlying table on form close.

On page 95 of the Database Application Developers Guide that ships
with Delphi it discusses the twoforms sample project and illustrates
a nice technique for using a ttable from a master form as the dataset
for a detail.

A:
One solution to your problem is to direct your DataSource component
on Form2 to use the DataSet on Form1. This can be achieved by
writing the following code in the OnActivate procedure of Form2:

MyDataSource.DataSet := Form1.MyTable;

This method has 3 advantages:
1) the changes you made is reflected immediately because you are
   usually the same logical table;
2) if you need to define settings for your table fields, such as
   DisplayFormat and EditMask, you only need to do it once on the
   Table on Form1, you don't need do it on every form that use it;
3) it can save resources and also faster because your application
   hold only one session to one table at one time. However, during
   design time, you need to have a TTable on your Form2 so you can
   choose fields for your db-aware controls; after that, you can
   delete the TTable.

Floating point numbers in DBGrid

Question

I have a project it uses DBGrid but the problem is. I have fields defined as
floats with one dec... But no Decimals appear when I input any data, and the
data is rounded.

Answer

A:
To display decimals in a grid select the table your grid is linked
with (via datasource).

Activate the field-editor (right mouse button) and select the field
you want to have decimals.

Change the value of the properties 'DisplayFormat' and 'EditFormat'
so that decimals are shown in the format you want (e.g. the value
'0.00' forces the grid to display the field with two decimals).

A:
On the table component on the form, double click on it. You will see an
'Add' button, click on it. This displays all the field in your table.
High light them from the "Available field" box and click on OK. Now
click on the field name, in the Object Inspector all your properties
will relate to each field, you can change the column title, the
"DisplayFormat" (this is what you are looking for, change it to ####0.0

Sorted DBLookupCombobox by secondary index

Question

Can somebody please tell me how to make a DBLookupComboBox become sorted
by a secondaryindex (if possible).

Answer

A:
One way to display your data in a different order would be to select the
data through a TQuery and include the "order by" clause in your SQL
statement.  Then you can set this query as the DataSource to your
DBLookupComboBox.

EXAMPLE:

If you have a Customer table containing "Customer_No" and "Customer_Name"
indexed by Customer_No your statement in the string list editor (the SQL
Property) for the TQuery would be:

  select Customer_No, Customer_Name from Customer
     order by Customer_Name

TQuery and TDBGrid

Question

1. How do I index the list generated by TQuery ?
2. How do I create an OnClick Event in a TDBGrid ?

Answer

A:
1) After your where clause place an order by clause
   Select fname, lname, title
   from T_EMPLOYEE
   where title = 'MGR'
   order by lname, fname


2) Try the ColEnter event.

Descanding indexes

Question

I can't figure out how to create a descending secondary index in Paradox!

Answer

A:
I just found a way of getting a descending index. It's pretty easy as
anything else
in Delphi.


     Table1.AddIndex('NewIndex', 'CustNo;CustName', [ixDescending]);

Cancel on related tables

Question

I have a data base app. in it there is a cancel Btn. I try to do this:
when the user press this button, the changes on related table will be
canceled. The code is somehow like this :

procedure TForm1.CancelSpdBtnClick(Sender: TObject);
begin
  ExTable.Cancel;
  ExTable.Refresh;
  PostSpdBtn.Enabled := false;
  CancelSpdBtn.Enabled := false;
  EditSpdBtn.Enabled := true;
end;

But to my surprising, after some modification has been done, I can not
cancel the changes, even I press this button. All of the changes have been
written into database no matter cancel or not.

Answer

A:
In 'Delphi unleashed' book pp 520, the author wrote:

   '...,you can still undo your work at any time, as long as you have
not either directly or indirectly posted. '
                       ^^^^^^^^^^

My problem was caused by assigning ExTable.Edit twince in diffrent
procedures. The codes somehow like this...

 Procedure1 ....
 begin

    ExTable.Edit ;
    ExTable.FieldByName('...').AsString := ...;
    ...

 end;


 procedure2 .....
 begin

    ExTable.Edit;
    .....
 end;


After these 2 procedures were called,  the CancelSpdBtnClick procedure
was called.  Actually, before  cancelling was done, posting was
indirectly called between 2 ExTable.Edit statements were executed.
^^^^^^^^^^        ^^^^^^^
Now I made the modification, the program works fine.

From database to variables

Question

I want to retrieve data from DB (or dbf) to variables or arrays.
For example, assign a variable with a data in a selected FIELD and RECORD,
use assignment (:=) to do it? or something?  How?

Answer

A:
You can retrieve field values programmatically like so:

aValue := TMyTable.FieldByName('SomeField').AsText;

or

aValue := TMyTable.FieldByName('SomeField').AsInteger;

or

aValue := TMyTable.Fields[1].AsFloat;

What you're actually doing here is getting a TField object from the TTable (or
TQuery) object, then calling a method of the TField object to get at the data
itself. You can also assign values to the field in the same way, if the TTable
object is in Insert or Edit mode. The AsFloat, AsInteger, AsDateTime, AsString
members are actually properties, and as such can accept assignment. You can
also create field objects using the Fields Editor by double-clicking on the
TTable or TQuery object. These field objects can be used instead of getting it
from the TTable or TQuery object every time.

Duplicating a set of records

Question

Does anyone know it there is a smart way of doing this:
given a set of records in a table, modify some field(s) and create new
records within the _same_ table. (Yes, this is a detail set of a master-
detail pair, the purpose being to create a new master record inheriting
all detail records.)
With "smart" I am aiming at something that does not look like jumping
here and there ("repeat read; modify; write until done").

Answer

A:
You could either use a second TTable object working on the same table, or you
could call the DisableControls method in your TTable object before making
your changes, followed by EnableControls.  If you want to keep your same
place you might try using a bookmark.  Sort of like this:

procedure TMyForm.MakeChanges;
var
  aBookmark: TBookmark;
begin
  Table1.DisableControls;
  aBookmark := Table.GetBookmark;
  try
    {do stuff}
  finally
    Table1.GotoBookmark(aBookmark);
    Table1.FreeBookmark(aBookmark);
    Table1.EnableControls;
  end;
end;

DBGrid MoveToNextField

Question

Does anyone know if there is a TDBGrid method (or similar) to perform the same
as entering a TAB, moving to next field in the grid?

Answer

A:
To go to the next field
   MyDBGrid.SelectedIndex := MyDBGrid.SelectedIndex + 1;

The grid columns are 0 indexed so SelectedIndex := 0 goes to the first
column. The FieldCount property gives you the number of columns so you can
handle wrapping at the end of the row.

Creating a table in a unit

Question

How can I create a Table in a unit (without a form) at runtime? The following
code doesn't work:

procedure CreateATableInAUnit;
var
   myTable : TTable;
begin
     myTable := TTable.Create(Self);
end;

Answer

A:
The TTable object can be created with an owner, or without one. Since you
are declaring it as local to a procedure you should not give it an owner.
Whenever you don't give it an owner, that means that its your job to free it
when you're done. Otherwise, the owner will free it whenever the owner is
itself freed. Make sense?  To create it without an owner, do the following:

procedure CreateATableInAUnit;
var
   myTable : TTable;
begin
  myTable := TTable.Create(nil);
  try
    myTable.DatabaseName := 'MyDB';
    myTable.TableName := 'MyTable.db';
    mytable.IndexName := 'MyIndex';
    myTable.Open;
    {do stuff}
  finally
    myTable.Free;
  end;
end;

Make index files in delphi

Question

Does someone know how to create, maintaince, refresh a index file in
delphi, please give me a help.

Answer

A:
If you are using dBASE or Paradox tables, you can use the AddIndex to create
a new index. For example:

     Table1.AddIndex('Articles','Title', []) ;

will create an index file named ARTICLES using the field TITLE as index key.
You can also specify various index options (e.g., unique, non-maintained,
etcetera) -- check the Delphi help file for more information.  NOTE: Your
table must be opened exclusively in order to use the AddIndex method.

As to maintaining/refreshing an index file, unless you specifically create a
non-maintained index file, it's automatic.

Changing the dir from an Alias at run time

Question

Is it possible to change the directory from an Alias at run-time?

Answer

A:
     I do this all the time. I have an INI file that  specifies wher to
     look for the tables and the directories to use here is mi code:
     
     procedure CheckTable( var Table : TTable; var TName : string );
     var
       ChangePath  : boolean;
       Path        : string;
       ActiveState : Boolean;
     begin
       if ( TName = '' ) then
         TName := Table.TableName
       else
         with Table do
         begin
           ActiveState := Active;
           Close;
           Path := ExtractFilePath( TName );
           ChangePath := HasAttr( DatabaseName, faDirectory  ) or
                         ( CompareText( DatabaseName, Path ) <> 0  );
           if ( Length ( Path ) > 0 ) and ChangePath then
                 DatabaseName := Path;
           if ( CompareText( ExtractFileName( Tname ), TableName ) <> 0 )
     then
             TableName := ExtractFileName( Tname );
           Active := ActiveState;
         end;
     end;

Pre-setting TDBLookupCombo boxes

Question

How do you "pre-set" the value of a TDBLookupCombo box?
Example:  when the user brings up a form and goes into edit mode.. there are
several TBDBLookupCombo boxes on it... (about 17-20 per form).
Aall of the combo's have their lookupsource, lookupdisplay, and lookupfield
properties set the same... but this still forces the user to select each
combo box and pick a value.
I'd like to have all of combo's pre-set when the user goes into edit mode.

Answer

A:
You can edit your datasource. Say, you want to save your data lookuping
from customer table into sales table - 'Cust No'. You can simply preset
(default value) by editing sales table "Cust No"

   with tbSales do
   begin
      Edit;
      FieldByName('Cust No').AsInteger := 1;
      Post;
   end;

Variable length record and phrase search

Question

I wonder if any of you have done this. I am trying to redo a Windows
application for a Helpdesk application.
The database must allow variable length text. And it must allow search based
on synonyms or fuzzy match. Example "PC kaput" will still retrieve "PC hang"
and "hanged PC" will get "PC hang" too.
I did this before but it was rather slow even when I have my operators up to
30 Meg RAM and CPU 100 Mhz Overdrive processors.

Answer

A:
For variable length text you can use a DBmemo.  Most people will do this by
scanning "on the fly" (when an operator poses a query), but to really speed
up  processing, try pre-scanning the way the "big boys" do it (ie. operators
of large databases):

1) when a new entry is placed into the database, it is scanned for keywords
(this can be either a pre-defined list of keywords or any word not in an
exclusion list [eg. "the", "of", "and"])

2) the keywords are added to a keyword list referencing the record number,
eg. "hang",46 or "PC",22.

3) when a user does a query, you retrieve all records meeting each keyword,
eg. "hang" might return record numbers 11, 46, and 22, while "PC" might
return record numbers 91, 22, and 15.

4) you merge the numbers on each list, which in the above example results in
record 22 (if it is an AND match) or records 11, 15, 22, 46, and 91 (if it
is an OR match).  You then retrieve and display those records.

5) for synonyms, you define a synonym table (eg. "hang","kaput") and look up
the synonyms too, adding them to the same list as the original word.

6) for common endings (eg. "hang" and "hanged"), you can either make them
synonynms, have your program analyze common word endings, or what most
systems do is allow a partial match (eg. "hang" will match any word whose
first 4 letters are "hang").

Of course, there are a lot of technical details to be determined, most
especially how to organize the lists so they are most efficiently merged and
managed.  This can give you very fast retrieval times (witness search
engines such as Nexus, Lycos, or WebCrawler, searching hundreds of thousands
of records in less than a second).

Tables in memory

Question

How do I make a virtual table (in memory)?

Answer

A:
{
This is an InMemoryTable example. Free for anyone to use, modify and do
whatever else you wish.
Just like all things free it comes with no guarantees. I cannot be
responsible for any damage this code may cause. Let me repeat this:

 WARNING! THIS CODE IS PROVIDED AS IS WITH NO GUARANTEES OF ANY KIND!

 USE THIS AT YOUR OWN RISK - YOU ARE THE ONLY PERSON RESPONSIBLE FOR

 ANY DAMAGE THIS CODE MAY CAUSE - YOU HAVE BEEN WARNED!

THANKS to Steve Garland <72700.2407@compuserve.com> for his help. He
created his own variation of an in-memory table component and I used it
to get started.

InMemory tables are a feature of the Borland Database Engine (BDE).
InMemory tables are created in RAM and deleted when you close them. They
are much faster and are very useful when you need fast operations on
small tables. This example uses the DbiCreateInMemoryTable DBE function call.
This object should work just like a regular table, except InMemory
tables do not support certain features (like referntial integrity, secondary
indexes and BLOBs) and currently this code doesn't do anything to
prevent you from trying to use them. You will probably get some error if
you try to create a memo field.
If you have comments - please contact me at INTERNET:grisha@mira.com
}

unit Inmem;

interface

uses DBTables, WinTypes, WinProcs, DBITypes, DBIProcs, DB, SysUtils;

type TInMemoryTable = class(TTable)
     private
       hCursor: hDBICur;
       procedure EncodeFieldDesc(var FieldDesc: FLDDesc;
       const Name: string; DataType: TFieldType; Size: Word);
       function CreateHandle: HDBICur; override;
    public
      procedure CreateTable;
    end;

implementation

{ luckely this function is virtual - so I could override it. In the
original VCL code for TTable this function actually opens the table - but since
we already have the handle to the table - we just return it }

function TInMemoryTable.CreateHandle;
begin
  Result := hCursor;
end;

{ This function is cut-and-pasted from the VCL source code. I had to do
this because it is declared private in the TTable component so I had no
access to it from here. }

procedure TInMemoryTable.EncodeFieldDesc(var FieldDesc: FLDDesc;
  const Name: string; DataType: TFieldType; Size: Word);
const
  TypeMap: array[TFieldType] of Byte = (
    fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, fldBOOL,
    fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, fldBYTES,
    fldVARBYTES, fldBLOB, fldBLOB, fldBLOB);
begin
  with FieldDesc do
  begin
    AnsiToNative(Locale, Name, szName, SizeOf(szName) - 1);
    iFldType := TypeMap[DataType];
    case DataType of
      ftString, ftBytes, ftVarBytes, ftBlob, ftMemo, ftGraphic:
        iUnits1 := Size;
      ftBCD:
        begin
          iUnits1 := 32;
          iUnits2 := Size;
        end;
    end;
    case DataType of
      ftCurrency:
        iSubType := fldstMONEY;
      ftBlob:
        iSubType := fldstBINARY;
      ftMemo:
        iSubType := fldstMEMO;
      ftGraphic:
        iSubType := fldstGRAPHIC;
    end;
  end;
end;

{ This is where all the fun happens. I copied this function from the VCL
source and then changed it to use DbiCreateInMemoryTable instead of
DbiCreateTable.
Since InMemory tables do not support Indexes - I took all of the
index-related things out }

procedure TInMemoryTable.CreateTable;
var
  I: Integer;
  pFieldDesc: pFLDDesc;
  szTblName: DBITBLNAME;
  iFields: Word;
  Dogs: pfldDesc;
begin
  CheckInactive;
  if FieldDefs.Count = 0 then
    for I := 0 to FieldCount - 1 do
      with Fields[I] do
        if not Calculated then
          FieldDefs.Add(FieldName, DataType, Size, Required);
  pFieldDesc := nil;
  SetDBFlag(dbfTable, True);
  try
    AnsiToNative(Locale, TableName, szTblName, SizeOf(szTblName) - 1);
    iFields := FieldDefs.Count;
    pFieldDesc := AllocMem(iFields * SizeOf(FLDDesc));
    for I := 0 to FieldDefs.Count - 1 do
      with FieldDefs[I] do
      begin
        EncodeFieldDesc(PFieldDescList(pFieldDesc)^[I], Name,
          DataType, Size);
      end;
      { the driver type is nil = logical fields }
      Check(DbiTranslateRecordStructure(nil, iFields, pFieldDesc,
        nil, nil, pFieldDesc));
      { here we go - this is where hCursor gets its value }
    Check(DbiCreateInMemTable(DBHandle, szTblName, iFields, pFieldDesc,
hCursor));
  finally
    if pFieldDesc <> nil then FreeMem(pFieldDesc, iFields *
SizeOf(FLDDesc));
    SetDBFlag(dbfTable, False);
  end;
end;

end.

{This code came from Lloyd's help file!}

Put a variable in a memo field

Question

How do I get a variable into a Memo Field. I want to ask the user for
his / her name and display that name, plus more, in a memo.

Answer

A:
If I realized your question, I thing you need something like this (to GET data)

  Memos := TStringList.Create;
  Memos.Assign(Table1Memo);
  yourvariable_0 := Memos[0];
  yourvariable_1 := Memos[1];
  ..........................
  yourvariable_n := Memos[n];
  Memos.Free;

and like this (to SET data)

  Memos := TStringList.Create;
  Memos.Add(yourvariable_0);
  Memos.Add(yourvariable_1);
  ..........................
  Memos.Add(yourvariable_n);
  Table1Memo.Assign(Memos);
  Memos.Free;

Table locking

Question

If one people is editing the record, the other people cannot view
the record. Can I prompt the user that the message " The record is currently
edited by other user"?

Answer

A:
When you get this or similars error, you can intercept these using the try
construct in this way (supposing you are trying to post a record):

  try
     Table1.Post;
  except
     MessageDlg ('Error posting record', etc...
     Table1.Cancel;
  end;

Otherwise, you -shouldn't- get an error if an looks to a record currently
viewed by another user (if you are using the Paradox database provided with
Delphi) if you had correctly set it. Paradox self-creates a file called
pdxusers.lck viewed by every users in the net dir, so every BDE on every
local machine can be able to lock a record forbiding other users to post it
until he had relased. I can't imagine what kind of things you are doing to
get this error, if I don't know some other specs.

Refreshing database data

Question

I have a form that  reads and updates from a paradox table. Its also calls
another form that will add records into the same table and this works fine. It
also displays another modal form that has a DBGRID in it pointing to the same
table.
Everything works to start with, however when I add or change data and then
call this modal form with the DBGRID in it, the DBGRID is not showing any
updated information eg it still shows deleted records and wont show any new
records that have been added. It is almost like it is pointing to the
'original' version of the table.
After I terminate my application and start it again the DBGRID does reflect
the true information, so it does prove that it is pointing to the correct
table. (I have checked this all out in the sample MASTAPP application
supplied with DELPHI and it all seems to work fine in a similar circumstance
- seemingly with no 'extra' code to handle this)

Answer

A:
Perhaps it will help to use the same table and datasource in your
modal form as in your main form. Try to modify the code of your modal
form like this:

unit myModalF;

interface
{...}

implementation
{...}

uses
     MainForm; {The file-name of the parent form of your modal form)

MyModalForm.OnCreate(Sender: TObject);
begin
        DBGrid1.DataSource := MyMainForm.DataSource1;
end;

Data aware outline control

Question

Another quick query, does anyone know of a data aware outline control?
My preference is obviously for a freeware one but shareware would do
if the source is available after registration.
If not, does anyone have any bright ideas on a method to store in a
database and display in an outline a relationship tree of undefined
depth?
To explain...we have a table of individuals and wish to relate
them to each other in a corporate hierarchy, ie. Bob reports to Peter,
Peter has Jane and Simon working for him etc... This structure could
have any number of levels and may have to start centred on any person,
at any level.

Answer

A:
I've done something like you would like to do.
I cannot explain everything but I can give you an idea on how to do.
You must have a table that make a relation between the individuals.
If Peter has Jane and Simon working for him you must have a table (RELATION)
with these two records

Master  Slave   ------- fields name
Peter     Jane
Peter     Simon

If George and Elisa work for Jane then the table becomes:
Master  Slave   ------- fields name
Peter     Jane
Peter     Simon
Jane      George
Jane      Elisa

and so on.

When you have to construct the tree starting at Peter you've to add a main
node called Peter in the tree an position the RELATION table on the first
record where Master = Peter. Than add a child node 
for each record that satisies the condition Master = Peter.
After adding a child you've to see if this child has child himself. The
child becomes now a probably father
so you've to position the RELATION table on the first record where Master =
child and so on recursively.
This grants you to build a correct tree.
Example

AddFather('Peter')
AddChild('Peter',1)


Procedure AddFather(Name: String)
Begin
  Tree.Add(Name);
End;

Procedure AddChildr(Name: String, Index:Integer)
Begin
  Relation.FindKey([Name])
  while RelationMaster.AsString = Name do
  Begin
    Tree.AddChild(Index,RelationSlave.AsString);
    AddChild(RelationSlave.AsString,Tree.ItemsCount);
    Relation.Next;
  End;
 End;

Maybe there's some error but the this is the way.

Code for age

Question

1. How do I use this function, where do I put it. I have a table listing
the birthdays in one column but not a column for age. Would like to have
a field on a form/notebook showing the age automatically on run.

2. What about the ages of those less than 1 year. How do I show the age
in months for those occasions?

Answer

Double-click on your TTable or TQuery component on your form to go to the
Fields Editor dialog (or right-click and select Fields Editor).  Add all the
fields that you will be working with in the form (even those you don't want 
to be visible but need access to--you can set the visible property to false 
for any fields you wish to suppress).  Then click on "Define..." to add a 
calculated field.  Type in a name for the calculated field that is not the 
same as any fields in the table, select a type (probably StringField) and 
enter a length (20 should be fine).  Make sure the 'calculated' box is 
checked.  Then add an event-handler to your TTable or TQuery object for 
'OnCalcFields'.  In this handler you will look at one of the real fields in
your table, do a calculation, and put the results into your calculated field 
object that you just created.  This will cause it to show up in the TDBGrid 
or you can add a TDBText control to display the value if you're using a data 
entry form rather than a grid.  

So far as displaying months as well as years, hear's a function which should 
do the trick.  Since not all the months are the same length, I just took an 
average, so it isn't going to be perfectly accurate, but for most people it 
should suffice:

function AgeStr(aDate: TDateTime): string;
var
  DaysOld : Double;
  Years,
  Months  : Integer;
begin
  DaysOld := Date - aDate;

  Years := Trunc(DaysOld / 365.25);
  DaysOld := DaysOld - (365.25 * Years);
  Months := Trunc(DaysOld / 30.41);

  Result := Format('%d years, %d months',[Years, Months]);
end;

So, in my case, my OnCalcFields method looked like this:

procedure TEntryForm.TableNameOrderCalcFields(DataSet: TDataset);
begin
  TableNameOrderAge.AsString := AgeStr(TableNameOrderDateOfBirth.AsDateTime);
end;

ASCII delimited file memo field into a DB table memo

Question

How do you get an ASCII delimited file Memo field into a DB Table Memo?
I create the Table and then populate it with the fields depending on the
type read on the first line of the ASCII delimited file.
I know that you can't do :
Table.Fields[MemoField].AsString := ASCII.MemoString;

Answer

A:
You need to use the getTextBuf procedure. Here's the example from
online help:

This example copies the text in an edit box into a null-terminated string,
and puts this string in another edit box when the user clicks the
button on the form.

procedure TForm1.Button1Click(Sender: TObject);
var
  Buffer: PChar;
  Size: Byte;
begin
  Size := Edit1.GetTextLen;       {Get length of string in Edit1}
  Inc(Size);                      {Add room for null character}
  GetMem(Buffer, Size);           {Creates Buffer dynamic variable}
  Edit1.GetTextBuf(Buffer,Size);  {Puts Edit1.Text into Buffer}
  Edit2.Text := StrPas(Buffer);   {Converts Buffer to a Pascal-style string]
  FreeMem(Buffer, Size);          {Frees memory allocated to Buffer}
end;

Problems with AddIndex

Question

I have problems creating a new secondary index.
I added field to the TTable then the table is created as by Table.Create. Next
I create the primary index with Table.AddIndex('PRIMARY','ID',[ixPrimary]);.
But when I want to create a secondary index I always get an error at runtime.

Answer

A:
I am using a paradox table on a local station.
I use the following commands:
Table.DatabaseName := 'ABC';
Table.TableName := 'TEST';
Table.CreateTable;
Table.AddIndex('Primary','ID',[ixPrimary]); (works fine)
Table.AddIndex('Number_IDX','NUMBER',[ixUnique]); (here I get a runtime error)

ID is a Long-Integer field
NUMBER is a char[15] field

Master-Detail form

Question

I am using a master-detail from (created with the Expert) to
display a list of people from one table and items from
another table for each individual. The problem is that I
can't seem to edit any of the fields on my form from either
of the tables. Also, the add, delete, and post buttons on
the VCR DB control are greyed out.

Answer

A:
This is normal for two reasons:

1) The Database expert by default creates query with RequestLive set to
False; if you want to modify something, you have to set RequestLive to True.

2) In a one-many relationship, due to referential integrity rules, you are
allowed to make changes only in the "many" form, not in the "one" form. BTW,
this is right, if you think about it. Suppose you have a one-many
relationship where the one are your customers, and the many are their
invoices: of course there could be more than one invoice per customer. If
the system would allow you to modify the customer by, for example, deleting
records, you'd find some invoice not related to anybody.

Create a Paradox table

Question

How can i do to create a Paradox table with the following structure:

       TableName = Empresa

        Field        Type
        CODEMP       AUTOINCREMENT (Key)
        NomeEmp      CHARACTER(50)

Answer

A:
unit Autoinc;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, DBTables, DB, ExtCtrls, DBCtrls, Grids, DBGrids, StdCtrls,
  DbiTypes, DbiErrs, DBIProcs;
const
    szTblName = 'CR8PXTBL'; { Name of table to be created. }
    szTblType = szPARADOX;  { Table type to use. }

{ Field Descriptor used in creating a table }
const
    fldDes: array[0..1] of FLDDesc = (
              ( { Field 1 - AUTOINC }
               iFldNum:      1;            { Field Number }
               szName:       'AUTOINC';    { Field Name }
               iFldType:     fldINT32;     { Field Type }
               iSubType:     fldstAUTOINC; { Field Subtype }
               iUnits1:      0;            { Field Size }
               iUnits2:      0;            { Decimal places ( 0 ) }
               iOffset:      0;            { Offset in record ( 0 ) }
               iLen:         0;            { Length in Bytes  ( 0 ) }
               iNullOffset:  0;            { For Null Bits    ( 0 ) }
               efldvVchk:    fldvNOCHECKS; { Validiy checks   ( 0 ) }
               efldrRights:  fldrREADWRITE { Rights }
              ),
              ( { Field 2 - ALPHA }
               iFldNum:      2; szName:       'ALPHA';
               iFldType:     fldZSTRING; iSubType:     fldUNKNOWN;
               iUnits1:      10; iUnits2:      0;
               iOffset:      0; iLen:         0;
               iNullOffset:  0; efldvVchk:    fldvNOCHECKS;
               efldrRights:  fldrREADWRITE
              )    );

type
  TForm1 = class(TForm)
    Button1: TButton;
    Database1: TDatabase;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
Var
  TblDesc: CRTblDesc;
  uNumFields: Integer;
  Rslt : DbiResult;
  ErrorString : Array[0..dbiMaxMsgLen] of Char;
begin
   FillChar(TblDesc, sizeof(CRTblDesc), #0);
   lStrCpy(TblDesc.szTblName, szTblName);
   lStrCpy(TblDesc.szTblType, szTblType);
   uNumFields := trunc(sizeof(fldDes) / sizeof (fldDes[0]));
   TblDesc.iFldCount := uNumFields;
   TblDesc.pfldDesc := @fldDes;

   Rslt := DbiCreateTable(Database1.Handle, TRUE, TblDesc);
   If Rslt <> dbiErr_None then
   begin
     DbiGetErrorString(Rslt, ErrorString);
     MessageDlg(StrPas(ErrorString),mtWarning,[mbOk],0);
   end;

end;

end.

***************** EXAMPLE 2 **************************************
Here's a bit of code for creating paradox tables:

with TTable.create(self) do begin
     DatabaseName := 'C:\temp';
     TableName := 'FOO';
     TableType := ttParadox;
     with FieldDefs do Begin
          Add('Age', ftInteger, 0, True);
          Add('Name', ftString, 25, False);
          Add('Weight', ftFloat, 0, False);
     End;
     IndexDefs.Add('MainIndex','IntField', [ixPrimary, ixUnique]);
     CreateTable;
End;

How to retrieve the path from an Alias or TDatabase

Question

I need help in determining the path of the database programmatically
so that I can put an .INI file there with it.

Answer

A:
Here's a quick demo to demonstrate how to get info about aliases in Delphi.
First, create a new project with a listbox and 3 labels (called ListBox1, 
Label1, Label2, and Label3).  Then add an OnCreate event handler for the form 
with this code in it:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Session.GetAliasNames(ListBox1.Items);
end;

Now add an OnClick event for the list box:

procedure TForm1.ListBox1Click(Sender: TObject);
var
  tStr: array[0..100] of char;
  Desc: DBDesc;
begin
  if ListBox1.Items.Count = 0 then
    exit;
  StrPLCopy(tStr, ListBox1.Items.Strings[ListBox1.ItemIndex], High(tStr));
  DbiGetDatabaseDesc(tStr, @Desc);
  with Desc do
  begin
    Label1.Caption := StrPas(Desc.szName);
    Label2.Caption := StrPas(Desc.szPhyName);
    Label3.Caption := StrPas(Desc.szDbType);
  end;
end;

Now add the following to the 'uses' clause at the top of the unit:

DB, DBTables, DBITypes, DBIProcs;

You'll see that this gets the path from all of your STANDARD aliases (Paradox 
and dBase).

Value of TDBLookupCombo

Question

I am trying to use the TDBLookupCombo on a dialog box that will display the
value of a database field. I then take that value returned from the
TDBLookUpcombo value and store it in a StringList of a ListBox.

Answer

A:
I think I have what you want, um, heres some source code I wrote
awhile to accomplish much the same thing.  When you reference the
LookUpValue property you get the field that was .... looked up.

I hope this is what you're after,.....  I havn't seen anyone else post
anything here for this so I guess no else thought of this..

unit clookup;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, DB, DBLookup;

type
  TDBJustLookupCombo = class(TDBLookupCombo)
  private
    { Private declarations }
  protected
    { Protected declarations }
    function GetLValue: TField;
  public
    { Public declarations }
    property LookUpValue: TField read GetLValue;
  published
    { Published declarations }
  end;

  TDBJustLookupList = class(TDBLookupList)
  private
    { Private declarations }
  protected
    { Protected declarations }
    function GetLValue: TField;
  public
    { Public declarations }
    property LookUpValue: TField read GetLValue;
  published
    { Published declarations }
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Data Controls', [TDBJustLookupList,
TDBJustLookupCombo]);
end;

function TDBJustLookupCombo.GetLValue: TField;
begin
  Result := LookupSource.DataSet.FieldByName(LookUpField);
end;

function TDBJustLookupList.GetLValue: TField;
begin
  Result := LookupSource.DataSet.FieldByName(LookUpField);
end;

end.

Properly Closing Delphi Database Application

Question

In Windows, we all know that a Windows app will automatically close when
you close Windows itself.
Let's say we have a Delphi app opened, and it has a number of data
tables opened.  Can we assume that if a user exits Windows without first
exiting the Delphi app, that all opened tables get updated and close
properly?
If this is not the case, how would one intercept the 'Windows Close'
event and then properly shut down the Delphi app first?  Or would there
be a way to intercept the 'Windows Close' event and tell the user that
they must first close down the Delphi application before exiting
windows?

Answer

A:
A very interesting and insightful question!!  I'm not entirely sure myself!
But I'll tell what I do know about what happens when there is a request
to shut down Windows:

1.  Windows sends a WM_QUERYENDSESSION message to the main
window of all running applications, in effect asking if it is ok to shut down.

If any app responds negatively to this message, Windows will abort the
shutdown.

2.  Delphi intercepts this message, and in turn calls TForm.CloseQuery,
(in the main form, presumeably), which triggers your OnCloseQuery event,
if you have one for that form, giving you the opportunity to stop Windows
from shutting down.

3.  If I remember correctly, if your app does not prevent Windows from
shutting down, Windows does NOT cause your app to do an orderly
shutdown, because there is no need for it to free up memory or resources,
etc.  So, if this is correct (and this could probably be tested quite easily,
but I'm too lazy at the moment), then this OnCloseQuery event is your one
chance to save any data to disk that may be necessary.  I don't see this
as a bad thing myself, but it is one of the quirks of Windows programming
that I did not catch the first time around, but several users found it and
reported it.  So far as I can know, the worst that can happen with a
database in this scenario is that a record that the user currently has been
editing might not get posted, but the same thing could just as easily happen
during a normal shutdown of the app.


A:
When you exit windows, you call the WM_CLOSE api (or something like
that) for each application currently running.  The program closes in
the exact same way it would close if you double clicked on the control
menu or called close from the main form.  You do not need to do
anything special to shut down your tables.

Default key field when inserting new record

Question

Does anyone know what is the best way to handle default key values for a
table. I am writing an application that has a Work Order form tied to a
Dbase Work Oder table. I want the user to be able to hit the 'Insert
Record' button on the DBNavigator and have the Work Oder Id automatically fill
in.
I am also storing the Work Oder Id in a TEdit control that is editable
by the user.  A second question is how to handle table navigation when
the user manually enters an Id in the Work Order field.

Answer

A:
There are a few different ways to give the table a unique ID.

1. You can use an autoincrement field

This method is not very safe. If your table is corrupted in any way, and you
want to rebuild it, the autoincrement fields will renumber. Although this is
a very easy solution for situation in which you don't refer to the table-id in
other tables, it is not wise to use it in any other situation.

2. You can use a ID-Table

If you have an application where several tables need unique ID's, build a ID
table with two fields:

Table Name A (primary key)
Last Id N

In the BeforePost method of the a table that needs a unique ID, do something
like this:

TableBeforePost(Sender: TObject)
var
  Id: Integer;
begin
  with TTable(Sender) do
  begin
    {some check to see if an ID already exists for this record}
    if Field[0].AsInteger=0  then
    begin
     {find the tablename in the ID table}
      IDTable.FindKey[Name]
      {retrieve the last Id - implicitly lock the record}
      Id := IDTable.FieldByName['Last Id'].AsInteger;
      Inc(Id);
      {write the new Id to the ID table - implicitly unlock the table}
      IDTable.FieldByName['Last Id'].AsInteger := Id;
      IDTable.Post;
     {write the retrieved ID to your table}
      Field[0].AsInteger := Id;
    end;
  end;
end;

end;

If you put this code in the BeforePost event of the table, you'll make sure
that all given ID's are sequential (no "holes"). The drawback is that if a
user tries to insert a new record, but changes her mind, you'll have a record
with only the ID filled in.

If you don't care about sequential ID's, put the above code in the table's
OnNewRecord event.

3. You can use an ID File

Using the same method as the one above, you can use a file instead of a table
to do the same. This has the advantage of higher speed, but in a multi user
environment you must make take care of record locking yourself.

Quickie DB searcher

Question


Answer

Here's a quickie DB search utility.  It does its work by finding a string
inside the field (it converts everything to an uppercase string, even floats).  
This may be the slowest way to search, but it seems to work faster 
than the other solutions I've found on the Net.  And it'll hit just 
about anything.  For ex. say you have float field that has the
number 4.509375354, and you search for 7, you'll hit it.  Also, It'll
search on more than one field at a time - handy if you have two address fields.
    I'm posting this little unit because when I first started in
Delphi, one of my biggest troubles was finding a search utility and I 
didn't know enough to write my own (fear struck deep into my bones).  
So I hope this will help those of you who need it like I did.
     It's fairly easy to understand, but if you need to know exactly 
how to use it, just e-mail me personally and I'll be glad to help.  
Look at the case statement to find out which field types are 
supported (easy to add more).

{<<<>>>>}
unit Finder;

interface

uses DB, DBTables, SysUtils;

function GrabMemoFieldAsPChar(TheField : TMemoField): PChar;
function DoFindIn(TheField : TField; SFor : String): Boolean;
function FindIt(TheTable : TDataSet; TheFields : array of integer; 
  SearchBackward : Boolean; FromBeginning : Boolean; SFor : String): Boolean;
{ex. of FindIt -
         if FindIt(NotesSearchT,
         [NotesSearchT.FieldByName('Leadman').Index],
           False, True, SearchText.Text) then DoSomething; }

implementation

function GrabMemoFieldAsPChar(TheField : TMemoField): PChar;
begin
with TBlobStream.Create(TheField, bmRead) do
  begin
    GetMem(Result, Size + 1);
    FillChar(Result^, Size + 1, #0);
    Read(Result^, Size);
    Free;
  end;
end;

function DoFindIn(TheField : TField; SFor : String): Boolean;
var
  PChForMemo : PChar;
begin
Result := False;
case TheField.DataType of
  ftString :
    begin
      if (Pos(SFor, UpperCase(TheField.AsString)) > 0) then
        Result := True;
    end;
  ftInteger :
    begin
      if (Pos(SFor, TheField.AsString) > 0) then Result := True;
    end;
  ftBoolean :
    begin
      if SFor = UpperCase(TheField.AsString) then
        Result := True;
    end;
  ftFloat :
    begin
      if (Pos(SFor, TheField.AsString) > 0) then Result := True;
    end;
  ftCurrency :
    begin
      if (Pos(SFor, TheField.AsString) > 0) then Result := True;
    end;
  ftDate .. ftDateTime :
    begin
      if (Pos(SFor, TheField.AsString) > 0) then Result := True;
    end;
  ftMemo :
    begin
      SFor[Ord(SFor[0]) + 1] := #0;
      PChForMemo := GrabMemoFieldAsPChar(TMemoField(TheField));
      StrUpper(PChForMemo);
      if not (StrPos( PChForMemo, @SFor[1] ) = nil) then Result :=
      True; FreeMem(PChForMemo, StrLen(PChForMemo + 1));
    end;
  end;
end;

function FindIt(TheTable : TDataSet; TheFields : array of integer;
  SearchBackward : Boolean; FromBeginning : Boolean; SFor : String): Boolean;
var
 i, HighTheFields, LowTheFields : integer;
 BM : TBookmark;
begin
TheTable.DisableControls;
BM := TheTable.GetBookmark;
try
LowTheFields := Low(TheFields);
HighTheFields := High(TheFields);
SFor := UpperCase(SFor);
Result := False;
if FromBeginning then TheTable.First;
if SearchBackward then
  begin
    TheTable.Prior;
    while not TheTable.BOF do
      begin
        for i := LowTheFields to HighTheFields do
          begin
            if DoFindIn(TheTable.Fields[TheFields[i]], SFor) then
              begin
                Result := True;
                Break;
              end;
          end;
        if Result then Break else TheTable.Prior;
      end;
  end else
  begin
    TheTable.Next;
    while not TheTable.EOF do
      begin
        for i := LowTheFields to HighTheFields do
          begin
            if DoFindIn(TheTable.Fields[TheFields[i]], SFor) then
              begin
                Result := True;
                Break;
              end;
          end;
        if Result then Break else TheTable.Next;
      end;
  end;
finally
TheTable.EnableControls;
if not Result then
  TheTable.GotoBookmark(BM);
TheTable.FreeBookmark(BM);
end;

end;

end.

DBGRID saving the user configuration

Question

Is their a way to save the column order of a grid after the user
reorders the columns via drag n drop.

Answer

I resolved this problem time ago for my one application. Following code is
adapted for you, not tested, but I think it works fine. It create, save and
load configuration's file for order AND SIZE too of fields. I'm at your
disposal for further into something.

procedure TMainForm.NewIni(const NomeIni: string);
var F: System.Text;
    i: Byte;
begin
  System.Assign(F, NomeIni);
  System.ReWrite(F);
  System.WriteLn(F, '[Campi_Ordine]');
  for i:=1 to Table1.FieldCount do
    System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].FieldName);
  System.WriteLn(F, '');
  System.WriteLn(F, '[Campi_Size]');
  for i:=1 to Table1.FieldCount do
    System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].DisplayWidth);
  System.Close(F);
end;

procedure TMainForm.SaveIni(const FN: String);
var Ini: TIniFile;
    i: Integer;
begin
  NewIni(FN);
  Ini := TIniFile.Create(FN);
  with Ini do
  begin
    for i:=1 to Table1.FieldCount do
    begin
      S:= Table1.Fields[i-1].FieldName;
      WriteString('Campi_Ordine', 'Campo'+IntToStr(i), 
        Table1.Fields[i-1].FieldName);
      WriteInteger('Campi_Size', 'Campo'+IntToStr(i),
        Table1.Fields[i-1].DisplayWidth);
    end;
  end;
  Ini.Free;
end;

procedure TMainForm.LoadIni(const FN: String);
var Ini: TIniFile;
    i: Integer;
    j: Longint;
    S: String;

    function MyReadInteger(const Section, Ident: string): Longint;
    begin
      result := Ini.ReadInteger(Section, Ident, -1);
      if result=-1 then
        raise Exception.Create('Errore nel file di configurazione.');
    end;

    function MyReadString(const Section, Ident: string): String;
    begin
      result := Ini.ReadString(Section, Ident, '');
      if result='' then
        raise Exception.Create('Errore nel file di configurazione.');
    end;

begin
  Ini := TIniFile.Create(FN);
  try
    with Ini do
    begin
      for i:=1 to Table1.FieldCount do
      begin
        S:= MyReadString('Campi_Ordine', 'Campo'+IntToStr(i));
        j:= MyReadInteger('Campi_Size', 'Campo'+IntToStr(i));
        Table1.FieldByName(S).Index := i-1;
        Table1.FieldByName(S).DisplayWidth := j;
      end;
    end;
  finally
    Ini.Free;
  end;
end;

DBGrid resize

Question

I have a form. In that an Edit field, an SQL Query, a DBGrid and a Button.
I can write into the edit, and the Query result will put into the grid.
How can I resize the grid and the form to the fields size which appears in
the grid. The fields Which I select with the query does not fill the full
size of the grid or does not fit into it.

Answer

A:
You can change the size of a column at run-time by changing the DisplayWidth
property of the underlying field object...

        MyTableMyField.DisplayWidth := Length(MyTableMyField.value);

If you need to actually calculate the width of the entire grid, use the
following (from a tips library)...

function NewTextWidth(fntFont : TFont; const sString : OpenString) :

  integer;
var
  fntSave : TFont;
begin
  result := 0;
  fntSave := Application.MainForm.Font;
  Application.MainForm.Font := fntFont;
  try
    result := Application.MainForm.Canvas.TextWidth(sString);
  finally
    Application.MainForm.Font := fntSave;
  end;
end;


{ calculate the width of the grid needed to exactly display with no   }
{ horizontal scrollbar and with no extra space between the last       }
{ column and the vertical scrollbar.  The grid's datasource must be   }

{ properly set and the datasource's dataset must be properly set,     }
{ though it need not be open.  Note:  this width includes the width   }
{ of the vertical scrollbar, which changes based on screen            }
{ resolution.  These changes are compensated for.                     }

function iCalcGridWidth
  (
  dbg : TDBGrid { the grid to meaure }
  )
  : integer; { the "exact" width }

const
  cMEASURE_CHAR   = '0';
  iEXTRA_COL_PIX  = 4;
  iINDICATOR_WIDE = 11;

var
  i, iColumns, iColWidth, iTitleWidth, iCharWidth : integer;
begin
  iColumns := 0;
  result := GetSystemMetrics(SM_CXVSCROLL);
  iCharWidth := NewTextWidth(dbg.Font, cMEASURE_CHAR);
  with dbg.dataSource.dataSet do
    for i := 0 to FieldCount - 1 do with Fields[i] do
      if visible then
      begin
        iColWidth := iCharWidth * DisplayWidth;
        if dgTitles in dbg.Options then
        begin
          iTitleWidth := NewTextWidth(dbg.TitleFont, DisplayLabel);

          if iColWidth < iTitleWidth then iColWidth := iTitleWidth;
        end;
        inc(iColumns, 1);
        inc(result, iColWidth + iEXTRA_COL_PIX);
      end;
  if dgIndicator in dbg.Options then
  begin
    inc(iColumns, 1);
    inc(result, iINDICATOR_WIDE);
  end;
  if dgColLines in dbg.Options
    then inc(result, iColumns)
    else inc(result, 1);
end;

-----

I had to use the function NewTextWidth, rather than the Grid's
Canvas.TextWith as the Canvas of the Grid may not initialized when you need
to call iCalcGridWidth.

Getting Deletes to cascade across linked tables

Question

Getting Deletes to cascade across linked tables? How do I do this I even tried
referential integrity. When I delete a record in one table I want to delete the
record LINKED in the other tables.

Answer

A:
I found this procedure at the Borland site and it works in my projects

procedure TForm1.Table1BeforeDelete(DataSet: TDataset)
begin
   with Table2 do begin
     DisableControls;
     First;
     While not EOF do
        Delete;
     EnableControls;
   end;
end;

Where table1 is the parent and table2 is the child.

dbGrid and Memo Fields

Question

How can I view the contents of a memo field within a dbGrid component (rather
than viewing ).

Answer

A:
In the GetText event of the TMemoField put this

Text := GrabMemoAsString(TMemoField(Sender));

and put this function somewhere accessible

function GrabMemoAsString(TheField : TMemoField): String;
begin
if TheField.IsNull then
  Result := '' else
  with TBlobStream.Create(TheField, bmRead) do
    begin
      if Size >= 255 then
        begin
          Read(Result[1], 255);
          Result[0] := #255;
        end else
        begin
          Read(Result[1], Size);
          Result[0] := Chr(Size);
        end;
      Free;
      while Pos(#10, Result) > 0 do
        Result[Pos(#10, Result)] := ' ';
      while Pos(#13, Result) > 0 do
        Result[Pos(#13, Result)] := ' ';
    end;
end;

Popup menu that construct from DB

Question

I just create a form and popup menu attached to it. Once user run the form,
it'll read a field from DB and update the popup menu.

Answer

A:
var m:TMenuItem;
    navidummy:TComponent;
  ..........................................................
 procedure  TMyForm.CreatePopUpMM(Sender: TObject);
begin
     Navidummy.free;
     Navidummy:=TComponent.create(self);
    
     While not NaviT.EOF do
     begin
      m := TMenuItem.create(navidummy);
      II:=II+1;
      with m do
       begin
            name :='MM'+IntToStr(II);
            caption := NaviT.Fieldbyname('MyWHAT').AsString ;
            tag := NaviT.Fieldbyname('MyTAG').AsInteger;
            visible:=True;
            OnClick:= NaviExec ;
       end;
      MyMenuItem.add(m);
      NaviT.Next;
     end;
     NaviT.Close;

end;

procedure TMyForm.NaviExec(Sender:TObject);
begin
     What.text := (Sender as TMenuItem).Caption; { There I get what I want ! }
     Key:= (Sender as TMenuItem).Tag ;
  
end;

DBGrid as Navigator

Question

I'd like to use a DBGrid as a navigating tool as follows:
 * Clients table with standard name, phone number etc
 * One DBText for each editable field
 * DBGrid contains just the name of the client
 * Click on a record in the DBGrid and the DBTexts are updated according
   to the record clicked.
At the moment, I'm using two DataSources, because I don't want phone
number etc to show in the DBGrid. However, when I click on the DBGrid,
because they're separate datasources, the DBTexts are not updated.
Is there any way I can do this? The clients table is going to be hundreds
of entries long, and paging through them is a pain. Also, editing them
all in a grid would result in a grid about 25 columns wide :-( and this is
also a pain.

Answer

A:
1. Place a table component on a blank form and link it to your Client table.

2. Place a Datasource component and link it to the table component above.

3. Place a grid component and link it the datasource component above.

4. Use the Fields Editor to create TField components for all the fields
   in the client table.

5. Set the Visible property of all the TField components, except Client
   Name (Or whatever field you would like displayed in the DBGrid), to
   False. The gird will now display only the Client Name.

6. Place DBEdit components below the grid to display fields from the
   Client table that you want the user to view or edit. They can use the
   same datasource as the grid.

The user can now use the grid to navigate and enter/edit data using the
DBEdits.

Display a customer database entry form from a work order form

Question

I am developing a work order program where in one of the fields on the
work order data entry form are "CUST LNAME" and "CUST FNAME". The
"CUST LNAME" field is in a DBLookupCombo box.
The DBLookupCombo box displays the records in the CUSTOMER.DB. If the
customer has no record in the CUSTOMER.DB (therefore new), the user
closes the DBLookupCombo box and has the option to click a "Add
Button".
Another form (lets call it form2) will then be displayed wherein the
user can insert the new customer's record by filling in a customer
database entry form.  When the user is done, the form closes and goes
back to the work order data entry form and finish the form.
My problem is:
1. What code do I write in the onclick event on the "Add Button" to
display and execute Form2?
2. Any uses statements that I have to implement?

Answer

A:
I've got a similar function in my project for location numbers for stock items

1. In the OnClick event I create the data entry form and then show it with
.ShowModal
Then I check .ModalResult -  if its mrOk, I post the record, otherwise I cancel

2. I put the unit name for my data entry form in my uses clause

Here's the basic outline of my code:

procedure TFrmItemNav.BtnChangeLocClick(Sender: TObject);
{var DlgItemLoc: TDlgItemLoc;}
begin
  DlgItemLoc := TDlgItemLoc.Create(FrmItemNav);
  DlgItemLoc.ShowModal;

  if DlgItemLoc.ModalResult = mrOk then
    {do whatever needs doing to Post}
  else 
    {cleanup and Cancel};

  DlgItemLoc.Free;
end;

StringGrid

Question

Has anyone successfully load a table into a stringGrid?  You might ask why
not use a DBGrid?  I need the fixcolumns property of the stringgrid and
rearranging of where some fields are drawn.

Answer

A:
Something like this should work (this is OTTOMH, may need some work):

   table.first;
   row := 0;
   grid.rowcount := table.recordCount;
   while not table.eof do begin
      for i := 0 to table.fieldCount-1 do
         grid.cells[i,row] := table.fields[i].asString;
      inc (row);
      table.next;
   end;

A:
I have my own reasons for using a TStringGrid also. Here's my
code that loads from a filtered table. It's not too elegant
since it was a quick-and-dirty hack. But it works great for
me. Very fast, even on hundreds of rows being loaded. There
are a number of external variable references. Hopefully they
aren't too obscure.

PROCEDURE	TformLookupDB.FillCells;
VAR	Row, i	:INTEGER
;	w		:INTEGER
;	grid		:TStringGrid
;
BEGIN
;	doGrid.RowCount := 0
;	IF NOT ASSIGNED(fDB) THEN EXIT
;	Row := 0
;	FOR i := LOW(fColWidths) TO HIGH(fColWidths) DO fColWidths[i] := 100
// This temporary grid object is used to prevent a zillion
// implicit Application.ProcessMessages events from being
// triggered by the database, causing really bad flicker
// on the doGrid object. So we'll load into the grid object
// and then copy the data column-wise at the bottom.
;	grid := TStringGrid.Create(Self)
;	grid.Visible := FALSE
;	WITH fDB
DO	TRY
	;	grid.ColCount := fFields.Count
	;	DisableControls
	// Filter should have been set through Self.Filter property
	;	First
	;	WHILE NOT EOF
	DO	TRY
		;	grid.RowCount := Row+1
		;	FOR i := 0 TO grid.ColCount-1
		DO	BEGIN
			;	grid.Cells[i,Row] :=
				FieldByName( fFields.Strings[i] ).AsString
			;	w := doGrid.Canvas.TEXTWIDTH( grid.Cells[i,
                                Row] )
			;	IF fColWidths[i]

Two tables in one DBGrid

Question

I'm trying to make a dbGrid as follow:
Col1: Table1Code
Col2: Table2Description

Answer

If you have D2 you can use the Lookup field properties. To use, double
click on the table object to bring up the fields editor. Then press
+N to add a new field. Just follow the comboboxes and there you
have a lookup field. The TDBGrid will automatically create a drop down
in the grid so the users can select the item.

Text File Line Counts

Question

I'm reading a large text file, extracting field values and inserting a record
for each line into a database. What is the quickest method of obtaining the
number of lines in the text file so that I can display a percentage gauge
while the import is running. The only related info. I could find in the help
file did NOT apply to text files, only binary.

Answer

If the files are not too large, you could do something like:

List := TStringList.Create;
try
   List.LoadFromFile('C:\FILE.TXT');
   Gauge.MaxValue := List.Count;
finally
  List.Free;
end;

But this reads the entire text into memory and I assume this won't do
for you. Another idea is not to use a line counter, but a byte
counter. In the beginning you request the filesize (using the Delphi
FileSize function), and in the loop you maintain the number of bytes
read as you are reading the lines. The loop would be something like
this (assuming you text file is of the standard Pascal type TEXT):

Gauge.MaxValue := FileSize(TextFile);
Reset(TextFile);
while not eof(TextFile) do
begin
  Readln(TextFile, Line);

  { Process the line }

  with Gauge do
  begin
     Progress := Progress + Length(Line) + 2; { 2 is for the CR/LF }
     Refresh;
  end;
end;

FindKey on multiple fields

Question

Could someone show me how to use FindKey with more than one field
without getting some kind of index error message.

Answer

A:
with Table1 do
begin
  SetKey;
  FieldByName('State').AsString := 'CA';
  FieldByName('City').AsString := 'Scotts Valley';
  GotoKey;
end;

You cannot use Findkey with DBase files on more than one field .


A:
oEmetb.indexName:='PrimaryKey';
if oEmeTb.findkey([prCLient,prDiv,prEme])then

where the parameters passed to findkey match the Primary Keyfields.

I have found the Index name to be case sensitive, which  can catch you out.

You can also use oEmeTb.indexfieldnames, but make sure your keyfield list
exactly matches the key fields you give it to search on or it wont seem to
find existing records.

oEmetb.indexfieldNames:='EmeClient;EmeDiv;EmeNo';
if oEmeTb.findkey([123,'A',96])then

Creating database aliases in code

Question

How can I create aliases in code?

Answer

Typically, you use the BDE Configuration Utility BDECFG.EXE to
create and configure aliases outside of Delphi.  However, with 
the use of the TDatabase component, you have the ability to 
create and use this ALIAS within your application-- not 
pre-defined in the IDAPI.CFG.

The ability to create Aliases that are only available within 
your application is important.  Aliases specify the location 
of database tables and connection parameters for database servers. 
Ultimately, you can gain the advantages of using ALIASES within 
your applications-- without having to worry about the existance 
of a configuration entry in the IDAPI.CFG when you deploy your 
application.  

Summary of Examples:
------- -- ---------
Example #1:  
	Example #1 creates and configures an Alias to use 
	STANDARD (.DB, .DBF) databases.  The Alias is
    	then used by a TTable component.
Example #2:
	Example #2 creates and configures an Alias to use
 	an INTERBASE database (.gdb).  The Alias is then
	used by a TQuery component to join two tables of
	the database.
Example #3:
	Example #3 creates and configures an Alias to use
        STANDARD (.DB, .DBF) databases.  This example 
	demonstrates how user input can be used to 
	configure the Alias during run-time.


Example #1:  Use of a .DB or .DBF database (STANDARD)
1.  Create a New Project.
2.  Place the following components on the form:
     - TDatabase, TTable, TDataSource, TDBGrid, and TButton 
3.  Double-click on the TDatabase component or choose Database 
    Editor from the TDatabase SpeedMenu to launch the Database 
    Property editor.
4.  Set the Database Name to 'MyNewAlias'.  This name will 
    serve as your ALIAS name used in the DatabaseName Property for 
    dataset components such as TTable, TQuery, TStoredProc.
5.  Select STANDARD as the Driveer Name.
6.  Click on the Defaults Button.  This will automatically add 
    a PATH= in the Parameter Overrides section.
7.  Set the PATH= to C:\DELPHI\DEMOS\DATA
    (PATH=C:\DELPHI\DEMOS\DATA)
8.  Click the OK button to close the Database Dialog.
9.  Set the TTable DatabaseName Property to 'MyNewAlias'.
10.  Set the TDataSource's DataSet Property to 'Table1'.
11.  Set the DBGrid's DataSource Property to 'DataSource1'.

12.  Place the following code inside of the TButton's 
     OnClick event.

	procedure TForm1.Button1Click(Sender: TObject);
	begin
          Table1.Tablename:= 'CUSTOMER';
          Table1.Active:= True;
	end;

13.  Run the application.


***  If you want an alternative way to steps 3 - 11, place the 
      following code inside of the TButton's OnClick event.

	procedure TForm1.Button1Click(Sender: TObject);
	begin
          Database1.DatabaseName:= 'MyNewAlias';
          Database1.DriverName:= 'STANDARD';
	  Database1.Params.Clear;
          Database1.Params.Add('PATH=C:\DELPHI\DEMOS\DATA');
          Table1.DatabaseName:= 'MyNewAlias';
          Table1.TableName:= 'CUSTOMER';
          Table1.Active:= True;
          DataSource1.DataSet:= Table1;
          DBGrid1.DataSource:= DataSource1;
	end;

*****

Example #2: Use of a INTERBASE database

1.  Create a New Project.
2.  Place the following components on the form:
     - TDatabase, TQuery, TDataSource, TDBGrid, and TButton 
3.  Double-click on the TDatabase component or choose Database 
    Editor from the TDatabase SpeedMenu to launch the Database 
    Property editor.
4.  Set the Database Name to 'MyNewAlias'.  This name will 
    serve as your ALIAS name used in the DatabaseName Property for 
    dataset components such as TTable, TQuery, TStoredProc.
5.  Select INTRBASE as the Driver Name.
6.  Click on the Defaults Button.  This will automatically add 
    the following entries in the Parameter Overrides section.

	SERVER NAME=IB_SERVEER:/PATH/DATABASE.GDB
	USER NAME=MYNAME
	OPEN MODE=READ/WRITE
	SCHEMA CACHE SIZE=8
	LANGDRIVER=
	SQLQRYMODE=
	SQLPASSTHRU MODE=NOT SHARED
	SCHEMA CACHE TIME=-1
	PASSWORD=

7.  Set the following parameters
	SERVER NAME=C:\IBLOCAL\EXAMPLES\EMPLOYEE.GDB
	USER NAME=SYSDBA
	OPEN MODE=READ/WRITE
	SCHEMA CACHE SIZE=8
	LANGDRIVER=
	SQLQRYMODE=
	SQLPASSTHRU MODE=NOT SHARED
	SCHEMA CACHE TIME=-1
	PASSWORD=masterkey

8.  Set the TDatabase LoginPrompt Property to 'False'.  If you 
    supply the PASSWORD in the Parameter Overrides section and set 
    the LoginPrompt to 'False', you will not be prompted for the 
    password when connecting to the database.  WARNING:  If an 
    incorrect password in entered in the Parameter Overrides 
    section and LoginPrompt is set to 'False', you are not prompted 
    by the Password dialog to re-enter a valid password.

9.  Click the OK button to close the Database Dialog.
10.  Set the TQuery DatabaseName Property to 'MyNewAliias'.
11.  Set the TDataSource's DataSet Property to 'Query1'.
12.  Set the DBGrid's DataSource Property to 'DataSource1'.

13.  Place the following code inside of the TButton's 
     OnClick event.

	procedure TForm1.Button1Click(Sender: TObject);
	begin
          Query1.SQL.Clear;
          Query1.SQL.ADD(
		'SELECT DISTINCT * FROM CUSTOMER C, SALES S
		WHERE (S.CUST_NO = C.CUST_NO)
		ORDER BY C.CUST_NO, C.CUSTOMER');
          Query1.Active:= True;
	end;

14.  Run the application.

Example #3: User-defined Alias Configuration

This example brings up a input dialog and prompts the 
user to enter the directory to which the ALIAS is to 
be configured to.  

The directory, servername, path, database name, and other 
neccessary Alias parameters can be read into the 
application from use of an input dialog or .INI file.

1.  Follow the steps (1-11) in Example #1.
2.  Place the following code inside of the TButton's 
    OnClick event.

procedure TForm1.Buttton1Click(Sender: TObject);
var
  NewString: string;
  ClickedOK: Boolean;
begin
  NewString := 'C:\';
  ClickedOK := InputQuery('Database Path', 
	'Path: --> C:\DELPHI\DEMOS\DATA', NewString);
  if ClickedOK then
  begin
    Database1.DatabaseName:= 'MyNewAlias';
    Database1.DriverName:= 'STANDARD';
    Database1.Params.Clear;
    Database1.Params.Add('Path=' + NewString);
     Table1.DatabaseName:= 'MyNewAlias';
    Table1.TableName:= 'CUSTOMER';
    Table1.Active:= True;
    DataSource1.DataSet:= Table1;
    DBGrid1.DataSource:= DataSource1;
  end;
end;

3.  Run the Application.


Creating and deleting TFields at run-time

Question

How can I create and delete TFields at run-time?

Answer

TField components (or more appropriately, descendants of the TField
component of types corresponding to field types) can be created at design-
time using the Fields Editor. The Fields Editor is invoked by double-
clicking on the design icon for a TTable or TQuery component. But TField 
descendants can also be created and deleted at run-time.

Descendants of the TField component (such as TStringField, TIntegerField, 
etc.) are created by invoking the Create method for the type of TField 
descendant appropriate to the field in the data set. That is, the Create 
method for the TStringField descendant of TField would be called to create
a TField descendant for a string-type field in the current data set. The 
Create method requires one parameter, that of the owner of the TField 
descendant, which is the containing TForm. After creating the TField 
descendant component, a number of key properties need to be set in order
to connect it with the field in the data set. These are:


FieldName: the name of the field in the table.
Name:      a unique identifier for the TField descendant component.
Index:     the TField descendant component's position in the array of 
           TFields (the Fields property of the TTable or TQuery with which
           the TField will be associated).
DataSet:   the TTable or TQuery with which the TField will be associated.

The code snippet below demonstrates creating a TStringField. The 
containing TForm is called Form1 (referred to here with the Self 
variable), the active data set is a TQuery named Query1, and the field for
which the TStringField component is being created is a dBASE table field
named CO_NAME. This new TField descendant will be the second TField in the
Fields array property of Query1. Note that the data set with which the new
TField descendant will be associated (in this case, Query1) must be closed
prior to adding the TField and then reopened afterwards.

procedure TForm1.Button2Click(Sender: TOObject);
var
  T: TStringField;
begin
  Query1.Close;
  T := TStringField.Create(Self);
  T.FieldName := 'CO_NAME';
  T.Name := Query1.Name + T.FieldName;
  T.Index := Query1.FieldCount;
  T.DataSet := Query1;
  Query1.FieldDefs.UpDate;
  Query1.Open;
end;

The example above example creates a new TStringField named Query1CO_NAME.

Deleting an existing TField descendant is merely a matter of invoking the 
Free method for that component. In the example below, the TForm's Find-
Component method is used to return a pointer to the TStringField component 
named Query1CO_NAME. The return value for the FindComponent will either be
of type TComponent if successful or nil if unsuccessful. This return value
can be used to determine whether the component actually exists prior to 
invoking its Free method.

procedure TForm1.Button1Click(Sender: TObject);
var
  TC: TComponent;
begin
  TC := FindComponent('Query1CO_NAME');
  if not (TC = nil) then begin
    Query1.Close;

    TC.Free;
    Query1.Open;
  end;
end;

As with creating a TField, if the data set associated with the TField 
descendant is currently active, it must be closed or deactivated prior to 
invoking this method.


Iterating through the fields of a table

Question

How can I iterate through the fields of a table?

Answer

There are a number of reasons why a program might need to query the
structure of a table used in the application. One reason is a prelude to
creating TField components at run-time that represent the fields in the
table. The information gleaned from the structure of the table form the
basis of the TField components to be created.

The example below demonstrates how to iterate through the fields available
in a TTable or TQuery. The example extracts information about the available
fields and displays the information in a TListBox, but the same methodology
can be used to provide information necessary for the dynamic building of
TField descendants. The example uses a TTable as the data set, but a TQuery
can be used in the same manner as both TTable and TQuery components incorp-
orate the Field-Defs property the same way.

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  F: TFieldDef;
  D: String;
begin
  Table1.Active := True;
  ListBox1.Items.Clear;

  with Table1 do begin
    for i := 0 to FieldDefs.Count - 1 do begin
      F := FieldDefs.Items[i];
      case F.DataType of
        ftUnknown: D := 'Unknown';
        ftString: D := 'String';
        ftSmallint: D := 'SmallInt';
        ftInteger: D := 'Integer';
        ftWord: D := 'Word';
        ftBoolean: D := 'Boolean';
        ftFloat: D := 'Float';
        ftCurrency: D := 'Currency';
        ftBCD: D := 'BCD';
        ftDate: D := 'Date';
        ftTime: D := 'Time';
        ftDateTime: D := 'DateTime';
        ftBytes: D := 'Bytes';
        ftVarBytes: D := '';
        ftBlob: D := 'BLOB';
        ftMemo: D := 'Memo';
        ftGraphic: D := 'Graphic';
      else
        D := '';
      end;
      ListBox1.Items.Add(F.Name + ', ' + D);
    end;
  end;
  Table1.Active := False;
end;


Loading bitmaps into dBase/Paradox BLOB fields

Question

How can I load bitmaps into dBase / Paradox BLOB fields?

Answer

There are a number of ways to load a bitmap image into the BLOB field of a
dBASE or Paradox table. Three of the easier methods involve 1) copying the
data from the Windows clipboard into a TDBImage component connected to the
BLOB field, 2) using the LoadFromFile method of the TBLOBField component,
and 3) using the Assign method to copy an object of type TBitmap into the
Picture property of a TBDBImage.

The first method, copying the bitmap from the clipboard, is probably most
handy when an application needs to add bitmaps to a table when the end-
user is running the application. A TDBImage component is used to act as an
interface between the BLOB field in the table and the image stored in the
clipboard. The PasteFromClipboard method of the TDBImage component is
invoked to copy the bitmap data from the clipboard into the TDBImage. When
the record is posted, the image is stored into the BLOB field in the
table.

Because the Windows clipboard can contain data in formats othher than just
bitmap, it is advisable to check the format prior to calling the CopyFrom-
Clipboard method. To do this, a TClipboard object is created and its Has-
Format method is used to determine if the data in the clipboard is indeed
of bitmap format. Note that to use a TClipboard object, the Clipbrd unit
must be included in the Uses section of the unit that will be creating
the object.

Here is an example showing the contents of the clipboard being copied into
a TDBImage component, if the contents of the clipboard are of bitmap
format:

  procedure TForm1.Button1Click(Sender: TObject);
  var
    C: TClipboard;
  begin
    C := TClipboard.Create;
    try
      if Clipboard.HasFormat(CF_BITMAP) then
        DBImage1.PasteFromClipboard
      else
        ShowMessage('Clipboard does not contain a bitmap!');
    finally
      C.Free;
    end;
  end;

The second method of filling a BLOB field with a bitmap involves loading
the bitmap directly from a file on dissk into the BLOB field. This method
lends itself equally well to uses at run-time for the end-user as for
the developer building an application's data.

This method uses the LoadFromFile method of the TBLOBField component, the
Delphi representation of a dBASE for Windows Binary field or a Paradox for
Windows Graphic field, either of which may be used to store bitmap data
in a table.

The LoadFromFile method of the TBLOBField component requires a single
parameter: the name of the bitmap file to load, which is of type String.
The value for this parameter may come from a number of sources from the
end-user manually keying in a valid file name to the program providing a
string to the contents of the FileName property of the TOpenDialog comp-
onent.

Here is an example showing the use of the LoadFromFile method for a
TBLOBField component named Table1Bitmap (a field called Bitmap in the
table associated with a TTable component named Table1):

  procedure TForm1.Button2Clicck(Sender: TObject);
  begin
    Table1Bitmap.LoadFromFile(
      'c:\delphi\images\splash\16color\construc.bmp');
  end;

The third method uses the Assign method to copy the contents