VCL

VCL

Vertical scrollbar in the TDBGrid
Create colored panels of various sizes and form positions at run time
Main difference between tabsets with notebooks and TabbedNoteBook
Missing visual cursor in a TStringGrid
Lost the highlight color for the selected cell in a TStringGrid
Include a combo box in a TDBGrid
Dynamically identifiers for checkboxes
Create listbox on runtime
Store variables in a listbox
Name property during runtime
Handle click for several buttons, using the caption
Text in vertical direction
Displaying memo field in DBGrid
Multiselect in a stringgrid
Center text in cells of TStringGrid
Change the number of tabs in a TTabSet at runtime
CheckBox array - using common event
Right-editing in TEdit and columns of TStringGrid
More than one line in a cell of TStringGrid
Events for runtime created components
Component Creation
CheckBox array, how to use common event
Shared Controls on a TTabbedNotebook
Screen sizes and stringgrids
Aligning cells in stringgrid
Coloured StringGrid
Testing for the existence of a Component Property
Set event-handler at run-time
Application events
ReleaseDC and TCanvas
Using TStringList in a visual component
#0 KeyPress
StringGrid Masks
StringGrid right alignment
FileName property in non-visual component
SendMessage and TLabel
Call the Hint method directly
Changing in TOpenDialog
Edit in StringGrid
How to detect a row focus change in TDBGrid
Combobox problems
Change Grid Cell Color
TEdit and OnEnter event
How do I create a component like TField
Accessing notebook pages
Change delete behavior in Memo
Listbox with graphic
Masked Find in TStringList
Dual list box
OnClose proc
Colouring fields in DBGRIDS
"Autosizing" StringGrids
Right aligning menus
Which button on panel is the Sender
Publishing properties that are sets
Right Text Alignment in edit box
Multi line Hints
TTimer question
Make TAB act like ENTER in StringGrid
Cycle through list of components
Splitter bar
Popup menu in dependence on mouse position
TabbedNotebook and common components on all pages
How to disable a tab(page) in a Notebook component
Insert text in MEMO
Name of the item in a TListBox
Sync'ing Tabset with Listbox
TDBGrid - Vertical Scrollbar
Variable control-names
OnkeyDown and Hot-key problem
Add an OnClick event to DBGrid
Events for components created at Run-Time
Alignment in Listbox
Two columns in DBLookupComboBox
Change the color of a grid cell in a TDBGrid
Cells' Position on DBGrid
Listbox - OnChange
TabbedNotbook enable / disable one page
SETFOCUS in the StringGrid
Different colors in DBGrid
Cursor Pos in TRichEdit
Trapping OnEnter in my component
Display popup menu
Canvas.TextWidth
TDBNavigator buttons
Tabbed Notebook and visible components at several pages
How to empty a DBEdit
OnDraw Event for TStringGrid
Hiding TabbedNoteBook Pages
Accessing memo field data
Word manipulation in TStringGrid
Restrict the length of a TStringGrid field
TextOut to a control's parent
StatusBar - How to display Clock/Date/Keyboard Status
Icons in a Popup menus
Accessing Components in a TGroupBox
Incrementing String Field
Using OnHint events among multiple forms
Moving to a tab by name on a tabset
Control font styles
Removing the vertical scrollbar from a TDBGrid
Getting a device context for a control
Disabling DBNavigator buttons
DBGrid that shows images
Using canvas in user-defined components
Accessing other components from a base component
Extending DBGrid
Creating a resizeable (elastic) panel
Assigning OnClick events for menu items created at run-time
Setting boundaries for newly created controls
Expanding a path to a TUutlineNode referenced by index
Associating a string with each component
Populating TDBComboBoxes and TDBListBoxes
Activating horizontal scrollbar for listboxes
Click and move components at run-time
Validating input in TEdit components
Different colored characters in a string grid
ISBN validation
Selecting multiple records in TDBGrid
Moving in a TMemo Field
Copy of component properties
TMenuItem - create and add an event at runtime
Scrolling a TRichEdit control
Mask Edit
List Box Horizontal Scroll
ComboBox dropdown
TProgressBar in TStatusPanel
TTreeView slow down


Vertical scrollbar in the TDBGrid

Question


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.



Answer


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.




Create colored panels of various sizes and form positions at run time

Question


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.



Answer


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;




Main difference between tabsets with notebooks and TabbedNoteBook

Question


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.



Answer


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.




Missing visual cursor in a TStringGrid

Question


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.



Answer


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.




Lost the highlight color for the selected cell in a TStringGrid

Question


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?



Answer


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.




Include a combo box in a TDBGrid

Question


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?



Answer


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




Dynamically identifiers for checkboxes

Question


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?



Answer


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".




Create listbox on runtime

Question


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 ??



Answer


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.




Store variables in a listbox

Question


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



Answer


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.




Name property during runtime

Question


It looks like Sender is looking at the value in the 'Name' property.  Is there a way to map the value in Sender to a component type ( such as TEdit, TCheckbox, Tlabel, etc. )?



Answer


A:

If ( Sender is TLabel ) then..

or if you know it is going to be something you can

TLabel( Sender ).Caption

                                                                                                                             A:                                                                                                                         To access the name property, use the either one of the following

examples:



formname:=(myform as tobject).name;



or



formname:=(myform as tcontrol).name;



this should do it for you. The reason why delphi does not want you to

do this is because it has certain controls that do not have specific

names and therefore, the compiler would have to check each call to

see if it is valid.



A:

If you use the ClassName property like this.



   with Sender as TForm do

      Label1.Caption := copy(ClassName,2,length(ClassName)-1);



This will give the desired effect without extra coding in the Form's create

method.



A:

'Sender' may well not be the form in question, and your program will throw an

exception on the invalid typecast. I'm not sure under which conditions (if

any) Sender actually would be the Form itself.



Anyway, you can protect yourself by bracketing the call with a exception

handler or just a simple test, like so:

  If Sender is TForm then

    Label1.Caption := (Sender as TForm).Name ;



If what you're trying to accomplish is the following:

  Label1.Caption := Form1.Name ;



That's a whole different kettle of fish. I've read a lot of complaints

that this or that property of a Form isn't available at run time, most

of the cases seem to be linked to a misapprehension regarding class

initialization. If you read the Delphi docs carefully, you'll note that

setting a property in the Object Inspector does NOT automatically set

that property for run-time purposes.  The answer to THIS situation is to

explicitly set the property (.Name in this case) in the Form's .Create

method. So, some code like the following WILL work:



procedure TForm1.Create( Sender : TObject ) ;

begin

  Form1.Name := 'Form1' ;

end ;



procedure TForm1.Button1Click( Sender : TObject ) ;

begin

  Label1.Caption := Form1.Name ;

end ;



A:

var

  TC: TComponent;

begin

  TC := label1.Owner;

  label1.Caption := TC.ClassName;

end;



A:

I added a button to my form and in its event handler I put:



	name := 'AName';



Then after clicking on the button, I could then click on the form and

the label's caption changed to 'AName'.

The solution is to define the property Name in the form's create event.

I.E. if you have a form named MyForm then in its OnCreate event you

should have:



	name := 'MyForm';



This will solve your problem, but I agree its a little abnoxious.


Handle click for several buttons, using the caption

Question


I want to write one piece of code to handle click for several buttons, using

the caption (or name) property of each button.

I can't, have had to use a long series of IFs with specific names for each

button: IF sender = button1 THEN... ...button1.caption....button2...button.

This is very inelegant, and I'm sure there's a cleaner way of doing it.



Answer


A:

It sounds like you have, but, first make sure that you have the OnClick

event for each button on the calculator ( numeric buttons 0..9 ) pointing a

common event handler.



In the common event handler for the numeric buttons extract the button

caption with something like:



	Edit1.Text := TButton(Sender).Caption;



A:

In this case I think it would be perfect to use the Tag property of each

button:

1) assign a unique Tag to each button (e.g. it's Arabic equivalence)

2) procedure TForm1.Button1Click(Sender: TObject);

   begin

     if (Sender is TButton) then

       with (Sender as TButton) do

	 {use Tag}

   end;



A:

If all you want is the caption, then there is indeed a very elegant way

to do it. Attach this event handler to all of your buttons and use typecasting:



procedure TForm1.Edit1Click(Sender: TObject);

begin

     edit1.text := (sender as TButton).caption ;

end;

It will not suffice to say:



	sender.caption



because the compiler has no way of knowing if "sender" will have a caption

property.




Text in vertical direction

Question


I am in need of displaying text in the vertical direction from either upward - downward or downward - upward directions, as well as inclined texts (that is, in non-horizontal directions). How could I do it?



Answer


A:

var

Hfont: Thandle;

logfont:TLogFont;

font: Thandle;

count: integer;

begin

       LogFont.lfheight:=30;

       logfont.lfwidth:=10;

       logfont.lfweight:=900;

       LogFont.lfEscapement:=-200;

       logfont.lfcharset:=1;

       logfont.lfoutprecision:=out_tt_precis;

       logfont.lfquality:=draft_quality;

       logfont.lfpitchandfamily:=FF_Modern;



       font:=createfontindirect(logfont);



       Selectobject(Form1.canvas.handle,font);



       SetTextColor(Form1.canvas.handle,rgb(0,0,200));

       SetBKmode(Form1.canvas.handle,transparent);



       {textout(form1.canvas.handle,10,10,'Rotated',7);}



for count:=1 to 100 do

    begin

	 canvas.textout(Random(form1.width),Random(form1.height),'Rotated');

       

SetTextColor(form1.canvas.handle,rgb(Random(255),Random(255),Random(255)));

    end;



deleteobject(font);



end;




Displaying memo field in DBGrid

Question


I'm trying to display the first 100 bytes of a TMemoField in a DBGrid. To do

that, I created a calculated string field and tried the following code:



procedure TfrmDiario.Query1CalcFields(DataSet: TDataset);

var

  s : String;

  p : ^Char;

begin

  with Query1Historico do begin   {Query1Historico is the TMemoField}

    if DataSize>0 then begin

      GetMem(p, DataSize);

      GetData(p);

      StrCopy(PChar(p), @(s[1]));

      s[0] := #100;

      FreeMem(p, DataSize)

    end

    else

      s := '';

    Query1StrHistorico.AsString := s;  {Query1StrHistorico is the calculated

                                        TStringField}

  end;

end;



The problem is that Query1Historico.DataSize is ALWAYS zero! I even

tried Query1Historico.SaveToFile and it indeed created a zero length file.



Answer


A:

I also finally loose the hope to see TMemoField.DataSize has another value

than zero. Maybe meaning of the DataSize is size of the part of Memo field

which saved in the .db file. Instead of this I using now the TBlobStream

object which works perfectly. It looks like :



Var

        pBuffer	: PChar ;

        Blob	: TBlobStream ;

begin

        {FDataField is the TMemoField}

        Blob := TBlobStream.Create( FDataField, bmRead ) ;

        try

	if Blob.Size > 0 then

        	try

            	       GetMem( pBuffer, Blob.Size ) ;

	       Blob.Read( pBuffer^, Blob.Size ) ;

{                        do something    }

                     FreeMem( pBuffer, Blob.Size ) ;

            except

                ShowMessage( 'Out of memory' );

            end ;

       finally

    	Blob.Free

      end ;




Multiselect in a stringgrid

Question


I have just started to work with the stringgrid component and want to try

to do a multiselect (as in a listbox). This is where a user can select

multiple (non-adjacent) rows or cells and copy some data from an editbox or

combobox to them. The help and literature on this are very sparse.

Is this possible or is there a component available which does this?



Answer


A:

I did the same thing with a DBGrid. (Not implemented the Shift-MouseDown,

Ctrl-MouseDown staff yet).

For the TStringGrid I think the idea is as following :

1. Then you filling the grid set the Objects[0, ARow] with a some

Boolean object

        like TBooleanObject = class(TObject)

                public

                        Flag    : Boolean ;

                end ;

2. In OnMouseDown and OnKeyDown events change the flag as needed.

3. In OnDrawCell event draw the row according to Objects[0,ARow] flag.




Center text in cells of TStringGrid

Question


How justify the text inside the cell of a grid?

Answer


A:

Supply your own drawcell method here's an example that we've used.



procedure Tsearchfrm.Grid1DrawCell(Sender: TObject; Col, Row: Longint;

  Rect: TRect; State: TGridDrawState);

var l_oldalign : word;



begin

  if (row=0) or (col<2) then

    grid1.canvas.font.style:=grid1.canvas.font.style+[fsbold]; {set the headings in bold}



  if col<>1 then

   begin

 l_oldalign:=settextalign(grid1.canvas.handle,ta_right);

 {NB use the righthand side of the drawing rectangle}

 grid1.canvas.textrect(rect,rect.right-2, Rect.top+2,grid1.cells[col,row]);

       settextalign(grid1.canvas.handle,l_oldalign);

   end

  else

   begin

     grid1.canvas.textrect(rect,rect.left+2,rect.top+2,grid1.cells[col,row]);

   end;



  grid1.canvas.font.style:=grid1.canvas.font.style-[fsbold];



end;


Change the number of tabs in a TTabSet at runtime

Question


I want to be able to change the number of tabs in a TTabSet at runtime.

According to the documentation, this can be done by adding/deleting strings to

the 'Tabs' property.

Answer


A:

Assuming that somewhere in your code is a line like:



  TabSet1: TTabSet;  { assume it is in form Form1 }



then the code that you need to clear all of the tab labels is:



  Form1.TabSet1.Tabs.Clear;



In order to add a new tab label just use this code:



  Form1.TabSet1.Tabs.Add('some label');



Please note that I have only qualified the name on the assumption

that you are referencing it from within the same unit where it is

defined [but ignored the possibility that it might be in an event

handler or method that might partially qualify it -- the extra

qualification won't hurt]. If you need to refer to it from some

other unit then add the unit name (and add unit to "uses"), e.g.



  Unit1.Form1.TabSet1.Tabs.Add('a label');



Since "TabSet1.Tabs" is of type TStrings you can also use all of

the methods (AddObject, LoadFromFile, et cetera) that apply to

the TStrings type on that property.


CheckBox array - using common event

Question


In VB I had an array of CheckBox's that used a common event handler.

I could determine and handle which CheckBox caused the event by using 

the INDEX argument passed to the event handler.

Answer


A:

Try putting the checkboxes into a TGroupBox component. At run-time (or design 

time) assign the common procedure to the Click event of all the checkboxes.

You could use the Controls array property of TGroupBox to iterate through the 

child TCheckBoxes (and you could typecast them into TCheckBox).  

Something like:

  for i := 0 to GroupBox1.ControlCount -1 do

    if (GroupBox1.Controls[i] as TCheckBox).checked then

       {do something};



A:

You can get the name of the sender as follows:



procedure TMain1.CheckBoxClick(Sender: TObject);

var

     whodidit  : string[63];



begin

     whodidit := TComponent(sender).name;

     end; 





By casting you can get other propeties as well. For instance the tag

property can be very

useful. You can set each checkbox with an ID number at creation time. You

can read the ID at the event handler to identify the sender.


Right-editing in TEdit and columns of TStringGrid

Question


Right-editing in TEdit and columns of TStringGrid

Answer


A:

8.  It seems to me that this could be handled in one of 2 ways:



8.1   Editing raw unformatted numbers on a left-justified basis,

      then redisplaying with formatting and right justification

      when the focus shifts away from the control in question.

      This would require us to switch ES_MULTILINE/ES_RIGHT on

      and off as the focus changes.



8.2   By on-the-fly reformatting as the user enters digits.  In

      my experience, this would require the Edit Control to have

      a callback function for the "real-time" reformatting and

      display of the partially edited results.  I come from

      the Macintosh world, so I need help in establishing whether

      MS Edit Controls support such a callback function.



A:

It has been bugging me that there seems to be no simple way of achieving right

-justified editing without setting the Windows Edit Control style to

ES_MULTILINE as well as ES_RIGHT. (Re-writing the whole MS Edit Control

would be a non-trivial task).


More than one line in a cell of TStringGrid

Question


Can i write more than one line in a cell? How make it?

Answer


A:

Yes but you have to overwrite the OnDraw Event.

here's a sample that has a multiple line heading which gets centered and

"Bolded"



procedure TForm1.grid1DrawCell(Sender: TObject; Col, Row: Longint;

  Rect: TRect; State: TGridDrawState);



 var l_oldalign : word;

     l_YPos,l_XPos,i : integer;

     s,s1 : string;

     l_col,l_row :longint;



begin

  l_col := col;

  l_row := row;

  with sender as tstringgrid do

  begin

    if (l_row=0) then

      canvas.font.style:=canvas.font.style+[fsbold];

    if l_row=0 then

    begin

      l_oldalign:=settextalign(canvas.handle,ta_center);

      l_XPos:=rect.left + (rect.right - rect.left) div 2;

      s:=cells[l_col,l_row];

      while s<>'' do

      begin

        if pos(#13,s)<>0 then

        begin

          if pos(#13,s)=1 then

            s1:=''

          else

          begin

            s1:=trim(copy(s,1,pred(pos(#13,s))));

            delete(s,1,pred(pos(#13,s)));

          end;

          delete(s,1,2);

        end

        else

        begin

          s1:=trim(s);

          s:='';

        end;

        l_YPos:=rect.top+2;

        canvas.textrect(rect,l_Xpos,l_YPos,s1);

        inc(rect.top,rowheights[l_row] div 3);

      end;

      settextalign(canvas.handle,l_oldalign);

    end

    else

    begin

       canvas.textrect(rect,rect.left+2,rect.top+2,cells[l_col,l_row]);

    end;



    canvas.font.style:=canvas.font.style-[fsbold];

  end;

end;


Events for runtime created components

Question


That is I created new component inherited from TSpeedbutton. This new button

is created runtime and that is OK, I can change it's properties etc. The

problem is I don't seem to get the idea how to respond to it's events because

at design time the component doesn't exist and therefor Object Inspector is

worthless in this case.

Answer


A:

Here's some code I wrote just now, I used a new project, with a button and a

menu.

(If you like, just create a new project, and add a button and a menu ... :)



{The procedure to create a new menu item ...}

procedure TForm1.Button1Click(Sender: TObject);

var

  NewItem: TMenuItem;

begin

  NewItem := TMenuItem.Create(Form1);

  NewItem.Caption := 'Dynamic Item ...';

  NewItem.OnClick := xyz;

  MainMenu1.Items.Insert(0, NewItem);     <- Note: have a look at the Delphi

end;                                               example for Insert ...



{Any old 'xyz' procedure (can also be a currently defined one eg

Form1.DblClick)}

procedure TForm1.xyz(Sender: TObject);

begin

  showmessage('Running this procedure !!');

end;





Note : If you use an un-defined procedure, you'll need to declare it.

       I did mine right at the top in the form's type declaration like so:



           private

             { Private declarations }

           public

             { Public declarations }

             procedure xyz(Sender: TObject);   <- Events other than Form1 can

                                                  access the code here ...



A:

Just set the event handler property (eg. OnClick, OnDblClick, OnMouseDown etc)

to the procedure which you have written to handle the events. You need to make

sure that the parameters match those expected by the specific event handler.

eg.

 MySpeedButton.OnClick := MyClickEventHandler;



where...



procedure MyClickEventHandler(Sender: TObject);

begin



end;


Component Creation

Question


I am creating a component that has the following declaration:

I want to publish the properties of the A, B and C panels to allow editing in

the property editor and I want to group them under an expandable group sort

of the way the Font property works in the property editor. What is the best way

to implement this?

Answer


A:

To group them like the Font property you need to create a TPersistent

subclass. For example:



TBoolList = class(TPersistent)

  private

    FValue1: Boolean;

    FValue2: Boolean

 published

   property Value1: Boolean read FValue1 write FValue1;

   property Value2: Boolean read FValue2 write FValue2;

end;



Then in you new component create a ivar for this subclass. You =must=

override the constructor for this to work properly.



TMyPanel = class(TCustomPanel)

  private

    FBoolList: TBoolList;

 public

   constructor Create( AOwner: TComponent ); override;

 published

   property BoolList: TBoolList read FBoolList write FBoolLisr;

end;



Then add this code in your constructor:



constructor TMyPanel.Create( AOwner: TComponent );

begin

  inherited Create( AOwner );

  FBoolList := TBoolList.Create;

end;


CheckBox array, how to use common event

Question


In VB I had an array of CheckBox's that used a common event handler.

Using common event?

Answer


A:

var

	CheckArray: array[1..x] of TCheckBox;

	i:integer;

begin

  for i:=1 to x do begin

    CheckArray[i]:=TCheckBox.Create(Form1);

    {Set properties}

    with CheckBox[i] do begin

      Left:=i*20;

      Width:=15;

      etc...

    end;

  end;



Apparently you can say    Check[i].OnClick:=xyz.



Don't know that part of it myself yet. Dynamic creation of components yes, but

of event handlers?



A:

There is a way to have an array of checkboxes with a common event handler.

You must place them in the form and give contiguous names

(Check1,Check2, etc.). Then set a common event handler to them. The

event handler should be like this:

    procedure TForm.Check1Click(Sender : TObject);

    var

      i : Integer;

    begin

      for i := 1 to 10 { assuming you have 10 checkboxes } do

        With TCheckBox(FindComponent('Check'+IntToStr(i))) do begin

          { do some stuff }

        end;

    end;


Shared Controls on a TTabbedNotebook

Question


How do I get Controls on a TTabbedNotebook to be shared among all pages or a

subset thereof?

Answer


A:

You could give the same impression by putting the controls into a panel or

some other holder wich is not a child of the notebook but is on top. As it

is not a child - changing the tabs will have no effect other than what code

you want to control it.  Visibly it gives the impression that each tab /

notebook has the same page of controls.



A:

What I do is place the shared controls on the Form rather than

on a particular page of the TTabbedNotebook.  To do this, you

need to set the TTabbedNotebook's Align property to something

other than alClient -- e.g. if the common controls are towards

the bottom of the Form, use alTop for the TTabbedNotebook, and

then move the bottom of the TTabbedNotebook up a bit from the

bottom of the Form, leaving a gap for the "shared" controls.



I you want to make the "shared" controls _look_ as if they are

on each page of the TTabbedNotebook, leave them on the Form,

but Bring them to the Front, and then move the bottom of the

TTabbedNotebook back down to the bottom of the Form.



This works for me. I havn't yet had a need for shared

controls on a _subset_ if the pages, but my first idea would

be to change the Visible property of such Controls to

true/false in the TTabbedNotebook's OnChange event.


Screen sizes and stringgrids

Question


I have an application with several screens. The application runs on

several PC's with various display capabilities. If I set the scale

property of the form to True then some of my stringgrids do not display

correctly, and some of my text won't fit like it should. I am missing

something simple, or is this the best that can be done?

Answer


A:

I've encountered the same problem you describe. I've observed that, while

Delphi scales the StringGrid's font properly, it doesn't change the

StringGrid's "DefaultColWidth" and "DefaultRowHeight" properties.  Thus the

StringGrid itself does not actually change size.



I've worked around this by changing these properties myself:



  with StringGrid1 do begin

      DefaultColWidth  := DefaultColWidth  * MyScaleFactor;

      DefaultRowHeight := DefaultRowHeight * MyScaleFactor;

  end;


Aligning cells in stringgrid

Question


I need to allign cells in a stringgrid, first cell to the left ,second to the

right and so on ...

Answer


A:

Here is some code that should do what you want.



procedure WriteText(ACanvas: TCanvas; const ARect: TRect; DX, DY: Integer;

                    const Text: string; Format: Word);

var

  S: array[0..255] of Char;

  B, R: TRect;



begin

  with ACanvas, ARect do

  begin

     case Format of

        DT_LEFT   : ExtTextOut(Handle, Left + DX, Top + DY, ETO_OPAQUE or ETO_CLIPPED,

                               @ARect, StrPCopy(S, Text), Length(Text), nil);



        DT_RIGHT  : ExtTextOut(Handle, Right - TextWidth(Text) - 3, Top + DY,

                               ETO_OPAQUE or ETO_CLIPPED, @ARect, StrPCopy(S, Text),

                               Length(Text), nil);



        DT_CENTER : ExtTextOut(Handle, Left + (Right - Left - TextWidth(Text)) div 2,

                               Top + DY, ETO_OPAQUE or ETO_CLIPPED, @ARect,

                               StrPCopy(S, Text), Length(Text), nil);

     end;

  end;

end;





procedure TBEFStringGrid.DrawCell(Col, Row: Longint; Rect: TRect; State: TGridDrawState);

var



   procedure Display(const S: string; Alignment: TAlignment);

   const

      Formats: array[TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);

   begin

      WriteText(Canvas, Rect, 2, 2, S, Formats[Alignment]);

   end;



begin

   { test the Col and Row arguments here and format the cells as you want }



   case Row of

   0     : { Center column headings }

           if (Col < ColCount) then

              Display(Cells[Col,Row], taCenter)

   else

      { Right justify all other entries }

      Display(Cells[Col,Row], taRight);

   end;



end;


Coloured StringGrid

Question


Presently working on a project which has to display differently colored cells

in a grid. Nothing fancy like formulas and printing, just displaying

time-related activities in hour or half-hour cells and color-coding these

activities on a per-cell-basis. I must also be able to read back color codes

by clicking in a cell. The grid must allow multiple area selections.

Answer


A:

You can try using a StringGrid. It has an Objects property that you

can assign objects. Create an object which contains a TColor variable

and assign it to Objects[col,row], so you can retrieve it any time. You

must assign an OnDrawCell Event to the StringGrid, drawing the text on

the Correct color. To retrieve the selection, you can use Selection

property, that contains the user Selection. This should be something like that:

    type

    TStrColor = class(TObject)

    public

      Color : TColor;  {you could also define a private variable and 

                        public access methods}

    end;

    ...

    procedure TForm1.FormCreate(Sender:TObject)

    var

      i,j : Integer;

    begin

      With StringList1 do

        for i := 0 to ColCount-1

          for j := 0 to RowCount-1

            Objects[i,j] := TStrColor.Create;

    end;

    ...

    procedure TForm1.StringGrid1DrawCell(Sender: TObject; Col, Row: Longint;

      Rect: TRect; State: TGridDrawState);

    var

     OldColor : TColor;

    begin

      with StringGrid1.Canvas do begin

        OldColor := Font.Color;

        Font.Color := (StringGrid1.Objects[col,row] as TStrColor).Color;

        TextOut(Rect.Left+2,Rect.Top+2,StringGrid1.Cells[Col,Row]);

        Font.Color := OldColor;

      end;

    end;

    ...

    procedure TForm1.ProcessSelection(Sender: TObject);

    var

      i,j : Integer;

    begin

      With StringGrid1.Selection do

        For i := left to right do

          for j := top to bottom do

            MessageDlg(IntToStr(i)+','+IntToStr(j)+'-'+

              IntToStr((StringGrid1.Objects[i,j] as TStrColor).Color),

              mtInformation,[mbOk],0);

    end;

    This component cannot allow multiple selections....


Testing for the existence of a Component Property

Question


I would like to iterate through all my components on a form, at form

activation time, and if a particular component has a Font property, I would

like to set the Font.Pitch value at that time. In general, how do I test to

see if a component has a particular property?

Answer


{you'll probably want to replace the "is TButton.." with some kind of set

that you define on your own.. then you just ask.. if Components[i] in myset

then}



{gee i dont know ... i just looked up TFont and it applies to 40 some

different objects, and everything that has a TFont also has a TPitch ...

there must be an easier way}



procedure TForm1.FormCreate(Sender: TObject);

var

  i: Integer;

begin

  for i := 0 to ComponentCount -1 do

     if Components[i] is TButton then

       TButton(Components[I]).Font.Pitch :=fpFixed ;



end;


Set event-handler at run-time

Question


I need to set the onTimer property of a TTimer-component at run-time.

Lets say I have a procedure called ThisOne and I , want it to be the

eventhandler - how is that done?

Answer


A:

It's pretty straightforward: you define the procedure (say ThisOne) that

takes the same parameter list as onTimer, then when you want it to be

attached to the timer you say:



   Timer1.onTimer := ThisOne;



To turn off the event, use:



   Timer1.onTimer := nil;



The procedure (ThisOne) does not need to be in the same class as the timer,

but it _must_ take the same parameters in the same order or you risk things

going off into la-la land.



A:

The way I'd do it is to put the component on the form and create the

appropriate event. (this gets better - honest). Then copy the procedure line

of the event, rename it and put it in the public section. This ensures that

the event has the right parameters - with some of the longer event lines,

typing the correct definition can be a real hassle.



Remove the component from the form if you don't want it. Put the code you

want called in the new event handler you've defined. When you create the

object put the address of the code into the ONevent handler of the object.



I think the appropriate line of code is:



Timer1.ontimerevent:=formdynamictimerevent;


Application events

Question


I am trying to allow my program to accept file manager drops...I have it

working OK with The Main window opened, but not minimised. The Accept files

works OK, but I am not sure how to get the WM_Drop message. I reallize it must

be somewhere in the OnMessage handler (I know how to create event Handlers for

Application events), but, in the OnMessage handler, how do I get the message?

Msg is NOT one of the parameters.

Answer


A:

The problem here is that when your Delphi application is minimized, it is a

different window handle than at other times. The Application object actually

has its own window handle! Application.Handle is the window that is active when

your app is minimized. When you minimize the app, all the forms are actually

just hidden. Notice the Application methods Minimize and Restore. Also notice

that there are two undocumented events in TApplication, OnMinimize and

OnRestore. These are there because there is no event handler in TForm that will

fire when you minimize the main window. Kind of bizarre. I think it is done

this way to support SDI applications.


ReleaseDC and TCanvas

Question


I have a canvas that I create, and set the Handle using GetWIndowDC.

I do explicitly destroy the Canvas, is is automatically calling RealeseDC?

Is this a safe method of doing this?

Answer


A:

TCanvas will not automatically call ReleaseDC. To create a canvas with

a WindowDC as its handle, probably the best idea would be to create a

descendant of TCanvas (modeled on TControlCanvas):



  type

    TWindowCanvas = class(TCanvas)

    private

      FWinControl: TWinControl;

      FDeviceContext: HDC;

      procedure SetWinControl(AControl: TWinControl);

    protected

      procedure CreateHandle; override;

    public

      destructor Destroy; override;

      procedure FreeHandle;

      property WinControl: TWinControl read FWinControl write =

SetWinControl;

    end;



  implementation



  destructor TWindowCanvas.Destroy;

  begin

    FreeHandle;

    inherited Destroy;

  end;



  procedure TWindowCanvas.CreateHandle;

  begin

    if FControl = nil then inherited CreateHandle else

    begin

      if FDeviceContext = 0 then

        FDeviceContext := GetWindowDC(WinControl.Handle);

      Handle := FDeviceContext;

    end;

  end;



  procedure TControlCanvas.FreeHandle;

  begin

    if FDeviceContext <> 0 then

    begin

      Handle := 0;

      ReleaseDC(WinControl.Handle, FDeviceContext);

      FDeviceContext := 0;

    end;

  end;



  procedure TControlCanvas.SetWinControl(AControl: TWinControl);

  begin

    if FWinControl <> AControl then

    begin

      FreeHandle;

      FWinControl := AControl;

    end;

  end;



Obviously, you should be careful to destroy the TWindowCanvas (or free

its handle) before destroying the control associated with it. Also,

note that the DeviceContext handle will not be released automatically

after completing the processing of each message (as happens with the

handles of TControlCanvas); you must explicitly call FreeHandle (or

destroy the Canvas) to release the handle.  Finally, note that

"WindowCanvas.Handle:= 0" will not release the handle, you must call

FreeHandle to release it.


Using TStringList in a visual component

Question


I need to build a buffer for queuing output requests to be handled by a

component I am trying to build. I thought I would just use a TStringList,

add to it's end and using and deleting the first entry when possible... It

doesn't seem to be this simple... does anyone know the difference between

TStrings and TStringList and which I should be using.... any tips????

Answer


A:

TStrings is an abstract base class used by many of the visual controls such

as TListBox. What you want is TStringList, or if all you need to keep track

of is objects, use TList instead. To add to the end of either, use the Add

method. To insert at a specific place in the list, use Insert. To get a

string from the list, use the Items property. Keep in mind that it is

zero-based, so the last item in the list is referenced by Count-1 (Count is

another property). To delete a string, use the Delete method. To find a

string in the list, use IndexOf. TStringList can be made to keep the list in

alphabetical order. To do that, set the Sorted property to true before you

add anything to it. It can also be used to keep an object for every string,

using AddObject and the Objects property. TList does most of the above, just

without the strings. To create a TStringlist, do this:



procedure MakeList;

var

  aList: TStringList;

begin

  aList := TStringList.Create;

  aList.Sorted := true;  {optional}

  aList.Duplicates := dupIgnore;  {or dupAccept or dupError}

  aList.Add('String 1');

  aList.Add('String 2');

  Edit1.Text := aList.Items[0];   {Edit1 now says 'String 1'}

  aList.Delete(0);

  aList.Free;

end;


#0 KeyPress

Question


I've seen this code in a component I've downloaded:



   if Key <> #0 then inherited KeyPress(Key);



Since Windows virtual keycodes range from 1 to 145 (decimal),

what does 'if Key <> #0' check for?

Answer


A:

It checks for whether a key was really pressed; by convention, #0 means a

key was not pressed. In some cases an event may get activated by other than

a keypress (eg. by direct call), or an ancestor may have already processed

the keypress, in which case Key will have been set to #0.


StringGrid Masks

Question


I have a column in a string grid that I need to be displayed like the

"comma" format of any Spreadsheet:

User types: 123456.7

Grid displays: 123,456.70

Answer


A:

What you want is tricky, but doable cause I've got a right aligned, floating

point, editable grid working right now (albeit a DrawGrid, but the concepts

should be the same).



You'll need a couple of things:

- An OnDrawCell handler to display the formatted, right-justified data.

- The cells of your stringgrid loaded with unformatted string representations of

  your data.



In your OnDrawCell handler you will need to do something like this.  This

code displays right justified comma-tized values:



  begin

  if (Row > 0) and (Col > 0) and (grdDivBudget.Cells[Col, Row] <> '') then

    begin

      {Format floating point string}

      strText := FloatToStrF(StrToFloat(grdDivBudget.Cells[Col, Row]),

ffNumber,         13,2);



      {Set font}

      grdDivBudget.Canvas.Font.Name := 'Courier';

      if StrToFloat(grdDivBudget.Cells[Col, Row]) < 0 then

        grdDivBudget.Canvas.Font.Color := clRed;

      grdDivBudget.Canvas.Font.Style := grdDivBudget.Canvas.Font.Style -

[fsBold];



      {Center the text in the cell from top to bottom, right justify it and

       move the right margin to the left two pixels.}

      X := Rect.Right - grdDivBudget.Canvas.TextWidth(strText);

      Y := Rect.Top + ((Rect.Bottom - Rect.Top -

        grdDivBudget.Canvas.TextHeight(strText)) div 2);

      Dec(Rect.Right, 2);



      grdDivBudget.Canvas.TextRect(Rect, X, Y, strText);

    end;



   Make sure you have DefaultDrawing := True so you only have to write code to

draw the cells which need to be specially formatted.



  That should do it.  When the user tries to edit the number in a cell it

should be displayed in its non-formatted state.  If not the you'll need an

OnGetEditText handler.



  Also, you'll find that validation of the data can be problematic (i.e.

what if the user enters 'TX' in a numeric column).  It's made worse by the

fact that the grid (well, the DrawGrid anyway) does not exhibit consistent

behavior for different ways of navigating the grid (i.e. it triggers

different events if you use the arrow keys, versus the mouse, versus simply

hitting enter).  If this becomes a problem post another message and I'll

give you my solution (which I'm not proud of, but it works).


StringGrid right alignment

Question


To right align the cells in a string grid, and have the numeric cells

containing comma, I did the following.



- Made all the contents of the cells the same length.  Padded with spaces

to ensure all were the same.

- This will enable you to right-align the cells.  Just make sure you use

a TT font else certain letters such as the letter i will take less space

than a letter such as M. I used Courier New as the grids font.

Answer


A:

I suppose this one is better method



procedure TForm1.GridSumaDrawCell(Sender: TObject; ACol, ARow: Longint;

  ARect: TRect; State: TGridDrawState);

var

   dx : integer;

begin

   with (Sender as TStringGrid).Canvas do begin

      Font := GridSuma.Font;

      Pen.Color := clBlack;

      if (ACol = 0) or (ARow = 0) then begin

{ Draw header }

          Brush.Color := clBtnFace;

          FillRect(ARect);

          TextOut(ARect.Left, ARect.Top, GridSuma.Cells[ACol, ARow])

      end

      else begin

{ Draw right aligned cell }

          Brush.Color := clWhite;

          FillRect(ARect);

          dx := TextWidth (GridSuma.Cells[ACol, ARow]) + 2;

          TextOut(ARect.Right - dx, ARect.Top, GridSuma.Cells[ACol, ARow])

      end

     end

end;


FileName property in non-visual component

Question


I'm trying to get a new non-visual component created, and one of the properties

will be a FileName property. How can I set it up so that at design time the

user will get the ellipses button on the property manager and get the file

open box if they click on it and then be able to select a file name that way?

Answer


A:

The following code is taken out of dsgnintf.pas (a file

worth exploring!) for the TMPLayer.filename

property, with help for C.Calvert..



In the component unit file header...



   TFileNameProperty = class (TStringProperty)

     public

      function getattributes: TPropertyattributes; override;

      procedure Edit; override;

    end;



add to the register function...

     RegisterPropertyEditor(Typeinfo(String),

       TMyComponent, 'Filename', TFileNameProperty);



 and the code...



function TFileNameProperty.GetAttributes;

begin

  Result := [paDialog];

end;



Procedure TFilenameProperty.edit;

var

  MFileOpen: TOpenDialog;

begin

  MFileOpen := TOpenDialog.Create(Application);

  MFileOpen.Filename := GetValue;

  MFileOpen.Filter := 'The Right Kind Of Files|*.*'; (* Put your own filter

here...*)

  MFileOpen.Options := MFileOpen.Options + [ofPathMustExist,ofFileMustExist];

  try

    if MFileOpen.Execute then SetValue(MFileOpen.Filename);

  finally

    MFileOpen.Free;

  end;

end;


SendMessage and TLabel

Question


How does one send a message to TLabel particularly since it's a non-windowed

control. I need to send a label WM_LButtonUp, but there's no handle.

Answer


A:

You can't send a message to a control that doesn't have an hWnd (i.e., a

Handle property in Delphi). One solution would be to send the message to

an invisible control that *does* have a handle, and do your Tlabel work

in a handler  for that control.


Call the Hint method directly

Question


Is it possible to call the  Hint method directly?

I've a case where I want to click on a Button, and the Hint for

another component (eg, an edit box) will appear for 1 second or

so, and then it will disappear after the button is released. I

saw something like "ActivateHint" but it seemed that I

couldn't make a direct call.

Answer


A:

 function RevealHint (Control: TControl): THintWindow;

{----------------------------------------------------------------}

{ Pops up Hint window for the specified Control, and returns a   }

{ reference to the hint object so it may subsequently be removed }

{ with RemoveHint (see below).                                   }

{----------------------------------------------------------------}

 var

   ShortHint: string;

   AShortHint: array[0..255] of Char;

   HintPos: TPoint;

   HintBox: TRect;

 begin

   { Create the window: }

   Result := THintWindow.Create(Control);



   { Get first half of hint up to '|': }

   ShortHint := GetShortHint(Control.Hint);



   { Calculate Hint Window position & size: }

   HintPos := Control.ClientOrigin;

   Inc(HintPos.Y, Control.Height + 6);    <<<< See note below

   HintBox := Bounds(0, 0, Screen.Width, 0);

   DrawText(Result.Canvas.Handle,

       StrPCopy(AShortHint, ShortHint), -1, HintBox,

       DT_CALCRECT or DT_LEFT or DT_WORDBREAK or DT_NOPREFIX);

   OffsetRect(HintBox, HintPos.X, HintPos.Y);

   Inc(HintBox.Right, 6);

   Inc(HintBox.Bottom, 2);



   { Now show the window: }

   Result.ActivateHint(HintBox, ShortHint);

 end; {RevealHint}



 procedure RemoveHint (var Hint: THintWindow);

{----------------------------------------------------------------}

{ Releases the window handle of a Hint previously popped up with }

{ RevealHint.                                                    }

{----------------------------------------------------------------}

 begin

   Hint.ReleaseHandle;

   Hint.Free;

   Hint := nil;

 end; {RemoveHint}



The line marked <<<< above is the one that positions the hint

window below the control.  This could obviously be altered if

you want a different position for some reason.


Changing in TOpenDialog

Question


I need to change caption in the check-box "Read Only" of a

TSaveDialog/TOpenDialog. I wish it becomes "Only selected records". Is it

possible? Or I have to create my own component?

Answer


A:

Try Searching for Open Dialog Box in the Windows API help file.  Look down

at the bottom of the topic at lpTemplateName.  Basically, you can create a

new dialog box for the Open Dialog Box and replace the standard one with

your own.


Edit in StringGrid

Question


An annoying problem with StringGrid:

I'm using a stringgrid to display some data, and i start the stringgrid with

GoEditing set to false (The user can't edit the data in the stringgrid),

and selectrow to true (The user can select only whole row).

I placed an "EDIT" button so when the user presses it, i set the GoEditing to

true & selectrow to false and now the user can edit the data in the table.

All is well up to now.

The problem:

When the user pressed EDIT, and edits a cell, then presses EDIT again to

switch back to normal row selecting mode, the cell that was last edited, stays

in an inverted color, meaning , if the focused cell is blue and the rest is

white, then after the user edits a cell and presses EDIT again , the whole line

is blue EXCEPT the cell that was last edited, which remains white.

I should add that the stringgrid i'm using is a modified stringgrid which each

col. can be alligned diffrently (first col. can be rightjustified, second left

justified, third centered justified and so on...) but i don't think this has

anything to do with my problem.

Answer


A:

Haven't tried these, but two possibilities come to mind:



1) On the second edit press, change focus to another field (eg. x.focus

where x is not the grid), reset goEditing and selectRow, then change

focus back to the grid.  (This technique has worked for me in several

places, eg. grids, memos.)



2) On the second Edit press, after resetting goEditing and selectRow,

try creating a tGridRect spanning the row you want to highlight, the

doing grid.Selection := gridRect;


How to detect a row focus change in TDBGrid

Question


There is (very surprisingly) no event triggered when the user changes the

focus from one row to another in a TDBGrid. Am I missing something here? Has

anyone got an easy way?

Answer


A:

You use the OnDataChange event of the Datasource to which the DBGrid is attached.

If the State in the event is dsBrowse then you've gone to a new row (or just

opened the table).



Why not have an event in the grid?  Because the grid may not be the only control

displaying data from the current row and might not be the only way to move from

row to row.  Using the Datasource give centralized event handling.



As to your question about a single click, not sure what you're trying to do, but

you can use TDatasource.OnDataChange to capture row changes and

TDBGrid.OnColEnter/Exit to capture column changes.



A:

The following works for me:



1. To detect row change, use the TDataSource's OnDataChange event.

   OnDataChange occurs whenever scrolling or clicking on a different row

   happens.  The event handler is something like this:



     procedure Form1.DSrc1DataChange(Sender: TObject; Field: TField);



   where Field is the column in which change occured.



   The TTable's fields can be used for comparing the currently selected

   row's fields (key) with whatever your requirement is. The TDBGrid's

   Fields property can also be used the same way. For instance:



     if tbl1.Fields[0].AsString = 'BlaBlaBla' then ...

   or, if dbGrid1.Fields[I].IsNull then ...



2. For column change, use TDBGrid's OnColExit & OnColEnter. The

   TDBGrid's properties SelectedField and SelectedIndex can be used to

   determine the currently selected column.



   When a different column on a different row is selected, you get

   OnColExit, OnColEnter, and then OnDataChange.



3. You can also do some fancy stuff by using the TDBGrid's

   OnDrawDataCell event which occurs when a cell is selected or

   when the grid is scrolled. The event handler looks like:



     procedure Form1.dbGrid1DrawDataCell(Sender: TObject; Rect: TRect;

               Field: TField; State: TGridDrawState);



   But of course you get a lot of draw events when cell changes, and

   you have to do your own filtering.



4. If you don't have a problem in creating "101 variations" on the

   standard components - which I don't 8-) then try this. It is

   easier.



   To access row or column index of the selected cell, you can

   derive a class from TCustomGrid and publish its Row, Col

   run-time properties (current grid row and column, not table's!!):



     type

       TSampleDBGrid = class(TCustomGrid)

       public

         property Col;

         property Row;

       end;



   in some procedure or event handler, do a typecasting:



     var

       G: TSampleDBGrid;

     begin

       G := TSampleDBGrid(myDBGrid1);

       if G.Row = I then ...

       if G.Col = J then ...

       

   This is because TDBGrid is a descendant of TCustomGrid, which

   has several properties on the grid coordinates, but aren't

   published in TDBGrid.



A:

From what I can see, you have to do it programmatically.  OTTOMH,

assuming the grids already exist and you have access to the

underlying ttable::



   grid.colcount := dbGrid.fieldcount;

   table.first;

   row := 0;

   while not table.eof do begin

      grid.rowcount := row + 1;

      for i := 0 to grid.colcount-1 do

          grid.cells[i,row] := dbGrid.fields[i].asString;

      table.next;

      inc (row);

   end;



May be some latent bugs in this, but it should do the trick.



A:

Look at the following code and see if it will help. It takes the 'Name'

property of a control and then places it into the 'Caption' property of a

label.



unit Unit1;



interface



uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

  Forms, Dialogs, StdCtrls;



type

  TForm1 = class(TForm)

    Label1: TLabel;

    Edit1: TEdit;

    Edit2: TEdit;

    Button1: TButton;

    procedure Button1Click(Sender: TObject);

    procedure Edit1MouseDown(Sender: TObject; Button: TMouseButton;

      Shift: TShiftState; X, Y: Integer);

    procedure Edit2MouseDown(Sender: TObject; Button: TMouseButton;

      Shift: TShiftState; X, Y: Integer);

  private

    { Private declarations }

  public

    { Public declarations }

  end;



var

  Form1: TForm1;



implementation



{$R *.DFM}



procedure TForm1.Button1Click(Sender: TObject);

begin

  close;

end;



procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  Label1.Caption := TEdit(Sender).Name;

end;



procedure TForm1.Edit2MouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  Label1.Caption := TEdit(Sender).Name;

end;



end.


Combobox problems

Question


I have a combo box. On the On Change event I have some processing, which if

OK will update the results, and the combo box selection is valid. However,

if the processing fails, the combo box selection should revert to the

previous value. How do I make it do this?

Answer


Try saving the Index value in a variable in the OnEnter method or the OnCreate

method of the form.  Then, to reject the user's selection, simply

  ComboBox1.ItemIndex := var1;


Change Grid Cell Color

Question


I have a dbeGird wich displays a lot of numbers. How can I change the color

of the cells which has a value below zero.

Answer


A:

Attach the following code to the DBGrid's OnDrawCell event handler:



procedure TForm1.DBGridDrawDataCell(....);

begin

  if Table1.FieldByName( 'SomeField').AsFloat < 0 then

      DBGrid1.Canvas.Font.Color := clRed

      else DBGrid1.Canvas.Font.Color := clBlack;

  DBGrid1.DefaultDrawDataCell( Rect, Field, State );

end;



[Patrick Allen, patrick@blueridge.com]



A:

In the dbgOrdRegDrawDataCell put the next lines.



if ((Field.FieldName = 'CalcAmout') and (tbOrdCalcAmount.AsFloat < 0)) then

   dbgOrdReg.Canvas.Font.Color := clRed

dbgOrdReg.DefaultDrawDataCell(Rect,Field,State);



This is working at my place and if you make a if .. else if ... else if..

you can test every grid cell and change the colors.



[Rene Groothuis, steelcover@dataweb.nl]



A:

Well I've found the solution. It's like this:



In the dbgOrdRegDrawDataCell put the next lines.



if ((Field.FieldName = 'CalcAmout') and (tbOrdCalcAmount.AsFloat < 0)) then

   dbgOrdReg.Canvas.Font.Color := clRed

dbgOrdReg.DefaultDrawDataCell(Rect,Field,State);



This is working at my place and if you make a if .. else if ... else if..

you can test every grid cell and change the colors.


TEdit and OnEnter event

Question


I would like to write a TEdit component, where on the OnEnter event,

it will display a hint in a specified label.  My question is how do

override the OnEnter event in my component to do the extra bit that I

want.

Answer


A:

interface



uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

  Forms, Dialogs, StdCtrls;



type

  TNewComponent = class(TEdit)

  private

    { Private declarations }

  protected

    { Protected declarations }

    procedure DoEnter; OverRide;

  public

    { Public declarations }

    constructor Create(AOwner:  TComponent); OverRide;

    destructor Destroy; Override;

  published

    { Published declarations }

  end;



procedure Register;



implementation



procedure TNewComponent.DoEnter;

begin

  inherited DoEnter;

  

end;





destructor TNewComponent.Destroy;

begin

  inherited destroy;

end;





constructor TNewComponent.Create(AOwner:  TComponent);

begin

  inherited Create(AOwner);



end;



procedure Register;

begin

  RegisterComponents('Samples', [TNewComponent]);

end;



end.


How do I create a component like TField

Question


I'd like to create component (subclass of TComponent) which will be

not visible on a form at design time just like subclasses of TField.

More precisely I want to have to non-window component types: first

called TListComponent and the second TListElement. Now I can write

the component editor for TListComponent and the editor can create

a plenty of TListElement components. The problem is:



  1. If I do not RegisterComponent for TListElement then creating

     it's instance at design time raises GPF.



  2. If I do RegisterComponent for TListElement then creating the

       instance of TListElement creates an icon placed on the form and

      I do not want it. I prefer to keep TListElement invisible at design time

      just like TTable does with TField.

Answer


A:

Have you considered not making it a component, but a class?  A class is

programmatic, not part of a form.  If you put the class in a unit (say

myclass.pas) and then in your program put "uses myclass;", then you can just

use it as a class, eg.



 type

    aninstance: tMyclass;

 begin

    new (aninstance);

        {equivalent to aninstance := tMyclass.create; }

    ...

      { use aninstance here }

    ...

    dispose (aninstance);

       { equivalent to aninstance.free; }

 end;


Accessing notebook pages

Question


I want to add components at runtime to a page of a notebook.

How do I do it so that when I change the page the components disapear and

reapear.

Answer


A:

When you add the components at runtime, you need to set each component's

parent to the desired _page_ of the notebook, not to the notebook itself.



You can do this the following way (this example is for a Button):



    MyButton := TButton.Create( Form1 );  {as usual...}

    ...

    ...

    MyButton.Parent := TTabPage( TabbedNotebook1.Pages.Objects[n] );

      { <== where 'n' is an index into the desired page ==> }



The notebook's 'Pages' property is a StringList containing a list of

captions and 'TTabPage' objects.



I used this technique a few months back myself.  I can't remember now where

I picked this information up.  I can't find documentation for it at the

moment.  Maybe someone else knows where this is documented?



A:

To add a component to a TabbedNotebook page at run-time a pointer to the

desired page must be assigned to the new component's Parent property before it

can be shown. The way to access all the pages of a TTabbedNotebook at run-time

is with the Objects array property of the TabbedNotebook's Pages property.

In other words, the page components are stored as objects attached to the page

names in the Pages string list property. The follow demonstrates the creation

of a button on the second page of TabbedNotebook1:



   var

     NewButton : TButton;

   begin

     NewButton := TButton.Create(Self);

     NewButton.Parent := TWinControl(TabbedNotebook1.Pages.Objects[1])

     ...



   This is how a TNotebook page would be used as a parent to a newly

   created component on that page:



   NewButton.Parent := TWinControl(Notebook1.Pages.Objects[1])



   This is how a TTabSet tab page would be used as a parent to a

   newly created component on that tab page:



   NewButton.Parent := TWinControl(TabSet1.Tabs.Objects[1])


Change delete behavior in Memo

Question


I need to change the behavior of the delete key in a memo

box.  The new behavior will stop the CR/LF from being deleted if delete is

pressed at the end of a line, or, backspace is pressed from the beginning of a

line. I want each character pressed to remain on the line it was intitially

input. There will always be 6 lines in the memo, and line 3 cannot be moved to

line 2. I still would like to be able to delete characters, just not the

CR/LF.

Answer


A:

Just change the Memo's OnKeyDown event handler to look like:-

  if Key = VK_DELETE then

  begin

    do whatever you want in here

  end;

  if Key = VK_BACK then

  begin

    do whatever

  end;

Probably better to use a CASE here but I'm not sure if CASE allows VK_?? in it.

May also need to include the Inherited for the keys you don't handle. Anyone

want to

clarify this ?

Also look up the SelStart to determine where you are in the line like so:-

  var

    Lpos, Cpos : Integer;

  Lpos := SendMessage(memo1.Handle,EM_LINEFROMCHAR,Memo1.SelStart,0);

  Cpos := SendMessage(memo1.Handle,EM_LINEINDEX,Lpos,0);

  CPos := Memo1.SelStart-CPos;



A:

since VK_? stuff are integers this will work :



case Key of

  VK_DELETE :

    begin

      Key := 0;  {this stops the keydown message from going any

                         farther, for ex. the form and its components}

      stuff to do;

    end;

  VK_BACK:

    begin

      Key := 0;  {this stops the keydown message from going any

                         farther, for ex. the form and its components}

      stuff to do;

    end;

  end;


Listbox with graphic

Question


Has anyone done something like putting a .BMP graphic besides text within a

listbox?  something you see when you click on "Options"-"Environment"->

"Palette".

Answer


A:

This is a sample code. You need to set the ListBox property

Style to lbOwnerDrawFixed. Then you draw the bitmap on the event

DrawItem (see OwnerDraw at the Delphi Help File).



unit Listemas;



interface



uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

  Forms, Dialogs, StdCtrls;



type

  TLTemas = class(TForm)

    ListBox1: TListBox;

    procedure FormActivate(Sender: TObject);

    procedure ListBox1DrawItem(Control: TWinControl; Index: Integer;

      Rect: TRect; State: TOwnerDrawState);

  private

    { Private declarations }

  public

    { Public declarations }

  end;



var

  LTemas: TLTemas;



implementation



{$R *.DFM}



procedure TLTemas.FormActivate(Sender: TObject);

var

   Dibujo: TIcon;

begin

     with ListBox1.Items do

     begin

          Dibujo := TIcon.create;

          Dibujo.LoadFromFile('.\ICO\justic.ico');

          AddObject('Poder Legislativo y Partidos Politicos',Dibujo);

          Dibujo := TIcon.create;

          Dibujo.LoadFromFile('.\ICO\justic.ico');

          AddObject('Poder Ejecutivo Nacional',Dibujo);

     end;

end;



procedure TLTemas.ListBox1DrawItem(Control: TWinControl; Index: Integer;

  Rect: TRect; State: TOwnerDrawState);

var

  Icon: TIcon;

  Offset: Integer;	{ text offset width }

begin

  with (Control as TListBox).Canvas do	{ draw on the control canvas, not on the form }

  begin

    FillRect(Rect);	{ clear the rectangle }

    Offset := 2;	{ provide default offset }

    Icon := TIcon((Control as TListBox).Items.Objects[Index]);	{ get the Icon for this item }

    if Icon <> nil then

    begin

         Draw(Rect.Left+1,Rect.Top+2,TIcon((Control as ListBox).Items.Objects[Index]));



      Offset := Icon.width + 9;	{ add four pixels between Icon and text }

    end;

    TextOut(Rect.Left + Offset, Rect.Top+7, (Control as TListBox).Items[Index])	{ display the text }

  end;

end;



end.



A:

Check out the OnDrawItem event for the listbox (or combobox etc).

There you will see that drawing graphics is as easy as writing text.

(Once you are comfortable with that check out OnMeasureItem event)



procedure ListDrawItem(Control: TWinControl; Index:

Integer;

  Rect: TRect; State: TOwnerDrawState);

var

 BitMap : TBitMap;

begin

{Initialize Bitmap here.... i.e. load a graphic onto it}



     With (Control as TListBox).Canvas do

       begin

         FillRect(Rect);

         Draw(Rect.Left, Rect.Top, BitMap);

         TextOut(Rect.Left + 2 + BitMap.Width, Rect.Top,

         DstList.items.strings[index]);  {DstList is the name of the 

list}

       end;



end;


Masked Find in TStringList

Question


I am trying to do a masked find or search in a TStringList Object. Something

like MyList.Find('LeftPelvic*') to find all strings that start with LeftPelvic

and have it return a TStringList or find the first and have a findnext method.

Answer


A:

   list : tStringList;   { the tStringList to search }

   target: string;       { the string to search for, eg. 'LeftPelvic' }



   { set start to where you want to start from, eg. 0 to start at the top

     or some integer to start within the list, eg. the last value of i + 1 }

   { following is case insensitive, to make it case sensitive remove the calls

     to ansiUpperCase }



   target := ansiUpperCase (target);

   for i := start to list.count-1 do

      if ansiUpperCase(copy(list.item[i], 1, length(target))) = target then

         found it!



If you want to do a looser search, eg. find 'LeftPelvic' anywhere in the

list item rather than just at the beginning, change the if statement to:



   if pos (target, ansiUpperCase(list.item[i])) > 0 then found it;


Dual list box

Question


Could anyone give me some hints on how to create a dual list box (add

and remove items betweem 2 listboxes) with the items of the src list come

from a table.

I have tried the dual list box template, but it is a listbox with

pre-entered items. I want to hv the items from a table instead. Which one

(ListBox, DBListBox or ...) should I use?

Answer


A:

I placed two listboxes on the screen and used the following code to fill one

of them

with data from a table.



tableName.Refresh;  {this may or may not be necessary in your case}

tableName.First;    {Make sure you are looking at the first record}

while not tableName.Eof do {loop through table}

begin

  listbox1.items.add(tableName.FieldByName('USRID').AsString); {add the item

to the listbox1}

  tableName.Next; {go to the next record}

end;



this is an actual procedure from some code that I am using to accomplish the

same thing.

I pass it a tablename and listbox1 name and listbox2 name.  I use several

tables that

have the same type of fields that is why I use this routine:



procedure TTemplateFrm.buildList(tableName: TTable; SelBox, AvailBox: TListBox);

begin

  {we are going to add data to listboxes in this procedure}

  {pick up any new data}

  tableName.Refresh;

  {make sure we are looking at the first record}

  tableName.First;

  {Now clear the list boxes}

  SelBox.Clear;

  AvailBox.Clear;

  {Now add the Stations}

  while not tableName.EOF do

  begin

    AvailBox.Items.Add(tableName.fieldByName('USRID').AsString +

       ' ' + tableName.fieldByName('DESCRIPTION').AsString);

    tableName.Next;

  end;

end;





How do you want to move data between the two boxes? If you want to drag and

drop then

use begindrag in your mousedown event of the source table like this:



  if Button = mbLeft then

    Tlistbox(sender).BeginDrag(false);



Then in your other listbox, the one receiving the data you type in the

follow code in DragOver event:



  if Source = ListBox1 then

    Accept := true

  else

    Accept := false;



Do not use "Accept := (Source is TListbox)" as most examples show, because

you have two list boxes

you need to refer to the name of the object and not the type or you will

confuse your program.



Finally in your dragDrop you can use the following code to add the data to

listbox2 and remove it from list

box1.



  Listbox2.Items.Add(Listbox1.Items[Listbox1.ItemIndex]);

  Listbox1.Items.Delete(Listbox1.ItemIndex);



Finally add a save button if you want to loop through the listbox2 and save

it to a database.



I hope this is what you were looking for and that you find this useful. If

you want to be able to move

data from listbox2 to listbox1 as well then use the same code but REVERSE

which boxes you place them in 

and make sure you reference the correct boxes in the code.


OnClose proc

Question


Just wanted to be sure of something...If an application is shut down, by

any method within the program, it DOES call the OnClose procedure for any

open forms, right?  But if by chance it was shut down by Windows, like a

situation where Task Manager was called and it is shut down from there,

then although the program itself is over, any Forms that might have been

open and were not created in the project source file, could still be lost

in memory, is that right?

Answer


A:

My experience is that OnClose is only called when Close is called.

OnDestroy, however is always called when the form being freed or

destroyed.  I had put my Table.Post stuff in OnClose, but it was not

always called.  Try putting MessageBeep into OnClose, then close your

app a few different ways to see exactly when it is called.





A:

I ran a test that suggested an Application.Terminate will fire the Destroy

event for the form but NOT the OnClose or OnCloseQuery events, unlike VB.





A:

It's my understanding that the Task Manager does not actually close

the application.  It just tells the application to close itself, by

way of sending messages directly to the application.  I would hope

that the default window proc. that Delphi puts into your application

would handle going through the list of child components that your

application has and tells them all to close as well.

You should be able to test this by starting something you've written

in Delphi and having the Task Manager close it.  Do this several

times and see if you free system resources decreases.


Colouring fields in DBGRIDS

Question


I currently have an application using a DBGRID. I want to display a memofield

in the grid in red if the memo has text in it, Default colour if not.

I have tried doing this by setting the default font for the grid to red

if the field about to be drawn is the required one and the memofield is

not null.

Answer


A:

Here's an example that uses the demo COUNTRY table and draws

the text of each row in red where the population is less than 10 million.

Hope it helps.



Respond to an OnDrawDataCell event.



   begin

     if Table1.FieldByName('Population').AsFloat < 10000000 then

        DBGrid1.Canvas.Font.Color := clRed;

     dbGrid1.DefaultDrawDataCell(Rect,Field,State);

   end;


"Autosizing" StringGrids

Question


Is there a way to make my Stringgrids "Autosize columns? I

am loading the stringgrid from static code and would like to

hae the column widths autosize to fit the text loaded.

Answer


A:

Not really, but OTTOMH you can do it programmatically like this (do this

after you've loaded the data, or if you're loading the data in columns you

can do it within this loop too):



   i,j,temp,max: integer;



   for i := 0 to grid.colcount-1 do begin

      max := 0;

      for j := 0 to grid.rowcount-1 do begin

         temp := grid.canvas.textWidth (grid.cells[i,j]);

         if temp > max then max := temp;

      end;

      grid.colWidths[i] := max + grid.gridLineWidth +1;

   end;



You may need to fudge with the +1 to give each column a little border space.


Right aligning menus

Question


How do I get a menu entry to right align in a window's menu bar? I want all

but Help left aligned, and then Help right aligned. The Windows API help file

suggests it can be done in a menu resource, but Delphi doesn't seem to

recognise the command.

Answer


A:

    It's not really fashionable any longer to right align the

    Help menu, so I only use the following trick to push a Debug

    menu over to the right.



    The technique is to prefix the menu caption with a Backspace

    i.e. Chr(8).



    I haven't found a way to do it at design-time, so you'll

    probably have to do at run-time (e.g. in the FormCreate

    procedure) with something like:



       MenuName.Caption := Chr(8) + MenuName.Caption;


Which button on panel is the Sender

Question


I've a panel on my application that has about 5 buttons.

A popup menu is attached to the Panel

When I right-click on the panel, how do I know which

button the right click fell on, and triggered the popup?

It seems that the Sender of the PopupMenu is the Panel.

But I need to know which button the click fell on.

I tried to put this in the button's OnMouseDown code but

it didn't work.



procedure TTBar2.ToolBarSpdBtn1MouseDown(Sender: TObject;

  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin

  if Shift = [ssRight] then ShowMessage('Right button!');

end;

Answer


A:

I think you might have to get rid of the PopupMenu property in the panel and

use the mouse down event of the button to do what you did above.  Also, I

think you could try to turn off the Auto property.  Then put this in your

OnMouseDown:



procedure TForm1.BitBtn1MouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

var

  P: TPoint;

begin

  with Sender as TBitBtn do

    if Button = mbRight then

      begin

        P := Self.ClientToScreen (

           Point (Left + (Width div 2), Top + (Height div 2)));

        PopupMenu1.Popup (P.X,P.Y);

      end;

end;


Publishing properties that are sets

Question


I need to publish a property that is a set such as



 type

   myset =  set of (msThis, msThat, msThese, msEtc);



   myClass = class(TComponent)

     fMySetProperty : myset;

   published

     MySetProperty : myset read fMySetProperty write fMySetProperty;

   end;



the compiler lets me put this guy in the public portion of the

declaration, but not published. Says that it can't be published.

Answer


I think your problem is that any field or method that is not explicitly

put under another kind of protection is assumed to be published, I

suspect what you meant to do was:



myClass = class(TComponent)

private { !! you omitted this protection directive }

  fMySetProperty : myset;

published

  MySetProperty : myset read fMySetProperty write fMySetProperty;

end;


Right Text Alignment in edit box

Question


One of these involved a form with a dbEdit box (TdbEdit) to

display the value of a float field) together with a standard

edit box (TEdit) to display the value of a float variable

(formatted as a text string).

The dbEdit box displayed the numerical value right-aligned,

which was most suitable indeed. I therefore also wanted the

standard edit box to do the same. However, I was unable to

get its text string to align itself in any other way than to

the left.

Can any of you Delphi gurus point me in the right direction

please. (How, for example, does Borland pull this stunt off

for dbEdit).

Answer


TEdit1  = class(TEdit)

  public

    procedure CreateParams(var Params: TCreateParams); Override;

  end;



procedure TEdit1.CreateParams(var Params: TCreateParams);

begin

  inherited CreateParams(Params);

  Params.Style := Params.Style or ES_MULTILINE or ES_RIGHT;

end;


Multi line Hints

Question


Is there a way to format the HINT property such that it spans

multiple lines versus a single long line?

Answer


A:

Not in the Object Inspector, but you can do it programmatically:



   edit1.hint := 'This is line 1'#13#10'This is line 2';



You could also do this as a hybrid by making a convention that a certain

character sequence represents a new line and replacing that at form create

time.  Eg., you could have the Hint property for Edit1 set to 'This is

line1::This is line 2' then in your FormCreate you can do:



   while true do begin

      i := pos ('::', edit1.hint);

      if (i = 0) then break;

      edit1.hint[i] := #13;

      edit1.hint[i+1] := #10;

   end;



Note that even if there are multiple lines the limit for a Hint is 255

characters.  You can also make the above a procedure and do it for several

components.


TTimer question

Question


I try to make a scheduler for starting an application at a certain time. To

be more specific, this scheduler should call a certain phone number and

leave a message. So I used a tTimer, set the Interval to a large value

(longint), only to discover that this value always put the phone ringing

after about 1 minute, yes, 65.535 milliseconds.

Is there a way to build a timer that uses a longint value for the interval?

Or does anybody know of an example or a component I can use to reactivate a

program after a longer period of time?

Answer


A:

Interval is defined as a word, so 65,535 is the most you're going to get

into it.  Don't know about other components, but to use the timer for over

one minute try this:



   var

      numOfMinutes: integer;

   ...

   numOfMinutes := 30;

   timer1.interval := 60000;

   timer1.enabled := true;

   ...

   procedure Timer1Timer;

   begin

      dec (numOfMinutes);

      if numOfMinutes = 0 then begin 

         { do whatever needs to be done }

      end;

   end;



A:

unit Alarm;



{

Copyright (c) 1996 Eric Nielsen. All Rights Reserved.



Permission to use, copy, modify, and distribute this software for

NON-COMMERCIAL or COMMERCIAL purposes and without fee is hereby granted.

}



interface



uses

  SysUtils, WinTypes, WinProcs, StdCtrls, Controls, Classes, ExtCtrls, Forms;



type

  TForm1 = class(TForm)

    Timer1: TTimer;

    Button1: TButton;

    Label1: TLabel;

    Label2: TLabel;

    Edit1: TEdit;

    procedure Timer1Timer(Sender: TObject);

    procedure Button1Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

    procedure FormAlarm(Sender: TObject);

  end;



var

  Form1: TForm1;



implementation



{$R *.DFM}

type

  TAlarm=class(TComponent)

  private

    FAlarmTime : TDateTime;

    FOnAlarm: TNotifyEvent;

    FTimer : TTimer;

    FEnabled : Boolean;

    procedure SetOnAlarm(Value: TNotifyEvent);

    procedure SetEnabled(Value: Boolean);

    procedure SetAlarm(Value: TDateTime);

    procedure AlarmTimer(Sender: TObject);

  protected

    procedure Alarm; dynamic;

    procedure UpdateAlarm;

  public

    property AlarmTime : TDateTime read FAlarmTime write SetAlarm;

    constructor Create(AOwner: TComponent); override;

    destructor Destroy; override;

  published

    property OnAlarm: TNotifyEvent read FOnAlarm write SetOnAlarm;

    property Enabled: Boolean read FEnabled write SetEnabled default False;

  end;



procedure TAlarm.SetOnAlarm(Value: TNotifyEvent);

begin

  FOnAlarm := Value;

  UpdateAlarm;

end;



procedure TAlarm.SetEnabled(Value : Boolean);

begin

  FEnabled := Value;

  UpdateAlarm;

end;



procedure TAlarm.SetAlarm(Value: TDateTime);

begin

  FAlarmTime := Value;

  UpdateAlarm;

end;



procedure TAlarm.Alarm;

begin

  If Assigned(FOnAlarm) then FOnAlarm(Self);

end;



procedure TAlarm.UpdateAlarm;

begin

  If Assigned(FOnAlarm) and FEnabled

    Then

      FTimer.Enabled := True

    Else

      FTimer.Enabled := False;

end;



constructor TAlarm.Create(AOwner: TComponent);

begin

  FTimer := TTimer.Create(Self);

  With FTimer do

    Begin

      Enabled := False;

      Interval := 250;

      OnTimer := AlarmTimer;

    End;

end;



destructor TAlarm.Destroy;

begin

  FEnabled := False;

  UpdateAlarm;

  FTimer.Free;

end;



procedure TAlarm.AlarmTimer(Sender: TObject);

begin

  If Now >= FAlarmTime

    Then

      Alarm;

end;



procedure TForm1.Timer1Timer(Sender: TObject);

begin

  Label1.Caption := DateTimeToStr(Now);

end;



procedure TForm1.Button1Click(Sender: TObject);

begin

  Label2.Caption := 'Waiting...';

  With TAlarm.Create(Self) do

    begin

      AlarmTime := StrToDateTime(Edit1.Text);

      OnAlarm := FormAlarm;

      Enabled := True;

    end;

end;



procedure TForm1.FormAlarm(Sender: TObject);

begin

  (Sender as TAlarm).Free;

  Label2.Caption := 'BEEP!';

end;





end.


Make TAB act like ENTER in StringGrid

Question


I've tried putting code in the

OnKeyPress event of the Grid, but it does not make the focuseed cell

moved to the next cell as the TAB key does.

Answer


A:

This code moves it to next column.  On reaching end of column, it moves

to a new row.  On reaching very end of grid, it goes back to the very top -

of course, you can modify it to go to the next control.



procedure TForm1.StringGrid1KeyPress(Sender: TObject; var Key: Char);

begin



  if Key = #13 then

    with StringGrid1 do

      if Col < ColCount-1 then {next column!}

        Col := Col + 1

      else if Row < RowCount-1 then begin {next Row!}

        Row := Row + 1;

        Col := 1;

      end else begin {End of Grid! - Go to Top again!}

        Row := 1;

        Col := 1;

        {or you can make it move to another Control}

      end;

end;


Cycle through list of components

Question


procedure TForm1.OnCreate(whatever)

var i : Integer

begin

  for i := 1 to 5 do

  Edit[i].X := 10; {received compiler error here, didn't recognize the Edit

control}

end;



What wrong here??

Answer


A:

procedure TForm1.FormCreate(Sender: TObject);

var

  I : integer;

begin

     for I:= 0 to ComponentCount -1 do

        if (Components[I] IS TEdit) then

           (Components[I] AS TEdit).{Yourparameter} := {your value};

end;





If you need to identify a particular set of edit components, you can put 

them on a panel & use something like



procedure TForm1.FormCreate(Sender: TObject);

var

  I : integer;

begin

     with MyPanel do

       for I:= 0 to ControlCount -1 do

          if (Controls[I] IS TEdit) then

             (Controls[I] AS TEdit).{Yourparameter} := {Your value};

end;



A:

The major one is that Edit1, Edit2 ect is not the same as

Edit[1], Edit[2]. If you want to access a series of controls as an array,

you have to put them into a TList.



MyArr := TList.Create;

MyArr.Add(Edit1);

MyArr.Add(Edit2);

  ...



For i := 0 To MyArr.count - 1 Do

  (MyArr.items[i] As TEdit).Enabled := False;



MyArr.Free;



A:

procedure TForm1.FormCreate(Sender: TObject);

var

  I: Integer;

begin

  for I := 0 to ComponentCount -1 do

     if Components[I] is TEdit then

       TEdit(Components[I]).Whatever := 10;

end;



A:

To access them just use:

TButton(mylist.items[i]).property := sumpin;

or

TButton(mylist.items[i]).method;



This is great for doing batch operations on the components or accessing them

in a linear fashion. For doing what you want there is a much easier way of

handling the problem and one that can be done at design time. Set the tag

property and take advantage of the fact that all components are derived from

TComponent.



Procedure TMyForm.MyButtonHandler(Sender: TObject);

Begin

  Case (Sender As TComponent).Tag Of

    1 : { do something }

    2 : { do something else }

     .

     .

  End;

End;



Just point the OnClick event to MyButtonHandler for all the buttons you want

to use this common handler.


Splitter bar

Question


Do you know where I can find "the code for a splitter bar" ?

Answer


A:

I use a TOutline aligned to alLeft, with desktop to the right of it. A

panel is placed after the TOutline, and also aligned alLeft. This makes it

stick to the TOutline. I call the new panel 'splitter'. Make the splitter

very narrow, bevel it if you like, and set it's cursor to east-west.

Replace the TOutLine with what ever type of component you need.

Attach the following to the mouse events:



 procedure TMainForm.SplitterMouseMove(Sender: TObject; Shift: TShiftState;

   X, Y: Integer);

 begin

   if ssLeft in Shift then begin

     outline.Width := outline.Width + X;   {replace outline with your object}

   end;

end;


Popup menu in dependence on mouse position

Question


I need to pop up a popup menu when a user stops highlighting text in a

TMemo..  Problem is i can not figure out who to get the mouse coordinates

with which to call PopUp (i want the menu to pop up at the place the mouse

is release).

Answer


A:

procedure TForm1.Memo1MouseUp(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

var ClientPoint,ScreenPoint: TPoint;

begin

 if Memo1.SelLength>0 then

 begin

  ClientPoint.X := X;

  ClientPoint.Y := Y;

  ScreenPoint := ClientToScreen (ClientPoint);

  PopupMenu1.Popup (ScreenPoint.X, ScreenPoint.Y);

 end;

end;


TabbedNotebook and common components on all pages

Question


I want to know how to define some component common to all the

pages on a tabbedNotebook without having to define them on every page

(like the OK and the Cancel Button from the TabbedNotebook template).

I would like to have some labels and entry fields to be available on

all pages.

Answer


A:

The components that you want to appear on all pages have to be owned by

the parent of the TTabbedNotebook (often the TForm on which it appears),

and, apparently, also need to be created AFTER the TTabbedNotebook.  The

simplest way I've found to get this to happen is to place the

TTabbedNotebook, but be sure that you can still get at the parent (i.e.

don't set the .Align property yet), then place the buttons (or whatever)

on the parent area, then set the .Align property for the

TTabbedNotebook, the controls you placed after the TTabbedNotebook,

should then appear on all pages (actually, they exist "on top" of the

TTabbedNotebook as a whole. If you have already placed components,

I've found that using the "Edit/Send to Back" command with the

TTabbedNotebook selected, will bring the desired components on top. You

can also edit the .DFM file directly to make sure the creation order is

what you want.


How to disable a tab(page) in a Notebook component

Question


On my form I have a TTabbed notebook component. However at runtime I want

to be able to disable a page or two on the fly. How do I do that?

Answer


A:

In the OnChange event of your TTabbedNotebook place somethig like this:



if (NewTab = 0) and (IWantToDisableTab0) then

   AllowChange := False;



if (NewTab = 1) and (IWantToDisableTab1) then

   AllowChange := False;



...



Yes, you could use a Case construction, but you's still need an If

for each value.


Insert text in MEMO

Question


Can it be true, that you can't insert a text in a TMemo at the cursor

position?

I wanted to use some buttons to insert standard phrases in a Memo field.

I solved the problem by using TEdit instead of TButton. Then I select the

text, copy it to  the clipboard, and paste from the clipboard to the

Memo.

That's OK, but I don't like to use the clipboard in the program. The user

might have stored something there too.

Answer


A:

Use the Windows API message-EM_REPLACESEL: (from Windows API Help)



EM_REPLACESEL

wParam = 0;                               /* not used, must be zero */

lParam = (LPARAM) (LPCSTR) lpszReplace;   /* address of new string  */



An application sends an EM_REPLACESEL message to replace the current

selection in an edit

control with the text specified by the lpszReplace parameter.



Parameter	Description

lpszReplace	Value of lParam. Points to a null-terminated string containing the

                            replacement text. { Pointer to the string }



Returns

This message does not return a value.



Comments

Use the EM_REPLACESEL message when you want to replace only a portion of the

text in an edit control.

If you want to replace all of the text, use the WM_SETTEXT message.



If there is no current selection, the replacement text is inserted at the

current cursor location.



A:

Make a list with your standard phrases and use the "OnClick" or

the "OnMouseDown" Event in combination with "Alt", "Shift" or

"Ctrl".  Example: When the user press the "Alt" key in combination

with the Right button of your mouse, pop-up the phrases list and

insert the selected phrase in your TMemo component.



A:

to insert a string into a memo :



procedure TForm1.Button1Click(Sender: TObject);

begin

     with Memo1 do begin

      SelStart:=10;

      SelLength:=0;

      SelText:='This is a string inserted into a memo, at 10th char position ';

   end;

end;



to insert a string AND replace some existing text :



procedure TForm1.Button1Click(Sender: TObject);

begin

     with Memo1 do begin

      SelStart:=10;

      SelLength:=20;

      SelText:='This is a string inserted, at 10th char position replacing 20 chars ';

   end;

end;



A:

Put the text you want to insert into a PChar variable, then insert

the text into the memo, using the SetSelTextBuf command, where

SelStart is set to the postion of the cursor in the TMemo.

It works  great ..



Another thing, you can get around the 32K limit on the TMemo

component, if you by-pass the Lines.LoadfromFile method/command. Its

has an inbuilt limit of 32K. If you load the file you want into a

pointer, and using the SetTexBuf command/method, you can load up to

64K of text into a TMemo.


Name of the item in a TListBox

Question


How do I get the name of the item in a TListBox?

Answer


A:

Use the Items and ItemIndex properties:



Foo := Bar.Items[Bar.ItemIndex];



Foo would now contain the selected item in the listbox.



A:

ShowMessage(  Listbox1.Items[Listbox1.ItemIndex] );


Sync'ing Tabset with Listbox

Question


I have a form with a tabset and a listbox.  The tabset has the strings

'A' - 'Z'.  The listbox has a list of name strings Ex. Holmes, Shane A

                                                       Doe, John D.

                                                       Doe, Jane J.



How does one syncronizr the tabset with the listbox.

For example:

   If the letter 'D' was chosen from the tabset, the listbox would

highlight the first item in the listbox beginning with the letter 'D'.

Answer


I've done something similar in the past, however, instead of using a Listbox,

I used a dbGrid with the following options:



[dgAlwaysShowEditor,dgTabs,dgRowSelect,dgAlwaysShowSelection,dgConfirmDelete,

dgCancelOnExit]



In addition, this is what I used when the tab was clicked upon.Thus changing

to the record in the dbgrid.



procedure TForm1.TabSet1Change(Sender: TObject; NewTab: Integer;

  var AllowChange: Boolean);

begin

Table1.FindNearest([Chr(NewTab+65)]);

Table2.FindNearest([Chr(NewTab+65)]);

end;



A:

procedure TForm1.TabSet1Click(Sender: TObject);

var

  I: integer;

begin

  with TabSet1 do

  begin

    if TabIndex > -1 then

    begin

      with ListBox1 do

      begin

        for I := 0 to Items.Count - 1 do

        begin

          if Pos(Tabs[TabIndex], Items[I]) = 1 then

          begin

            ItemIndex := I;

            break;

          end;

        end;

      end;

    end;

  end;

end;


TDBGrid - Vertical Scrollbar

Question


I use the TDBGrid frequently for display of tables. The only real

inconveniance is that slider of the vertical scrollbar in Delphi 1 and

2 only has three positions (top, middle, bottom).

Answer


There are several good reasons for this behavior. First, the program needs

to be consistant. Second, Delphi deals with a number of different DBs,

engines, and sizes of databases. If it were to present a slider which

represented position in the database in a more realistic manner, it would

have to know how many records were in the database. However, because the

access methods differ so much, that information is in some cases difficult

or very slow to retrieve. Thus, I was told, Borland decided not to have that

be the behavior.



If you have source, you may be able to inherit and modify...


Variable control-names

Question


I have a form with (let's say..) 100 labels. Then there is a function

generating a integer value between 1 and 100; The value identicates the

number of the label which caption I want to change. How can I manage this

without writing very long code.

Answer


    Here is a sample code I just made to test dynamic component creation;

    I hope this helps you...:



unit Unit1;



interface



uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

  Menus, StdCtrls, Buttons;



type

  TForm1 = class(TForm)

    procedure FormCreate(Sender: TObject);

  private

    { Private declarations }

    arr : array [1..30] of TBitBtn;

    procedure BitButtonClick(Sender: TObject);

  public

    { Public declarations }

  end;



var

  Form1: TForm1;



implementation



{$R *.DFM}



procedure TForm1.BitButtonClick(Sender: TObject);

begin

  if TBitBtn(Sender).Font.Style = [] then

  begin

    TBitBtn(Sender).Font.Style := [fsBold];

    TBitBtn(Sender).Font.Color := clRed;

  end

  else

  begin

    TBitBtn(Sender).Font.Style := [];

    TBitBtn(Sender).Font.Color := clBlack;

  end;

end;



procedure TForm1.FormCreate(Sender: TObject);

var

  x, y,

  f : integer;

begin

  x := 20;

  y := 20;

  for f := 1 to 30 do

  begin

    arr [f] := TBitBtn.Create (Form1);

    if arr [f] <> nil then

    begin

      arr [f].Parent := Form1;

      arr [f].left := x;

      arr [f].top := y;

      arr [f].width := 35;

      arr [f].Height := 25;

      arr [f].caption := IntToStr (f);

      arr [f].tag := f;

      arr [f].OnClick := BitButtonClick;

      inc (x, arr [f].width + 20);

      if (x + arr [f].width) > form1.width then

      begin

        x := 20;

        inc (y, arr [f].height + 20);

      end;

    end;

  end;

end;



end.



A:

There are two ways I can think of:



(1) Using FindComponent. Will have to use the components name:



	var

	   lab: TLabel;

	begin

	     lab := TLabel(FindComponent('Label1'));

	     lab.Caption := 'Hey!'

	end;





(2) Using the Tag property. Set each label's Tag property (ie: 1 to 100)

and then

    you will be able to do something like:



	procedure TForm1.Button2Click(Sender: TObject);

	var

	   i: smallint;

	begin

	     for i := 0 to ComponentCount-1 do

	         if (Components[i] is TLabel) AND (Components[i].Tag = 3) then

	         begin

	            TLabel(Components[i]).Caption := 'This is the one!';

	            break;

	         end;

	end;



A:

How about iterating through all your components (Check your form's

Components property).

Use something like this:



var

  I: Integer;

begin

  for I := 0 to ComponentCount -1 do

     if Components[I] is TLabel then

       TLabel(Components[I]).Caption := 'This is the one!';

end;



A:

Put the labels into a TList. You can then use :



 TLabel(mylist.items[MyValue]).Caption := 'This is the one!';



A:

function LabelCaption(MyValue: Integer; Text: string) : Boolean;



function LabelCaption(MyValue: Integer; Text: string) : Boolean;

var

  LabelCount,

  Loop: Integer;

begin

  Result := False;

  LabelCount := 0;

  for Loop := 0 to ComponentCount - 1 do

  begin

    if Components[Loop] is TLabel then  { is it a label ? }

    begin

      INC(LabelCount);

      if LabelCount = MyValue then  { does it match my index value ? }

      begin

        Component[Loop].Caption := Text;

        Result := True;

      end;

    end;

  end;

end;



Call it as follows:-



   if LabelCaption(10, 'This is the one!') then

     WriteLn('Found it!');



The other approach which is more in line with what you want would be to

use a

StringList of some sort in which case you could do an IndexOf on the list

to find

your label. You could use the above Loop to add the label names to the

StringList.



This would allow you to do a lookup as follows:-



  MyLabels[MyLabels.IndexOf(Label10)].Caption := 'This is the one!';


OnkeyDown and Hot-key problem

Question


If got the following code, that works fine but...I get an annyoing beep.

I have the Form's KeyPreview set to True.



 If (Shift = [ssAlt]) then

    begin

      If Key = $4F then    {O}

        BitBtn1Click(BitBtn1);

      If Key = $55 then    {U}

        BitBtn4Click(BitBtn4);

      If Key = $58 then    {X}

        BitBtn2Click(BitBtn2);

      If Key = $48 then    {H}

        BitBtn3Click(BitBtn3);

    end;

Answer


I've found that there's a strange interaction between onKeyPress and

onKeyDown; setting key to 0 in onKeyDown doesn't necessarily kill it in

onKeyPress.  Apparently you're doing the above in onKeyDown; if you do it in

onKeyPress and set the key value to 0 the beeps should go away.

Alternatively, you can do something like this:



in onKeyDown:

   killKey := false;

   If key = $4f then begin

      killKey := true;

      bitBtn1Click (bitBtn1);

   end;



in onKeyPress:

   if killKey then key := #0;


Add an OnClick event to DBGrid

Question


How can I add an OnClick event to the Delphi 1.0 TDBGrid component?

Answer


TGroothuisGrid = class(TDBGrid) {!}

published

  property OnClick;

end;



That's all! The OnClick is already declared in TControl as a

protected property. All you have to do is publish it, register the

derived component (see ch. 8 op the Component Writer's Guide) and

use your component in stead of TDBGrid.


Events for components created at Run-Time

Question


How can i code an event for a component that has been

created at Run-Time? Is the windows message system the

only way?

Answer


You would manually create a method that has the same calling parameters as the

event you want to handle. Then you would manually set the OnXXX property to

point to the method you created.



i.e.



        TForm1 = class(TForm)

          procedure FormCreate(Sender: TObject);

        private

          FMyButton: TButton;

        protected

          procedure Button1Click(Sender: TObject); {Manually code this to

                                                    match}

                                                   {the TNotifyEvent

                                                    structure}

        end;



        procedure TForm1.FormCreate(Sender: TObject);

        begin

          FMyButton := TButton.Create;

          {Set the position, caption, etc here}

          FMyButton.OnClick := MyButtonClick;

        end;



        procedure TForm1.MyButtonClick(Sender: TObject);

        begin

          ShowMessage('Hey! You just clicked my button!');

        end;


Alignment in Listbox

Question


I'm developing a KWIC index and need to display the results in a listbox.

They have to line up around the central keyword, eg.

If I'm using a fixed font, this is easy, just blank pad on the left.  But if

I'm using a proportional font, it's much harder.  I could blank pad on the

left and used textWidth to check whether I'm near the middle, but because

character widths vary my keyword alignment is going to be off by a few pixels.

Answer


     You could take the TextWidth of the string before the keyword and

     adjust the position based on that.



     i.e.



     var

       J, TempInt, LongPrefixLen, CurrPrefixLen: Integer;



     begin

         {Calculate the TextWidth of the widest pre-keyword string}

         {Set CurrPrefixLen to the TextWidth of the pre-keyword of the

          Indexth string}

       LongPrefixLen := 0;

       for J := 0 to ListBox1.Items.Count-1 do

         with ListBox1.Canvas do

         begin

           TempInt:= TextWidth(Copy(Items[J],1,Pos(KeyString,Items[J]-1)));

           if LongPrefixLen < TempInt then

             LongPrefixLen:= TempInt;

           if J = Index then

             CurrPrefixLen:= TempInt;

         end;

           {PrevTextLeft - TextLeft = Where we want to write new item}

       TextOut(LongPrefixLen-CurrPrefixLen,Y,Items[I]);

     end;


Two columns in DBLookupComboBox

Question


I want to let shown 2 colums from one Datasource within a

TDBLookupComboBox in Delphi 2.0.

Answer


Try using anything like this:



DBLookupCombo1.LookupDisplay := 'Company;City;Country';



eg to specify more than one field to display, separate each field name with

a semicolon.


Change the color of a grid cell in a TDBGrid

Question


How do I change the color of a grid cell in a TDBGrid?

Answer


Enter the following code in the TDBGrid's OnDrawDataCell event:



Procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;

Field: TField; State: TGridDrawState);



begin



If gdFocused in State then

with (Sender as TDBGrid).Canvas do

begin



Brush.Color := clRed;

FillRect(Rect);

TextOut(Rect.Left, Rect.Top, Field.AsString);



end;



end;



Set the Default drawing to true.  With this, it only has to draw the

highlighted cell.  If you set DefaultDrawing to false, you must draw all the

cells yourself with the canvas

properties.


Cells' Position on DBGrid

Question


Is there any way to get the position of the selected cell (wich has the

focus) of a TDBGrid Component, in D1.  For example: left, top, rigth,

etc.

Answer


TCustomGrid defines the method CellRect, which is protected,

unfortunately. This means that it can only be called by TCustomGrid

itself or a descendant of TCustomGrid. There is way, though, to call

the method, though it's a bit tricky:



type

  TMyDBGrid = class(TDBGrid)

  public

    function CellRect(ACol, ARow: Longint): TRect;

  end;



function TMyDBGrid.CellRect(ACol, ARow: Longint): TRect;

begin

  Result := inherited CellRect(ACol, ARow);

end;



Then you can do the conversion by typecasting your DBGrid to

TMyDBGrid (This is possible, because the new CellRect is a static

method) and call CellRect:



Rectangle := TMyDBGrid(SomeDBGrid).CellRect(SomeColumn, SomeRow);



A:

procedure TfmLoadIn.DBGrid1DrawColumnCell(Sender: TObject;

  const Rect: TRect; DataCol: Integer; Column: TColumn;

  State: TGridDrawState);

const

  Disp = 2;	//Displacement to cause component to line up properly

begin

  inherited;

  if (gdFocused in State) then begin

    if (Column.FieldName = 'TYPEDescription') then begin

      dlTYPEDescription.Left := Rect.Left + DBGrid1.Left + Disp;

      dlTYPEDescription.Top := Rect.Top + DBGrid1.top + Disp;

      dlTYPEDescription.Width := Rect.Right - Rec