Overview

The Beep Wizard System is architected as a decoupled core-engine / UI-host pair:

  • WizardManager — static façade; creates and shows wizards in one call.
  • WizardInstance — the runtime engine; owns navigation, validation, and state. Contains no UI code.
  • WizardFormFactory — instantiates the correct form host for the chosen WizardStyle.
  • IWizardFormHost — interface that all four form types implement; keeps the engine UI-agnostic.
  • WizardContext — typed key/value data bus shared across all steps.
Namespace: TheTechIdea.Beep.Winform.Controls.Wizards
Form types: HorizontalStepper · VerticalStepper · Minimal · Cards
Entry point: WizardManager.ShowWizard(config) or WizardManager.CreateWizard(config)

Architecture

// Layer overview
WizardManager            // static façade — create / show / close wizards
  └─ WizardInstance      // core engine: navigation, validation, state, events
       ├─ WizardConfig   // immutable construction config + step list
       ├─ WizardContext  // typed key/value data shared between steps
       └─ IWizardFormHost─┬─ HorizontalStepperWizardForm
                          ├─ VerticalStepperWizardForm
                          ├─ MinimalWizardForm
                          └─ CardsWizardForm

Decoupling principle

WizardInstance calls only IWizardFormHost methods (UpdateUI, ShowValidationError, GetContentPanel, etc.). Swapping a visual style at creation time is the only change needed — no engine logic changes.

Wizard Styles (WizardStyle enum)

ValueForm ClassDescription
HorizontalStepperHorizontalStepperWizardFormNumbered step pills across the top, animated slide transition between steps.
VerticalStepperVerticalStepperWizardFormTimeline-style vertical step list on the left panel, content area on the right.
MinimalMinimalWizardFormClean single-line progress indicator, maximises content area, no step sidebar.
CardsCardsWizardFormClickable step-card deck on the left; selecting a card jumps to that step.

Key Classes & Models

WizardConfig

All wizard settings passed to WizardManager.CreateWizard(). Immutable after creation.

PropertyTypeDefaultDescription
Keystring(auto GUID)Unique key for the wizard instance. Used by WizardManager.GetWizard().
Titlestring"Wizard"Title displayed in the wizard heading.
Descriptionstring""Optional sub-heading or description text.
SizeSize900×650Initial form size.
StyleWizardStyleHorizontalStepperVisual layout style (see above).
ShowProgressBarbooltrueShow or hide the progress bar.
ShowStepListbooltrueShow or hide the step list / sidebar.
AllowCancelbooltrueEnable the Cancel button.
AllowBackbooltrueEnable the Back button.
AllowSkipboolfalseShow Skip button for optional steps.
ShowHelpboolfalseShow a Help button.
HelpUrlstring""URL opened when Help is clicked.
NextButtonTextstring"Next"Default Next button label.
BackButtonTextstring"Back"Default Back button label.
FinishButtonTextstring"Finish"Finish button label (shown on last step).
CancelButtonTextstring"Cancel"Cancel button label.
SkipButtonTextstring"Skip"Skip button label.
ShowInlineErrorsbooltrueShow validation errors inline instead of MessageBox.
AutoHideErrorsbooltrueHide validation error panel when navigating to the next step.
StepsList<WizardStep>[]Ordered list of wizard steps.
OnCompleteAction<WizardContext>nullCallback fired when the wizard finishes successfully.
OnCancelAction<WizardContext>nullCallback fired when the wizard is cancelled.
OnStepChangingFunc<int,WizardContext,bool>nullCalled before each step transition; return false to cancel.
OnStepChangedAction<int,WizardContext>nullCalled after each step transition.
OnProgressAction<int,int,string>nullProgress callback for long async operations (current, total, message).

WizardStep

Each step in the Config.Steps list.

PropertyTypeDescription
KeystringUnique key for this step (used in WizardContext).
TitlestringStep title shown in the stepper / card UI.
DescriptionstringSub-title or hint shown under the step title.
IconstringSVG/image icon path shown in the step indicator.
ContentControlThe UserControl or Control displayed in the content panel for this step.
StateStepStateCurrent state: Pending · Current · Completed · Error · Skipped.
IsOptionalboolMarks the step as optional (Skip button shows when AllowSkip=true).
NextButtonTextstringOverrides Config.NextButtonText for this step only.
BackButtonTextstringOverrides Config.BackButtonText for this step only.
ValidatorsList<IWizardValidator>Validators run automatically before navigating away.
CanNavigateNextFunc<WizardContext,bool>Delegate gate for next navigation. False = block with no error message.
CanNavigateBackFunc<WizardContext,bool>Delegate gate for back navigation.
ShouldSkipFunc<WizardContext,bool>If returns true at runtime, this step is automatically skipped.
OnEnterAction<WizardContext>Sync callback when the step becomes active.
OnLeaveAction<WizardContext>Sync callback when the step is navigated away from.
OnEnterAsyncFunc<WizardContext,Task>Async enter callback (e.g. load remote data).
OnLeaveAsyncFunc<WizardContext,Task>Async leave callback (e.g. submit step data).
TagobjectArbitrary attached data.

WizardContext (Data Bus)

A typed key/value dictionary shared across all steps. The engine sets CurrentStepIndex and TotalSteps; your code reads/writes all other keys.

Method / PropertyDescription
SetValue(key, value)Store any value under a string key.
GetValue<T>(key, default)Retrieve strongly-typed value; returns default if missing or unconvertable.
ContainsKey(key)Test whether a key has been set.
Remove(key)Delete a key.
GetStepData(stepKey)Per-step WizardStepData bag (isolated namespace per step).
CurrentStepIndexint — set by the engine during navigation.
TotalStepsint — set at construction time.
StepValidationDictionary<int,bool> — engine writes true after each step validates.
NavigationHistoryStack<int> — push/pop order of visited step indices.

Enumerations

StepState

ValueMeaning
PendingStep has not been visited yet.
CurrentStep is currently active.
CompletedStep was successfully passed through.
ErrorStep has unsatisfied validation errors.
SkippedStep was automatically bypassed via ShouldSkip.

WizardResult

ValueMeaning
NoneWizard is still running.
CompletedUser clicked Finish and all validation passed.
CancelledUser clicked Cancel.
FailedAn unhandled error occurred during completion.

WizardManager (Entry Point)

WizardManager is a static class that acts as the single entry point for creation, display, and lifecycle management.

MemberTypeDescription
DefaultStyleWizardStyleGlobal default style used when config doesn't specify one. Default: HorizontalStepper.
EnableAnimationsboolEnable/disable step-transition animations globally. Default: true.
CreateWizard(config)WizardInstanceCreate a wizard without showing it. Use to hook events before display.
ShowWizard(config)DialogResultCreate and show as modal dialog; returns OK (Completed) or Cancel.
ShowWizard(config, owner)DialogResultSame but with explicit owner window for correct centering.
GetWizard(key)WizardInstanceRetrieve a running wizard by its Key.
CloseWizard(key)voidClose a wizard by Key.
CloseAllWizards()voidClose all currently open wizard instances.

WizardInstance Events

EventArgs TypeDescription
StepChangingStepChangingEventArgsRaised before navigating away from the current step. Set e.Cancel = true to block navigation.
StepChangedStepChangedEventArgsRaised after successfully navigating to a new step. Contains old and new step indices.
CompletedWizardCompletedEventArgsRaised when the user clicks Finish and all final validation passes.
CancelledWizardCancelledEventArgsRaised when the user clicks Cancel.

WizardInstance navigation methods

MethodReturnsDescription
NavigateNextAsync()Task<bool>Validate current step, fire events, advance to next step (skipping ShouldSkip steps). Returns false if blocked.
NavigateBackAsync()Task<bool>Navigate to the previous step. Returns false if on first step or AllowBack=false.
CompleteAsync()Task<bool>Validate final step, fire Completed event, fire OnComplete callback.
CancelAsync()TaskFire Cancelled event and OnCancel callback, then close the form.
CurrentStepWizardStepThe currently active step.
CurrentStepIndexint0-based index of the active step.
IsFirstStepboolTrue when on step 0.
IsLastStepboolTrue when on the last step (Next becomes Finish).
ResultWizardResultFinal result after the wizard closes.
ContextWizardContextThe shared data bus.
ConfigWizardConfigThe construction configuration.

Validation

Validators are attached to a WizardStep.Validators list. They implement IWizardValidator and are called automatically by the engine when Next / Finish is clicked. The first failing validator blocks navigation and shows the error inline.

Built-in Validators

ClassWhat it checks
RequiredFieldValidator(keys…)Ensures the listed context keys exist and are non-empty.
CustomValidator(func)Full control — supply a Func<WizardContext, WizardStep, WizardValidationResult>.
PredicateValidator(pred, msg)Simple bool predicate. Fails with msg when predicate returns false.
RegexValidator(key, pattern, msg)Tests a context string value against a regex pattern.
EmailValidator(key)Validates that the context key contains a valid email address.
RangeValidator(key, min, max)Validates a numeric context value is within [min, max].
StringLengthValidator(key, min, max)Validates string length.

ValidationPatterns constants

ValidationPatterns.Email    // ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
ValidationPatterns.Phone    // ^\+?[\d\s\-()]{10,}$
ValidationPatterns.Url      // ^https?://[\w\-]+(\.[\w\-]+)+[/#?]?.*$
ValidationPatterns.ZipCode  // ^\d{5}(-\d{4})?$
ValidationPatterns.Integer  // ^\-?\d+$
ValidationPatterns.Decimal  // ^\-?\d+(\.\d+)?$

WizardValidationResult

// Factory methods — use these in custom validators
WizardValidationResult.Success()
WizardValidationResult.Error("Error message")
WizardValidationResult.Error("Error message", "contextKey")  // focuses specific field

Delegate-based quick validation (no validator class needed)

new WizardStep
{
    Key = "terms",
    Title = "Terms",
    CanNavigateNext = ctx =>
        ctx.GetValue<bool>("termsAccepted", false),
}

Step Templates

Pre-built step UserControl classes that implement IWizardStepContent. Derive from WizardStepTemplateBase to get automatic OnStepEnter / OnStepLeave lifecycle and ValidationStateChanged event.

Template ClassPurposeKey Properties
WelcomeStepTemplateIntroductory step with icon, title, and message text.WelcomeTitle, WelcomeMessage, WelcomeIcon
SummaryStepTemplateFinal review screen displaying collected data before Finish.SummaryTitle, SummaryBuilder (Func<WizardContext, string>)

Creating a custom step template

public class AccountStep : WizardStepTemplateBase
{
    private TextBox _emailBox;

    public AccountStep()
    {
        InitializeComponent();   // your designer code
    }

    protected override void LoadData()
    {
        // called on enter — populate UI from context
        _emailBox.Text = Context.GetValue<string>("email", "");
    }

    protected override void SaveData()
    {
        // called on leave — persist UI back to context
        Context.SetValue("email", _emailBox.Text.Trim());
    }

    public override WizardValidationResult Validate()
    {
        if (string.IsNullOrWhiteSpace(_emailBox.Text))
            return WizardValidationResult.Error("Email is required", "email");
        return WizardValidationResult.Success();
    }
}

WizardFormFactory — Custom Styles

Register a factory function to provide a completely custom form for any WizardStyle value:

// Register a branded corporate wizard form
WizardFormFactory.RegisterFactory(
    WizardStyle.Cards,
    instance => new MyCorporateWizardForm(instance)
);

// Now all Cards style wizards use your custom form
WizardManager.ShowWizard(new WizardConfig
{
    Style = WizardStyle.Cards,
    // ...
});

Animations

WizardHelpers provides two built-in transitions used by the form hosts:

MethodEffectDetails
AnimateStepTransition(from, to, forward, onComplete)Slide left/right12 frames at ~60 fps with ease-out cubic deceleration. Forward=true slides left (next), false slides right (back).
AnimateFadeTransition(from, to, onComplete)Cross-fadeOpacity blend between the old and new step controls.

Disable all animations globally: WizardManager.EnableAnimations = false;

Quick Start

// Minimal 3-step wizard shown as modal dialog
var result = WizardManager.ShowWizard(new WizardConfig
{
    Title       = "New Connection",
    Style       = WizardStyle.HorizontalStepper,
    AllowCancel = true,

    Steps = new List<WizardStep>
    {
        new WizardStep
        {
            Key     = "welcome",
            Title   = "Welcome",
            Content = new WelcomeStepTemplate
            {
                WelcomeTitle   = "New Connection Wizard",
                WelcomeMessage = "This wizard will guide you through setting up a data connection."
            }
        },
        new WizardStep
        {
            Key     = "connection",
            Title   = "Connection Details",
            Content = new ConnectionDetailsStep(),      // your UserControl
            Validators = new List<IWizardValidator>
            {
                new RequiredFieldValidator("serverName", "database"),
                new RequiredFieldValidator("username")
            }
        },
        new WizardStep
        {
            Key          = "summary",
            Title        = "Summary",
            Content      = new SummaryStepTemplate
            {
                SummaryTitle   = "Review Connection",
                SummaryBuilder = ctx =>
                    $"Server: {ctx.GetValue<string>("serverName")}\n" +
                    $"Database: {ctx.GetValue<string>("database")}"
            },
            NextButtonText = "Finish"
        }
    },

    OnComplete = ctx =>
    {
        var server = ctx.GetValue<string>("serverName");
        var db     = ctx.GetValue<string>("database");
        CreateConnection(server, db);
    }
}, this);   // owner window

if (result == DialogResult.OK)
    MessageBox.Show("Connection created!");

Advanced Examples

Hooking events before display

var instance = WizardManager.CreateWizard(new WizardConfig { /* ... */ });

instance.StepChanging += (s, e) =>
{
    if (e.ToIndex == 2 && !PreFlightCheck())
    {
        e.Cancel = true;
        MessageBox.Show("Please complete the pre-flight check first.");
    }
};

instance.StepChanged += (s, e) =>
    statusBar.Text = $"Step {e.ToIndex + 1} of {instance.Config.Steps.Count}";

instance.Completed += (s, e) =>
    SaveWizardData(e.Context);

instance.ShowDialog(this);

Async step callbacks

new WizardStep
{
    Key   = "validate",
    Title = "Validate Connection",

    OnEnterAsync = async ctx =>
    {
        // runs when step becomes active — e.g. load defaults from API
        var defaults = await ApiClient.GetConnectionDefaultsAsync();
        ctx.SetValue("defaultServer", defaults.Server);
    },

    OnLeaveAsync = async ctx =>
    {
        // runs when navigating away — e.g. persist/validate server-side
        var server = ctx.GetValue<string>("serverName");
        bool ok = await ApiClient.TestConnectionAsync(server);
        if (!ok)
            throw new InvalidOperationException("Cannot reach server");  // blocks navigation
    }
}

Conditional step skipping

new WizardStep
{
    Key        = "advancedOptions",
    Title      = "Advanced Options",
    IsOptional = true,
    ShouldSkip = ctx => !ctx.GetValue<bool>("showAdvanced", false),
    Content    = new AdvancedOptionsStep()
}

Custom CardStyle wizard with branded header

WizardFormFactory.RegisterFactory(WizardStyle.Cards, inst => new BrandedWizardForm(inst));

WizardManager.ShowWizard(new WizardConfig
{
    Style       = WizardStyle.Cards,
    Title       = "Onboarding",
    Size        = new Size(1000, 700),
    AllowSkip   = true,
    ShowHelp    = true,
    HelpUrl     = "https://docs.example.com/onboarding",
    Steps       = /* ... */
});

Reading collected data after completion

var instance = WizardManager.CreateWizard(config);
instance.ShowDialog(this);

if (instance.Result == WizardResult.Completed)
{
    var ctx = instance.Context;
    var name     = ctx.GetValue<string>("firstName")  + " " + ctx.GetValue<string>("lastName");
    var plan     = ctx.GetValue<string>("selectedPlan", "Free");
    var accepted = ctx.GetValue<bool>("termsAccepted", false);
    // process...
}

Best Practices

  • Use WizardManager.ShowWizard() for fire-and-forget wizards. Use CreateWizard() only when you need to hook events before display.
  • Store all cross-step data in WizardContext — do not use static fields or class-level variables. Context data is available in the OnComplete callback and in WizardInstance.Context after the dialog closes.
  • Prefer Step.Validators over CanNavigateNext delegates when you want inline error messages. CanNavigateNext silently blocks navigation without a user-facing message.
  • Use WizardStepTemplateBase for custom step controls — the LoadData() / SaveData() pattern keeps concerns clean.
  • Mark optional steps with IsOptional=true and enable AllowSkip in WizardConfig to give users an explicit Skip button.
  • Use ShouldSkip for programmatic conditional steps (e.g. skip billing step for Free plans). This is automatic and invisible to the user.
  • Prefer OnEnterAsync / OnLeaveAsync for I/O (API calls, DB queries). The engine awaits the task before updating the UI.
  • Disable animations during automated tests: WizardManager.EnableAnimations = false;
  • Always pass an owner window to ShowWizard(config, this) so the dialog centres correctly.
  • Register a custom factory once at startup in WizardFormFactory.RegisterFactory() to globally change the UI for a given style.