Session D-WIZ

Entwicklung von eigenen Wizards

Nathalie Mengel
INDISoftware GmbH


Einleitung

Wizards sind das Standard-Interface für ablauforientierte Benutzersteuerungen. Diese Session zeigt einen Weg, sich eigene Wizards zu bauen. Ich habe die Wizard-Klasse von Microsoft als Anregung genommen und mit einigen Modifikationen, die ich für sinnvoll hielt, eine eigene Wizard-Klasse erstellt. Zum Beispiel kann man mit dem vorgestellten Wizard Bearbeitungsschritte komplett ausblenden und bei Bedarf einblenden, ohne an der Programmierung etwas zu verändern. Dies ist oft besser, als Schritte nur zu deaktivieren, die für den Benutzer gar nicht sichtbar sein sollen. Außerdem werden automatisch beim Aktivieren der Schritte für jeden Schritt eigene Hook-Methoden aufgerufen, die die erforderlichen Voreinstellungen für diesen Schritt erledigen.  

Funktionelle Kurzbeschreibung

Ein Wizard ist eine Form, in der der Benutzer durch einen festen Ablauf von Bearbeitungsschritten geführt wird. Wizards werden unter Windows vielfach eingesetzt und sind deshalb für den Benutzer ein bekanntes und leicht zu bedienendes Control. Oft fühlen sich Benutzer überfordert von den vielfältigen Möglichkeiten ereignisorientierter Oberflächen. Mit dem Wizard wird der Benutzer geleitet und kann einen komplexen Ablauf durchführen, auch wenn er eigentlich selber keinen Überblick über die durchzuführenden Aufgaben hat.

Für den Benutzer sind wenige, einfache Bedienungselemente sichtbar. Jeder einzelne Schritt wird mit Infotext und kennzeichnender Bitmap auf einer vermeintlich eigenen Maske dargestellt, so dass man in einfachen, übersichtlichen Schritten arbeiten kann. Die Schritte können entweder in einer festen Schrittfolge über die Steuer-Buttons einzeln nacheinander angewählt werden oder – für Benutzer, die sich mit den Bearbeitungsschritten schon auskennen – über eine Combobox direkt ausgewählt werden. Dabei sind nur solche Schritte anwählbar, die im Zusammenhang auch sinnvoll sind. Andere, vielleicht später zu bearbeitende Schritte sind in der Combobox sichtbar, aber deaktiviert.

Komponenten des Wizards

Der Wizard besteht aus zwei Klassen...

Wizard-Form (frmwizard)

Wizard-Controller (cusWizard)

… und einer Header-Datei (dwiz.h).

Zentrales Steuerelement für die Bearbeitungsschritte ist das Step-Array des Wizard-Controllers.

Damit sehen wir auch schon den ersten Unterschied zum Standard-Wizard von Microsoft. Dieser hat nämlich sämtliche Funktionalitäten in der Wizard-Form untergebracht. In unserem Beispiel sind Oberfläche (frmwizard) und Funktionalität (cuswizard) voneinander getrennt. Dadurch erlangen wir eine bessere Übersichtlichkeit und leichtere Wartbarkeit. Um die Funktionalität einfach erweitern zu können, liegt der Wizard-Controller einzeln vor und ist nicht auch auf die Wizard-Form gelegt und der Form-Klasse eingebunden.

Der Wizard-Controller steuert also (fast) sämtliche Eigenschaften und Aktionen der Wizard-Form. Dies beinhaltet zum Beispiel das Füllen der Steps oder den Aufruf eines Steps. Um die einzelnen Steps anzusprechen, benutzt er das Step-Array. Mit dem hier vorgestellten Konzept habe ich versucht, größtmögliche Flexibilität bei optimaler Übersichtlichkeit zu erreichen. 

Das Step-Array und die Header-Datei

Das Step-Array ist eine der zentralen Steuer-Komponenten. Es ist eine Eigenschaft des Wizard-Controllers. Es speichert alle notwendigen Informationen über alle Bearbeitungsschritte, die mit dem Wizard bearbeitet werden können. Das Step-Array besteht aus sieben Spalten. Die Anzahl der Zeilen entspricht den initialisierten Steps (siehe CusWizard.InitSteps).

In der Header-Datei stehen die Defines zum Ansprechen des Step-Arrays. Die Spalten-Anzahl wird hier nochmals im Define WZ_STEP_ALEN bestimmt. So muss man nicht alle DIMENSION-Befehle im Code ändern, nur weil eine Spalte hinzugekommen ist.

Für jede Eigenschaft eines Steps ist im Step-Array eine Spalte angelegt. Diese sind: 

#DEFINE  WZ_STEP_TEXT         1  && Der Text für die Step-Auswahl-Combobox

#DEFINE  WZ_STEP_NO           2  && Die Step-Nummer

#DEFINE  WZ_STEP_METHOD       3  && Der Name der Step-Methode

#DEFINE  WZ_STEP_PAGENO       4  && Die Page dieses Steps

#DEFINE  WZ_STEP_BITMAP       5  && Die Bitmap für diesen Step

#DEFINE  WZ_STEP_HELP         6  && Die Help-ContextID des Steps

#DEFINE  WZ_STEP_DESCRIPTION  7  && Die Beschreibung für diesen Step

 

#DEFINE  WZ_STEP_ALEN         7  && Die Anzahl der Spalten des Step-Arrays. Alle Dimensionierungen werden über diesen Define durchgeführt, nicht über die absolute Anzahl der Zeilen.

 

#DEFINE WZ_STEPFINISH         99 && Ein Define für den Finish-Step. Mit ihm können Sie den Finish-Step aktivieren, unabhängig davon, welche Step-Nummer er tatsächlich hat

Die Wizard-Form (frmWizard)

Komponenten der Wizard-Form

Die Wizard-Formklasse enthält folgende Objekte:

Dabei liegen die Buttons, die Step-Combobox, die Step-Beschreibung und das Step-Image auf der vordersten Sichtbarkeits-Ebene.

Die Wizard-Form

Kurzbeschreibung

Die Wizard-Form dient uns lediglich als Hülle für alles weitere. Ich habe die Form deutlich größer gemacht, als dies normalerweise unter Windows üblich ist. Schließlich soll sie übersichtlich und einfach zu bedienen sein! Bei der Wizard-Form sind lediglich das Init-Event und das QueryUnload-Event mit Methodencode gefüllt.

frmwizard.init

Hier wird zunächst geprüft, ob der Wizard-Controller bereits auf der Form liegt und sich bei der Form in der Eigenschaft oWizControl angemeldet hat (siehe cuswizard.Init). Ist dies nicht der Fall, wird er erstellt. Wenn bei der Erstellung etwas schief geht, geben wir eine benutzerfreundliche sprechende Fehlermeldung aus und brechen ab:

IF TYPE(“this.oWizControl.class”) # C

   IF !this.AddObject("WizControl1", "cuswizard")

      MESSAGEBOX(“No Control - No Wizard”)

      RETURN(.F.)

   ENDIF

ENDIF 

Row-Source der Step-Combobox ist das Step-Array des Wizard-Controllers. Die Row-Source wird erst jetzt gesetzt, damit es keine Fehlermeldung beim Starten der Form gibt, wenn der Wizard-Controller noch nicht auf der Form liegt.

this.cboStep.RowSource     = "thisform.oWizControl.aSteps"

this.cboStep.RowSourceType = 5

this.cboStep.Value         = 1 

Da der Wizard einen in sich abgeschlossenen Funktionsablauf darstellt, könnte man sich vorstellen, dass er als eigene Applikation mit eigener Hilfe-Datei ausgeliefert wird. Diese wird im Init der Form aktiviert, falls vorhanden.

 

IF !EMPTY(this.oWizControl.cHelpFile)

   this.oWizControl.cOriginalHelpFile = SYS("HELP",1)

   SET HELP TO this.oWizControl.cHelpFile

ENDIF

 

RETURN .T. 

frmwizard.QueryUnload

Im QueryUnload der Form wird wieder die Original Hilfe-Datei aktiviert:

IF TYPE(“this.oWizControl.class”) = "C" .AND. ;

   !EMPTY(this.oWizControl.cOriginalHelpFile)

 

   SET HELP TO this.oWizControl.cOriginalHelpFile

 

ENDIF

Step-Image (ImgStep) und Step-Beschreibung (EdtStep)

Diese Controls werden zur Laufzeit mit der Beschreibung, bzw. der Bitmap für den aktuellen Step gefüllt. Sie haben keine weitere Funktion, außer schön auszusehen und den Benutzer über die Möglichkeiten des gerade aktuellen Steps zu informieren.

Die Steuer-Buttons

Die Steuer-Buttons rufen in ihren Click-Events lediglich die entsprechenden Methoden des Wizard-Controllers auf. Hier zum Beispiel das Click-Event des Next-Buttons:

RETURN thisform.oWizcontrol.Next() 

Die Step-Pageframes (PgfFirstFinish und PgfSteps)

Die eigentlichen Bearbeitungsschritte werden auf den Step-Pageframes abgelegt. Wir haben hier zwei ineinander verschachtelte Pageframes. Der äußere (pgfFirstFinish) enthält immer zwei Pages, nämlich für den ersten und den letzten Bearbeitungsschritt. Der innere Pageframe (pgfSteps) enthält alle dazwischen liegenden Bearbeitungsschritte. Standardmäßig sind zwei Pages für die Bearbeitung vorgesehen. PgfSteps wird auf der ersten Page des pgfFirstFinish platziert. Durch diese Technik hat man die Möglichkeit, beliebig viele Bearbeitungsschritte hinzuzufügen oder zu entfernen, ohne am Ablauf etwas ändern zu müssen. Man braucht nur den inneren Pageframe bei Bedarf zu erweitern.

Auf die Pages werden die eigentlichen Bearbeitungscontainer oder –Controls gelegt. Sie können bei unserem Wizard alle möglichen Pages mit Bearbeitungscontainern vorbelegen. Welche Schritte dem Benutzer zur Laufzeit tatsächlich zur Verfügung stehen, entscheidet sich beim Initialisieren der Steps über den Wizard-Controller (cusWizard.InitSteps). Die Steps, die nicht initialisiert werden, werden auch gar nicht angezeigt.

Die Steuer-Buttons

Die Steuer-Buttons rufen in ihren Click-Events lediglich die entsprechenden Methoden des Wizard-Controllers auf. Hier zum Beispiel das Click-Event des Next-Buttons:

thisform.oWizControl.Next() 

Die Step-Auswahl-Combobox (CboStep)

Auch die Step-Auswahl-Combobox enthält in ihrem Click-Event nur den Aufruf der entsprechenden Methode des Wizard-Controllers:

thisform.oWizControl.ActivateStep(this.Value)

 

Der Wizard-Controller (CusWizard)

Kurzbeschreibung

Der Wizard-Controller bildet das Herzstück des Wizards. Er ist als eigenständige Klasse vorhanden, damit er beliebig erweitert werden kann. Sie können ihn bereits zur Design-Zeit auf die Wizard-Form ziehen oder erst zur Laufzeit automatisch von der Wizard-Form erstellen lassen (siehe frmwizard.Init).

Initialisierung

Überblick

Die Initialisierung des Wizard-Controllers besteht aus dem Init-Event selbst und der Initialisierung des Step-Arrays. Wie schon oben erwähnt, läuft das so variabel ab, dass man Steps beliebig anzeigen und ausblenden kann, je nachdem, unter welchen Bedingungen der Wizard aufgerufen wurde.

cusWizard.Init

Im Init des Wizard-Controllers wird die Initialisierung der Schritte aufgerufen und der erste Schritt aktiviert. Dann meldet sich der Wizard-Controller bei seiner Form an:

this.InitSteps()

this.ActivateStep()

thisform.oWizControl = this

cusWizard.InitSteps

Die Methode InitSteps ist dazu gedacht, die Steps zu füllen, die dem Benutzer zur Verfügung stehen sollen. Sie ist im Basis-Wizard nicht gefüllt. In dieser Methode können Sie zum Beispiel unter bestimmten Bedingungen bestimmte Steps füllen lassen. Wir füllen hier ein lokales Array mit den Nummern der Steps, die im Wizard auftauchen sollen und übergeben dieses Array an die Methode SetSteps des Wizard-Controllers.

Wir sehen hier, dass nur die Schritte 1, 3, 4 und 7 initialisiert werden. Alle anderen Schritte werden gar nicht erst angezeigt! Dies hat also noch nichts mit der Aktivierung und Deaktivierung von Schritten zu tun! Im richtigen Programmiererleben würden Sie hier die Steps nach irgendwelchen geprüften Bedingungen (z.B. Benutzerberechtigungen) initialisieren.

LOCAL ARRAY laStepsToInit[4]

 

laStepsToInit[1] = 1

laStepsToInit[2] = 3

laStepsToInit[3] = 4

laStepsToInit[4] = 7

 

RETURN this.SetSteps(@laStepsToInit) 

cusWizard.SetSteps()

Auch diese Methode ist in der Basis-Wizard-Klasse nicht gefüllt. Hier werden die Steps tatsächlich initialisiert, und zwar nur solche, die über das Parameter-Array auch dazu bestimmt wurden.

DIMENSION this.aSteps[ALEN(taStepsToInit), WZ_STEP_ALEN]

lnZeile = 1

 

Für jeden möglichen Step wird das Array durchsucht, ob der Step dort vorkommt und also initialisiert werden soll:

IF ASCAN(taStepsToInit,1) > 0

   this.aSteps[lnZeile,WZ_STEP_TEXT]         = “Der erste Step in der Combobox“

   this.aSteps[lnZeile,WZ_STEP_METHOD]       = ""

   this.aSteps[lnZeile,WZ_STEP_PAGENO]       = 1

   this.aSteps[lnZeile,WZ_STEP_BITMAP]       = "..\BMPS\BILD1.BMP"

   this.aSteps[lnZeile,WZ_STEP_HELP]         = 0

   this.aSteps[lnZeile,WZ_STEP_DESCRIPTION]  = “Hier steht die genaue Erklärung, was der Benutzer mit diesem Step anzufangen hat.”

   lnZeile = lnZeile + 1

 

ENDIF  

Aktivierung eines Steps

Überblick

Wenn ein Bearbeitungsschritt aufgerufen wird, passieren mehrere Dinge:

cusWizard.ActivateStep()

Die Aktivierung eines Steps wird im Init des Wizard-Controllers und bei jedem Anwählen eines Steps aufgerufen. Dabei wird die Step-Methode des angewählten Steps ausgeführt, die entsprechende Page aktiviert und die Navigations-Buttons aktualisiert.

PARAMETERS tnStepNo

 

Wenn kein Parameter übergeben wurde, wird einfach der erste Step aktiviert:

IF VARTYPE(tnStepNo) = "N" AND tnStepNo > 0

   lnStepNo = tnStepNo

ELSE

   lnStepNo = 1  

ENDIF 

Ausstieg, wenn nichts zu aktivieren ist.

IF this.nCurrentStepNo = lnStepNo

   RETURN .T.

ENDIF  

Die Page feststellen und aktivieren. Die Pages können beliebig den Steps zugeordnet werden. Die Ausnahme bildet der letzte Step. Bei diesem wird immer PgfFirstFinish.Page2 aktiviert!!!

IF lnStepNo = ALEN(this.aSteps,1)

   thisform.PgfFirstFinish.ActivePage = 2
 

Die Step-Methode aufrufen

   this.StepFinish()

ELSE   

Aktivierung eines Bearbeitungsschrittes. Vom Äußeren Page-Frame muß die erste Page aktiviert werden, auf der ja der innere Page-Frame liegt:

   thisform.PgfFirstFinish.ActivePage = 1 

Feststellen, auf welcher Page der Step liegt, den wir aktivieren wollen. Es wird die Page aktiviert, die im Step-Array in der Spalte WZ_STEP_PAGENO eingetragen ist.

   lnPageno = this.aSteps[lnStepNo, WZ_STEP_PAGENO]

   thisform.PgfFirstFinish.Page1.PgfSteps.ActivePage = lnPageNo 

Feststellen, wie die Step-Methode heißt, die aufgerufen werden soll. Standardmäßig entspricht die Methode gleich "StepMethodPage" + der Page-Nr des Steps (also z.B. StepMethodPage1 für den ersten Step). Man kann aber auch im Step-Array eine andere Methode einstellen.

   IF EMPTY(this.aSteps[lnStepNo, WZ_STEP_METHOD])

      lcMethod = "stepMethodPage" + ALLTRIM(STR(lnPageNo))

   ELSE

      lcMethod = this.aSteps[lnStepNo, WZ_STEP_METHOD]

   ENDIF  

Aufruf der Step-Methode. Sie erhält als Parameter die Page, des zu aktivierenden Steps.

   lcPage = "thisform.PgfFirstFinish.Page1.PgfSteps.Page" + ALLTRIM(STR(lnPageNo))

   loPage = &lcPage

   this.&lcMethod(loPage)

 

ENDIF && letzter Step aufgerufen? 

Zum Schluß werden noch die Navigations-Buttons, die Bitmap, die Beschreibung und die Combobox aktualisiert

this.nCurrentStepNo = lnStepNo

this.SetNavButtons()

thisform.imgStep.Picture = this.aSteps[lnStepNo, WZ_STEP_BITMAP]

thisform.edtStep.Value   = this.aSteps[lnStepNo, WZ_STEP_DESCRIPTION]

thisform.cboStep.Value  = lnStepNo 

Step-Methoden

Oft ist es sinnvoll, eine Methode bei der Aktivierung eines Steps ausuführen. So können zum Beispiel die Controls des Bearbeitungscontainers eines Steps mit aktuellen Werten gefüllt werden. Der Wizard-Controller führt die Methoden bei jeder Aktivierung aus. Im Wizard-Controller sind Standard-Methoden für jeden Step vorgesehen. Sie können entweder diese Methoden füllen oder eine andere Methode aufrufen lassen. Dies wird durch die Spalte WZ_STEP_METHOD des Step-Arrays bestimmt. In unserem Beispiel ist die Spalte leer, so dass die jeweiligen Standard-Methoden aufgerufen werden. Die Methoden heissen StepMethodPage1 bis StepMethodPage9. Wenn Sie mehr als 9 Pages (also Schritte) haben, dann müssen Sie die entsprechenden Methoden zu der Klasse des Wizard-Controllers hinzufügen.

Eine Step-Methode erhält die Page, auf der der zu aktivierende Step liegt. Sie könnte folgendermaßen aussehen:

IF TYPE(“thisform.oCntBeispiel1.Class”) = "C"

   thisform.oCntBeispiel1.DoActivate()

ENDIF

 

RETURN .T. 

Wir gehen hier davon aus, dass der Bearbeitungscontainer sich bei der Form angemeldet hat und eine Referenz auf sich selbst in eine entsprechende Eigenschaft der Form geschrieben hat (hier: oCntBeispiel1). Dies ist ein einfaches und effektives Verfahren, die Container anzusprechen. Sonst müsste unser Beispiel-Bearbeitungscontainer cntBeispiel1 z.B. über

thisform.PgfFirstFinish.PageFirst.PgfSteps.Page3.cntBeispiel1 

angesprochen werden. Das sieht in dem Code-Beispiel oben doch viel angenehmer aus (und ist schneller, weil nicht so viele Objektreferenzen ausgewertet werden müssen).

Steuerungsmechanismen

Überblick

Da der Wizard die Aktionen des Benutzers möglichst genau steuern soll, ist es wichtig, nur solche Bearbeitungsschritte anwählbar zu machen, deren Bearbeitung zum jeweiligen Zeitpunkt auch sinnvoll ist. Zu den Steuerungsmechanismen des Wizard-Controllers gehören das Enablen/Disablen der Steps, das Enablen/Disablen der Navigations-Buttons und das Vorwärts/Rückwärts-Blättern über die Navigations-Buttons.

Enable/Disable der Steps

Zum enablen und disablen der Steps gibt es jeweils zwei Methoden: EnableSteps/DisableSteps nehmen ein Array von Step-Nummern auf und rufen für jede Step-Nummer die Methoden EnableOneStep/DisableOneStep auf. So muss man nicht jeden Step einzeln enablen/disablen, sondern kann einfach ein Array mit Step-Nummern übergeben.

Als Beispiel sehen wir EnableSteps:

LPARAMETERS taSteps

LOCAL lnSteps, lnI

LOCAL lcParName

EXTERNAL ARRAY taSteps

 

IF TYPE("taSteps[1]") # "U"

   lnSteps = ALEN(taSteps)

ELSE

   lnSteps = 1

ENDIF  

Beim Aktivieren/Deaktivieren und Anwählen deaktivierter Steps kann die Combobox sich de-synchronisieren. Hier wird sie wieder synchronisiert.

IF thisform.cboStep.ListIndex <> thisform.cboStep.Value

   thisform.cboStep.ListIndex =  thisform.cboStep.Value

ENDIF 

Aufruf der Methode zum enablen für jeden Step:

   FOR lnI = 1 TO lnSteps

      EnableOneStep(taSteps[lnI])

   NEXT

 

Aktualisierung der Step-AuswahlCombobox:

thisform.cboStep.Requery 

... und EnableOneStep:

Hier kommt der Define WZ_STEPFINISH als Stepnummer für den Finish-Step zum Einsatz. So kann man immer den ersten und letzten Step enablen/disablen, ohne wissen zu müssen, wie viele Bearbeitungsschritte es gibt. Der erste Step ist immer Step-Nummer = 1, der letzte Step ist immer Step-Nummer = WZ_STEPFINISH (= 99). Diese Vorgehensweise wird unbedingt benötigt, wenn man erst zur Laufzeit entscheiden will, welche Bearbeitungsschritte angezeigt werden sollen.

IF tnStepNo = WZ_STEPFINISH

   lnStepNo = ALEN(this.aSteps,1)

ELSE

   lnStepNo = tnStepNo

ENDIF

Deaktivierungskennzeichen löschen

IF LEFT(this.aSteps[lnStepNo, WZ_STEP_TEXT],1) = "\"

   this.aSteps[lnStepNo, WZ_STEP_TEXT] = ;

      SUBSTR(this.aSteps[lnStepNo, WZ_STEP_TEXT], 2)

ENDIF

Enable/Disable der Steuer-Buttons

Was weiter oben als "diverse Steuerbuttons" bezeichnet wurde, sind genauer gesagt die Buttons zum Vorwärts- und Rückwärtsblättern und der Button zur direkten Anwahl des Finish-Steps. Die Enabled-Eigenschaft der Buttons wird in der Methode SetNavButtons des Wizard-Controllers erledigt.

Dazu wird zunächst für den Rückwärts-Button geprüft, ob das Step-Array einen Step enthält, der vor dem aktuellen Step liegt und der enabled ist. Ob ein Step enabled ist, entscheiden wir, indem wir seinen Text prüfen. Wenn als erstes Zeichen ein "\" steht, ist der Step disabled. Wir hätten dem Step-Array auch eine extra-Spalte für Enabled gönnen können, aber für die Step-Auswahl-Combobox wird sowieso der "\" als Disabled-Kennzeichen benötigt. Wenn also vor dem jetzigen Step noch ein anwählbarer Step vorhanden ist, wird der Rückwärts-Button enabled.

FOR lnI = 1 TO this.nCurrentStepNo - 1

   IF LEFT(this.aSteps[lnI, WZ_STEP_TEXT], 1) # "\"

      llEnableBack = .T.

      EXIT

   ENDIF

NEXT

 

thisform.cmdBack.Enabled = IIF(llEnableBack, .T., .F.)

 

Ensprechendes wird für den Vorwärts-Button durchgeführt...

FOR lnI = this.nCurrentStepNo + 1 TO ALEN(this.aSteps, 1)

   IF LEFT(this.aSteps[lnI, WZ_STEP_TEXT], 1) # "\"

      llEnableNext = .T.

      EXIT

   ENDIF

NEXT

 

thisform.cmdNext.Enabled = IIF(llEnableNext, .T., .F.)

 

... und für den Finish-Button:

IF LEFT(this.aSteps[ALEN(this.aSteps, 1), WZ_STEP_TEXT], 1) # "\"

   thisform.cmdFinish.Enabled = .T.

ELSE

   thisform.cmdFinish.Enabled = .F.

ENDIF

Vorwärts-/Rückwärts-Blättern über die Navigations-Buttons

Die Navigations-Buttons zum Vorwärts- und Rückwärtsblättern rufen die Methode Next und Back des Wizard-Controllers auf.

Als Beispiel haben wir hier die Methode Next:

lnNextStep = 0 

Den nächsten Step suchen, der nicht deakiviert ist:

FOR lnI = this.nCurrentStepNo + 1 TO ALEN(this.aSteps, 1)

   IF LEFT(this.aSteps[lnI,WZ_STEP_TEXT],1) # "\"

      lnNextStep = lnI

      EXIT

   ENDIF

NEXT 

Wenn gefunden, aktivieren wir den nächsten Step. Wenn nicht, war eigentlich der Vorwärts-Button fälschlicherweise enabled!

IF lnNextStep > 0

   RETURN this.ActivateStep(lnNextStep)

ELSE

   RETURN .F.

ENDIF

HowTo ...

Wenn Sie die Wizard-Klassen nach obiger Beschreibung erstellt haben (oder von der Begleit-CD kopiert haben) müssen Sie noch Folgendes tun, um den Wizard benutzen zu können:

Und schon geht’s los. Auf der Begleit-CD zur DEVCON ist ein einfaches Beispiel-Projekt.