views:

231

answers:

5

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

A: 

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.

Kragen
The problem here is scale. We have a few hundred packages, most containing several forms. The base class was (I think) an attempt to make sure all the forms behave in a consistent way, but as the years passed and people came and went, the base wasn't used consistently. I am attempting to enforce this consistency.
Cobus Kruger
+1  A: 

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.

Jim McKeeth
+4  A: 

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.

Larry Lustig
+1 I got rid of the base because people didn't use it and it was a pain to police it. So I really would like to fault your logic here, but you're right. I still can't find a reliable way to take care of one or two of the requirements without a base, so I guess I'll have to put it back. I definitely think I should skip the visual part of the inheritance though, because that just doesn't work well.
Cobus Kruger
I'm not certain, but it's possible that the Delphi IDE can be configured to at least assist you in policing the class on which new forms are based.
Larry Lustig
A: 

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.

Brian Frost
See my comment on Kragen's answer for the scale of the project. If I can't police the use of a common base class effectively, anything that requires a change to each form is dead in the water. In smaller projects, you're right of course.
Cobus Kruger
A: 

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;
M Schenkel