views:

756

answers:

2

Hello

I was wondering why Delphi (2007) provides three widgets that seem to do the same thing, and what the advantages/disadvantages are for each.

On the same topic, if I want to display different sets of controls, why should I favor eg. PageControl + TabSheets + Frames, instead of just displaying different frames directly on the parent form?

Thank you.

+14  A: 

From the helpfile on TTabSet:

Tab set controls are commonly used to display tabbed pages within a dialog box. TTabSet is provided for backward compatibility. Use TTabControl component in 32-bit Windows applications.

So the real question is, what's the difference between TTabControl and TPageControl? The difference is that TTabControl only has one "page", whereas TPageControl has one page for each tab. This makes them useful in different situations.

TPageControl is useful for dialogs where you want to fit more UI on the screen than you have screen space to fit it in. Organize your UI into categories and put each category on one page. You see this pattern a lot in Options dialogs, for example.

TTabControl, on the other hand, works well for working on an array/list of objects. Create a UI to display and edit the properties of a single object, and place it on a TTabControl, then create one tab for each object and set up the event handlers so it'll load a new object from the array into the controls whenever you change tabs.

As for the frames question, the main reason to use a TPageControl in conjunction with frames is because it provides a prebuilt way to decide which frame to display. That way you don't have to reinvent a mechanism for it.

Mason Wheeler
Thanks Jim. So a TTabControl is a prefered solution when filling a tab with objects at run-time, while a PageControl + TabSheets + Frames is a better option when setting the UI at design-time?
OverTheRainbow
No. A TTabControl is the preferred solution when editing the properties of a list of objects of the same type, since changing tabs doesn't change the controls you see, only the tab index. A page control is for putting more than one page worth of different controls on a form at a time. (And Jim didn't write that answer; I did. He made an edit to it.)
Mason Wheeler
Thanks Mason, makes sense. For my use, it looks like a PageControl + some tabsheets, each holding a frame is the prefered solution for what I need to do.
OverTheRainbow
+4  A: 

One method that I have used with great success is to use frames with a TPageControl and late bind my frames to the tPageControl the first time the page is selected. This keeps the form load time down, by not creating frames which are never viewed, but yet allows the flexibility of being created, the state is maintained when changing between tabs. Recently I switched to using forms and embedding them instead of frames...but the concept is the same.

The same can be done using a single "mount point" on a TTabControl and switching it out as the tab is changed, but then the issue of how to deal with the tab state as tabs are switched back too comes up.

[EDIT] The question comes up how do I handle communication between the frame and the parent form. This actually is very easy to do using interfaces. Just create a new unit that will be shared by the form AND the frame and add two interfaces:

type
  IFormInterface = interface
    [guid]
    procedure FormProc;
  end;

  IFrameInterface = interface
    [guid]
    procedure SetFormController(Intf:IFormInterface);
  end;

Have the form implement the IFormInterface, and the frame implement the IFrameInterface. When you click on a tab and show a frame, then run code like the following:

var
  FrameIntf : IFrameInterface;
begin
  if Supports(FrameObj,IFrameINterface,FrameIntf) then
    FrameIntf.SetFormController(Self);
end;

your frame implementation of the SetFormController method would then hold onto the reference passed, which would allow it to call upwards into the form.

procedure TFrame1.SetFormController(Intf:IFormInterface);
begin
  fFormController := Intf;
end;

Procedure TFrame1.Destroy; override;
begin
  fFormController := nil;  // release the reference
  inherited;
end;

Procedure TFrame1.Button1Click(Sender:tObject);
begin
  if fFormController <> nil then
    fFormController.FormProc
  else
    Raise Exception.Create('Form controller not set');
end;
skamradt
Thanks for the tip. When you used a PageControl and multiple frames, how did you check from the parent form which button was clicked in which frame?
OverTheRainbow