Delphi MDI Application and the titlebar of the MDI Children - delphi

I've got an MDI application written in Delphi 2006 which runs XP with the default theme.
Is there a way of controlling the appearance of the MDI Children to avoid the large XP-style title bar on each window?
I've tried setting the BorderStyle of the MDIChildren to bsSizeToolWin but they are still rendered as normal Forms.

All your need - overload procedure CreateWindowHandle, like this:
unit CHILDWIN;
interface
uses Windows, Classes, Graphics, Forms, Controls, StdCtrls;
type
TMDIChild = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
procedure CreateWindowHandle(const Params: TCreateParams); override;
end;
implementation
{$R *.dfm}
procedure TMDIChild.CreateWindowHandle(const Params: TCreateParams);
begin
inherited CreateWindowHandle(Params);
SetWindowLong(Handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
end;
end.

The way MDI works doesn't gel with what you're trying to do.
If you need the "MDI" format, you should consider using either the built-in or a commercial docking package, and use the docking setup to mimic the MDI feel.
In my Delphi apps, I frequently use TFrames and parent them to the main form, and maximizing them so they take up the client area. This gives you something similar to how Outlook looks. It goes a little something like this:
TMyForm = class(TForm)
private
FCurrentModule : TFrame;
public
property CurrentModule : TFrame read FModule write SetCurrentModule;
end;
procedure TMyForm.SetCurrentModule(ACurrentModule : TFrame);
begin
if assigned(FCurrentModule) then
FreeAndNil(FCurrentModule); // You could cache this if you wanted
FCurrentModule := ACurrentModule;
if assigned(FCurrentModule) then
begin
FCurrentModule.Parent := Self;
FCurrentModule.Align := alClient;
end;
end;
To use it, you can simply do this:
MyForm.CurrentModule := TSomeFrame.Create(nil);
There is a good argument that you should use interfaces (creating an IModule interface or something) that you use. I often do that, but it's more complex than needed to explain the concept here.
HTH

I don't think there is; in my experience, MDI in Delphi is very strictly limited and controlled by its implementation in the VCL (and perhaps also by the Windows API?). For example, don't try hiding an MDI child (you'll get an exception if you try, and you'll have to jump through a couple of API hoops to work around that), or changing the way an MDI child's main menu is merged with the host form's.
Given these limitations, perhaps you should reconsider why you'd like to have special title bars in the first place? I guess there are also good reasons why this MDI stuff is standardized --- your users might appreciate it :)
(PS: nice to see a Delphi question around here!)

Thanks onnodb
Unfortunately the client insists on MDI and the smaller title bar.
I have worked out one way of doing it which is to hide the title bar by overriding the windows CreateParams and then create my own title bar (simple panel with some Mouse handling for moving). Works well enough so I think I might run it by the client and see if it will do...

Related

Creating a new component by combining two controls (TEdit and TTrackBar) in Delphi VCL

I am developing a Delphi 10.1 VCL application for Windows.
For integer or float input I need a number input field which is connected with a slider. When the user changes the number in the input field the slider position changes accordingly. When the user changes the slider position the number in the number field is updated.
I can solve this by using a TEdit and a TTrackBar and add the necessary update functionality in their OnChange event handlers.
The problem is that I need many of such inputs on different forms. Therefore I would like to create a new component which combines the two controls TEdit and TTrackBar in one component.
Is the creation of a new component the best strategy for the multiple use of such a slider input?
What is the best way to create such a new component?
Is the creation of a new component the best strategy for the multiple
use of such a slider input?
Not necessarily true all the time. (by my standards at least).
What is the best way to create such a new component?
I know three ways to solve your problem.
Way number 1:
create the component using the new component wizard where you create dynamically the TEdit and the TTrackBar sub components in a TGroupBox descendant.
the following is how I would do that.
unit Combindedittrack;
interface
uses
System.SysUtils,
System.Classes,
Vcl.Controls,
Vcl.comctrls,
Vcl.StdCtrls;
type
TCombindEditTrack = class(TGroupBox)
private
{ Private declarations }
FEdit: TEdit;
FTrackBar: TTrackBar;
procedure EditOnChangeProc(Sender: TObject);
procedure TrackBaroOnChangeProc(Sender: TObject);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TCombindEditTrack]);
end;
constructor TCombindEditTrack.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
SetBounds(0, 0, 250, 50);
FEdit := TEdit.Create(Self);
with FEdit do
begin
Text := ''; //<-- you control the appearence here
Top := 10;
Left := 10;
Height := 27;
Width := 50;
Parent := Self;
OnChange := EditOnChangeProc; // your Onchange event handler for the Tedit
end;
FTrackBar := TTrackBar.Create(self);
with FTrackBar do
begin
Top := 10; //<-- you control the appearence here
Left := 60;
Height := 30;
Width := 50;
Parent := self;
Onchange := TrackBaroOnChangeProc; // your Onchange event handler for the Ttrackbar
end;
end;
destructor TCombindEditTrack.Destroy;
begin
FTrackBar.Free;
FEdit.Free;
inherited;
end;
procedure TCombindEditTrack.TrackBaroOnChangeProc(Sender: TObject);
begin
// <-- track bar onchange handling here.
end;
procedure TCombindEditTrack.EditOnChangeProc(Sender: TObject);
begin
// <-- edit onchange handling here.
end;
end.
Way number 2:
Use frames like this (I'm on delphi 10 seattle).
File-->New-->Other-->(search for frames on delphi files).
Now add the edit and the track bar and set their Onchange events.
Save the unit.
on the tool palette (the standard component section) click on the frame component.
choose the frame you just created.
You will have a replica of the frame each time you use it.
Way number 3:
Use component template like this (again I'm on delphi 10 seattle)
Select your already created and modified tedit and ttrackbar.
On the toolbar's "component" click "create component template".
Name your template and press OK.
Select the Template palette and then your template.
Now notice that even your code (events) are added as well to your project.
Finally
With the level I have on delphi and the IDE I'm really unable to give you a clear answer to which is the best way but nevertheless I have shared all what I know that could help you.
edit: Since a lot of comments are insisting that the answer should state which is the best way to do this. this is the best way based on the following.
let's put some of the key point that should be accounted for when choosing
1. Ease of modifying the combined control(s) if you wish so (by my experience you will).
2. time needed to complete this task (it means the time it will take
you to fully complete the task with minimum debugging and coding).
3. general source code readability.
4. usefulness for the future of your projects.
Now lets start criticizing the three methods based on the those criteria.
Way number 1:
C1(criteria number 1): Just modify the the source implementation of the component and each replica/use will have the same effects and properties. However this is not the case for way number 3.
C2: It depends on your knowledge of component writing but for this component it took me 5 min to create it and I'm only a beginner in delphi. For the debugging if something went wrong and the problem is in the component implementation than you just need to fix once (see C1)
C3: their is no implementation in your form(s) source code for your component just add it to your form and every thing is hidden (for example add a tedit and go to see the implementation in your forms source).
C4: You are creating a component after all this will open the door for you to create your own set of components like the Flatstyle or Indy open source components. so next time you need some thing like this you just drop it in your form designer and you are done.
Way number 2: frames
C1: It is like way number 1 because you are creating a component but it is visually this time. modifying the source frame will change the effects and properties of the replicas, also you can add extra handling to your replicas.
the event handler of the replica's Onchange event is like this
procedure TForm1.Frame2Edit1Change(Sender: TObject);
begin
Frame2.Edit1Change(Sender); //<-- this is the original onchange event you setup at the beginning
//<-- you can extra handling here if you want one of the replicas to behave differently than the original
end;
C2: same time and maybe faster than way number 1.
C3: over all, it has the same out come as way number 1.
C4: unlike way number 1 you can not use frames created in project A in project B. So your coding and debugging will stay in project A.
Way number 3: component template.
C1: you are not creating a component you are creating a repleca/macro of the exact steps you did in your last project. changing one will not change the others they are separated.
C2: same time and maybe faster than way number 1.
C3: each time you add a template to your form the events code will be added (not a good view if it is a long Onchange code).
C4: You can use templates created in project A in project B. However what you wrote in project A will be in project B (see c1) even the references of variables that don't exist in project B (this can be hard to debug and misleading, considering the period of time between each use of the template).
Conclusion: each of the ways presented will consume time to code and debug and all of them will do the task, How ever for the sake of simplicity and the reuse with minimum risks Way number 1 is the safe choice here because it will give you the chance to update and upgrade safely. also debug faster.
the good thing also about way number 1 is that after a while when you will forget the implementation and how things are working internally. The only thing that should stay in mind is the purpose of the component because it will become one of the various component you use (you don't know how Tedit is implemented and you don't need to but yet you use it in every single project you create).
based on the criteria given Way number 1 is the best.
Maybe using a container control that contains both controls is a simpler alternative. I am using ccpack for this.
https://sourceforge.net/projects/ccpack/
Custom Containers Pack (CCPack) is an integrated tool and component mini-
library to produce and maintain composite controls (or simply “composites”)
and other containers (forms, data modules and frames). The process of
building composite components looks like ActiveForm and Frame creating, but
the result is the native VCL component. You can create new composites just
as usual forms.
You can create a Frame and then register that Frame as a component. The end result is very similar to creating a code only component where the sub components are created in the constructor (Nasreddine's number 1 option). However this method allows you to visually design the component and use the object inspector to create your event handlers.
Here is a Stack Overflow question that shows how to register the frame:
How to Improve the Use of Delphi Frames

Freeing focused custom control dynamically causes crash

A custom control that I wrote is implicated in a crash when it is destroyed. It is hard to pin down the exact circumstances and it might be a factor that the control is parented by a 3rd party control.
Edit 8 October 2014
I've now got a much better SSCCE that illustrates the crash using only TMediaPlayer (from the Delphi VCL) on TForm. So I've deleted a lot of what I wrote before. Please see the edit history for that. (It turns out that CM_EXIT in the former call stack was a red-herring.)
Here's the SSCCE:
unit Unit1;
interface
uses
System.Classes, Vcl.Controls, Vcl.Forms, Vcl.Menus, Vcl.MPlayer;
type
TForm1 = class(TForm)
MainMenu: TMainMenu;
CrashMenuItem: TMenuItem;
procedure CrashMenuItemClick(Sender: TObject);
procedure FormShow(Sender: TObject);
private
fControl : TMediaPlayer;
end;
var
Form1: TForm1;
implementation
uses
Vcl.Dialogs;
{$R *.dfm}
procedure TForm1.CrashMenuItemClick(Sender: TObject);
begin
ShowMessage('Message');
fControl.Free;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
fControl := TMediaPlayer.Create(Form1);
fControl.Parent := Form1;
end;
end.
The call to ShowMessage immediately before freeing the control is crucial.
After dismissing the dialog, the TMediaPlayer control gets a WM_SETFOCUS.
It's destructor is then called. Inherited TCustomControl.Destroy frees the canvas and then inherited TWinControl.Destroy calls TWinControl.RemoveFocus, so it gets a WM_KILLFOCUS.
TMediaPlayer.WMKillFocus calls Paint directly, which tries to use the freed canvas and crashes.
(Previously I had a custom control where CMFocusChanged called Invalidate. The effect was the same but the call stack was rather more involved.)
My original 3 questions, that NGLN has answered below:
Am I doing something wrong merely calling FreeAndNil(fMyControl)? Must I unparent it before destroying it? But this doesn't seem necessary with any other controls, so more likely that will just hide the underlying bug.
Should my control have something in its destructor to fix this so that TWinControl knows not to try to repaint it?
Is there perhaps a bug in the 3rd party parent control? Is it the case that my control should certainly not receive a WM_PRINTCLIENT message once it has started to be destroyed? (The 3rd party control seems to make an explicit call to its inherited TWinControl.Update when it receives CM_EXIT as a consequence of my control losing focus.)
But the real question remains: Is there anything wrong with the code in my SSCCE, or is there a bug in the Delphi VCL?
(Incidentally, the same problem will occur with any descendent of TCustomControl. I used TMediaPlayer for convenience.)
Am I doing something wrong merely calling FreeAndNil(fMyControl)?
No, every control should be able to be freed at any given time, as long as all references to the control are cleared (nilled) and the instance's code isn't run anymore.
Must I unparent it before destroying it? But this doesn't seem necessary with any other controls, so more likely that will just hide the underlying bug.
No, indeed no need to.
Should my control have something in its destructor to fix this so that TWinControl knows not to try to repaint it?
No, normally there is no need to. The VCL has this all build in already. For testing purposes or as a (temporary) workaround, you could try to override PaintWindow with something like if not (csDestroying in ComponentState) then.
Is there perhaps a bug in the 3rd party parent control? Is it the case that my control should certainly not receive a WM_PRINTCLIENT message once it has started to be destroyed? (The 3rd party control seems to make an explicit call to its inherited TWinControl.Update when it receives CM_EXIT as a consequence of my control losing focus.)
The parent control indeed receives CM_EXIT, because it had a focussed control, and now it has not anymore (ie. Form.ActiveControl = nil). So that's normal behaviour. As for why the parent sends a WM_PRINTCLIENT to the control (how do you know that request comes from the parent? It seems to start at the Update call.) I do not know. To rule out the possibility of a buggy parent, retry your case with a different parent.
Update (due to question edit):
TMediaPlayer.WMKillFocus calls Paint directly...
procedure TMediaPlayer.WMKillFocus(var Message: TWMKillFocus);
begin
Paint;
end;
That is taboo! That is definitely a bug in the VCL. Paint should never be called directly other than by a request for painting via a WM_PAINT message. I have submitted a report on QC.
(Previously I had a custom control where CMFocusChanged called Invalidate. The effect was the same but the call stack was rather more involved.)
...
(Incidentally, the same problem will occur with any descendent of TCustomControl. I used TMediaPlayer for convenience.)
That is not the case with a test here in D7 and XE2.

Component property not considered on runtime in Delphi XE2

I am developing a component but I can't make it consider a property set at design-time.
The following is an excerpt of the component:
TRVEditFrame = class(TFrame)
...
private
{ Private declarations }
FRVEditor:TCustomRichView;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
protected
function GetRVEditor:TCustomRichView;
procedure SetRVEditor(Editor:TCustomRichView);
published
property RVEditor:TCustomRichView read GetRVEditor write SetRVEditor;
end;
...
constructor TRVEditFrame.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
SetRVEditor(FRVEditor);
...
end;
function TRVEditFrame.GetRVEditor:TCustomRichView;
begin
Result:=FRVEditor;
end;
procedure TRVEditFrame.SetRVEditor(Editor:TCustomRichView);
begin
if Assigned(Editor) then begin
FRVEditor:=Editor;
end;
end;
I can register the component, place it in ther form and set FRVEditor on design-time.
Problem is when I run the application the code inside SetRVEditor() is not executed because Editor=nil.
If I was able to set FRVEditor on design-time, how come that it is=nil on run time ? How can I fix this ?
I add here my further comments because the explanation is too long
#Kenneth, thank you for your reply
TCustomRichView is part of a third part component set that manages
hypertext documents and has 4 more specialized descendents and you
are right, TCustomRichView shouldn't be used in a real application.
TRVEditFrame is the component I am developing.
The idea behind my component is to create one single frame (hence the choice of the component TFrame) with menus, shortcuts, popup menus etc to manage each of the 4 TCustomRichView descendents.
This is exactly the reason why I use TCustomRichView: I can "slot" any of the 4 descendents into my component-frame. This is the same principle of TDatasource that can be connected with TTAble and TQuery (they have the same ancestor).
I suppose the reason why the VCL doesn't link RVEditor to the TCustomRichView descendent I set on design-time is because TFrame has no OnCreate event, like TForm for instance.
So far I managed to solve the issue by calling TRVEditFrame.SetRVEditor manually in the TForm.OnCreate that hosts TRVEditFrame but I was wondering if there are better methods to do so and that is why I have asked advice here.
I know you can create a OnCreate event for TFrames as well, maybe I can place TRVEditFrame.SetRVEditor in there but, again, I was wondering if there was a better method.
Regarding the last part of your comment, I am aware of the register procedure but take into account the component is under development. When I develope components I never install them in the IDE because I prefer to keep the test stuff outside the "official" one.
I use this method and as soon as the component is ready then I register it with the procedure you mention. If I want to implement other features to the same component I can work on the test ones and keep on using the "official" one I have in the IDE at the same time.
My suggestion is to change the design in order to get the frame component referencing the custom editor by using the Notification method, instead of manually setting a property. So, change your frame class to
TRVEditFrame = class(TFrame)
...
private
{ Private declarations }
FRVEditor: TCustomRichView;
protected
procedure Notification(aComponent: TComponent; aOperation: TOperation); override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
public
property RVEditor:TCustomRichView read FRVEditor;
end;
The implementation of the Notification method does the magic of connecting/disconnecting the frame to the custom editor
procedure TRVEditFrame.Notification(aComponent: TComponent;
aOperation: TOperation);
begin
inherited;
if aComponent is TCustomRichView then
if aOperation=opRemove then begin
if aComponent=FRVEditor then
FRVEditor := nil;
end else
FRVEditor := TCustomRichView(aComponent);
end;
I don´t know if you need any special handling when an editor is set/reset to the frame, so this code does nothing in special, just assigns the component or nil to the FRVEditor data member at the proper moment.
You should create child components.
constructor TRVEditFrame.Create(AOwner: TComponent);
begin
inherited; // Create(AOwner);
FRVEditor := TRichView.Create(self);
end;
DFM streaming engine may load properties of already created class, but it cannot create the class for you for two reasons:
1) DFM can not know of any special tuning done on created component, like what should be constructor parameters (if any), what constructor to use (of many), which niotifications and event handlers to attach and so on.
2) DFM can not know which type the property should be. For example i published TStrings oroperty - object of which class should be created to feel in ? TStrings? TStringList? TRichEditStringList? THashedStringList ? they all were inherited from TStrings and thus any of them is fine choice for DFM - but not for the ocmponent you write.
Thus DFM streaming subsystem is responsible for saving and loading properties of objects, but creating those object is purely your responsibility.
I believe you also may cut the corners by learning how IDE Designer creates your forms and making RVEdit a variable rather than property:
TRVEditFrame = class(TFrame)
...
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
published
var RVEditor:TCustomRichView; // just like users' forms are created by IDE
end;
Then hopefully TFrame constructor would create the content for this variable for you. But this design is fragile because any outer code would be able by any stupid mistake to make something like MyEditFrame.RVEditor := ... and cause a memory leak and unexpected failures of all the attached connections between the editor and the frame.

TJvDockServer and dockable controls

I'm programming in Delphi (BDS 2006) and the JVCL library, using the docking modules. I have one problem - if the control has properties DragKind = dkDock and DragMode = dmAutomatic, then inexplicably TJvDockServer component takes the controls are both clients and provides docking. This is wrong, because, as I found out, JVCL's docking functions normally only control class TForm which contain a component class TJvDockClient. I would like to know whether it is possible in some way to prevent TJvDockServer from docking controls whose class is different from TForm? During a typical docking in Delphi for each event is called OnGetSiteInfo dock and it is possible to filter clients, but there is no such event in TJvDockServer.
The property DragKind and DragMode are standard VCL properties. Docking is built into the VCL, and from looking at it, it seems to work pretty good without any Jedi Code involved.
The ability to dock something other than a form, is already built into the VCL. Therefore that you find this inexplicable suggests to me that you thought Jedi added docking to the VCL. No, it just added some pretty things like "tabbed notebook docking" and "conjoined areas" with fake window titlebars.
That being said, Forms are also inheriting from TCustomControl, and any TCustomControl can in fact, be docked. And just like the VCl lets you drag and dock and land on top of TPanels. Okay it's a quirky feature, that your panel can turn into a form on you at runtime, but if you don't believe me, try it. It's the VCL doing this to you, not Jedi.
If in your wisdom, you want to block anything that is not a TForm, I thought that you can.
Surely you can right? Update. Yes you can. OnDockOver works fine to block docking on any panel you want to block docking on. The trick with the Jedi JvDockPanels is that you don't see them at designtime, so you need to access their events by hooking them up in code, at runtime.
Just like regular TPanels, JvDockPanels have a TPanel.OnDockOver event, and if you want to check the thing you're docking, and set the Accept to false, it will be prevented from docking.
Okay, this works:
type
TCustomControlAccess = class(TCustomControl);
procedure TMainForm.FormCreate(Sender: TObject);
begin
TCustomControlAccess(dockServer.TopDockPanel).OnDockOver := MyDockOverEvent;
TCustomControlAccess(dockServer.CustomDockPanel).OnDockOver := MyDockOverEvent;
...
end;
The JvDockPanel.OnDockOver panel events DO fire, but you need to resort to a hack like the above hack, to actually handle the events yourself.
Update previously thought there was no way to block this. But I was wrong. Figured it out.
while i cannot reproduce exactly your behaviour in Delphi XE2, generally i seem manage to block VCL-frag-n-drop for JediVCL components.
Maybe it is not the best possible way, but i don't know which were original ideas of the framework creator.
http://wiki.delphi-jedi.org/wiki/JVCL_Help:TJvDockServer claims only forms should be docked. Did not enforced that, just hardwired JVCL check routine to be always called.
unit JvDockSupportControl;
....
TJvDockCustomControl = class(TJvCustomControl)
....
protected
procedure GetSiteInfo(Client: TControl; var InfluenceRect: TRect;
MousePos: TPoint; var CanDock: Boolean); override;
...........
function TJvDockCustomControl.GetJvDockManager: IJvDockManager;
begin
// Result := IJvDockManager(DockManager);
DockManager.QueryInterface(IJvDockManager, Result);
end;
procedure TJvDockCustomControl.GetSiteInfo(Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
var jdm: IJvDockManager; idm: IDockManager;
begin
idm := DockManager;
if nil <> idm then
idm.QueryInterface(IJvDockManager, jdm);
if nil = jdm
then CanDock := false
else jdm.GetSiteInfo(Client,InfluenceRect, MousePos, CanDock);
end;
unit JvDockTree;
.....
procedure TJvDockTree.GetSiteInfo(Client: TControl;
var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
CanDock := IsDockable(DockSite, Client);
If CanDock then begin
GetWindowRect(DockSite.Handle, InfluenceRect);
InflateRect(InfluenceRect, DefExpandoRect, DefExpandoRect);
end;
end;
http://issuetracker.delphi-jedi.org/view.php?id=5271
http://issuetracker.delphi-jedi.org/view.php?id=5974

Form is hidden behind other forms when ShowModal is called

My application is based on modal forms. Main form opens one form with ShowModal, this form opens another with ShowModal, so we have stacked modal forms. There is sometimes a problem that when we call ShowModal in new form, it hides behind previous forms, instead of showing on top. After pressing alt+tab, form comes back to the top, but this is not good solution. Did You meet this problem and how did you handle it?
EDIT:
I use Delphi 7.
You didn't mention which version of Delphi...
Newer Delphi versions have added two new properties to TCustomForm: PopupMode and PopupParent. Setting PopupParent of your modal dialog to the form that's creating that dialog makes sure that the child form stays on top of it's parent. It usually fixes the problem you're describing.
I think this pair of properties were added in Delphi 2006, but it may have been 2005. They're definitely there in Delphi 2007 and up.
EDIT: After seeing you're using Delphi 7, the only suggestion I have is that, in the code that displays your modal form, you disable the form creating it, and re-enable on return. That should prevent the creating window from receiving input, which may help keep the Z-order correct.
Something like this may work (untested, as I'm no longer using D7):
procedure TForm1.ShowForm2;
begin
Self.Enabled := False;
try
with TForm2.Create(nil) do
begin
try
if ShowModal = mrOk then
// Returned OK. Do something;
finally
Free;
end;
end;
finally
Self.Enabled := True;
end;
end;
If Form2 creates a modal window (as you've mentioned), just repeat the process - disable Form2, create Form3 and show it modally, and re-enable Form2 when it returns. Make sure to use try..finally as I've shown, so that if something goes wrong in the modal form the creating form is always re-enabled.
Sorry for adding a separate answer, but I have done a bit more research, and some of it indicates that my previous answer (DisableProcessWindowsGhosting) doesn't help. Since I can't always reproduce this issue, I cannot say for sure.
I found a solution that appears to appropriate. I referenced the code in Delphi 2007 for the CreateParams method and it matches pretty close (without having all of the other code that handles PopupMode).
I created the unit below which subclasses TForm.
unit uModalForms;
interface
uses Forms, Controls, Windows;
type
TModalForm = class(TForm)
protected
procedure CreateParams(var params: TCreateParams); override;
end;
implementation
procedure TModalForm.CreateParams(var params: TCreateParams);
begin
inherited;
params.WndParent := Screen.ActiveForm.Handle;
if (params.WndParent <> 0) and (IsIconic(params.WndParent)
or not IsWindowVisible(params.WndParent)
or not IsWindowEnabled(params.WndParent)) then
params.WndParent := 0;
if params.WndParent = 0 then
params.WndParent := Application.Handle;
end;
What I do then is include this unit in with a form unit, and then change the form's class (in the .pas code file) from class(TForm) to class(TModalForm)
It works for me, appears to be close to CodeGear's solution.
From this link it appears that the problem is with the "Ghosting window" that was introduced in 2000/XP. You can disable the ghosting feature by calling the following code at startup.
procedure DisableProcessWindowsGhosting;
var
DisableProcessWindowsGhostingProc: procedure;
begin
DisableProcessWindowsGhostingProc := GetProcAddress(
GetModuleHandle('user32.dll'),
'DisableProcessWindowsGhosting');
if Assigned(DisableProcessWindowsGhostingProc) then
DisableProcessWindowsGhostingProc;
end;
The only issue that I can see is that it will cause problems with the feature that allows for the user to minimize, move, or close the main window of an application that is not responding. But in this way you do not have to cover each call with the Self.Enabled := False code.
Just set the Visible property of the form, that you want to open modal, to False. Then you can open it with .ShowModal(); and it will work.
I have found that using the "Always On Top" flag on more than one form causes problems with the Z order. And you may also find the need for the BringWindowToTop function.
When launching a message box using the built-in WinAPI (MessageBox), I have found that passing the calling window's handle is necessary in order to make sure that the the prompt appears on top all the time.
try it
OnShowForm:
PostMessage(Self.Handle, WM_USER_SET_FOCUS_AT_START, 0, 0);

Resources