How to position a form before it shows?
Our application used to make use of a common base form that all forms were meant to inherit from. I'd like to get rid of it for a number of reasons, ranging from the need to police that everyone uses it to several annoyances relating to Delphi's VFI implementation. It turns out that the bulk of the features it offered can be done in other, more reliable ways.
The one that I am not so sure about, is automatically positioning all forms in the center of their callers. So if I open Dialog A from my main form, it should be placed over the center of the main form. And if I then open Dialog B from Dialog A, it should be placed over the center of Dialog A and so on.
We used to take care of all this by setting the base form's Position property to poOwnerFormCenter and it worked great. But how do I do this app-wide?
I thought of using Screen.OnActiveFormChange, but I think this ha开发者_Go百科ppens each time the form receives focus. I also thought of using Application.OnModalBegin but there doesn't seem to be an obvious way to find the form at the point this is called.
Has anyone tried this?
Well, obviously form inheritance is provided to solve exactly the problem you're trying to solve. Any solution is probably going to wind up mimicking form inheritance in some way.
Could you do something as simple as globally searching your code for "= class(TForm)" and replacing the TForm class with either your existing base form or a new, simplified base form class with only the functionality you need?
Failing that, you could try to modify the original TForm class itself to have the positioning behavior you want. Obviously, modifying the supplied classes is a little on the dangerous side.
If you are not going to go with a common base form, then I would suggest placing a non-visual component on each form. That component can inject the behaviors you want into the base form. If you want to have various different behaviors on different forms then give your component a role property that defines what role that form should have, and it can then inject different characteristics based on that role.
BTW, you can also have non-visual form inheritance, which is my preferred method of creating a common base class for all forms. It also has the advantage of adding properties to the form, and then based on those properties you can change the role or behavior of the form.
Without knowing more about your application, my advice would be to add the positioning code to each form individually - the advantages of not having a base class is that it makes it easier to have certain forms that do things slightly differently, and it keeps all the logic of a form together in one place.
I normally use the FormShow event for this, using the SetBounds() procedure. With other non-form controls you can do the same thing by overriding the CMShowing message.
I took your idea of OnModalBegin and ran with it. The following is a "Hack", but it seems to work. To test simply drag around the form and click the button.
procedure TMainForm.Button1Click(Sender: TObject);
var
mForm: TForm;
begin
mForm := TForm.create(self);
mform.width := 300;
mform.height := 300;
mForm.ShowModal;
mForm.Free;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
application.OnModalBegin := modalbegin;
end;
procedure TMainForm.FormShow(Sender: TObject);
begin
if Screen.FormCount>1 then begin
screen.forms[Screen.FormCount-1].left := round((screen.forms[Screen.FormCount-2].left + screen.forms[Screen.FormCount-2].width/2) - screen.forms[Screen.FormCount-1].width/2);
screen.forms[Screen.FormCount-1].top := round((screen.forms[Screen.FormCount-2].top + screen.forms[Screen.FormCount-2].height/2) - screen.forms[Screen.FormCount-1].height/2);
application.processmessages;
screen.forms[Screen.FormCount-1].Caption := inttostr(screen.forms[Screen.FormCount-1].top)+','+inttostr(screen.forms[Screen.FormCount-1].left);
end;
end;
procedure TMainForm.ModalBegin(Sender: TObject);
begin
if Screen.FormCount>=0 then
screen.forms[Screen.FormCount-1].OnShow := FormShow;
end;
精彩评论