Changing MainForm at runtime - c++builder

How can I change MainForm of my application at runtime ?

Once the MainForm has been established by the first call to TApplication::CreateForm(), it cannot be changed afterwards. You can either write code to control which TForm class gets passed to the first call to CreateForm(), or you can use a hidden TForm as the MainForm and then have it display secondary forms as needed.

Related

Move some of the methods into a different file?

I have a lot of OnClick and similar events in my main form. It's hard to navigate between them where they all are in Unit1.pas.
I moved them to a different pas file and added this in my Unit1.pas:
{$INCLUDE Menu.pas}
But now the Delphi wants to remove OnClick events. So it doesn't work properly.
How do I split my code into different files?
You can't move a Form's event handlers to an .inc file, the IDE won't know how to handle that. Event handlers assigned at design-time need to be locatable and editable, and that means they must be in the same source file as the Form itself.
What you can do instead is move the event handlers to a TDataModule that is created before, and is used by, the Form in question. As long as the DataModule's unit is included in the Form's uses clause in the Form's interface section, the IDE should allow you to assign the event handlers at design-time. If you use the Object Inspector to create new event handlers, you will have to move them to the DataModule manually and re-assign the events accordingly.
Otherwise, all you can really do is refactor your code to reduce the amount of code in each event handler, or even reduce the number of event handlers used. And then you can use the Code Editor's code folding feature to hide the remaining code so you don't see it but it still exists to the IDE and compiler.

Can a component replace it's owner form's event (OnClose) with it's own handler?

I'm working on a component that is placed on every form of my project. Is it possible, in runtime, to have the component include code into it's owner form's OnClose event handler. In other words, the form will trigger it's own OnClose event handler but the component will also include additional event handler code to run on the owner form's OnClose event. (Is that what is called vector replacement?)
Thank you.
You need to get the component to declare a field to store the form's original OnClose. Then you can do in the component's constructor:
FOriginalFormClose := (Owner as TForm).OnClose;
(Owner as TForm).OnClose := FormClose;
Then the component's FormClose would read:
TMyComponent.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// do stuff for this component
if Assigned(FOriginalFormClose) then
FOriginalFormClose(Sender, Action);
end;
Naturally the as cast ties this component to being owned by forms but if you want more flexibility you could easily cater for that.
This is a direct answer to the question that you asked, but it would be remiss of me not to question your overall design. If you want a component to live on every form in your app then surely you should derive a subclass of TForm that contains your customisations. Then make every form in your app be based on that common base form class.
That approach has many other benefits. For example, #LachlanG adds the following very apt comment with which I wholeheartedly concur:
Having a component meddle with it's owning form is undesirable. The vast majority of components should be self contained entities, altering the component owner breaks the expected contract of a Delphi component.
The common base form approach solves this by placing the code that works with the form inside the form.
If you are do go down the route of having a common base form then you should override DoClose rather than using the OnClose event. Always use the DoXXX event raisers rather than the events themselves when you are creating a common base class or a component.

Delphi ignores CS_OWNDC flag when creating TFrame component, wglMakeCurrent subsequently fails

I'm trying to put together some components for using OpenGL in RAD Studio 2009. I want to have multiple Rendering Contexts.
My idea is to have something like a "main OpenGL component", called GLMaster. It's a TFrame descendant so it should provide me with a DC. Also there's a component for a GLMonitor, acting as camera.
I create an OpenGL RC in the overriden GLMaster.Loaded inside an "if not (csDesigning in ComponentState) then"-clause. The DC for this I do not keep, it's stated in the RAD Studio help that you shouldn't:
"TWinControl.Handle
Do not refer to the Handle property during component creation or streaming. The underlying window does not exist until the first time the Handle property is referenced. When this occurs, the HandleNeeded method is called automatically."
I handle this by function pointers in components using GLMaster pointing to GLMaster.GetCurrentDC (returns a HDC).
During destruction GLMonitor wants to clean up some render-to textures and other OpenGL resources. When retrieving the DC for a call to wglMakeActive the function pointer is followed and execution jumps to GLMaster.GetCurrentDC. Pointer(Self) tells me that it's the same GLMaster that we created the "master RC" in during the streaming of the components. The property [GLMaster.]Handle is now invalid! If I however free the GLMonitor in the app form's OnClose (using simply GLMonitor_1.Free;) the Handle inside GLMaster.GetCurrentDC is valid end everything works.
I managed to see that the handle used in GLMaster.Loaded is not the same (!?!?) as after the initialization is done and the app form is showed to the user. When I found this I googled my problem and added an overriden CreateParams to add CS_OWNDC to the GLMaster component. A breakpoint there tells me that it's being executed.
Why does the VCL/Delphi silently provides the newly created components with other Handles, and thereby indirectly other DCs, even if I provide the OWNDC flag? Is there someway to get an "ALL components are now loaded and their properties are read from the .dfm file and"-message so I can run GLMaster.InitEverything inside the component?
Right now I'm thinking the fastest way onward is to put this InitEverything in the main form's OnShow handler. And then a call to GLMatser.CleanAll in the main form's OnClose. But I don't want any code in the form, that's the reason I created the components in the first place!
P.S.
Not that I think it matters for this particular problem but I use OpenGL 3.2 Core Profile.
I'm answering the "Why does the VCL/Delphi silently provides the newly created components with other Handles".
Why an control's Window Handle might change at run-time
Your TFrame (an TWinControl descendant) is placed on an other TWinControl, let's say a TForm. The parent container provides property wrappers around many settings, allowing us to easily make changes; Some of those changes can't be implemented without re-creating the windhow handle. You can get a long list of properties that cause this to happen by doing a search for RecreateWnd in Forms.pas, Controls.pas etc.
Examples of properties that call RecreateWnd to implement a change at run-time:
TScollBox.BorderStyle
TForm.BorderIcons (for MDI childs)
When your TFrame's parent needs to change Window Handle, your TFrame is going to have to change Window Handle as well. Your Delphi Control, no matter the descendense, needs to be handle this, and other controls have it worst: Controls that are implemented as wrappers around Windows controls need to save state and reload state (TEdit, TComboBox, ...)
What I think is going on
While streaming (loading your form) some control does something that needs a Window Handle before loading finishes. It's very likely your own code! After everything finishes loading the Form might need to re-create it's Handle, and this in turn causes your Frame's handle to be changed.
What you might want to override
Given the way the VCL works, you need to be prepared for your Window Handle to change. You should do a search in Controls.pas for the words Handle, HDC. If your control is so intimately linked to it's Window Handle and HDC, it's in your best interest to read up on this stuff.
Take a look at those routintes. Maybe you can sopot a better place to hook:
CreateHandle
CreateWnd
DestroyHandle
DestroyWnd
GetDeviceContext
If the title is the question, then no, Delphi does not ignore CS_OWNDC when creating a frame. Just tested with the below procedure with a frame on a form. When the flag is not specified, the line is drawn on itself again and again, resulting with a line from (0,0) to (10,10). When the DC is owned, the line extends from (0,0) to (50,50), no need to tell but the retrieved DC is same at all times.
procedure TFrame2.WmPaint(var Msg: TWMPaint);
var
i: Integer;
DC: HDC;
Pt: TPoint;
begin
inherited;
for i := 1 to 5 do begin
DC := GetDC(Handle);
GetCurrentPositionEx(DC, #Pt);
LineTo(DC, Pt.X + 10, Pt.Y + 10);
ReleaseDC(Handle, DC);
end;
end;
if your question is this:
ALL components are now loaded and their properties are read from the .dfm file and
The answer is:
Yes, just override the Loaded method of your component. It is called when all the references are valid (after dfm reading is finished).

Creating a compound control (parent of other controls) at runtime

I has a piece of code where I override TStringGrid's inplace editor and the hint window. For this, I created my own string grid based on TStringGrid and use a TEdit for inplace editor and a TPanel for tool tips. In TMyStringGrid.Create constructor I initialize them like this:
Constructor TMyStringGrid.Create();
Begin
inherited Create(AOwner);
MyEditor:= TEdit.Create(Self);
MyEditor.Parent := Self;
End;
In this case the owner (the main form) is freeing the controls. I used this for years and it worked.
The thing is that other people argue that the programmer should use NIL instead the Self when instantiation the child controls and later to manually free them in the Destroy destructor. It seems that the second alternative has gigantic advantage over the first one, especially when you dynamically create lots of child controls (not my case). Other problem with my code, they say, is that the child controls may be freed after an Application.ProcessMessages call while the application may still want to use them.
So, I should let my code unchanged or should I manually create and free the child controls?
There is a any Borland example of compound controls?
Delphi 7, Win XP
Reference: http://delphi.about.com/od/kbcurt/ss/dynamiccreateno.htm
Yes you can use your code without changing it.
There is a any Borland example of compound controls?
Best example is to check the implementation of TLabeledEdit.
It was creating the label in constructor
if Assigned(FEditLabel) then exit;
FEditLabel := TBoundLabel.Create(Self);
FEditLabel.FreeNotification(Self);
FEditLabel.FocusControl := Self;
There's no good reason to pass nil instead of Self in this situation. That AOwner parameter is there specifically for that reason. Take advantage of it.
There's a reason to pass nil when creating a control and manually destroy it, but for a completely different situation: if you're creating a control (typically a form) inside a function. This is a pretty common pattern, for example:
MyDialog := TMyDialog.Create(nil);
try
result := MyDialog.ShowModal;
finally
MyDialog.Free;
end;
There, you want to free it immediately instead of waiting around until the current form gets destroyed, which could be much later. But when it comes to sub-components, you usually want them to be destroyed at the same time as the parent component, not later, so pass Self to AOwner and let the VCL take care of it for you.
Considering that constructor, the grid instance is owned by Aowner (which is tipically a TForm or a TFrame). The inplace editor is owned and parented by the grid instance. I don't see how ProcessMessages would cause destruction of the child objects, since they will be destroyed in the destroying loop of TMyStringGrid. That's nothing I can see of wrong on that implementation - and I use that same design for the components I create. Ownership is there on VCL to easy our lifes when managing objects' lifetime. And is not the case where nil is recomended as owner, which is shown in Mason' answer.
In the pattern shown by Mason , the reason for the NIL is that, without a owner, the object destruction will not enter in a Notification loop. If you create a lot of components which destruction you handle manually, you want to make sure the owner is NIL, otherwise a lot of code get executed (without need) in each component construction/destruction.
Many moons ago, there was an excelent white paper on the web archive of (now defunct) eagle-software.com

How to explicitly release GDI handles allocated by TForm derived class owned by the Application?

A single class derived from TForm appears to hold onto GDI handles until the application is closed.
class TTestForm : public TForm {
public:
TTestForm(TComponent*);
};
std::auto_ptr<TTestForm> test(new TTestForm(NULL));
test->ShowModal();
I'm quite new to VCL, so please bear with me. This test was done with a form that contains no controls. As far as I udnerstand, all objects are owned by the Application if no owner is specified.
My application creates (and destroys) a lot of forms dynamically. 3-4 new GDI handles are allocated each time a form is displayed. Is there a way to explicitly release those GDI handles during application lifetime?
Caveat: I'm a Delphi programmer, not C++, but the VCL is basically the VCL. You can try the form's Release() method instead of free(). Or alternatively, in the OnClose event set the Action parameter passed to caFree - thats supposed to tell the VCL to free the window's resources when the form closes, rather than hiding it.
I guess another question is - do you need to keep creating/destroying the forms? Can you create them once and then reuse them?
It turns out that the leak was caused by an incorrectly set TImageList.ShareImages property.

Resources