Avalon Wizard MVVM

For the past year I’ve been relying upon a wonderful library called Avalon Wizard. It can be found over at its CodePlex site. I did my best trying to contact the maintainer of the site, but wasn’t able to get a hold of him. Turns out that he answered my discussion post, but for some reason CodePlex never alerted me to his reply! I actually had my CodePlex account setup to send to a wrong email. Avalon Wizard is a WPF control that lets you take a Window object and turn it into a wizard that looks like it came straight from Windows. Do to this fact it makes a wonderful base for creating an installer. This is exactly why I’ve been using it. Now the company I work for hasn’t released its product that utilizes this installer and more over this tool is more for internal use but I still acquired the code under a GPL v2 license.  I’ve put this off for a while, but I need to get my modifications back out as according to that license. Attached to this post is my version of the Avalon Wizard code. This code represents the .NET 4.0 version of the Avalon Wizard at the time the 1.1.0.0 build was released. I’m attempting to merge my changes into the CodePlex repository but it appears that many changes have happened since I’ve started using this library. Here are the features of my version of Avalon Wizard

  • Kind-of-mvvm capable – I added a collection of WizardViewModelBase objects.  The wizard will then use data templates to figure out how to draw the display. I know this isn’t the best way to implement MVVM but the WizardViewModelBase object provides enhanced support. More on this later.
  • Allows for dynamic creation of wizard pages.
  • Steps or guide for sections throughout the wizard.
  • Step navigation. The steps or sections along the side can be used as navigation between points in the wizard.

The wizard works by ViewModels implementing WizardPageViewModelBase. Ideally it shouldn’t matter what type the ViewModel implements. That is the biggest change that can done to this code to make it better (to better follow the MVVM model). What should be done is something like this:

<style>
  <Setter Property="Title" Value="{Binding Model.Title}"/>
  <Setter Property="Step" Value="{Binding Model.Step}"/>
  <Setter Property="AllowBack" Value="{Binding Model.AllowBack}"/>
  <Setter Property="ShowBack" Value="{Binding Model.ShowBack}"/>
  <Setter Property="AllowNext" Value="{Binding Model.AllowNext}"/>
  <Setter Property="ShowNext" Value="{Binding Model.ShowNext}"/>
</style>

This is very similar to how AvalonDock has setup their MVVM. As I work on my installer I’ll be working on improving this code. This will be in the next version (not in the linked version). Here is a pretty standard declaration of the wizard:

  <wizard:Wizard Name="AvalonWizardMVVMTextWizard"
    StepNavigationEnabled="{Binding Path=EnableStepsNavigation}"
    Cancelled="AvalonWizardMVVMTextWizard_Cancelled_1"
    Finished="AvalonWizardMVVMTextWizard_Finished_1"
    PageViewModels="{Binding Path=WizardPages}" />

Cancelled and Finished are both events that are tied to the view. StepNavigationEnabled is a True/False enabling and disabling the step navigation along the left side of the wizard. The PageViewModels is the collection of ViewModels (implementing WizardPageViewModelBase). In the MainViewModel for the MainWindow a collection of pages is built in this manner:

/// <summary>
    /// The <see cref="WizardPages" /> property's name.
    /// </summary>
    public const string WizardPagesPropertyName = "WizardPages";

    private ObservableCollection<WizardPageViewModelBase> _WizardPages = new ObservableCollection<WizardPageViewModelBase>();

    /// <summary>
    /// Sets and gets the WizardPages property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public ObservableCollection<WizardPageViewModelBase> WizardPages
    {
      get
      {
        return _WizardPages;
      }

      set
      {
        if (_WizardPages == value)
        {
          return;
        }

        RaisePropertyChanging(WizardPagesPropertyName);
        _WizardPages = value;
        RaisePropertyChanged(WizardPagesPropertyName);
      }
    }

Now here is an example of a wizard page’s ViewModel:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MVVMWizard;

namespace WizardTest40.ViewModel.WizardPages
{
  public class InfoPageViewModel : WizardPageViewModelBase
  {
    #region Properties
    #region InfoText
    /// <summary>
    /// The <see cref="WelcomeTitle" /> property's name.
    /// </summary>
    public const string InfoTextPropertyName = "InfoText";

    private string _InfoText = string.Empty;

    /// <summary>
    /// Gets the WelcomeTitle property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public string InfoText
    {
      get
      {
        return _InfoText;
      }

      set
      {
        if (_InfoText == value)
        {
          return;
        }

        _InfoText = value;
        RaisePropertyChanged(InfoTextPropertyName);
      }
    }
    #endregion

    #region CheckBoxText
    /// <summary>
    /// The <see cref="CheckBoxText" /> property's name.
    /// </summary>
    public const string CheckBoxTextPropertyName = "CheckBoxText";

    private string _CheckBoxText = "You must check this checkbox before you can continue!";

    /// <summary>
    /// Sets and gets the CheckBoxText property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public string CheckBoxText
    {
      get
      {
        return _CheckBoxText;
      }

      set
      {
        if (_CheckBoxText == value)
        {
          return;
        }

        RaisePropertyChanging(CheckBoxTextPropertyName);
        _CheckBoxText = value;
        RaisePropertyChanged(CheckBoxTextPropertyName);
      }
    }
    #endregion

    #region IsChecked
    /// <summary>
    /// The <see cref="IsChecked" /> property's name.
    /// </summary>
    public const string IsCheckedPropertyName = "IsChecked";

    private bool _IsChecked = false;

    /// <summary>
    /// Sets and gets the IsChecked property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public bool IsChecked
    {
      get
      {
        return _IsChecked;
      }

      set
      {
        if (_IsChecked == value)
        {
          return;
        }

        RaisePropertyChanging(IsCheckedPropertyName);
        _IsChecked = value;
        RaisePropertyChanged(IsCheckedPropertyName);
      }
    }
    #endregion
    #endregion

    public InfoPageViewModel()
    {
      this.Header = "Info";
      this.StepName = "Information";
      this.AllowNext = false;

      this.InfoText = "This is an information page.";

      this.PropertyChanged += InfoPageViewModel_PropertyChanged;
    }

    void InfoPageViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
      if (e.PropertyName.Equals(IsCheckedPropertyName))
        this.AllowNext = this.IsChecked;
    }
  }
}

This view model can be found in the test project included in the linked source. Hopefully this can be considered as releasing my changes back into the source. I am going to attempt to make the style selector change as mentioned above in my next release. I also am going to do my best to merge my changes into the latest version of the Avalon Wizard code over on CodePlex. If I can get into a good discussion with the maintainers of the project I’d like to see the step navigation added at least. They probably have a better handle on the MVVM design for their wizard for the most part. In any regards, please feel free to let me know what you think of the library and how it can be improved. Make sure you have Visual Studio 2012 and Get the Project! Download It!

EDITED

I guess WordPress or GoDaddy hosting didn’t like that I was trying to host a 7zip file. I reuploaded it as a zip file. Hopefully everyone can download now.

Tagged , . Bookmark the permalink.

About Mike

I'm a software engineer. Look into the about page for more information about me.

2 Responses to Avalon Wizard MVVM

  1. JohnSpencer says:

    I am kind of new to WPF and Avalon Wizard. I wonder if you have some sample code to pass data from one page to next, e.g., just to display the data on a summary page.

    • Mike says:

      Hi John,

      Thanks for the question. I actually have an updated version of the project. I just wrote a post about it here! The solution has an example project where I collect some test information and display it on a summary screen. Feel free to download it and provide some feedback.