|
Forms / Dialogs
Different dialogs from form template
Dialogs, those modal considering the whole system
Multi-form Menus
Dragging windows without titlebars
Close an application on deactivate
Getting the name of a form
Different Resolutions - different font sizes
Form width
Positioning self-created forms
Procedure to Wrap dialog box with an object
Running without a form
Transparent splash screen
Form.TForm.Create(???)
Does this form exist
Freeing form
How do you keep the user from resizing a form
Creating a form with a width < 102
Disable ALT-F4
Dialog box title
Forms / Scaled
Set focus while modal dialog is open
Overriding ESC-key on a Form
Destroy a Modal Form on Deactivate
Adding BorderIcons to a form
Closing a modal form
Event for Form move
Copy the screencontents into a form
Putting Forms into a stream
Forms - Dragging without clicking the caption bar
Making apps show minimized
Transparent window
Controlling form maximization
Making a form selectable without the main form
Scrolling forms with PgUp and PgDn
Creating own hotkeys
Hiding a form's title bar
Using an alternate main form
Different dialogs from form template
Question
My main problem is that i am using different 'dialogs' from the forms
template. Where i have used a form i can amend the code to create it at
runtime ie when needed with the following code
formx := tformx.create(appliaction);
formx.showmodal;
formx.free;
However if i use the same code on the form using the form name/aliasname
the form does not show.
Answer
A:
try this code:
Application.CreateForm(TFormX, FormX);
FormX.ShowModal;
FormX.Free;
don't forget that the visible property of this form have to be false
when it is created, if it isn't you will get an error when you want
to show it modal.
Dialogs, those modal considering the whole system
Question
with showmodal i show a dialog, which is modal considering the app. how
can i show a dialog, which is modal to the whole system (no switches to other
tasks should be allowed)?
i want to show dialogs, which comes on the top of all windows if the app
running at background. how to realize this?
Answer
A:
Check out API-function SetSysModalWindow
Check out API-function BringWindowToTop
A:
If you just want a simple dialog with a bit of text, an optional
icon and some buttons, use MessageBox in WinProcs
and use MB_SYSTEMMODAL in the TextType parameter.
A:
Use the SetSysModalWindow() Windows API function. The following code
snippet demonstrates its use. There can be only one system-modal window at
a time, and that handle is returned by SetSysModalWindow(). I suggest that
you save that return value and then reset it when you are finished, as
demonstrated below.
procedure TForm1.Button1Click(Sender: TObject);
var
x : word ;
begin
x := SetSysModalWindow(AboutBox.handle) ;
AboutBox.showmodal ;
SetSysModalWindow(x) ;
end;
Multi-form Menus
Question
I have a project that has multiple NON-MDI forms. The problem I am having is that if I put a menu on a form other than the form designated as the "Main Form," it does not work correctly. The menu on the main form pops up whenever you hit the Alt key. If the main form does not have a menu, the program just beeps at you obnoxiously
and tries to switch focus back and forth.
Can non-Main form's have menus or not? If so, how do I convince my APP to let the user access them with the alt key.
Answer
A:
In you "Main Form" you need to catch key events and pass them to the
NON-MDI forms by calling their proceedures, ie. using the NON-MDI forms
event handlers. If you like this idea then can use the onkeydown event in
the "Main Form" to check to see if the user has pressed an ALT key
combination before calling the NON-MDI forms proceedure. Don't forget to
add the NON-MDI forms unit to the USES section of the "Main Form". I
assumed you probably already have done this anyhow.
If you don't like that idea, then the only other way I know is to set the
focus to the NON-MDI form before the user presses a key.
Dragging windows without titlebars
Question
I want to know how to move a window by dragging the components on the form or the form itself because I want to create a window without a title bar.
(e.g. move the window by dragging a panel on the form)
Answer
A:
Here is how you do it...
{most basic form definition}
TForm1 = class(TForm)
private
procedure WMNCHitTest(var M: TWMNCHitTest); message wm_NCHitTest;
end;
{... and this is the actual message handler procedure}
procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);
begin
inherited; { call the inherited message handler }
if M.Result = htClient then { is the click in the client area? }
M.Result := htCaption; { if so, make Windows think it's }
{ on the caption bar. }
end;
It works by tricking Windows into thinking that the mouse is in the forms
caption area. This may cause problems since the mouse is always considered to
be in the caption area whenever it is in client area but it is a very elegent
solution because the forms border changes to the standard fuzzy outline while
it is being dragged. There may be another message that you can trap which
produces the same result if this doesn't quite work for you.
A:
1. Disable all BorderIcons for the form.
2. Make sure the caption is an empty string.
3. BorderStyle = bsNone
4. Override the form's CreateParams procedure as shown below
type
TForm1 = class(TForm)
{ ...
a bunch of stuff
...}
protected
procedure CreateParams(var Params : TCreateParams); override;
public
end;
{... other stuff ...}
implementation
{...}
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
Style := Style or ws_Border or ws_ThickFrame;
end;
{...}
end.
A:
Var
Moving : Boolean;
OldX, OldY : Integer;
...............
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
If Button = mbLeft Then {only interested in left button}
Begin
OldLeft := X; {Store current position}
OldTop := Y;
Moving := True;
End;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
{If required move the window relative to its original position}
If Moving Then Self.SetBounds
(Self.Left + X - OldLeft, Self.Top + Y - OldTop, Self.Width,
Self.Height);
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
If Button = mbLeft Then
Moving := False; {Stop Movement}
end;
Remember to assign these methods to each control on your form eg.
Self.Panel1.OnMouseDown := Self.OnMouseDown;
Close an application on deactivate
Question
How to close an application if it will deactivated?
Answer
A:
Try the following code:
procedure TForm1.AppDeactivate(Sender: TObject);
begin
close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnDeactivate := AppDeactivate;
end;
Getting the name of a form
Question
I'm trying to get the name of a form at run time.
Answer
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.
Different Resolutions - different font sizes
Question
A user with a different resolution than yours, because the font size, get
your form with another appearance, _IF_ you didn't set fpVariable in the font
properties.
But if you create a maximized form, this doesn't work!
Answer
A:
I have had a great deal of bother with this subject and I have done some
experimentation now.
The font fpVariable thing is important, but also are some other properties
that are important.
1. I got stupid results on changing resolution until I changed the font
size to be defined in pixels instead of points. You can set font.height and
you can set font.size. I find things are much better if you specify
font.height because it is then defined in pixels and everything stays in
proportion if the size of a pixel changes.
Also you will find that the default font can't be used below a certain
height. If you change to the MS sans-serif font instead of SYSTEM, things
are better.
2. There is a property for a form that is called "scaling" (I think, from
memory). I find it better to have this turned off. Apparently if it is
switched on, Delphi or Windows tries to scale everything appropriately when
the size of the form changes. I found that this is quite good at wrecking
the relative positions of things on a form so I turn it off.
When it is off and a form is maximised, you will find that your components
tend to be in the top left quadrant of the screen. Where this is not
desirable I find out the screen resolution (Screen.Height and Screen.Width)
and make appropriate run time changes to the top and left properties of
components before the form appears (in OnCreate method).
On the whole, though, turning scaling off and using pixel sizes for fonts
instead of point sizes gives reasonable results.
Form width
Question
I want to create a Form with the width set to 130, however when I do
this either by trying to set the property at design time or by
setting the width in FormCreate ir always returns to 142.
Answer
A:
Could it because of some components you have on your forms?
Try creating a _new_ form with no components and set it to 130 and see if
the same thing happens.
A:
You may be running up against a Windows behavior in which it sets a default
minimum width. If so, you can try creating a message handler for
wm_GetMinMaxInfo to set the minimum width of a window. The way this message
works is by passing a pointer to a structure (in lParam). You can also set
the maximum size of a window, and the maximized size of a window, and the
position of the window when maximized. Here is an example of how to use it
to only change the minimum width:
Add this to the private declaration of your TForm descendant:
procedure WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo); message
wm_GetMinMaxInfo;
and this would be the implementation:
procedure TForm1.WMGetMinMaxInfo(var Msg: TWMGetMinMaxInfo);
begin
Msg.MinMaxInfo^.ptMinTrackSize.x := 130;
end;
Note that Windows 95 is a little different than Windows 3.1 on minimum window
widths. Since Win95 adds new buttons in the caption bar of windows in the
case that you specified using either a Minimize or Maximize button to be in
the caption, the minimum width in that case is a bit more than in Windows
3.1. Also, it might be a bit different depending on the size of the caption
font.
Positioning self-created forms
Question
In my MainForm (Form1) I create another form, the position of which I want
to control. Form1 is a standard blank form, Form2 too but with DialogBox
style (but I have for sure tested all combinations of borderstyles etc).
Now, with a simple "child" form (no MDI!) code like
procedure Form1.Button1Click(Sender: TObject);
begin
Form2 := TForm2.Create(self); { from within Form1 }
with Form2 do begin
Top := Form1.Top + somevalue;
Left := Form1.Left + someothervalue;
ShowModal;
Free;
end;
end;
which works as expected.
Answer
A:
Delphi How-To book published by the Waite Group:
unit Runtime1; { How-To 1.6 }
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TNewForm = class(TForm)
NewEdit: TEdit;
NewButton: TButton;
procedure NewButtonClick(Sender: TObject);
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ OnClick handler for generated forms }
procedure TNewForm.NewButtonClick(Sender: TObject);
begin
NewEdit.Text := 'Hi!';
end;
{ Generates and shows form from scratch }
procedure TForm1.Button1Click(Sender: TObject);
var
NewForm: TNewForm;
begin
{ Create new form }
NewForm := TNewForm.CreateNew(Application);
{ Set its properties }
with NewForm do
begin
Top := 140;
Left := 220;
Width := 435;
Height := 300;
Caption := 'Runtime!';
{ Create new edit component }
NewEdit := TEdit.Create(NewForm);
{ Set its properties }
with NewEdit do
begin
Parent := NewForm;
Left := 153;
Top := 40;
Width := 121;
Height := 29;
TabOrder := 1;
Text := 'Edit1';
end;
{ Create new button component }
NewButton := TButton.Create(NewForm);
{ Set its properties }
with NewButton do
begin
Parent := NewForm;
Left := 153;
Top := 176;
Width := 121;
Height := 33;
TabOrder := 0;
Caption := 'Change Edit';
{ Wire the component to OnClick handler }
OnClick := NewButtonClick;
end;
Show;
end;
end;
end.
In fact, they set the coordinates but they don't try to give some new
values. Moreover, couldn't you try the poScreenCenter property, only to see
if it could work?
Another opportunity would be to store in an Ini file values for your
positions, but you probably already tought about it.
Procedure to Wrap dialog box with an object
Question
I would like to create an object that has contained in it a dialog box.
The effect I am looking for is similiar to the FileOpen/Save dialogs, where you
set/get properties, then call an Execute method.
Answer
A:
I would go for a new form, Dialog style, fill in the necessary components,
and use it roughly like
with TMyForm.Create(self) do begin
Parent := Self;
MyExecute(inParam, outParam);
Release;
end;
where MyExecute() would do the Show[Modal] and other things
Running without a form
Question
Is it possible to run a Delphi application without creating a(ny) form?
Answer
A:
Yes, using the project manager window, you can Remove the Form which is created
automatically when you start a new project.
Additionally (to get the .exe down to a reasonable size) you can remove the
"Application.Run" statement and the "uses Forms" from the project source.
Obviously, you will then have to add _some_ functionality to the application
in the Project Source (or provide it in a separate unit, and call one or more
of the routines in that unit from the Project Source).
Transparent splash screen
Question
It's splash screen is a circle, instead of the normal square. Everything behind
the circle (ie. what would normally be covered by the square) was visible.
Does anyone have any idea how they did this, or how I could do something
similar?
Answer
A:
Get the DC of the Screen and link it to a Canvas. Use the Canvas as you
would a forms canvas. Free the DC.
Form.TForm.Create(???)
Question
I have some questions about creating a form,
when and/or why do I use code line 1,2,3 or 4????
(MainForm and NameOnForm are FormStyle = fsNormal).
Procedure MainForm.BtnOpenFormClick(Sender: TObject);
Begin
1) NameOnForm:=TNameOnForm.Create(APPLICATION);
2) NameOnForm:=TNameOnForm.Create(NAMEONFORM);
3) NameOnForm:=TNameOnForm.Create(SELF);
4) NameOnForm:=TNameOnForm.Create(???????????);
NameOnForm.ShowModal;
NameOnForm.Free;
end;
Answer
A:
Don't use (2)! You'll either GPF or, if this is a second instance of
NameOnForm you'll lose your pointer to the first.
My understanding of how (1) and (3) differ is: For a showModal, (1)
and (3) are equivalent. For a show, (1) would keep nameOnForm open
until the application is closed (or until the user closes
NameOnForm), while (3) would close nameOnForm when mainForm is
closed.
Does this form exist
Question
How can I check to see if a form exists yet or not? I have a button that
creates a subform and displays it. When the user clicks on close it does NOT
destroy it but just hides it. If the button is click again, I want to show
the form, not create a new instance. How can I tell if the form has been
created or not??
Answer
A:
Normally, dynamically instantiated forms (or subforms in this case) are
created with a statement like...
frmNewForm := TNewForm.Create( owner );
To achive what you have asked for, I can think of 3 possible solutions.
1) declare frmNewForm as a global var hence you can code:
IF frmNewForm = NIL THEN
frmNewForm := TNewForm.Create( owner );
frmNewForm.Show;
or for those with an aversion to global vars
2) Find the refernce to frmNewForm that was saved in the components list of
the owner when TForm.Create( owner ) was called.
In most cases this will be the components array property of the main form.
3) If you do not have a main form that you want to be the owner of the
subform then use the TAppllication components CreateForm( TForm, refernce )
method. Later when you need to refer to the subform you can scan the
components array property of you application object. Find the form
by component name and retrieve the required reference.
A:
One important point, if you do release the object, you should set the
pointer to nil yourself. Otherwise you may get a GPF next time round.
frmNewForm.Release;
frmNewForm := nil;
A:
If you specify the form's owner as 'Application' when you create it, you
can then iterate through Application's components to check if your form
still exists.
In order to do this, you can create the form either of these ways:
Application.CreateForm(TSubForm, SubForm);
- or -
SubForm := TSubForm.Create(Application);
Then, you can check for your form's existence by iterating through
Application's components:
for i := 0 to Application.ComponentCount-1 do begin
if Application.Components[i] = SubForm then ... {form exists};
Notes:
------
1. If you use 'Self' instead of 'Application' when creating the form, the
form will not be a component of Application, so it won't be found when
you iterate through Application's components.
2. Instead of iterating through Application.Components, you could use
'Application.FindComponent' *if* you've assigned a name to the form
(e.g., SubForm.Name := 'SubForm';) after you've created it.
'FindComponent' searches the Components array by *name*, and forms you
create are NOT given a name until you assign one.
3. Testing for a form's existence by using something like
'if SubForm = nil' will NOT work UNLESS you specifically assign nil to
SubForm after you 'Free' it. Delphi does NOT assign nil to forms when
it frees them.
Freeing form
Question
Am I right in believing that when a form is not visible it frees its
resources? Or do I have to Destroy it. How would i do that whithout
closing the app.
Answer
A:
When a form isn't visible it is does set its resources free.
What you need is to create it at run time.
Use Release method, not Free.
Try this:
unit Unit1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
Form2: TForm;
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
if Form2 <> nil then
begin
Form2.Release;
Form2:= nil;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if Form2 = nil then
begin
Form2 := TForm.Create(Application);
Form2.Show;
end;
end;
end.
[Francisco Torres, ftorres@cenpes.petrobras.gov.br]
A:
- In your main don't use this:
begin
Application.Create(myForm);
Application.Run;
end.
- But this:
begin
myForm := TmyForm.Create(Application);
{ insert here what you want }
myForm.Show; { ... or myForm.ShowModal }
{ insert here what you want }
myForm.Hide; { ... if you want to hide it }
{ insert here what you want }
myForm.Free;
end.
- You can "Create" and "Free" forms when you want and many times you want.
Remember to free-up all forms before ending your app!
How do you keep the user from resizing a form
Question
We want to prevent the user from resizing a form vertically, but let them
resize it horizontally (like the Delphi main control form with all the buttons
and the menu on it). How do you do this?
Answer
A:
Well, one way to do this is to trap the WM_NCHITTEST and if it returns
HTBOTTOM, HTBOTTOMLEFT, HTBOTTOM, HTTOP, HTOPLEFT or HTTOPRIGHT right then
eat it. This will make windows completely ignore the Window resize if the
mouse is anywhere on the lower or upper border of the Window. In fact,
windows will not even show the resizing border. Look up this message in the
Windows API.
A:
You can intercept the message (See Message keyword in help) WM_GETMINMAXINFO. It
comes with a structure TMINMAXINFO. You can insert your own values in that
structure
and so limit the position or size that the user is changing.
Check the online help for more information.
Creating a form with a width < 102
Question
Does anybody know how to stop Delphi from resetting a form's width to 102
pixels whenever you set the width to anything less than that?
Answer
A:
Add this definition to intarface section of your form,
procedure WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo);
message WM_GETMINMAXINFO;
and add this to implementaion section.
procedure TForm1.WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo);
begin
with message do begin
MinMaxInfo^.ptMinTrackSize.x := 20;
end;
end;
A:
I havn't figured anyway to do it from the IDE but you can do at
run-time with
procedure TForm1.Form1OnCreate(Sender: TObject);
begin
Width := 50;
end;
Disable ALT-F4
Question
How do I disable ALT-F4 for a form?
Answer
Look at the closequery event of a form - if you return false - unless a
certain value is set, then it will only close when it is closed YOUR way----
otherwise, you could put any important shutdown code in there as well....
Dialog box title
Question
Is there any way to set the title for a dialogbox created with
MessageDlg(). In standard (not using CTL3D.DLL) dialogbox it can be
setted (in API-function).
Answer
A:
The dialog-caption gets set from inside the
CreateMessageDialog inside Dialogs.pas. It does a LoadStr to get
the Warningcaption, Cautioncaption and so on so you have two
choices: Either you modify Dialogs.pas or you change the strings
in the .res file.
Forms / Scaled
Question
We're curious about what the relationship is among Scaled, PixelsPerInch
and dynamically creating forms...
Answer
A:
I think the documentation for "PixelsPerInch" is confusing, because in my
experience, this refers to the size in pixels/inch of the system's FONTS,
not of the form.
For instance, Delphi's default PixelsPerInch property will typically be 96
for any forms you create, REGARDLESS OF YOUR SYSTEM'S SCREEN RESOLUTION.
On my system, this seems to be true whether I'm running at 640x480,
800x600, 1024x768, etc.
However, when changing your screen's resolution, if you specify "Large
Fonts" instead of "Small Fonts", Delphi's default PixelPerInch will now
typically be 120 for any forms you create, again regardless of the
resolution you're running at.
I think Delphi does this for the following reason: Delphi needs to know
both the PixelsPerInch of the form as it was designed and at runtime. With
this information, Delphi can determine if the form needs to be scaled in
order for all the text (i.e., fonts) to fit as it was designed.
Example:
-------
- You create a form using "Small Fonts". You leave the PixelsPerInch
setting at its default of 96.
- Your user tries to run your app using "Large Fonts". This setup means
that all the form's text would now be larger relative to the rest of the
form, because of the larger font sizes. The PixelsPerInch property of
this user's setup would be 120.
If your form's "Scaled" property is set to False, Delphi will not do
anything to compensate. Your text will probably be larger than when you
designed it, and some of it my be clipped because it won't fit.
However, if your form's "Scaled" property is set to True, Delphi will
automatically scale the form by a factor of 120/96. This way, the form has
now "grown" to accomodate the larger fonts. Everything will look as you
designed it.
NOTE: This scaling will only take place if the user's PixelsPerInch
property is different from the designer's. As far as I know, this only
happens when you choose a different font size when changing your screen's
resolution.
In my case, my forms NEVER scaled themselves, whether at 800x600, 1024x768,
etc., because I was always using "Small Fonts". This really had me going
for awhile. My solution was to scale the forms manually, using
ScaleBy(...).
I suppose another solution would be to change the form's PixelsPerInch
property at runtime, when the form is about to be created. This would
"trick" Delphi into thinking that the form was designed using a different
PixelsPerInch, so it would adjust accordingly. For example, changing a
form's PixelsPerInch property from 96 to 80 during creation would cause
Delphi to scale the form by a factor of 96/80.
Set focus while modal dialog is open
Question
I'm trying to create a lookup table for my users that will hang around
during the life of my application. I've got one sticky problem: if I create
a data entry form and showModal it, then the lookup table can't receive
focus!
Answer
A:
The way I am seeing it, the form with the lookup table should be displayed when
you need it from either the main or data entry form by whatever means, a Lookup
Button etc.
I would envisage something like this:
in fMain.formCreate:
fLookup := tFLookup.create (self);
{show code removed from here}
in fMain.btn1Click:
fEntry := tFentry.create (self);
fEntry.showModal;
in fMain.LookupButtonClick:
fLookup.showMODAL;
in fEntry.LookupButtonClick:
fLookup.showMODAL;
in fLookup.DoneButtonClick:
fLookup.Hide;
Overriding ESC-key on a Form
Question
I have a form on which a couple of TEdits are placed. Now i would
like to overrule the ESC button. Instead of closing the form, the text
in the active Edit-field should be deleted.
Answer
A:
(a) check the TEdit's OnKeyPress event for Key = #27
(b) if that fails, set the form's KeyPreview property
(set = make it true, reset means make it false) and then in the form's
OnKeyPress event, check for Key = #27.
If these fail, you could always use the windows message system to check for
all keypresses coming in to your application. I did it for a couple of apps for
some other keys, and it's not that hard. Write if you want an example.
Destroy a Modal Form on Deactivate
Question
I would like to destroy a Modal Form when the application is de-activated.
I've done some testing and research. Basically, I would use Application
Deactivate event handler.
How do I simulate letting the Modal Form close peacefully?
For a non-modal form, it's so much less complicated... a hide will do.
Note my "Form1" is declared globally that Application.OnDeactivate can
see it.
Answer
A:
procedure TForm1.AppDeactivate(Sender: TObject);
var
hw: HWnd;
CurTask: THandle;
WndStyle:Longint;
begin
CurTask:=GetWindowTask(handle);
hw:=GetWindow(GetDesktopWindow, GW_CHILD);
while GetWindowTask(hw)<>CurTask do
hw:=GetWindow(hw, GW_HWNDNEXT);
while (hw<>handle) and (GetWindowTask(hw)=CurTask) do
begin
PostMessage(hw, WM_Close, 0, 0);
hw:=GetWindow(hw, GW_HWNDNEXT);
end;
end;
Adding BorderIcons to a form
Question
Does anyone know how to add additional border icons to the caption frame
area of a form?
Answer
I've only ever done this stuff iin C, so I'm not sure about how to do it via
native Delphi code, however:
For Windows v3.1x, you'll need to override the WM_NCPAINT message, and use
the GetSystemMetrics function to determine the size of the buttons etc.,
then after calling the inherited WM_NCPAINT, paint your button. You'll also
need to override WM_NCMOUSEDOWN etc. so that you can process click messages.
You might be able to get an actual TButton or TSpeedButton up there, but it
might end up being harder.
For the system menu, call GetSystemMenu() to get it's handle. From there
you can call AddMenuItem to add your own items. You may need to override
WndProc to handle menu picks etc.. There's probably an easier way to do it
in Delphi though.
Closing a modal form
Question
How do I close a modal form without going through a button event handler?
Answer
I think the problem is that the FormActivate handler is called as part of
the focus change to the new form, so Delphi suppresses any other focus
changes while the handler executes. If you have ever tried buggering
about with focus changes using the Windows API you'll know that
changing the focus while the focus is being changed leads to an infinite
loop within Windows !!!
The solution should be simple. Post a WM_CLOSE message to the form that
you want to close, at the end of the OnActivate handler. This works
because the message is queued by Windows until sometime after the
OnActivate handler has finished executing (and hence the focus change has
ended).
I tried it with a simple program; one form with a button on it that
activates an About dialog, which closes itself immediately. Not very
useful I admit, but illustrates the principle.
Unit Form1;
.... (usual rubbish ) ...
implementation
uses
About;
procedure TForm1.Button1Click(Sender: TObject);
begin
AboutBox.ShowModal;
end;
Unit About;
.... (usual rubbish ) ...
implementation
uses
Messages;
procedure TAboutBox.FormActivate(Sender: TObject);
begin
PostMessage( Handle, WM_CLOSE, 0, 0 );
end;
I hope this helps. If not, do get back.
A:
The better solution is: Put an invisible button in the modal form,
set the property ModalResult to mrOK, and put this code at the end of
the OnActivate procedure:
postmessage(Button1.Handle, wm_mousedown,0,0);
postmessage(button1.Handle, wm_mouseup,0,0);
So the function returns idOK, and you can check if the cancel button
was pressed.
Event for Form move
Question
Event for Form move?
Answer
It is a windows message, it gets sent to the form when the form is moved.
You would declare a procedure and put it in the protected area of the form
class..
Procedure WMMove(Var Message : TWMMove); message WM_Move;
Procedure TForm1.WMMove(Var Message : TWMMove);
begin
Label1.Caption := 'X = '+IntToStr(Message.XPos)+', Y = '+IntTOStr(Message.
YPos);
end;
The event gets called during the move as well. So you might need to check if
the mouse is still down.
Copy the screencontents into a form
Question
how to copy the screencontents into a form?
Answer
A:
procedure TScrnFrm.GrabScreen;
var
DeskTopDC: HDc;
DeskTopCanvas: TCanvas;
DeskTopRect: TRect;
begin
DeskTopDC := GetWindowDC(GetDeskTopWindow);
DeskTopCanvas := TCanvas.Create;
DeskTopCanvas.Handle := DeskTopDC;
DeskTopRect := Rect(0,0,Screen.Width,Screen.Height);
ScrnForm.Canvas.CopyRect(DeskTopRect,DeskTopCanvas,DeskTopRect);
ReleaseDC(GetDeskTopWindow,DeskTopDC);
end;
A:
var Image3: TImage;
procedure TSaverForm.CopyScreen;
var
DeskTopDC: HDc;
DeskTopCanvas: TCanvas;
DeskTopRect: TRect;
begin
Image3 := TImage.Create(SaverForm);
With Image3 do
begin
Height := Screen.Height;
Width := Screen.Width;
end;
Image3.Canvas.copymode := cmSrcCopy;
DeskTopDC := GetWindowDC(GetDeskTopWindow);
DeskTopCanvas := TCanvas.Create;
DeskTopCanvas.Handle := DeskTopDC;
Image3.Canvas.CopyRect(Image3.Canvas.ClipRect, DeskTopCanvas,
DeskTopCanvas.ClipRect);
Image2.Picture.Assign(Image3.Picture);
{image2 is on the saver-form, align at client}
end;
procedure TSaverForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Image3.Free;
end;
Currently I am also digesting some other replies to my question.
A:
Try the following HAX 244 from the Aug/Sept issue of Visual Developer
magazine. It works and works well.
{ see text description following END. }
unit Scrncap;
interface
uses WinTypes, WinProcs, Forms, Classes, Graphics;
function CaptureScreenRect( ARect: TRect ): TBitmap;
function CaptureScreen: TBitmap;
function CaptureClientImage( Control: TControl ): TBitmap;
function CaptureControlImage( Control: TControl ): TBitmap;
implementation
{ use this to capture a rectangle on the screen }
function CaptureScreenRect( ARect: TRect ): TBitmap;
var ScreenDC: HDC;
begin
Result := TBitmap.Create;
with Result, ARect do
begin
Width := Right - Left;
Height := Bottom - Top;
ScreenDC := GetDC( 0 );
try
BitBlt( Canvas.Handle, 0, 0, Width, Height,
ScreenDC, Left, Top, SRCCOPY);
finally
ReleaseDC( 0, ScreenDC );
end;
end;
end;
{ use this to capture the entire screen }
function CaptureScreen: TBitmap;
begin
with Screen do
Result := CaptureScreenRect( Rect( 0, 0, Width, Height ));
end;
{ use this to capture just the client area of a form or control...}
function CaptureClientImage( Control: TControl ): TBitmap;
begin
with Control, Control.ClientOrigin do
Result := CaptureScreenRect( Bounds( X, Y, ClientWidth,
ClientHeight ));
end;
{ use this to capture an entire form or control }
function CaptureControlImage( Control: TControl ): TBitmap;
begin
with Control do
if Parent = nil then
Result := CaptureScreenRect( Bounds( Left, Top, Width,
Height ))
else
with Parent.ClientToScreen( Point( Left, Top )) do
Result := CaptureScreenRect( Bounds( X, Y, Width, Height ));
end;
end.
{
From: Visual Developer, HAX #244, Aug/Sept 1996
Taking Snapshots of the Screen with Delphi
In Delphi, if we want to capture the image of a form's client area, we
can call GetFormlmage. But sometimes we want a snapshot of the entire
form--title bar, frame, and all. Or we want a snapshot of the entire
screen. If we were desperate, we might pop up a message box that says,
"Press Print Screen button NOW!" and then figure out a way to get the
screen's mug off the clipboard.
But we're not that desperate. Combining Delphi canvases with a few GDI
functions makes capturing the screen in code a snap.
CaptureScreenRect, in Listing 1, demonstrates this. It gets the screen's
device context with GetDC(O), and then it copies a rectangular area from
that DC to a bitmap's canvas. To do the copying, it uses BitBlt. The key to
using BitBlt--or any GDI function--with Delphi is remembering that a canvas'
Handle is the DC that Windows needs.
The remaining screen capture functions in Listing 1 gobble up rectangles
and farm out the real work to CaptureScreenRect. CaptureScreen throws
together a rectangle for the whole screen. CaptureClientImage and
CaptureControlImage throw together rectangles for the client area and for
the entire area of a control, respectively.
These four functions can be used to capture any arbitrary screen area,
as well as the screen images of forms, buttons, memos, combo boxes, and
so on. We just tell the little buggers to say cheese--and free the bitmaps
when we're done.
}
Putting Forms into a stream
Question
I am looking for a way to put a form and all its components (such as
a list box, and its items), into a stream so that I can save it for
latter retrieval.
Answer
Delphi has a great function to do it:
procedure WriteComponentResFile(const FileName: string; Instance: TComponent);
Just fill the file name you want to save in and the component to save
and to read it back:
function ReadComponentResFile(const FileName: string; Instance: TComponent):
TComponent;
Forms - Dragging without clicking the caption bar
Question
How can I make a form move by clicking and dragging in the client area
instead of on the caption bar?
Answer
{
A: The easiest way to do this is to "fool" Windows into thinking that
you're actually clicking on the caption bar of a form. Do this by
handling the wm_NCHitTest windows message as shown in the sample unit
below.
TIP: If you want a captioness borderless window similar to a floating
toolbar, set the Form's Caption to an empty string, disable all of the
BorderIcons, and set the BorderStyle to bsNone.
}
unit Dragmain;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure WMNCHitTest(var M: TWMNCHitTest); message wm_NCHitTest;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);
begin
iinherited; { call the inherited message handler }
if M.Result = htClient then { is the click in the client area? }
M.Result := htCaption; { if so, make Windows think it's }
{ on the caption bar. }
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end;
end.
{ The text representation of the .DFM file is below:
object Form1: TForm1
Left = 203
Top = 94
BorderIcons = []
BorderStyle = bsNone
ClientHeight = 273
ClientWidth = 427
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'System'
Font.Style = []
PixelsPerInch = 96
TextHeight = 16
object Button1: TButton
Left = 160
Top = 104
Width = 89
Height = 33
Caption = 'Close'
TabOrder = 0
OnClick = Button1Click
end
end
}
Making apps show minimized
Question
When I select the "Run Minimized" option in Program Manager
to attempt to make my Delphi application execute in a minimized
state, the Delphi application seems to ignore the setting and
run normally. Why is this, and how to I fix it?
Answer
A: Delphi's Application object creates a hidden "application
window," and it is that window, rather than your main form,
that is being sent the command to show minimized. To fix this,
just enter this line of code in your main form's OnCreate event handler:
ShowWindow(Handle, cmdShow);
Transparent window
Question
How to make windows transparent?
Answer
To create a transparent window, put 'Brush.Style:=bsClear;' in the OnCreate
event for the form.
Controlling form maximization
Question
How to control form maximization?
Answer
I just wrote out a simple little program that will allow you to control the size
of a form when it is maximized. It does this in such a way as to remove the
flickering that you see if you've been adjusting the size via the resize event.
Basically, this code traps the wm_getminmaxinfo message. To use this style form
instead of the Delphi standard, simply compile the following text pascal file
into a DCU. Then replace occurences of TForm with TMaxForm and add (if you
called the pascal file maxform.pas) maxform to your uses clause. Here now is
the short program:
unit Maxform;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs;
type
TMaxForm = class(TForm)
private
{ Private declarations }
fmh, fmw, fml, fmt : word;
procedure mymax(var m: TWMGETMINMAXINFO);
message wm_getminmaxinfo;
published
property maxheight : word read mh write mh;
property maxwidth : word read mw write mw;
property maxleft : word read ml write ml;
property maxtop : word read mt write mt;
constructor create(AOwner : TComponent); override;
end;
implementation
procedure TMaxForm.mymax(var m : TWMGETMINMAXINFO);
begin
m.minmaxinfo^.ptmaxsize.x := fmw;
m.minmaxinfo^.ptmaxsize.y := fmh;
m.minmaxinfo^.ptmaxposition.x := fml;
m.minmaxinfo^.ptmaxposition.y := fmt;
end;
constructor TMaxForm.create(Aowner : TComponent);
begin
fmw := screen.width;
fmh := screen.height;
fmt := 0;
fml := 0;
inherited create(aowner);
end;
end.
Making a form selectable without the main form
Question
How do I make it so that only the form I select comes to
the top? (i.e. without the main form)
Answer
Try this in any secondary window that you DON'T want
dragging the program along:
...
private {This goes in the for's type declaration.}
{ Private declarations }
procedure CreateParams(VAR Params: TCreateParams); override;
...
procedure TForm2.CreateParams(VAR Params: TCreateParams);
begin
Inherited CreateParams(Params);
Params.WndParent := GetDesktopWindow;
end;
By setting the form's parent window handle to the
desktop, you remove the link that would normally force the
whole application to come to the top when this form comes to
the top.
DISCLAIMER: You have the right to use this technical information
subject to the terms of the No-Nonsense License Statement that
you received with the Borland product to which this information
pertains.
Scrolling forms with PgUp and PgDn
Question
How can you do scrolling functions in a TForm component
using keyboard commands? For example, scrolling up and down
when a PgUp or PgDown is pressed. Is there some simple way to
do this or does it have to be programmed by capturing the
keystrokes and manually responding to them?
Answer
Form scrolling is accomplished by modifying the
VertScrollbar or HorzScrollbar Postion properties of the
form. The following code demonstrates how to do this:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
const
PageDelta = 10;
begin
With VertScrollbar do
if Key = VK_NEXT then
Position := Position + PageDelta
else if Key = VK_PRIOR then
Position := Position - PageDelta;
end;
DISCLAIMER: You have the right to use this technical information
subject to the terms of the No-Nonsense License Statement that
you received with the Borland product to which this information
pertains.
Creating own hotkeys
Question
How can I trap for my own hotkeys?
Answer
First: set the form's KeyPreview := true;
Then, you do something like this:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (ssCtrl in Shift) and (chr(Key) in ['A', 'a']) then
ShowMessage('Ctrl-A');
end;
DISCLAIMER: You have the right to use this technical information
subject to the terms of the No-Nonsense License Statement that
you received with the Borland product to which this information
pertains.
Hiding a form's title bar
Question
How to hide a form's title bar?
Answer
{ You must USE Winprocs & WinTypes for this to work }
Procedure TYourFormName.HideTitlebar;
Var
Save : LongInt;
Begin
If BorderStyle=bsNone then Exit;
Save:=GetWindowLong(Handle,gwl_Style);
If (Save and ws_Caption)=ws_Caption then Begin
Case BorderStyle of
bsSingle,
bsSizeable : SetWindowLong(Handle,gwl_Style,Save and
(Not(ws_Caption)) or ws_border);
bsDialog : SetWindowLong(Handle,gwl_Style,Save and
(Not(ws_Caption)) or ds_modalframe or ws_dlgframe);
End;
Height:=Height-getSystemMetrics(sm_cyCaption);
Refresh;
End;
end;
Procedure TYourFormName.ShowTitlebar;
Var
Save : LongInt;
begin
If BorderStyle=bsNone then Exit;
Save:=GetWindowLong(Handle,gwl_Style);
If (Save and ws_Caption)<>ws_Caption then Begin
Case BorderStyle of
bsSingle,
bsSizeable : SetWindowLong(Handle,gwl_Style,Save or ws_Caption or
ws_border);
bsDialog : SetWindowLong(Handle,gwl_Style,Save or ws_Caption or
ds_modalframe or ws_dlgframe);
End;
Height:=Height+getSystemMetrics(sm_cyCaption);
Refresh;
End;
end;
Using an alternate main form
Question
Can I use an alternate main form?
Answer
The following logic allows you to check command-line parameters for a
certain condition, and determines which of two forms to make the main form:
var
bCondition: Boolean;
{$R *.RES}
begin
bCondition := ( ParamStr(1) = 'Form2'); {or whatever trigger condition }
if bCondition then begin {<<<}
Application.CreateForm(TForm1, Form1);
end {<<<} else
Application.CreateForm(TForm2, Form2);
Application.Run;
begin...end {<<<} is imperative to let you use the Project Options Dialog.
This tip is adapted from the excellent book 'Delphi Programming Problem
Solver' by Neil Rubenking 1-56884-795-5.
|