views:

593

answers:

5

Is there any way to cancel or abort form creation from within the form's OnCreate event handler or C++Builder constructor?

Basically, I'd like to be able to call Close() from OnCreate or from the constructor and have it skip showing the form altogether. I have several forms that as part of their initialization may determine that they shouldn't be shown at all. (I realize that I could split apart this portion of the initialization or add extra checks from the calling form or similar, but if there's a way to cleanly do all of this from within OnCreate or the constructor, that seems simplest.)

Edit: In response to a few comments, some of the don't-show-at-all logic is UI logic and not business logic; the form might display a confirmation before showing, or it might use a common dialog box to get input for the form then abort if the user cancels that dialog. (Some of it is business logic and needs to be refactored, but it's often hard to find the time to refactor everything that needs it.)

+1  A: 

Just raise an exception in OnCreate. You'll need also redefine behavior of HandleCreateException method (as default is to display an error message, and not to cancel creation).

Alexander
+5  A: 

You can always call Release in the OnCreate handler, but that will lead to the form quickly appearing and then being closed. Not a very professional thing.

So here's another idea. Let the forms have a public function or property to return whether they are in fact to be shown. Then where you would usually have

TheForm := TSomeForm.Create(Self);
TheForm.Show;

you would have

TheForm := TSomeForm.Create(Self);
if TheForm.ShouldAppear then
  TheForm.Show
else
  TheForm.Release;

Having said that - any other way of coding this (so you don't create a form that will be immediately destroyed) is surely better. Especially if you want to maintain a clear separation between UI and business layer it would be much better to have the code that decides whether the form is to be shown outside of the form. Create the form only after you have made the decision.

mghie
I answered your question explicitly without using exceptions, because I'd consider this a misuse. If it's as likely that the form will be shown as not then there's nothing exceptional about it.
mghie
The Abort exception is specifically designed for this. Not a misuse by any means.
Jim McKeeth
May be. But not good design by any means either.
mghie
+4  A: 

Use Abort in the constructor. It raises a silent exception. If an object has an exception in the constructor, then the destructor is called and the memory released. The advantage of Abort is then you don't need to worry about an exception dialog being displayed if you don't add exception handling code.

Jim McKeeth
This works, but overriding the constructor instead of adding an OnCreate handler seems to me overkill, especially since the whole idea smells to begin with. Just my 0,02 Euro.
mghie
We do most of our work in C++Builder, where overriding the constructor is preferred to adding an OnCreate handler.
Josh Kelley
+4  A: 

Add a class function that returns an instance when needed. Then the method that determines if the form should be shown is still in that class, but it can determine if it is necessary before the form is actually constructed. Call it like "CreateIfNeeded" and it will work just like a constructor, but won't actually construct the form if it isn't needed. Minimal code changes, and maximum flexibility.

Jim McKeeth
+6  A: 

I would think it is much better to not even have to create the form in the first place. IF you're performing some logic which determines that the form is not even necessary, and that logic contains state which is important to the form, then re-factor the logic into a separate object (or even a data module) and pass the object to the form as a property. Here is a simple example (using the object approach):

UNIT1

type
  TOFormTests = class
    fStateData : string;
  public
    function IsForm1Needed( someparam : string) : boolean;
    property StateData : string read fStateData write fStateData;
  end;

UNIT2

uses
  : 
  UNIT1;

type
  TForm1 = class(tForm)
  :
  procedure SetFormTests(value : tOFormTests);
  property FormTests : TOFormTests read fFormTests write SetFormTests;
end;

procedure SetFormTest(Value:TOFOrmTests);
begin
  fFormTests := Value;
  // perform gui setup logic here.
end;

then someplace in your code, where you are wanting to determine if you should show your gui or not use something like the following:

var
  Tests : TOFormTests;
begin
  tests := tOFormTests.create;
  try
    if Tests.IsForm1Needed('state data goes here') then
      begin
        Form1 := tForm1.create(nil);
        try
          Form1.FormTests := Tests;
          if Form1.ShowModal = mrOk then
            // handle any save state logic here.
          ;
        finally
          FreeAndNil(Form1);
        end;
      end;
  finally
    freeAndNil(Tests);
  end;
end;

This also assumes that the form is NOT in the auto-create list and needs to be shown modal.

skamradt
+1 I totally agree: this is business logic, so it should be determined before the form is created at all.
Jeroen Pluimers
This true only if you call the form from one place. If you have forms which can be called from different places, it certainly makes more sense to be able to put the logic in the form (and not having to remember to call some specific routine to test whether it should be shown). But as with many topics, there will be different camps for this.
Edelcom