views:

358

answers:

6

Hello

I'm using a TPageControl with the TTabsheets' tabs hidden so as to have the frames they contain appear to be stacked, and build some kind of wizard by displaying a different frame at different steps in the process.

Problem is, after calling "PageControl1.ActivePageIndex := x;" to display the next frame, once in the new frame I need to know how I got here, i.e. which frame was previously displayed.

If someone else has used frames in a pagecontrol to build a wizard, how do you keep track of previous/next frames?

Thank you.

+4  A: 

TPageControl has an event called OnChanging that is called before the change, and allows you to cancel the change by setting a parameter to false.

You can also use this event to record what page was active before the change.

I would encourage you to make something not as strongly coupled to a gui-component, though. Try to make some kind of class responsible for holding frames and checking if changing from one to another should be allowed and so on. This would make it easier to switch what type of gui control you would use to visualize the wizard. And it would for sure make it much more easier to test to see if your wizard gives the step-by-step progression that you want, if validation rules is enforced and so on.

Vegar
Actually, when you have the TabVisible property set to false for a page, the OnChanging event is not called (since it relates to the *tabs* not the *pages*
Nat
@Nat, your comment only applies to certain methods for changing the activepage. Onchanging _does_ fire, even with tabvisible=false, as illustrated in my answer to this question.
Argalatyr
+1  A: 

On delphi.about.com is an article how to create a wizard-like user interface. You can use the TPageControl.SelectNextPage method to move forward and backward through a set of pages.

Erwin
A: 

You might also want to look at the Wizard component that's part of the JEDI VCL library.

TheArtTrooper
A: 

I have written a wizard framework which does almost what your trying to do. Most of it is documented on my blog. The solution I used was to push the request for a next/prev page to the frame itself (using interfaces), and let the frame decide what the next frame should be.

If you wanted to merge the two ideas (keeping your tPageControl as is), you can just route the request to tPageControl.ActivePage, and for the implementation of NavigateToPage could do a lookup to the pages you have named rather than attempt to find a specific class.

The design of my wizard framework was to handle a few complex wizards which contained multiple paths based on decisions made along the way. I stored the previous frames in a list, and each frame's "state" was stored in a globally accessible class which was cleared once the wizard finished/canceled.

skamradt
A: 

You have to keep track of your previous page index manually. For example, in your Next and Previous button's OnClick event handlers, you could do something like this:

procedure TWizardForm.NextClick(ASender: TObject);
begin
  SwitchPage(True);
end;

procedure TWizardForm.PreviousClick(ASender: TObject);
begin
  SwitchPage(False);
end;

SwitchPage() would look something like this:

procedure TWizardForm.SwitchPage(AForward: boolean);
var
  LGotoPage: integer;
begin
  LGotoPage := PageControl.ActivePageIndex;

  if AForward and (PageControl.ActivePageIndex < PageControl.PageCount) then
    inc(LGotoPage)
  else if PageControl.PageIndex > 0 then
    dec(LGotoPage);

  if (LGotoPage <> PageControl.ActivePageIndex) 
    and AllowSwitchFrom(ActivePageIndex) then
  begin
    FPreviousPage := PageControl.ActivePageIndex;
    PageControl.ActivePageIndex :=  LGotoPage;
  end;

end;

Or something to that effect. :)

Edit: Argalatyr is correct, and I am incorrect, in the case where you want to move through the wizard in sequential order, which I will assume is the case here.

The OnChanging handler does indeed fire, and the page you are coming from is (still) the active page.

The OnChanging event doesn't fire when you set the page index directly, as in my example, so you have to keep track of the old page index. This comes from coding up wizards with optional pages (not sequential).

I should have checked a little better before I posted. Appologies for the incorrect answer.

N@

Nat
This isn't necessary, see my answer
Argalatyr
+1  A: 

Contrary to Nat's answer, the tpagecontrol.onchanging event will work just fine if you use the right methods to change pages:

Create a VCL forms application, and drop on a TPageControl, set Align property to alTop and leave some room below. Right-click on the PageControl and add some pages, setting TabVisible:=false for each. Now add a couple of buttons below. Attach the PageControl's onchanging event, and the buttons' onclick events, to the respective code below:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if PageControl1.ActivePageIndex < PageControl1.PageCount - 1 then
    PageControl1.SelectNextPage(true, false);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if PageControl1.ActivePageIndex > 0 then
    PageControl1.SelectNextPage(false, false);
end;

procedure TForm1.PageControl1Changing(Sender: TObject;
  var AllowChange: Boolean);
begin
  showmessage(format('now leaving page number %d', [PageControl1.ActivePageIndex]));
end;

This works as expected (BDS2006, RAD2009).

Argalatyr
Obviously, you can hook and use the OnChange event in the same way.
Argalatyr