Delphi-Talk: majordomo@bridge.net the very best of delphi-talk.
Summary September 1995
Last updated: 14. September 1995 2250 cet

These questions and answers are taken from the delphi-talk of
majordomo@bridge.net and was build up from:
Marko Tietz
tietz@mailserv.rz.fh-merseburg.de

It will be regular updated!

I can't guarantee that all answers are correctly! Please use all informations
at your own risk!




Mail me:
- if you find this file useful
- if you want make some suggestions
- if you want get updates every month

I'm planning following:
- put this file on WWW, you can download it every time you want
- sort this file by various topics
- distribute it as a windows .hlp-file



=============================================================================

 Contents

 1. Save 500 chars from array into a file
 2. Property name at runtime
 3. Rebuild out of date index
 4. No showing of P_RECNO
 5. Checking the number of components at runtime
 6. 25.55 becomes to 24.499999 in a field of a calculated paradox-field
 7. Different dialogs from form template
 8. Dialogs, those modal considering the whole system
 9. Difference between using the StrPCopy function the pointer characters
10. Difference between two string types
11. Add #0 to a pascal string
12. Capture the minimize button, before application will be minimized
13. Vertical scrollbar in the TDBGrid
14. Set database property during runtime
15. Call GetProcAddress procedure
16. Load bitmaps from .RES-file
17. Reappering forms
18. Create colored panels of various sizes and form positions at run time
19. One table and database information on two physical forms
20. Stop the DBGrid control from auto-appending a new entry
21. Passwords on dBase-files
22. Current record number for a dataset
23. Respond an EConvertError before the application notifies the user
24. SQL-comand JOIN for more tables
25. Main difference between tabsets with notebooks and TabbedNoteBook
26. Missing visual cursor in a TStringGrid
27. Send escape codes to HP laser printer using the canvas printer option
28. Override of existing identical methods
29. Lost the highlight color for the selected cell in a TStringGrid
30. Searching with SQL
31. Two or more commands in TQuery-SQL Property
32. Determine a CD-Rom-Drive
33. Create Paradox-Tables
34. Use ChartFX.VBX
35. Include a combo box in a TDBGrid
36. Set dragging size of a form
37. .RES file containing multiple bitmaps
38. Write a stream into a BLOB-field
39. Dynamically identifiers for checkboxes
40. Date conversion - adding century
41. Multi-form Menus
42. Create listbox on runtime
43. Copy a DBMemo contents to another DBMemo field
44. Store variables in a listbox

=============================================================================

1.
Q:
I load an array of 500 chars at run time and I would
like to save it to a file.

A:
The following code should get you started.

Type
TCharArray = Array[500] of Char;

Procedure WriteToFile(Var aArray : TCharArray; sFileName : String); {Note:
Declaring the array as a Var parameter causes a pointer to the array to be
passed, rather than the copying of the entire array onto the stack - you
may prefer to take the slight overhead for the safety provided by not
passing it as a Var parameter.}

Var
nArrayIndex : Word;
fFileHandle : TextFile;
Begin
AssignFile(fFileHandle, sFileName);
Rewrite(fFileHandle);

For nArrayIndex := 1 to 500 Do
Begin
Write(fFileHandle, aArray[nArrayIndex]); End;

CloseFile(fFileHandle);
End; {end Procedure, WriteToFile()}

-----------------------------------------------------------------------------

2.
Q:
'Name' liefert fuer Forms zur Laufzeit stets einen
Leerstring (Bug ???).

A:
Du kannst die Eigenschaft dennoch in der von
die gewuenschten Weise verwenden, indem Du
ihr innerhalb der FormCreate-Methode den
entsprechenden Namen zuordnest:

procedure TForm1.FormCreate(Sender: TObject);
begin
   Name := 'TForm1';
...
end;

Um die Existenz einer Form festzustellen, empfehle
ich die folgende Vorgehensweise:

var FormExists: Boolean;
...
FormExists := Application.FindComponent('TForm1') <> nil;

-----------------------------------------------------------------------------

3.
Q:
Is there anyone out there that has been able to successfully rebuild an out
of date index (Paradox & BDE error) using DBIRegenIndexes?  I would really,
really, really appreciate seeing a snippet of code before I have to shoot
myself.

Table1 will not open because it's indexes are crashed -> Table1.Handle=nil ->
calling DBIRegenIndexes(Table1.Handle) results with an error "not a valid handle"

A:

-----------------------------------------------------------------------------

4.
Q:
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?

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.


-----------------------------------------------------------------------------

5.
Q:
Is there a way of checking how many components exist (at runtime) for the
entire application without having to run through every other components
ComponentCount property.

Included in the above question is to find components that are on forms
that are only created at runtime.  (This can exclude any additional
components created on these created forms at runtime.).

I would have thought that TApplications ComponentCount property would
indicate this but it does not.

A:
You can find out how many Components your application uses (which
have already been created...)

Every form that is in existence is stored in Screen.Forms, which is an array of Forms.
Every form has a ComponentCount.
So, you would use a routine like this:

function GetTotalComponents : Integer;
var
  TotalComps,
  CurForm : Integer;
begin
  TotalComps := 0;

  for CurForm := 0 to (Screen.FormCount - 1) do begin
    TotalComps := TotalComps + Screen.Forms[CurForm].ComponentCount;
  end;

  Result := TotalComps;
end;

In fact, I just ran and compiled it and it worked... although I
didn't check it for components that are made at runtime.  I do know
that it doesn't give an accurate count for Forms that are in the .dpr
file, but which aren't autocreated. So I'll have to work on that.

Also, I did a check of what Application.ComponentCount gives me, and
it gives you the # of forms/windows that it has. This includes one
invisible window which is the application window.

-----------------------------------------------------------------------------

6.
Q:
Why 25.55 become 24.499999?
I have a paradox table that contains currency values. I am trying to
calculate totals for some of the fields programmatically with the
following code:

 Table1.Open;
   Table1.first;
   VATTotal := 0;
   SalesIncVAT := 0;
   While not Table1.EOF do
     begin
       VATTotal := VATTotal + Table1VAT.value;
       SalesIncVAT := SalesIncVAT + Table1GrandTotal.value;
       Table1.next;
     end;
     Label2.caption := FloatToStr(SalesIncVAT);
     Label3.caption := FloatToStr(VATTotal);
     Label1.caption := FloatToStr(SalesIncVAT - VATTotal);

A:
The label captions all show the same type of answer as the subject
line.  Can someone explain what I've done wrong.

You're not to blame for the calculation error!

I've found the same type of problems in an accounting package I'm writing
(=serious!).
Seems to me that Borland has some calculating coding to do.

You can get around the problem using the round function.
SalesIncVAT:=round(SalesIncVAT*100)/100;  {gives you two decimal places}

A:
that's nothing extraodrinary, this is a basic property of floating point
math, which is precise only to a given number of decimal digits. More
specifically float is precise for intermediate range integers and for
fractions which are a sum of components which are powers of 2, any other
number is rounded at the float accuracy (7 digits for single precision, 15
for double, 20 for extended). One has to use round or the str procedure:
var
  s : string;
begin
  str(SalesIncVat:10:2,s); {10 characters altohether (with point) and 2
                            decimal digits}
  Label1.Caption:=s;

A:
In the help it is written that FloatToStr formats the string with 15 decimal
digits - that is why your number is displayed in a fancy fashion, try this
function with a number like 25.5, 25.25, 25.125 or so i.e. one that has a
finite representation in binary notation and you should get it back.

Or use the function FloatToStrF which takes also specification of total
width and number of decimal digits as its arguments.

-----------------------------------------------------------------------------

7.
Q:
My main problem is that i am using different 'dialogs' from the forms
template. Where i have used a form i can amend the code to create it at
runtime ie when needed with the following code
   formx := tformx.create(appliaction);
   formx.showmodal;
   formx.free;
However if i use the same code on the form using the form name/aliasname
the form does not show.

A:
try this code:

          Application.CreateForm(TFormX, FormX);
          FormX.ShowModal;
          FormX.Free;

don't forget that the visible property of this form have to be false
when it is created, if it isn't you will get an error when you want
to show it modal.

-----------------------------------------------------------------------------

8.
Q:
with showmodal i show a dialog, which is modal considering the app. how
can i show a dialog, which is modal to the whole system (no switches to other
tasks should be allowed)?
i want to show dialogs, which comes on the top of all windows if the app
running at background. how to realize this?

A:
Check out API-function SetSysModalWindow
Check out API-function BringWindowToTop

A:
If you just want a simple dialog with a bit of text, an optional
icon and some buttons, use MessageBox in WinProcs
and use MB_SYSTEMMODAL in the TextType parameter.

A:
Use the SetSysModalWindow() Windows API function.  The following code
snippet demonstrates its use.  There can be only one system-modal window at
a time, and that handle is returned by SetSysModalWindow().  I suggest that
you save that return value and then reset it when you are finished, as
demonstrated below.

procedure TForm1.Button1Click(Sender: TObject);
var
   x : word ;
begin
     x := SetSysModalWindow(AboutBox.handle) ;
     AboutBox.showmodal ;
     SetSysModalWindow(x) ;
end;

-----------------------------------------------------------------------------

9.
Q:
What is the difference between using the strPCopy Function and just
using the pointer characters ^ or @?

A:
The strPCopy function converts a Pascal length byte string into PChar
format primarily for use with Windows APIs that expect them.

-----------------------------------------------------------------------------

10.
Q:
Is there anything wrong with the following segment of code?
var
  s : string;
  p : pstring;

begin
     s := 'Sample String';
     p := @s {or p := s^, for that matter}

A:
Nothing is "wrong" with it, but a pstring <> a PChar.

A:
There should be no real problem, but remember that the first byte of
a pascal style 'string' contains the length of the string, so you
really would want to reference the second byte.

A:

STRING <> PCHAR
You are trying to pass a string as an agruement that wants a memory
location (pointer). Either declare the parameter as String in your
function or pass a pointer to the string you are creating.
Anyway...in brief, without lecturing on memory usage, etc:
CHAR is the variable type for a SINGLE character
PCHAR is the variable type for a POINTER to A character which is NOT
limited to ONE character but can extend to 65,535 characters depending on
the memory you allocate for it -> that is the key here, it can occupy the
space you ALLOCATE or you will run-over memory and Windows will be sad.
However, remember that your are still only "pointing" to one character, it
is just that the memory that follows it has been allocated by you and can
contain other characters (you can walk down the memory by adding 1 to the
pointer as you go along - hence you are limited to the 64K PCHAR block in a
16-bit OS).

STRING is the variable type for a pre-allocated array of characters with
a max size of 255.

-----------------------------------------------------------------------------

11.
Q:
I have seen many different pieces of code, some of which append a #0
to the end of a string etc., but this seems to work just fine for me.
If there is something wrong, please elaborate under what circumstances it
would rear it's ugly head.

A:
The problem may be that you're not clear when you're calling standard
Windows APIs that expect PChars, vs. Delphi's wrapper functions for many of
these, which usually take Pascal style strings.  My experience has been that
it's easy to get junk characters in a string or even throw a GPF if you're
not careful with how you handle this stuff.

The best "hack" for this that I know of is to pass the address of the
[1]th element of a string to the API call (this skips the length byte of
the string).  Many APIs do rely on the presence of the null byte at the
end of a string for length determination so it's wise to add it, even if
it sometimes appears to work without it.  Something like:

var
  S : string ;

begin
  S := 'This is the string to pass' + #0 ;
  SomeAPICall( @S[1] ) ;

You can also use a Pascal string as a return buffer in similar fashion
with APIs that specify a maximum buffer length and return the number of
characters retrieved as the function's value:

var
  S : string[128] ;

begin
  S[0] := Chr( SomeOtherAPICall( @S[1], 128 )) ;

Since .INI file entries and String resource items are both limited to
255 characters anyway, and it's often easier to manipulate Pascal style
strings programmatically, these techniques can be quite useful for some
of the more common Windows string purposes.

-----------------------------------------------------------------------------

12
Q:
Does anybody know how to capture the minimize button press and act on
it before it actually minimizes the form?

A:
You should intercept WM_SYSCOMMAND messages like this:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    public
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}
procedure TForm1.WMSysCommand;
begin
  if (Msg.CmdType = SC_MINIMIZE) or
     (Msg.CmdType = SC_MAXIMIZE) then
    MessageBeep(0);
  DefaultHandler(Msg);
end;

end.

-----------------------------------------------------------------------------

13.
Q:
I've noticed that the vertical scroll bar in the TDBGrid doesn't
behave as it should (cannot guage the current record's position in
the table by the scroll bar).  I would  like to know if there is any
way to get the scroll bar to work like the one in the Paradox TableFrame.

A:
Well.... yes and no.  The problem with the scroll bar is that not all
data formats (in fact, almost none) support record order No's.  In
other words, there is no unique identifier within a table that
specifies where in the table a record appears.  For example in dBase
tables, the records have a recNo based upon the order they were
entered.  If you put an index on the fields, these numbers are totally
out of whack.  SQL tables & queries on the other hand has never heard
of a record order no and never will.  Paradox tables are the only ones
that include a logical sort order no in each index that they create.
For whatever reason, Borland choose to make the scroll bar behave the
same regardless of whether or not you were using Paradox tables.  You
can get around this by turning off the scroll bars and using a
TScrollBar component to simulate the behaviour that you want.

-----------------------------------------------------------------------------

14.
Q:
I want to set Database property of TTable in a program code. I tried:
TTable.Database := Database
It does not worki. Delphi says that the Database property is RdOnly.

A:
Set the DatabaseName property to either:
  the directory where your table files are.
  the BDE alias of your database.
  the DatabaseName of your TDatabase component if you have one.

-----------------------------------------------------------------------------

15.
Q:
I have to call GetProcAddress procedure, but I don't know how.

A:
First you need to declare a procedural data type. Here is a quick example
that should explain it. BTW you will need a procedural data type for all of
the different command lines you will be calling.

type
  TMyProcType = function( X, Y: Integer ): Integer;

  ...
  ...

var
 nHandle: THandle;
 MyProcType: TMyProcType
begin
  nHandle := LoadLibrary( 'MYDLL.DLL' );
  
  if nHandle < 32 then 
    raise EDLLLoadError.Create( 'Cant load the sucker' );

  @MyProcType := GetProcAddress( nHandle, 'MYFUNCNAME' );

  { Now call it like a function, ex:}
  z := MyProcType( 10, 10 );

  FreeLibrary( nHandle );

-----------------------------------------------------------------------------

16.
Q:
I have a .RES file that contains bitmaps. How do I get an image on a
form to display them?

A:
Be sure to bind it to the EXE by doing a {$R RESFILENAME.RES} then you can
use the LoadBitmap.

 TImage.Picture.Bitmap.Handle := LoadBitmap( Handle, 'BITMAPNAMEHERE' )

-----------------------------------------------------------------------------

17.
Q:
I my current project, the main window is set to hide itself upon creation if
a certain command-line option is set. This works great (another form,
created from the main form, shows up and takes user interaction), but
when the application is done, and the main form is closed, it reappears
for a second, then disappears. Actually, only the gray background on
the form shows up. But I'd like it NOT to show up (sigh). The app works
fine, just this cosmetic glitch.

A:
I've had this problem before and the work around is a trick I stumbled
upon by accident. Just move your "hidden" window off the screen! You
can place it at (-1000,-1000) and it will not show up when it closes.
Either do this when it starts (when you hide it) or just before it closes.

-----------------------------------------------------------------------------

18.
Q:
I'm trying to make a type of time line display form a shedule system. What I
need to do is create Colored panels (tpanels) of various sizes and form
positions at run time and attach an onclick method to them.
I have tried to make an array of Tpanel and create the panels but after
setting valid sizes and colors and callen show, I can't get them to appear
on screen.

A:
This is just a guess without seeing your code, but did you set the parent of
the panels? You NEED the following two lines in the OnCreate event of the
form to display a control on that form :

MyPanel := TPanel.Create(Self);
MyPanel.Parent := Self;

-----------------------------------------------------------------------------

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

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.

-----------------------------------------------------------------------------

20.
Q:
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?

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;

-----------------------------------------------------------------------------

21.
Q:
I have a problem. 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.

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.

-----------------------------------------------------------------------------

22.
Q:
How can I determine the current record number for a dataset?

A:
If the dataset is based upon a Paradox or dBASE table then the record number
can be determined with a couple of calls to the BDE (as shown below).  The
BDE doesn't support record numbering for datasets based upon SQL tables, so
if your server supports record numbering you will need to refer to its
documentation.

The following function takes as its parameter any component derived from
TDataset (i.e. TTable, TQuery, TStoredProc) and returns the current record
number (greater than zero) if it is a Paradox or dBASE table.  Otherwise,
the function returns zero.

NOTE: for dBASE tables the record number returned is always the physical
record number.  So, if your dataset is a TQuery or you have a range set
on your dataset then the number returned won't necessarily be relative to
the dataset being viewed, rather it will be based on the record's physical
position in the underlying dBASE table.


    uses DbiProcs, DbiTypes, DBConsts;

    function RecordNumber(Dataset: TDataset): Longint;
    var
      CursorProps: CurProps;
      RecordProps: RECProps;
    begin
      { Return 0 if dataset is not Paradox or dBASE }
      Result := 0;

      with Dataset do
      begin
        { Is the dataset active? }
        if State = dsInactive then DBError(SDataSetClosed);

        { We need to make this call to grab the cursor's iSeqNums }
        Check(DbiGetCursorProps(Handle, CursorProps));

        { Synchronize the BDE cursor with the Dataset's cursor }
        UpdateCursorPos;

        { Fill RecordProps with the current record's properties }
        Check(DbiGetRecord(Handle, dbiNOLOCK, nil, @RecordProps));

        { What kind of dataset are we looking at? }
        case CursorProps.iSeqNums of
          0: Result := RecordProps.iPhyRecNum;  { dBASE   }
          1: Result := RecordProps.iSeqNum;     { Paradox }
        end;
      end;
    end;

A:
{Retrieves physical record number a la xBase  Requires DBITYPES, DBIPROCS,
 and DBIERRS in the uses clause of the unit.  Function takes one argument
 of type TTable (e.g., Table1).}
function Form1.Recno( oTable: TTable ): Longint;
var
  rError: DBIResult;
  rRecProp: RECprops;
  szErrMsg: DBIMSG;
begin
  Result := 0;
  try
    oTable.UpdateCursorPos;
    rError := DbiGetRecord( oTable.Handle, dbiNOLOCK, nil, @rRecProp );
    if rError = DBIERR_NONE then
      Result := rRecProp.iPhyRecNum
    else
      case rError of
        DBIERR_BOF: Result := 1;
        DBIERR_EOF: Result := oTable.RecordCount + 1;
	else
        begin
          DbiGetErrorString( rError, szErrMsg );
          ShowMessage( StrPas( szErrMsg ));
	  end;
	end;
  except
    on E: EDBEngineError do ShowMessage( E.Message );
  end;
end;

-----------------------------------------------------------------------------

23.
Q:
I'm using a DBEdit component for the user to enter data into a table's Date
field. If the user enters an invalid date - like 4/32/95 - Delphi will
raise an EConvertError. What I would like to do is respond to that error
before the application notifies the user of the bad data. I have tried the
Table's BeforePost and OnExit events - the DataSource's OnDataChange and
OnUpdateData - and the DBEdit's own OnExit.  All seem too late - the
exception has already been displayed to the user.

Any place to catch the DBEdit.Text after the user tries to leave the
component but before Delphi attempts the conversion?

A:
You could write your own exception handler to filter out the EConvertError.

Something like this:

Declare this procedure in your main form object:

    procedure MyException(Sender:TObject; E:Exception);

Then write it like this:

procedure TMyForm.MyException(Sender:TObject; E:Exception);
begin
  if (E.ClassType.ClassName='EConvertError') then
  begin
    {do something great to (or for) the user}
  end
  else Application.ShowException(E); {let Delphi take it from here}
end;

Finally set your application to use your new handler:

procedure TMyForm.FormCreate(Sender: TObject);
begin
     Application.OnException := MyException;
end;

----------------------------------------------------------------------------

24.
Q:
I've been messing around with doing JOINS on different tables using
the query component. Joining two tables is no problem, but what if
one wants to join three or four tables?
Maybe the SQL to do this isn't supported by the Delphi/BDE...?

A:
It's really easy. Just use ReportSmith to do the job for you. Specify
the tables, set inclusion parameters etc, and then look at the
generated SQL code. Copy it into a query component and... It works!

-----------------------------------------------------------------------------

25.
Q:
Can someone tell me what the main difference is between using these
two types of components. Tabsets with notebooks and TabbedNoteBook
component?  Is one a lot heavy on resources?  I suspect that the
Tabbed Notebook would be since it does everything for you.

A:
As I see it, the Tabbed Notebook
+ is easier to work with (design time) thanks to the integration of the
  tabset and the notebook
- is lacking the option to place the tabset where you like
Considering that Borland (a) within Delphi mostly uses the bottom-attached
style, and (b) didn't supply the sources even in the full VCL source kit,
one might wonder a bit, but I'll leave that to you.
The Orpheus VCL components has a "tabbed multi-page form" using which you
may put the tabset where you like plus more features.

A:
Resource-wise, they appear to be virtually identical. The biggest difference
(other than the obvious aesthetic difference in terms of the placement of the
tabs) is that you are forced to have all tabs visible when using the
TabbedNotebook. When using the Tabset and Notebook combination, you can allow
horizontal scrolling to show additional tabs (a la Component Palette).
Depending upon the situation, that may be preferable.

-----------------------------------------------------------------------------

26.
Q:
I have the options in a TStringGrid set to the same values of another
TStringGrid (separate app). When I run the app and try to edit one of
the cells, I don't get a visual cursor.

A:
I had the same problem and it took forever to find it. If DefaultRowHeight
<= 14, the visual cursor disappears!  My solution was to set DefaultRowHeight
to 15.

-----------------------------------------------------------------------------

27.
Q:
What is the best way to send the escape codes to my HP laser printer using
the canvas printer option.

A:
You basically need to call the Windows API function Escape() using the
Passthrough method. ReportPrinter has a PrintData command to take care of
this but TCanvas does not. Here's the code line you need if you want to code
it yourself:

  Escape(Printer.Handle,PASSTHROUGH,0,@Buffer,nil);

Where Buffer contains a length (16 bit) followed by the raw data to send to
the printer.

-----------------------------------------------------------------------------

28.
Q:
I need to add a couple of properties, methods and events to each of the
standard components and override a couple of the existing methods. Since
all of these except the overriden ones are identical for all or most of the
components, it makes sense to write a class which handles these.

A:
You could write a unit in which you override each of the standard
components that you would like to modify.  However, instead of
copying virtually identical code into every component, just add
calls to a few standard procedures that all of the components
share.

E.g.:

procedure SharedProcedure(Sender : TComponent);
begin
  {do standard processing to manipulate Sender component}
end;

procedure TMyLabel.Whatever; {overridden method of TLabel descendant}
begin
  SharedProcedure(Self);
end;

procedure TMyListBox.Whatever; {overridden method of TListBox descendant}
begin
  SharedProcedure(Self);
end;

This way, all you duplicate are the procedure calls.  The bulk of
the code just resides in a single place.  This is almost as good
as having the multiple inheritance...almost.

-----------------------------------------------------------------------------

29.
Q:
I have a StringGrid component with an over-rided OnDrawCell event. The
problem is that I have lost the Highlight color for the selected cell(s).
What do I need to add to gain this back?

A:
Obviously you have changed DefaultDrawing to false.  This is a bad
move, in my opinion, since you lose almost all the nice drawing that
the TGrids take care of for you. When I created my TWrapGrid, I
initially turned it off, colored the grids the way I wanted, but then
realized that it was better to just output text and let
DefaultDrawing handle the rest.
If you really insist on coloring your cells differently AND have the
highlight color for the selected cell, you should take a look at the
Grids source code and extract it from there.

-----------------------------------------------------------------------------

30.
Q:
And using SQL, can I do a search for "John" and have it come up with
the field "Lennon, John"?

A:
Assuming:

1. Your person table is defined along the lines of...

last_name char (n),
first_name char (n)

do...

select
  last_name+', '+first_name
from
  person
where
  first_name='john'

2. Your person table is defined along the lines of...

person_name char (n)  (eg. Lennon, John)

do...

select
  person_name
from
  person
where
  person_name like '%John'  <--- 'John' has to be at end of string, else use '%John%'

-----------------------------------------------------------------------------

31.
Q:
Is there a way in local sql use more than one sql-command? I got an error if
I want to use a second sql command in TQuery-component.

A:
I presume you are trying to attach a new query to an existing TQuery.

  Query1.Sql.Clear;
  Query1.Close;
   Query1.Sql.Add('select * from "monitor.dbf" order by location,dept');
  Query1.Open;
  Query1.Refresh;

The trick is in closing your query before assigning a new one.

-----------------------------------------------------------------------------

32.
Q:
How can i determine if a CD-ROM drive is on the machine. I tried using the 
WIN API GetDriveType but it return the same number (4) if it is a CD-ROM or 
a network drive.

A:
This should do the trick..

function IsCDROM(DriveNum: Integer): Boolean; assembler;
asm
  MOV   AX,1500h { look for MSCDEX }
  XOR   BX,BX
  INT   2fh
  OR    BX,BX
  JZ    @Finish
  MOV   AX,150Bh { check for using CD driver }
  MOV   CX,DriveNum
  INT   2fh
  OR    AX,AX
  @Finish:
end;


BTW under Win32 GetDriveType properly returns a CD-ROM drive.

-----------------------------------------------------------------------------

33.
Q:
How to create Paradox-Tables?

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;

-----------------------------------------------------------------------------

34.
Q:
I am trying to use the CHARTFX VBX componenet in an application. I am still
relatively low on the learning curve for Delphi and I've had no experience
with Visual Basic. Besides the Object Inspector, how can I find out what
methods, etc. are available for manipulating this component?

A:
Although its usefulness can be debated, there is context-specific help
available for ChartFX. Drop the component onto a form, then press F1 while
it is selected.

-----------------------------------------------------------------------------

35.
Q:
I have in my app a TDBGrid, and one of their fields is a selection of 
predetermined values (like a boolean one, with True or False). I want 
to put a combo box in that line to give a more confortable way to the
user to enter the data. How can i obtain that?

A:
Basically what you want to accomplish is
1. Create and draw a TComboBox every time you enter one of the cells
in your grid in that column
2. Get the current value from the field (if any) and put it into the
CB
3. After done, put the new value from the CB into the field
4. get rid of the CB

-----------------------------------------------------------------------------

36.
Q:
I want to set a minimum tracking size for a MDI form in my application.

A:
Put the following line in the private part of your form class...

 procedure WMGetMinMaxInfo(var Message : TWMGetMinMaxInfo); message 
WM_GETMINMAXINFO;

... and the next function in the implementation section of the unit...

procedure YourClassNameHere.WMGetMinMaxInfo(var Message : TWMGetMinMaxInfo);
begin
{
The following code prevents the user from changing the size of the form but 
replace Width and Height 
with the min and max values that you require. (anyway you get the idea).
}
    Message.MinMaxInfo^.ptMinTrackSize := Point(Width, Height);
    Message.MinMaxInfo^.ptMaxTrackSize := Point(Width, Height);
end;

-----------------------------------------------------------------------------

36.
Q:
I have a .res file that has several bitmaps in it. I'd like to use 
some of the bitmaps for the TSpeedButtons of a component, could anybody 
show me a simple sample code for that?

A:
Put the following compiler directive in the implementation section of your 
unit.
{$R ResourceFilename}

Then in the OnCreate handler of your form that contains the speedbuttons do 
the following:

 SpeedButton.Glyph.Handle := LoadBitmap(HInstance, 'BITMAP_NAME');

or use '#999' where 999 is the numeric resource id if you used numbers instead 
of a string to identify bitmap resources in your .res file. The bitmaps for 
each of the button states must be in the same bitmap resource.

-----------------------------------------------------------------------------

38.
Q:
How can I write a Stream into a BLOB-Field?

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 <BufferName>[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}
  <Blob/Memo/GraphicFieldName>.LoadFromStream( oMemory );

  { No leaky Apps}
  oMemory.Free;

end;

-----------------------------------------------------------------------------

39.
Q:
My code looks something like this...
  if CheckBox(var).checked=True then.... 
  where (var) is a counter in a for loop.
Is the number of checkboxes not known when coding ? ie created only at run 
time?

A:
When in design mode, you (the programmer) really should know how many
checkboxes are on a given form.  When the App is running...
Use Delphi's Run Time Type Information (RTTI).
For a given form, try the following code snippet:

var
   i : Integer
begin
   for i := 0 to ComponentCount - 1 do:
      if Components[i] is TCheckBox then
         (Components[i] as TCheckBox).Checked then
         begin
            ... insert your code here ...
         end;
end;

In addition, the following code is a valid statement in Delphi:

        if Components[i] = CheckBox5 then
           DoSomething;

Also, each component in Delphi has a Published Property called 'Tag',
you can use this to your advantage by setting the Tag to some non-zero
number at design time, then using it at run-time, ie:

var
   i : Integer
begin
   for i := 0 to ComponentCount - 1 do:
      if Components[i] is TCheckBox then
      with (Components[i] as TCheckBox) do
         Case Tag of
            1 : if Checked then DoSomethingOnBox1;
            2 : if Checked then DoSomethingOnBox2;
            ... etc ...
         end;
end;

For more info, keyword search Delphi's help on "ComponentCount".

-----------------------------------------------------------------------------

40.
Q:
I have to convert a Date from the format 'DD/MM/YY' to 'DDMMYYYY'

A:
LongDate := FormatDateTime('ddmmyyyy', StrToDate(ShortDate));

This converts a date held in the format specified in the
Control Panel's short date format (which could well be
DD/MM/YY) to the format specified in the Format string
(in the example DDMMYYYY).

If the DD/MM/YY is an input field, and DDMMYYYY is
a database field, it might clever to use code like this as
it should still work correctly for a user who wants to use
a different date format, and accordingly specifies this in
his/her Control Panel.

(Of course YYYYMMDD would normally be better than
DDMMYYYY as a database field because it would be
easier to sort records to date sequence.

-----------------------------------------------------------------------------

41.
Q:
I have a project that has multiple NON-MDI forms. The problem I
am having is that if I put a menu on a form other than the form
designated as the "Main Form," it does not work correctly. The menu
on the main form pops up whenever you hit the Alt key. If the main
form does not have a menu, the program just beeps at you obnoxiously
and tries to switch focus back and forth.
Can non-Main form's have menus or not? If so, how do I convince my
APP to let the user access them with the alt key.

A:
In you "Main Form" you need to catch key events and pass them to the 
NON-MDI forms by calling their proceedures, ie. using the NON-MDI forms 
event handlers.  If you like this idea then can use the onkeydown event in 
the "Main Form" to check to see if the user has pressed an ALT key 
combination before calling the NON-MDI forms proceedure.  Don't forget to 
add the NON-MDI forms unit to the USES section of the "Main Form". I 
assumed you probably already have done this anyhow.
If you don't like that idea, then the only other way I know is to set the 
focus to the NON-MDI form before the user presses a key.

-----------------------------------------------------------------------------

42.
Q:
I'll like to create a list box dynamically at run-time that fills half the
size of the form ..... and I'll like it to be adjusted too when
the form resizes (so that there will be no scroll-bars on the form).
How can I determine the size of the form and create this list box
dynamically?  also, can list boxes take more than 5,000 items ??

A:
Setting the ListBox alignment to alLeft will cause the ListBox to be resized
whenever the form is resized. Setting the width is easy (remember that the
Width you see on the right side of the assignment is the Form's 
Width property).

The number of elements that a ListBox will hold is limited only by available 
memory.

procedure TForm1.CreateListBox;
var
   LB : TListBox;
begin
   LB := TListBox.Create;
   LB.Align := alLeft;
   LB.Width := Width div 2;
end;

A:
Here's logic that creates the list box dynamically and resizes it when the
window is resized.  I hope it is helpful.  Also, I believe that a listbox is
limited to 32K of data.

unit Unit1;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs,
  StdCtrls  { you'll need this for the ListBox }  ;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  listbox: TListBox ;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
     listbox := TListBox.Create(self) ;
     listbox.Parent := self ;
     listbox.Top := 0 ;
     listbox.Left := 0 ;
     listbox.Width := self.Width div 2 ;
     listbox.Height := self.Height div 2 ;
     listbox.items.add('test 1') ;
     listbox.items.add('test 2') ;
     { etcetera ... }
end;

procedure TForm1.FormResize(Sender: TObject);
begin
     listbox.Width := self.Width div 2 ;
     listbox.Height := self.Height div 2 ;
end;

end.

-----------------------------------------------------------------------------

43.
Q:
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".

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

-----------------------------------------------------------------------------

44.
Q:
In a delphi listbox is there any way to store a variable with each entry (in
a sorted list box).

A:
A TListBox has a TStringList to store the strings. TStringList conatins an
items property (or something like that) that stores a pointer (or anything
the size of a pointer or smaller). So, for example, you can store an
object, component, record, or a variable to correspond to each string in the
TListBox.

-----------------------------------------------------------------------------
