# Wizards

## NuGet Package Details

**Package Name**  
[Llamachant.ExpressApp.Wizard](https://www.nuget.org/packages/Llamachant.ExpressApp.Wizard)  
**Blazor Package**  
[Llamachant.ExpressApp.Wizard.Blazor](https://www.nuget.org/packages/Llamachant.ExpressApp.Wizard.Blazor)  
**WinForms Package**  
[Llamachant.ExpressApp.Wizard.Win](https://www.nuget.org/packages/Llamachant.ExpressApp.Wizard.Win)

## Overview

The Wizard Module allows you to present multi-step workflows using a sequence of detail views, effectively turning object editing into a guided wizard experience. This enhances usability by breaking complex forms into manageable pages, making data entry clearer and more intuitive.

You can configure the wizard to appear:

- Only on object creation (e.g. when using the New action), or  
- Every time an object is opened, replacing the default detail view

Each step in the wizard is a standard detail view defined in the Model Editor. These views are then assigned as wizard pages within a parent detail view using model configuration. This approach gives you full design-time control over wizard flow and layout without needing custom UI code.

#### TIP

Ensure all view variants are removed for any view that is used as a wizard page as this can cause unexpected behavior in the wizard

## Installation

### Step 1: Install the Module

Install the platform-specific module:

```
Install-Package 'Llamachant.ExpressApp.Wizard.Blazor'
```

OR

```
Install-Package 'Llamachant.ExpressApp.Wizard.Win'
```

### Step 2: Register the Module

Register the platform-specific module (Blazor or Winforms):

```
public override void Setup(XafApplication application) {
    base.Setup(application);
    // Blazor:
    this.RequiredModuleTypes.Add(typeof(LlamachantFrameworkWizardModuleBlazor));
    // WinForms:
    this.RequiredModuleTypes.Add(typeof(LlamachantFrameworkWizardModuleWin));
}
```

OR

```
services.AddXaf(Configuration, builder => {
    builder.UseApplication<ExpressAppBlazorApplication>();
    builder.Modules
        // Blazor:
        .AddLlamachantFrameworkWizardModuleBlazor();
        // WinForms:
        .AddLlamachantFrameworkWizardModuleWin();
});
```

## Setup

### Step 1:

Most of the Wizard module setup is done in the Model. To start, create detail views to represent the pages or steps of your wizard (these can be clones of your parent detail view with the layout changed).

For example:

| Application              | **Llamachant Framework** | Values                               |
|--------------------------|--------------------------|--------------------------------------|
| Views                    |                          |                                      |
| Llamachant.ExpressApp    |                          |                                      |
| BusinessObject           |                          |                                      |
| BusinessObject_DetailView| ShowInWizard            | Never / New Only / Always            |
| BusinessObject_DetailView_Page1 |                 |                                      |
| BusinessObject_DetailView_Page2 |                 |                                      |

### Step 2:

Once your views are created, add them to the Wizard node on the parent detail view, then set the parent detail view's ShowInWizard property to either NewOnly or Always.  
For example:

| Application              | **Llamachant Framework** | Values                               |
|--------------------------|--------------------------|--------------------------------------|
| Views                    |                          |                                      |
| Llamachant.ExpressApp    |                          |                                      |
| BusinessObject           |                          |                                      |
| BusinessObject_DetailView| ShowInWizard            | Never / New Only / Always            |
|                          | WizardStyle              | WizardStyle.WizardAero / WizardStyle.Wizard97 |
| Wizard                   | WizardTitle             | String                               |
| Page1                    |                          |                                      |
| Page2                    | Description              | String                               |
|                          | Name                     | String                               |
|                          | ThrowValidationExceptions | True / False                         |
|                          | View                     | View Node - Example: BusinessObject_DetailView_Page2 |

### Step 3:

Each wizard view can be assigned a Description, Id, Index, Name, and the View you want the page to display.

#### TIP

The Description property only works in Blazor, or in Wizard 97 mode in WinForms.

## Show a Wizard Using an Action

Sometimes you may not want to show a wizard directly from the New action. You may instead want the option to only display the wizard when an action is executed. In order to do this, follow these steps:

### Step 1:

If you want your action to display a wizard, but you don't want the New action to display a wizard, use a different Detail View as your parent view (If you're okay having both functions in place, skip to step 2).

For example:

| Application              | **Llamachant Framework** | Values                               |
|--------------------------|--------------------------|--------------------------------------|
| Views                    |                          |                                      |
| Llamachant.ExpressApp    |                          |                                      |
| BusinessObject           |                          |                                      |
| **BusinessObject_DetailView_Wizard** | ShowInWizard | Never                          |
| Wizard                   | WizardTitle             | String                               |
| Page1                    |                          |                                      |
| Page2                    |                          |                                      |
| BusinessObject_DetailView_Page1 |                |                                      |
| BusinessObject_DetailView_Page2 |                |                                      |

### Step 2:

Set up your controller to display your parent detail view. This can be set up the same way you would set up any other controller. Then, in your CustomizePopupWindowParams event, set Context of the CustomizePopupWindowParamsEventArgs equal to **LlamachantFrameworkWizardModule.WIZARDCONTEXT**.

For example:
```csharp
public class ClientWizardController : ViewController
{
    public PopupWindowShowAction CreateClient { get; set; }
    public ClientWizardController()
    {
        CreateClient = new PopupWindowShowAction(this, nameof(CreateClient), DevExpress.Persistent.Base.PredefinedCategory.Edit);
        CreateClient.CustomizePopupWindowParams += CreateClient_CustomizePopupWindowParams;
        CreateClient.Execute += CreateClient_Execute;
    }
    private void CreateClient_CustomizePopupWindowParams(object sender, CustomizePopupWindowParamsEventArgs e)
    {
        IObjectSpace space = ObjectSpace.CreateNestedObjectSpace();
        Client client = space.CreateObject<Client>();
        DetailView dv = Application.CreateDetailView(space, "Client_DetailView_Wizard", false, client);
        // Setting the context to this Wizard Context will make the popup window use the wizard instead of the regular view
        e.Context = LlamachantFrameworkWizardModule.WIZARDCONTEXT;
        e.View = dv;
    }
    private void CreateClient_Execute(object sender, PopupWindowShowActionExecuteEventArgs e)
    {
        // Perform any logic on your created object here
    }
}
```

## Wizard Page Skipping

Skip wizard pages conditionally based on runtime state using the `ShouldSkip` predicate on `WizardPageDetails`.

### How It Works

Each `WizardPageDetails` has a `ShouldSkip` property of type `Func<bool>`. When it returns `true`, the navigator skips that page during forward and backward navigation. The predicate is evaluated at navigation time, so changes to the underlying data are reflected immediately without re-wiring.

`CanMoveForward`, `CanMoveBackward`, and `CanFinish` all respect skip conditions, so button states update automatically.

### Basic Usage

Subscribe to the `WizardInitialized` event and set the `ShouldSkip` predicate on the desired pages:
```csharp
public class MyWizardController : ViewController<DetailView>
{
    protected override void OnActivated()
    {
        base.OnActivated();
        var wizard = Frame.GetController<WizardController>();
        wizard.WizardInitialized += Wizard_Initialized;
    }

protected override void OnDeactivated()
    {
        var wizard = Frame.GetController<WizardController>();
        wizard.WizardInitialized -= Wizard_Initialized;
        base.OnDeactivated();
    }

private void Wizard_Initialized(object sender, WizardInitializedEventArgs e)
    {
        var addressPage = e.WizardTemplate.WizardNavigator.Pages
            .FirstOrDefault(p => p.WizardView.Name == "AddressStep");

if (addressPage != null)
            addressPage.ShouldSkip = () => !((MyObject)View.CurrentObject).RequiresAddress;
    }
}
```

### Skipping Multiple Pages
```csharp
private void Wizard_Initialized(object sender, WizardInitializedEventArgs e)
{
    var navigator = e.WizardTemplate.WizardNavigator;

foreach (var page in navigator.Pages)
    {
        switch (page.WizardView.Name)
        {
            case "AddressStep":
                page.ShouldSkip = () => !((MyObject)View.CurrentObject).RequiresAddress;
                break;
            case "PaymentStep":
                page.ShouldSkip = () => ((MyObject)View.CurrentObject).Total == 0;
                break;
        }
    }
}
```

### Refreshing Button State After Data Changes

If a user changes data on the current page that affects skip conditions (e.g., checking a box that makes the current page the last non-skipped page), the Next/Back/Finish button states won't update until the next navigation. To force an immediate refresh in Blazor, call `RefreshActions()`:
```csharp
Frame.GetController<BlazorWizardDialogController>()?.RefreshActions();
```

### Notes
- `ShouldSkip` defaults to `() => false` — all pages are shown unless explicitly configured
- If all remaining pages are skipped, `CanMoveForward` returns `false` and `CanFinish` returns `true`, presenting the Finish button
- Skip conditions apply equally to forward and backward navigation

**Wizard Example**
