Fondest greetings, and welcome to the blog home of the Merlin Wizard Framework! In spite of some tangential posts, this blog will be geared toward getting you up and running with the slickest, simplest, and richest WinForms wizard framework for .NET. If you read the blog posts in chronological order, it may even read like a tutorial.

Tuesday, March 10, 2009

A Case for Wizards

In this post, I’d like to take a step back and consider a fundamental design question: do we really need wizards? Here’s what Alan Cooper et. al. write about wizards in their monumental tome on user interaction design, About Face 3:

Wizards are an idiom unleashed on the world by Microsoft… they are a fine example of interrogation tactics on the program’s part, and violate the design principle: Provide choices, don’t ask questions…  The user is like the conductor of a robot orchestra, swinging the baton to set the tempo but otherwise having no influence on the proceedings…. A better way to create a wizard is to make a simple, automatic function that asks no questions of users but that just goes off and does the job… Wizards were purportedly designed to improve user interfaces, but they are, in many cases, having the opposite effect.

                   About Face 3 (561-562), Alan Cooper et. al., 2007 Wiley Publishing, Inc.

Wow! That’s quite a searing indictment. But is it accurate? I can’t help but wonder if Mr. Cooper does his own taxes. Probably not… Because if he had, and if he used any popular tax preparation product, such as TurboTax or TaxCut, what he would see is a essentially a spiffed-up wizard. And I would imagine his life (and countless others) would be quite miserable should those programs use any other paradigm.

Of course, he does have a point… it’s quite easy to overuse or misuse the wizard paradigm (and Merlin makes it even easier ;-)). For tasks such as creating a new PowerPoint presentation, an example Cooper uses in his text, a Wizard is in fact superfluous. Just let the application create a new document as it sees fit, and let the user make the changes. So when is it a good idea to use a wizard after all?

The following are some of the indicators that a wizard is called for:

  • A task must be broken down. That is, when the task performed is too immense for a user to consider in one step. The tax computation example easily comes to mind.

  • Asking for forgiveness is not an option. In other words, it’s not possible to do the job first and then let the user make changes. An easy example of this is the installer. It doesn’t make sense to install the application first and only then to ask which directory it should be installed into, whether or not certain files should have been overwritten, whether some component should not have been installed at all, etc. Similarly, we wouldn’t want our tax programs to just pre-fill the 1040 form with last year’s data and make us go through the completed IRS form making adjustments.

  • The user’s attention must be directed. If instead of a wizard we give the user one big jolly tabbed window with options speckled throughout, we have no guarantee that the user will set or even see these options. If sensible defaults are available and the options can be changed later, that’s not a problem. However, if the options presented must be brought to the attention of the user, or if some data must be collected, a Wizard is a preferable way.

  • It is not known in advance which sections are relevant. At some point in a TurboTax or TaxCut sequence, the application will ask if you have received a 1099-INT form or have interest to report. If you have not and do not, it will not offer you to enter that data. Now imagine this working with any other paradigm. Either visual elements will be appearing and disappearing as the “have 1099-INT” setting is toggled, or they will be disabled and enabled. GUI users are not accustomed to having visual elements manipulated except by their direct command. Harder still will be the times when the user is not sure which setting enables and disables (or hides and shows) other settings… He may spend many a minute wondering why information he just filled out suddenly vanished.

Long story short, the wizard as an idiom is no more great or poor than the icon, the toolbar, or a hyperlinked table of contents. As Cooper and friends themselves proselytize throughout their opus, we must design based the goals and needs of our users and the models that are natural to them.

Sunday, January 18, 2009

Step Sequence Manipulation

Putting aside the unrelenting bliss over the 1.0 release (and my propensity for exaggerating it), it’s time to lay down some more Merlin features. Specifically, we’re going to cover manipulating the step sequence when the wizard is already running.

Here’s how that works:

Somewhere in a step (generally in the NextHandler), you would access the wizard controller and call one or more of the following methods on it:

  • public void AddAfterCurrent(IStep step);
    Adds the specified step immediately after the current step. After the new step executes, the step that originally followed the current step will execute. 
    stepadd
  • public void AddAfterCurrent(IEnumerable<IStep> steps);
    Adds all the steps in the argument immediately after the current step. After all the new steps execute, the step that originally followed the current step will execute. 
    multiplestepadd
  • void DeleteAllAfterCurrent();
    Deletes all steps that follow the current step.
    delete

There are two blatant pitfalls possible here:

  • If a current step has a “Next” button (i.e. it’s not the last in a sequence), but then you delete all the steps that follow, the user will click Next and “run off the end” of the wizard. The wizard will just close, and the Wizard Controller will consider the wizard successfully completed.

  • If the current step is the last one, it will appear with a “Finish” button. If you dynamically add steps afterward, the user will be surprised that clicking the “Finish” button actually leads to more steps. To avoid this phenomenon, just place a dummy step after the first one, so that the first step appears with a “Next” button. Then, you can use DeleteAllAfterCurrent() to delete the placeholder step prior to adding new steps.
  • To see these features in action, check out our step sequence manipulation demo. In this demo, the user chooses a fruit type (apples or oranges), and the subsequent steps are determined by which fruit type the user has chosen.

    Bon app├ętit!

We're 1.0!

Pop the champagne, folks! But first, head on down to the downloads page, and grab yourself a sizzling-hot cup of Merlin magic.

As always, we'd love to hear your comments or suggestions either in the blog comments or in our forum on Codeplex.

Saturday, January 17, 2009

Step Library! Part 2.

The last step in the step library for the 1.0 release is a simple text form. Merlin is all about getting you through your wizard creation quickly, so we wanted to give you a way to get text input from the user without needing build your own controls. Of course, if you do need more flexibility than the text form offers, the option to build your own UI is still there.

Ok, here’s a 5-second text form:

var textFormStep = new TextFormStep("Additional Information", 
    "Please answer the following questions:", "Name:", "Age:", "Hobby:");

Once the wizard controller executes this step, here's what we see:

textformSimple

After this step has run, the textFormStep.Answers property will return an IEnumerable<string> containing the answers in the order in which their respective questions were specified in the constructor. Here’s another way of defining exactly the same form:

var textFormStep = new TextFormStep("Additional Information",
    "Please answer the following questions");
var nameQuestion = textFormStep.AddQuestion("Name:");
var ageQuestion = textFormStep.AddQuestion("Age:");
var hobbyQuestion = textFormStep.AddQuestion("Hobby");

There are two advantages to this approach: one is that it allows me to retrieve the answers without going through an enumeration and remembering the question order. I can just do:

MessageBox.Show("Your age is: " + ageQuestion.Answer);

The second advantage for the longhand approach is that it allows you to provide some additional options. Have you noticed how the “Finish” button appears in the above wizard even though we haven’t typed anything? We’d probably want to impose some minimal validity criteria before we’re allowed to move on:

var nameQuestion = textFormStep.AddQuestion("Name:",
    Validation.MinLength(3));
var ageQuestion = textFormStep.AddQuestion("Age:", Validation.NonEmpty);

Now, the “Next/Finish” button will not be enabled until at least 3 characters have been typed into the “Name” field and the “Age” field is non-empty. There are more validations available, and you can always plug in your own delegate/lambda expression that takes a string and returns a bool.

If you want to ask for a password, you can just as easily use a password masking character:

var hobbyQuestion = textFormStep.AddQuestion("Password:", 
     Validation.Length(6, 12), '*');

Disclaimer: this UI provides no more password security than a ye olde winform with a textbox.

And last but not least, you may want to pre-populate the textbox with a default answer or a previously-specified value. Easier done than said:

var nameQuestion = textFormStep.AddQuestion("Name:");            nameQuestion.Answer = "Merlin";

Now, “Merlin” will appear in the “Name:” textbox when this step is executed.

Oh, and one really last thing: if your questions are of very uneven length, the textboxes can appear quite far away from the text of the shorter questions:

textformdemo

If you’d like all the textboxes to start right next to their respective labels, just set the step’s LineUpQuestions property to false (it’s true by default). Then, your question will appear like this:

textd2

‘Till next time!

Step Library!

Fondest greetings to all. The 1.0 release is microscopically-near, and you can already savor its features in the latest release candidate. And among these oh-so-savory features is the new step library! In the previous entries, we covered creating steps from your own Controls. But some kinds of steps are so common, we’ve pre-built them for you. For example, choosing one option out of several:

SelectionStep

This step took all of one line of code to create (highlighted below):

var steps = new List<IStep>();
var choiceStep = new SelectionStep("Color choice",
"Please pick a color", "Red", "Blue", "Green", "Turqoise");

steps.Add(choiceStep);
new WizardController(steps).StartWizard("Hello");
MessageBox.Show((string)choiceStep.Selected);

Once the step has run, you can retrieve the selection via the Selected property.

Next up on our step library hit parade is the file selection step. Patently simple: often you need the user to select a file. This step will do that:

FileSelectionStep

The code to create this step:

var fileStep = new FileSelectionStep("License",
"Please provide your license file");
fileStep.Filter = "License Files (.lnc)|*.lnc";

After the step has run, fileStep.SelectedFullPath will return the full path of the selected file.

There’s one more step to cover, but I’ll save it for the next entry. Let me just add that the examples above do not cover every feature and tweak of these steps. We’ll post the complete API reference somewhere after 1.0 is out the door.

Tuesday, December 23, 2008

Delays, Delays

Let's go back to the demo from the previous post, for a moment. Once you got to the "Advanced User Information" step, you may have noticed an unsightly disabled text box at the top of the screen:

Screen Shot

The purpose of that text box is to display the name of the user entered in the previous step. To do that, I need to populate the text box only when the controller reaches this step and not before. The cleanest (but not the only) way to do that is by setting the step's StepReachedHandler property to a void delegate that will do the initialization for you. Here's the modified Main method from the previous post's demo demonstrating how this would be done (changes in bold):

var steps = new List<IStep>();
var simpleEntryUI = new SimpleEntryUI();
var simpleEntryStep =
    new TemplateStep(simpleEntryUI, "User Information");
var advancedEntryUI = new AdvancedEntryUI();
var advancedEntryStep =
    new TemplateStep(advancedEntryUI,
"Advanced User Information");
advancedEntryStep.StepReachedHandler = () =>
{
    advancedEntryUI.FullName = simpleEntryUI.FullName;
};
steps.Add(simpleEntryStep);
steps.Add(new ConditionalStep(
    () => { return simpleEntryUI.ShowAdvancedOptions; },
    advancedEntryStep));

Label farewellMessage = new Label();
farewellMessage.Text = "Well, done. Bye, now!";
steps.Add(new TemplateStep(farewellMessage, 10, "Goodbye!"));
new WizardController(steps).StartWizard("User Data Wizard");

The StepReachedHandler does not get evaluated until the controller reaches the step, hence it has access to all the data provided by the preceding steps. Note, that if you run the example above but do not check the "Show Advanced Settings" checkbox, the StepReachedHandler will not run at all. This is what you would expect: if a step is inside a conditional and the condition is false, the StepReachedHandler of that step will not run.

Maybes and What-ifs

In Merlin, we represent a wizard as a sequence of steps. Of course, a wizard isn't always a fully-defined sequence - sometimes the end user's input determines what step comes next or whether a step occurs at all. In this post we will investigate the simplest technique Merlin offers for dynamically determining whether a step occurs.

It's called a ConditionalStep, and as the name implies, it occurs only if some condition is true. Creating a ConditionalStep is easier than gaining weight on a cruise ship:

var conditionalStep = new ConditionalStep(condition, actual step);

The condition above is a boolean delegate. The actual step is another step. If (and only if, for the mathematical purists) condition is true, actual step occurs. If the condition is false, we skip on down to the next step. This implies, of course, that a conditional step cannot be the last step in the step sequence. If you try to give such a sequence to the wizard controller, it will throw a ConditionalStepAtEndException.

I put a small demo together to show off this functionality. You can get the complete code here, but here's the gist: For our first step, we have a simple form (shown below). If the user clicks next, she goes straight to our farewell message. But, if the user checks the "Show Advanced Options" checkbox, she gets to enter some more data, and then sees the farewell message.

image 

And here's the code that makes all this magic possible:

var steps = new List<IStep>();
var simpleEntryUI = new SimpleEntryUI();
var simpleEntryStep = new TemplateStep(simpleEntryUI, "User Information");
var advancedEntryStep =
    new TemplateStep(new AdvancedEntryUI(), "Advanced User Information");
steps.Add(simpleEntryStep);
steps.Add(new ConditionalStep(
    () => { return simpleEntryUI.ShowAdvancedOptions; },
    advancedEntryStep));


Label farewellMessage = new Label();
farewellMessage.Text = "Well, done. Bye, now!";
steps.Add(new TemplateStep(farewellMessage, 10, "Goodbye!"));
new WizardController(steps).StartWizard("User Data Wizard");

When the wizard runs, the condition does not get evaluated until the controller reaches the conditional step, which allows you to use data from previous steps in your conditions. That data will be available by the time the condition gets evaluated.

If you put a breakpoint inside the condition, you will also notice that the condition does not get evaluated on the way back. In other words, if skip down to the farewell message and click the "back" button, the condition will not be evaluated to determine whether or not you see the advanced data step. Instead, the result of the last condition evaluation will be used. But, if you go backward through a conditional step and then go forward again, the condition will be reevaluated. So to summarize: conditions are evaluated only when the conditional step is reached by clicking the "next" button.