Title: Conserving Windows resources when using TPageControl
Question: Complex forms can use up a lot of the Windows resources. This article describes a method for reducing the drain on the Windows resources.
Answer:
In my application there is a form with lots of components on it; 1047 components to be exact. In my development environment, WindowsNT, this did not cause any problems but under Windows 98, my application would crash if a user tried to create a second instance of this form. The problem was that in Windows 98, there are a limited number of Windows resources and one instance of my form used 42% of the Windows resources. With the operating system already using 20% of the resources, there weren't enough resources left to create a second instance of the form.
In researching this problem I came across a thread in alt.comp.lang.borland-delphi by Ken Phipps, Bruce Roberts, and M.H. Avegaart that gave most of a solution to the problem. The thread extended from Jan. 13, 2001 to Jan. 16, 2001. There is also an article on the Borland web site that gives some related information (TI1375D.txt; http://community.borland.com/article/0,1410,16375,00.html).
The following is the solution I implemented in my program. It solved my problem.
First, I created the ConserveResourcesUnit shown below. I called FreeFormResources in the OnCreate event handler of each form that had a TPageControl. I called FreePageControlResources in the OnChange event handler of each TPageControl. I could have gone further and made a component that did the same thing after the page control was created and when the ActivePage was changed but it didn't seem worth the effort. I could also have called FreePageControlResources when the ActivePage of a TPageControl was changed programatically but I haven't gotten around to that yet. Another option would have been to redesign the form to use fewer TWinControls but that would have been a lot of effort and would have created other problems.
There is one tricky bit. If you have a TPageControl on a tab of another TPageControl and you want to set the TabVisible property of one of the tabs on the former TPageControl you may need to call HandleNeeded for all the tabs on the former TPageControl before setting the TabVisible property. You can call FreePageControlResources afterwards to free-up resources. If you don't do this, an error can occur in the VCL when you try to set the TabVisible property.
If you use a TTabbedNotebook rather than a TPageControl, see the article on the Borland web site cited above.
unit ConserveResourcesUnit;
interface
uses Windows, Classes, Controls, Forms, comctrls;
Procedure FreePageControlResources(const APageControl : TPageControl;
const FormHandle : HWND);
Procedure FreeFormResources(const AForm : TForm);
implementation
type
TMyWinControl = class(TWinControl);
Procedure FreePageControlResources(const APageControl : TPageControl;
const FormHandle : HWND);
var
Index : integer;
begin
// LockWindowUpdate prevents any drawing in a given window}
LockWindowUpdate(FormHandle);
with APageControl do
begin
for Index := 0 to PageCount -1 do
begin
// DestroyHandle is protected so a typecast is required
// to expose it.
// Usually, the handles will be automatically recreated when needed.
// However, in setting the TabVisible property, they may not be recreated
// without a direct call to HandleNeeded.
if Pages[Index] ActivePage then
TMyWinControl(Pages[Index]).DestroyHandle;
end;
end;
{Release the Lock on the Form so any Form drawing can work}
LockWindowUpdate(0);
end;
Procedure FreeFormResources(const AForm : TForm);
var
AComponent : TComponent;
Index : integer;
begin
for Index := 0 to AForm.ComponentCount -1 do
begin
AComponent := AForm.Components[Index];
if AComponent is TPageControl then
begin
FreePageControlResources(TPageControl(AComponent), AForm.Handle);
end;
end;
end;
end.