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