Calling Show() of embedded form should show parent form - delphi

I use embedded forms in my application and I was trying something like this:
At designtime the form is a normal form which don't know anything about embedding/docking.
With a ButtonClick I can make the form visible by calling the Show() method of the form.
At runtime it can happen that the form gets docked into another form and becomes an embedded form.
When I press the Button for showing the form again (which is now embedded) nothing happens because
I have to call Show() for the form which contains the embedded form now.
I am lookin for a method to force showing the parent form when the Show() method of the embedded form is called. I could handle this by checking the existance of a parent form before calling Show() but I don't want to include this specific handling.
I would prefer to do this handling in the parent form wich gets notified when a form is docked.
The Show() method only sets the Visible property to true (RAD Studio Help), so I don't think that a message is fired ...?
Do you know a method to realize something like this?
Edit
I want to put some information about my application because I guess that it is more a designing problem than a programming problem.
The application uses several plugins to adapt to the connected hardware. The exe provides a drag&dock environment and contains a base class for dockable forms. The plugins don't have any knowledge about the docking implementation. By creating a dockable form and embedding a form from the plugin the plugin form becomes dockable. This is the reason why I want do get the parent form shown when somewhere the method Show() of the embedded form is called.

You could create a common ancestor for your embedded forms or even for all forms in the application, and then derive your forms from it:
type
TEmbeddedForm = class(TForm)
public
procedure Show;
end;
procedure TEmbeddedForm.Show;
var
ParentForm: TCustomForm;
begin
inherited Show;
ParentForm := GetParentForm(Self);
if ParentForm <> Self then
begin
// Form is Embedded
Update;
ParentForm.Show;
// You might alternatively consider to send custom
// message to the Parent form, and let it decide what to do e.g.
// SendMessage(ParentForm.Handle, CM_MY_EMBEDED_FORM_SHOW, 0, 0);
end;
end;
Then when you call:
procedure TForm1.Button1Click(Sender: TObject);
begin
// MyEmbeddedForm is derived from TEmbeddedForm
MyEmbeddedForm.Align := alClient;
MyEmbeddedForm.BorderStyle := bsNone;
MyEmbeddedForm.Parent := Form3.Panel1;
MyEmbeddedForm.Show;
end;
Form3 is showing.
Another option (which I only confirmed with Spy++) is to intercept WM_CHILDACTIVATE or WM_WINDOWPOSCHANGING in the TEmbeddedForm. it is sent to it when the child form is calling TEmbeddedForm.Show, and act accordingly i.e. GetParentForm(Self).Show.

Use SetFocus instead of Show on the embedded form.
This will enforce showing of the parent form too.

Related

Delphi destroy dynamic control onDestroy of the parent form

How do I make sure a dynamic control is destroyed along with it's parent form?
So, from the main form, I create a button for a secondary form, then I display the secondary form with the button on it.
Now, I want to make sure the created button is destroyed together with the secondary form.
Would it be enough to make the Parent of the Button, the secondary form? Would that do it?
I am using a custom descendant of the TButton Class - TMyButton. So in my constructor I have the next code:
constructor TMyButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Self.OnClick := Self.MyButtonClick;
Self.Parent:=TWinControl(AOwner);
self.Visible := true;
end;
Is this going to be ok?
It works for me, it throws no errors, but I want to make sure that the said button is destroyed together with the form on which it is placed.
The MyButton will be placed on a secondary form, let's say 'Form2'
So there will be a code like:
var
bt:TMyButton;
begin
bt:=TMyButton.Create(Form2);
bt.Parent:=Form2;
...
form2.Show;
end;
First of all: setting a control's parent in its constructor is wrong! For a control created in design time, the IDE is and will be responsible for setting a parent. For a control created in runtime, the creating code should be responsible for setting a parent.
How do I make sure a dynamic control is destroyed along with it's parent form?
A control which has a Parent will automatically be destroyed when that immediate Parent is destroyed. Furthermore, a control which has an Owner will automatically be destroyed when that Owner is destroyed. It the Owner is in the Parent chain of the control then everything is fine. If the Owner is not in the Parent chain of the control, then you have a design problem which could result in unwanted destruction.
Your current constructor is completely unnecessary. Casting the Owner (which could be nil!) to a TWinControl without checks is wrong. But as said, setting the Parent should not be there. Visible is true by default, and assigning the OnClick event will prevent it for further use. Instead, override Click.
Regarding your comment, the calling code would become:
procedure TForm1.Button1Click(Sender: TObject);
var
Button: TButton;
begin
Button := TButton.Create(Form2);
Button.OnClick := Form2.ButtonClick;
Button.SetBounds(10, 10, 75, 25);
Button.Parent := Form2;
end;
But this leaves you with no reference to the button. Instead add the variable to a higher scope.
With this code you should not have problems.
You're making both the Owner and the Parent is the child form. So when released form. This frees the objects it contains.
I will explain the difference between Parent and Owner:
When assigning the Parent, you are indicating "visual container". Is the object that show ... a Form a Panel, a Frame.
Moreover, the Owner is the owner. The Object owner is responsibility to release all objects "slaves".
Normally both the Parent and the Owner is the same, because we design interfaces automatically and put the components visually.
When you want to create visual components at runtime must be controlled well who is the Parent, and whom the Owner.
Different is if your in the Owner will set Nil:
Obj := TObject.Create(nil);
There if you have to indicate when you should release it. It's your responsibility.
EDIT:
You can use the TButton. Something like this:
procedure TForm1.CreateButton;
var obj: TButton;
begin
obj := TButton.Create(Form2);
obj.Parent := Form2;
Obj.OnClick := MyEventOnClick;
end;
EDIT 2:
Zarko Gajic, in About.com has an excellent article is worth reading.
This can help supplement what you have offered. You can view the article in http://delphi.about.com/od/objectpascalide/a/owner_parent.htm
The short answer is: Yes, your button will be destroyed along with the form.
But it would be useful if you know how to verify this for yourself.
Write the following code:
(NOTE: Don't forget override where you declare the method.)
destructor TMyButton.Destroy;
begin
inherited Destroy;
end;
Now put a breakpoint on the line inherited Destroy;. Run your application in the debugger, and ensure the code stops at the breakpoint. I.e.
Open your child form.
Destroy your child form. (Emphasis on Destroy, because if CloseAction is caHide, simply closing your form won't be sufficient for the test.)
Verify that you reach the breakpoint.
Press F9 to continue running.
Also verify that you reach the breakpoint only once per button. (Otherwise there would be a double-destroy bug that could lead to access violations and unpredictable behaviour.)
You can also enable Debug DCU's in your compiler options. Then when you hit the breakpoint examine the call stack Ctrl+Alt+S to see where in VCL code your button is being destroyed from.

How can I host a FireMonkey form client aligned inside another?

My Delphi XE7 FireMonkey project is growing controls and naturally I've moved to using frames. Where I've used frames in the VCL there have been situations where I've simply chosen to host one (complex) VCL form inside another instead, creating and displaying it in the form's OnShow and setting it client-aligned (the benefit of this is that you don't get issues with dangling inherited controls when you edit the frame).
With FireMonkey though, things have changed slightly and my attempt to get a child form client aligned inside another is stumbling. I came across this very useful SO link which shows how to host a FireMonkey form inside a VCL form so I built on this with my code as follows:
procedure THostForm.FormCreate(Sender: TObject);
begin
FForm := TChildForm.Create( Self );
FForm.BorderIcons := [];
FForm.BorderStyle := TFmxFormBorderStyle.None;
FForm.Visible := True;
FForm.Parent := Self;
ResizeForm;
end;
procedure THostForm.FormResize(Sender: TObject);
begin
inherited;
ResizeForm;
end;
procedure THostForm.ResizeForm;
begin
if Assigned(FForm) then
FForm.SetBounds( Round(ClientRect.Left), Round(ClientRect.Top), Round(ClientWidth), Round(ClientHeight));
end;
This produces a child form which changes size with the host form, but remains at the top left of the screen. I've tried various position options in the ResizeForm routine too. It seems to me that a form might not be able to be the parent of another because TForm is not IAligneableControl whereas TFrame is. So, I tried 'docking' my child form to a TRectangle client aligned in the host form and this behaves the same way.
Has anyone examined this?
* SOLUTION DETAIL AS SUGGESTED BY MARCO BELOW *
Marco's solution is very neat and reduces the 'hosting' to just two lines of code. You do need to ensure that your child (hosted) form has everything inside another client aligned control - Marco suggested using a TLayout, but I already had a TPanel that I am using for a background so I had no modifications to the child form at all. So, to host this child form TChildForm inside a THostForm simply do:
procedure THostForm.FormCreate(Sender: TObject);
begin
FForm := TChildForm.Create( Self );
FForm.Panel1.Parent := Self;
end;
Job done. Thanks Marco.
Mixing forms and controls in FireMonkey is not such a good idea as it is the VCL, because in the VCL controls and form are all TWinControl descendant with their own Windows handle, while in FireMoneky the form is associuated with an operating system object while the controls are not.
The address scenario, I've used a different solution. Created a form with a client-aligned, useless TLayout with all of the controls inside it. At runtime, create this form and parent the Layout to the new container (for example a tab in a multi tab control).
I've used this a few times, never found big issues with it, and a nice way to dynamically crate tab pages keeping the visual development model.

How can I change the owner of a DataModule after creation?

I am trying to pass a DataModule to a form in the form's constructor. I also want the form to be the "Owner" of the DataModule so that the form will destroy the DataModule when it is closed. That creates the problem of both objects needing each other in their constructors.
I tried to set the owner of the DataModule after creation but that is a read only property.
My second form looks like this:
type
TSecondPopup = class(TForm)
private
FMyData: TMyData;
public
constructor Create(MyData: TMyData); reintroduce;
end;
var
SecondPopup: TSecondPopup;
implementation
{$R *.dfm}
constructor TSecondPopup.Create(MyData: TMyData);
begin
FMyData := MyData;
inherited Create(nil);
end;
There is no special code in my data module.
In my main form I want to do something like this when showing the second form:
procedure TMainApp.Button1Click(Sender: TObject);
var
MyData: TMyData;
SecondPopup: TSecondPopup;
begin
MyData := TMyData.Create(nil);
SecondPopup := TSecondPopup.Create(MyData);
// Can't change owner now. It is a read only property.
// MyData.Owner := SecondPopup;
SecondPopup.Show;
end;
I know I can change the DataModule to be a property on the form. Then I could create the form first, then create the data module setting the owner, and finally set the property on the form. I am trying to use constructor dependency injection on this project. It had been working great when I had a shared data module that the main form passed to multiple forms. In that case the main form holds on to the data module until it exists. In this case there is only one form that needs this data module so I wanted to force it to manage the life of the data module by setting the owner.
Another option would be to explicitly free the DataModule when I close the second form. However that form has no way of knowing if the caller also passed the datamodule to a different form.
Is there a way to use the constructor to inject my object but still get the form to manage the lifetime?
Currently using Delphi XE3.
Changing ownership of a component is possible with NewOwner.InsertComponent(TheComponent). And since a component can only be owned by one component at a time, the RTL takes care of removing ownership from the previous owner automatically.
But...
Another option would be to explicitly free the DataModule when I close the second form. However that form has no way of knowing if the caller also passed the datamodule to a different form.
If you want the possibility to pass a single DataModule to multiple Forms, then changing ownership of the DataModule is not the solution: the one Form that owns the DataModule will not be able to deside whether it may free the DataModule. Thus the conclusion is that the DataModule cannot be owned by any form, except by the MainForm. (Then I would prefer to let it own by the Application object, but that's a matter of taste.)
Subsequently, you will then need a reference counting mechanism within the DataModule for the Forms which it is attached to.
You don't have to change the DataModule's Owner. You can simply destroy the DataModule whenever you want, even if it has an Owner assigned. When the DataModule is freed, it will simply remove itself from its Owner so it is not freed a second time. If you go this approach, you should also add a call to FreeNotification() so that you are notified if the DataModule gets freed (by its Owner or anyone else) while your form is still referencing it:
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
constructor TSecondPopup.Create(MyData: TMyData);
begin
inherited Create(nil);
FMyData := MyData;
if FMyData <> nil then
FMyData.FreeNotification(Self);
end;
destructor TSecondPopup.Destroy;
begin
if FMyData <> nil then
FMyData.RemoveFreeNotification(Self);
inherited Destroy;
end;
procedure TSecondPopup.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent = FMyData) then
FMyData := nil;
end;
If you absolutely want to change the DataModule's Owner, that is also doable, via the TComponent.RemoveComponent() and TComponent.InsertComponent() methods:
constructor TSecondPopup.Create(MyData: TMyData);
begin
inherited Create(nil);
FMyData := MyData;
if FMyData <> nil then
begin
// InsertComponent() will call RemoveComponent() internally for you...
Self.InsertComponent(FMyData);
end;
end;
You want to let the form own the data module. Since ownership is most naturally specified at construction time, then the conclusion is that the form should be created first.
So, instead of passing the data module to the form's constructor, pass something that allows the form to invoke the instantiation of the data module. For instance, you could pass a function to the form's constructor that accepts the form as a parameter and returns the newly minted data module. For instance.
type
TCreateDataModule = function(Owner: TMyForm): TMyDataModule of object;
You could perfectly well create the data module outside the form but with no owner. That pass the object to the form constructor. The form can then destroy the data module in its destructor. That sounds the cleanest solution to me. I find it hard to see past this option.
I think you considered this option already but rejected it with this reasoning:
However that form has no way of knowing if the caller also passed the data module to a different form.
If that is so, then nothing can save you. You cannot have two objects both in charge of the lifetime, unless they use reference counting or similar.
Why create a Data Module that is meant to be used by a form, prior to creating the form?
1) Why not just add the Data Module unit to the interface uses list of the form, declare a private variable of the Data Module type in the form, and create the form's Data Module variable in the OnCreate event of the form...and then you can FreeAndNil the Data Module in the OnDestroy event.
Additionally, you can further declare a public property of the form's Data Module variable, with which you can access the Data Module from the calling unit (i.e. TestForm.DataModule)
2) If you are thinking you must create the Data Module outside of the form, perhaps to do a great number of Data Module-involved initializations, processes, etc. first, and then passing the Data Module off to the form and forgetting about it ... and assuming this form you are creating, that will be utilizing the Data Module, will be a NON-modal form (that bit of information would be of great help), you could always do 1) {above} first, then access the 'TestForm.DataModule' to apply all of your initializations, processes, etc. to before calling the form's Show method.

Dynamically create form by name?

Is there a way to create forms dynamically by only their names;
The concept goes like this. I have a main form, and by some user selection, a number of predefined forms must be created and docked on tabitems on a pagecontols on the main form.
I do know the names of the forms and i do know when to create each one of those, but i would like to know if a better way of creating these forms by a single procedure call, and not having all of these information in my code.
Its Delphi XE3 firemonkey, on win 7.
Thanks in advance for any help
Apparently on Firemonkey Delphi doesn't automatically register form classes to be available by name, so you'll first need to add something like this to the end of the unit that holds your form class:
unit Form10;
[ ... ]
// Right before the final "end."
initialization
RegisterFmxClasses([TForm10]);
end.
This will automatically register TForm10 so it'll be available by name. Next you can use this kind of code to create a form at Runtime by it's class name:
procedure TForm10.Button1Click(Sender: TObject);
var ObjClass: TFmxObjectClass;
NewForm: TCustomForm;
begin
ObjClass := TFmxObjectClass(GetClass(ClassName));
if ObjClass <> nil then
begin
NewForm := ObjClass.Create(Self) as TCustomForm;
if Assigned(NewForm) then
NewForm.Show;
end
end;
You can only create objects when you have a class reference for it. To get a class reference for something given its string name, call FindClass. Call the constructor on the result. You may have to type-cast the result to a different metaclass before the compiler will allow you to access the constructor you want. In the VCL, you might use TFormClass, but plain old TComponentClass will work, too, since all FireMonkey objects are descendants of TComponent; the important part is that you have access to the right constructor, and that's where the one you need is introduced.
It only works for classes that have been registered. Your form classes should be registered by Delphi automatically, but if they're not, you can call RegisterClasses manually, or RegisterFmxClasses if you need to put your classes in groups.
Delphi.About.com has a VCL demonstration.

Cancel / abort creating a new form in Delphi / C++Builder?

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;

Resources