views:

307

answers:

3

As an extension of this question:

TForm.OnResize is sometimes fired before a form is first shown, but not always. For example, if BorderStyle is either bsDialog or bsNone, then OnResize will not fire. For all other BorderStyle values (and with all other properties at their defaults), OnResize does fire.

Are there other things that affect whether OnResize will fire before the form is shown? For example, other properties, or combinations of properties, that can affect this?

The OnResize event is a result of the ShowWindow API function sending a WM_SIZE message to the window. That bears repeating: the message is coming from Windows, not from Delphi. It's a Windows function (ShowWindow) that's (sometimes) sending the message that triggers the event -- so the VCL source code is not really helpful in this case.

Bonus points for definitive answers based on documented ShowWindow / WM_SIZE behavior, e.g. references to MSDN documentation or Petzold books.

A: 

I believe that OnResize will fire when an event dispatch a message saying that form size (left, bottom, width, height) will be modified.

Since you already discovered which message fires that event, you need now trace where the message is sent in the vcl.

Look at the vcl source code to see if you can spot those operations.

Edit: let's go low level. Forms in windows (grossly talking) have what is called "window class" (it's not a class like we know it oop). All times the window class of the form is resized (and form is visible), the WM_SIZE is sent.

So it will not happen all the times the form is shown, but only the it's dimensions are changed compared with underlying window class.

As you have observed, many properties valuez change the dimensions of the form (even a few pixels).

This is a very superficial explanation, that's a ton of other details - but it's my understanding how things works "under the hood".

Fabricio Araujo
The message is sent by windows, not the VCL
Gerry
I'm familiar with window classes, but I don't know if that's an adequate explanation or not. With all properties at defaults, OnResize does fire. So the CreateWindow call is (AFAIK) using the same settings as the window class that the VCL registers for "TForm1". Your thought about the values being changed from the window class doesn't seem to fit.
Joe White
I don't know if the window class is created **exactly** as designed or if some properties are applied after the creation of the it. Never dug this deep on Delphi source code to spot this.
Fabricio Araujo
A: 

There's no substitute for testing. How about creating a form in code, setting the properties you're interested in and recording when the resize event is called.

If you'll excuse the ugliness of the code, here's a rough proof of concept that tests all combinations of BorderStyle and Position without explicitly coding for each one. You can add more properties and take it as far as you like. A tool like CodeSite would make the logging cleaner and easier, too.

Create an application with 2 forms. Make sure the second one isn't auto-created.

In the second form, add a property and add a little logging code to the form's Resize event:

  private
    FOnResizeFired: TNotifyEvent;
  public
    property OnResizeFired: TNotifyEvent read FOnResizeFired write FOnResizeFired;
  end;

...

procedure TForm2.FormResize(Sender: TObject);
begin
  if Assigned(FOnResizeFired) then
    FOnResizeFired(self);
end;

In the main form, add TypInfo to the uses clause and drop a button and a memo on the form.

Add a simple procedure:

procedure TForm1.ResizeDetected(Sender: TObject);
begin
  Memo1.Lines.Add('    *** Resize detected');
end;

Now add the following to the ButtonClick event:

procedure TForm1.Button1Click(Sender: TObject);
var
  lBorderStyle: TFormBorderStyle;
  lBorderStyleName: string;
  lPosition: TPosition;
  lPositionName: string;
  lForm: TForm2;
begin
  Memo1.Clear;
  for lBorderStyle in [low(TFormBorderStyle) .. high(TFormBorderStyle)] do
  begin
    for lPosition in [low(TPosition) .. high(TPosition)] do
    begin
      lBorderStyleName := GetEnumName(TypeInfo(TFormBorderStyle), Integer(lBorderStyle));
      lPositionName := GetEnumName(TypeInfo(TPosition), Integer(lPosition));
      Memo1.Lines.Add(Format('Border: %s  Position: %s', [lBorderStyleName, lPositionName]));

      Memo1.Lines.Add('  Creating form');
      lForm := TForm2.Create(self);
      try
        Memo1.Lines.Add('  Form Created');
        lForm.OnResizeFired := ResizeDetected;
        Memo1.Lines.Add('    Setting border style');
        lForm.BorderStyle := lBorderStyle;
        Memo1.Lines.Add('    Setting Position');
        lForm.Position := lPosition;
        Memo1.Lines.Add('    Showing form');
        lForm.Show;
        Memo1.Lines.Add('    Form Shown');
        lForm.Close;
        Memo1.Lines.Add('    Form Closed');
      finally
        FreeAndNil(lForm);
        Memo1.Lines.Add('    Form Freed');
      end;
    end;
  end;
end;

You'll notice that resize fires when some properties are set before the form is shown, and I see that in some combinations, resize seems to fire twice when the form is shown. Interesting.

Bruce McGee
I like your RTTI solution, but it assumes that I know which properties I want to test. The whole problem is that I don't. I could easily find out which properties affect it in isolation, but there's no guarantee that that gives me a full list of every property that could ever affect it in combination. I'm left with trying every possible value for every enum, Boolean, and set-type property, plus all the edge cases for integer properties like Constraints, and other stuff I haven't thought of yet. That gets exponential fast.
Joe White
+1  A: 

Maybe it even depend on user's display settings or desktop theme or Windows version. If OnResize were giving me problems like this, I would build my program to always expect it and handle it in any situation, no matter what I think to be the cause.

Havenard
Oh man, that's a good point. Wouldn't be the first time we've had a bug that only manifests under the Classic theme in XP, and not in Luna.
Joe White