I created a TestCase with DUnitX which automatically generated some code and I'm confused as to how I should structure the test case.
The (automatically generated) code looks like the following:
procedure TestTBtnMgmtForm.SetUp;
begin
FBtnMgmtForm := TBtnMgmtForm.Create;
end;
procedure TestTBtnMgmtForm.TearDown;
begin
FBtnMgmtForm.Free;
FBtnMgmtForm := nil;
end;
procedure TestTBtnMgmtForm.TestFormCreate;
var
Sender: TObject;
begin
// TODO: Setup method call parameters
FBtnMgmtForm.FormCreate(Sender);
// TODO: Validate method results
end;
However, TBtnMgmtForm.Create will automatically call .FormCreate. Is it good practice to separate these two somehow? What is the difference or should be the difference between .Create and .FormCreate?
The constructor of a form will call the OnCreate event handler, if it is assigned. In your case it is assigned, to the function named FormCreate.
As a general rule, event handlers are invoked by the framework and should not be called directly. I can't see enough of your code to be sure but my instincts tell me that you should not be calling FormCreate at all. It is the job of the framework to do that.
The point of OnCreate is that it allows you to inject code into the constructor of the form class without having to override the constructor. You can use the form designer to add the event handler and fill in the code. Personally I regard OnCreate as somewhat facile. Once you know how to override constructors it seems more explicit to do that.
You however appear to have a form with a parameterless constructor. That is odd. Usually you would override the virtual constructor declared in TComponent. I wonder why you are not doing that.
Related
I have already found something on stackoverflow but it doesn't really solve my doubt. I know that the correct way to create an object is, after the creation, surround the code in a try-finally block. But what about:
procedure TForm3.FormCreate(Sender: TObject);
begin
a := TClassX.Create;
end;
And then call:
procedure TForm3.FormDestroy(Sender: TObject);
begin
a.Free;
end;
Where a: TClassX; is a public declaration inside TForm3 class. Should I create the constructor and the destructor for the form, or I can use the code above? Is it safe?
The try/finally is there, or at least something equivalent. It just exists outside your code, higher up the call stack. Something like:
Form1 := TForm1.Create(nil);
try
// do stuff
finally
Form1.Free;
end;
Your OnCreate and OnDestroy handlers are called from the constructor and destructor, respectively, and so are protected.
So long as everybody plays by the rules nothing leaks. And the rules here are that objects created in the constructor and to be destroyed in the destructor. Whoever actually creates the object is responsible to ensure that it is destroyed no matter what. But that's the task of the consumer of your class rather than you.
I had some issues with this events some time ago so I would not recommend to use OnCreate/OnDestroy events in your application. Here are just some cases I remember:
VCL by default handles exception from OnCreate/OnDestroy events. Actually if a:=TClassX.Create; will generate an exception, it will be shown by application exception handler but the form will be created successfully keeping "a" variable equals to nil. This may lead to Access Violations if you try to access this variable later.
Depending on OldCreateOrder these events may be called either from constructor/destructor or from AfterConstruction/BeforeDestruction methods. It may also lead to Access Violations if you occasionally change OldCreateOrder in form descendants
And even more, it looks strange for me when you are using events instead of virtual functions. In most cases events are used to delegate the functionality to another objects (for example you assign a form's method to a button's OnClick event).
I have a procedure with Sender: TObject paramater
procedure TForm1.FormCreate(Sender: TObject);
How can I call this procedure again?
This version does not work:
TForm1.FormCreate(Sender: TObject);
Thanks in advance.
I assume that you wish to call the procedure FormCreate of an instance of a TForm1.
You can do FormCreate(Self) or FormCreate(nil) (or you can pass any TObject instance as the parameter) if you are inside the TForm1 class. Otherwise, you have to write Form1.FormCreate(Self) or Form1.FormCreate(nil) or similarly, where Form1 is the appropriate instance of the TForm1.
At any rate, it is not particularly 'elegant' to call the FormCreate procedure at later times. Indeed, the name clearly suggests that the procedure is called when the form is created.
If a particular piece of logic of your FormCreate method is needed at other stages and you simply do not want to duplicate code, you should probably implement that piece in the form of a separate method (procedure or function) and call it in FormCreate as well as in other parts of your program, as necessary.
But then, you might be better off moving that part of your business logic from FormCreate at all. What we typically do in FormCreate is create/initialise objects that are later uninitialised/destroyed in FormDestroy (if needed). So, think carefully what you are doing in FormCreate, maybe there's a better, logically more appropriate place for some or all of the actions you've implemented in this method.
Is there any way to allow one form to use the event procedures from another form?
E.g. I have a form called PongForm and another called ObstPongForm. There is a ticker on PongForm and another one on ObstPongForm. Is it possible to get ObstPongForm to use the code from PongForm's 'tick' event in it's own 'tick' event? Maybe by letting ObstPongForm inherit from PongForm?
You can simply assign it by code (as long as you have access to both instances):
ObstPongForm.Ticker.OnTick := PongForm.TickerTick;
Yes, forms are just classes like any other, and Delphi supports visual inheritance, so you can call inherited methods normally.
If ObstPongForm is a specialized version of PongForm then inheritance makes sense, but be careful as ObstPongForm will inherit all visual controls from the PongForm, including whatever you may add in the future.
Also since I assume you already have both forms, making one inherit from another is doable but requires some manual DFM editing, mainly changing the
Object ObstPongForm: TObstPongForm
to
Inherited ObstPongForm: TObstPongForm
If the code you want to reuse may be needed in several unrelated forms, then moving the code to a common unit used by these forms may be the best solution
It would be better style to have both of the forms call another class that implements the logic used by both. If you're writing all your program logic in your OnTimer event handler, you're heading down a bad road that many delphi programmers take years to realize was a bad idea
So one form needs to call your method, it does it like this:
procedure TForm1.DoSomething;
begin
DataModule1.LogicMethod;
end;
Elsewhere there is a timer...
procedure TForm2.Timer1Timer(Sender:TObject);
begin
DataModule1.LogicMethod;
end;
And then the method itself:
procedure TDataModule1.LogicMethod;
begin
// Everything that you used to have in Timer1Timer goes here, except the setting of
// UI properties in Form1 which is kept in Form1:
Inc(FCounter);// stupid example.
//
if Assigned(FOnResults) then
FOnResults(Self, FCounter, FDataObject1);
// Form2 is connected to FOnResults event, and stores the
// result in the UI somewhere.
end;
Event handlers are just normal procedures. If your ObstPongForm tick handler has additional code that it needs to run in addition to the PongForm's code, then you can call the PongForm's tick handler manually when needed, eg:
uses
..., PongForm;
procedure ObstPongForm.TickHandler(Sender: TObject);
begin
...
PongForm.TickHandler(Self);
...
end;
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.)
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.
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.
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.
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.
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).
I would override ShowModal
function TfHtmlEditor.ShowModal: Integer;
begin
if TabControl1.Tabs.Count=0 then
Result := mrAbort
else
Result := inherited;
end;
Does Delphi call inherited on overridden procedures if there is no explicit call in the code ie (inherited;), I have the following structure (from super to sub class)
TForm >> TBaseForm >> TAnyOtherForm
All the forms in the project will be derived from TBaseForm, as this will have all the standard set-up and destructive parts that are used for every form (security, validation ect).
TBaseForm has onCreate and onDestroy procedures with the code to do this, but if someone (ie me) forgot to add inherited to the onCreate on TAnyOtherForm would Delphi call it for me? I have found references on the web that say it is not required, but nowhere says if it gets called if it is omitted from the code.
Also if it does call inherited for me, when will it call it?
No, if you leave the call to inherited away, it will not be called. Otherwise it would not be possible to override a method and totally ommit the parent version of it.
It is worth mentioning that not calling inherited in Destroy of any object can cause memory leaks. There are tools available to check for this in your source code.
The inherited call has to be made explicitly. In general no language automatically calls the inherited function in equivalent situations (class constructors not included).
It is easy to forget to make the inherited call in a class constructor. In such a situation if a base class needs to initialize any data you have an access violation waiting to happen.
Perhaps you could override DoCreate and DoDestory in your TBaseForm class so you could ensure some code is executed regardless of the implementation of child classes.
// interface
TBaseForm = Class(TForm)
...
Protected
Procedure DoCreate(Sender : TObject); Override;
End
// implementation
Procedure TBaseForm.DoCreate(Sender : TObject);
Begin
// do work here
// let parent call the OnCreate property
Inherited DoCreate(Sender);
End;
Inherited must be explicitly called in descendant objects as well as in visual form inheritance. If you use class completion then it adds inherited automatically if you flagged the definition as override (but not for reintroduce). If you are using visual form inheritance then when you add a new event hander through the form editor then it will add inherited as well.
The inherited code is not called implicitly, as the others have indicated. You must call it explicitly. This gives you some useful flexibility. For instance, you might want to do some preprocessing code prior to the inherited code, then do some post processing code afterwards. This might look like:
procedure TMyCalcObject.SolveForX;
begin
ResetCalcState;
inherited SolveForX;
PostProcessSolveForX;
end;
You must call it explicitly. This allows a lot of flexibility, since you can choose at which point in code to call the inherited method. But it's also a big source of bugs. It's easy to forget to call the inherited function and compiler has no way to tell if you did it deliberately or you just forgot.
There should be some kind of directive "skip_inherited" to tell compiler that you don't want to call the inherited method.
Compiler would then easily report error if it didn't find either "inherited" or "skip_inherited". That would mean you forgot. But unfortunately nobody in CodeGear thought of that.
No. That's the whole point of overriding.