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.

Monday, December 22, 2008

Getting Picky

Just like covering up the wall sockets in a home with small children, Merlin lets you disable certain elements and validate others, so as to prevent your users from doing unsavory things.

Let's go back to the example in the previous post. There, we have a step whose UI consists of a single text box. Let's now make sure that the user cannot advance to the next step without having typed something in the box. We rewrite the main method as follows:

var steps = new List<IStep>(); //We'll define wizard steps here.
var t = new TextBox();
var myStep = new TemplateStep(t, 10, "Name Entry");
myStep.NextHandler = () => {
    if (string.IsNullOrEmpty(t.Text))
    {
        MessageBox.Show("You gotta enter your name before I let you continue");
        return false;
    }
    else return true;
};
steps.Add(myStep);
var result = new WizardController(steps).StartWizard("My SuperCoolWizard");
if (result == WizardController.WizardResult.Finish)
MessageBox.Show("Hello, " + t.Text);

That's a bit more code than we had before. Let's look at the new stuff:

myStep.NextHandler = () => {
    if (string.IsNullOrEmpty(t.Text))
    {
        MessageBox.Show("You gotta enter your name before I let you continue");
        return false;
    }
    else return true;
};

The TemplateStep class offers the NextHandler, PreviousHandler, and CancelHandler properties to handle the "Next/Finish", "Previous", and "Cancel" buttons, respectively. The first two are boolean delegates. If they return false, the controller will not leave the current step. Since the purpose of a cancel button is to drop everything and abort, it does not have a return value, and a cancellation will occur no matter what.

But why wait for the user to hit the "Next/Finish" button? Why not just gray it out altogether? I'm glad I asked!

var steps = new List<IStep>();
var t = new TextBox();
var myStep = new TemplateStep(t, 10, "Name Entry");
myStep.AllowNextStrategy = () => {
    return !string.IsNullOrEmpty(t.Text);
};
t.KeyUp += (object sender, KeyEventArgs keyArgs) => {
   myStep.StateUpdated();
};
steps.Add(myStep);
var result = new WizardController(steps).StartWizard("My Wizard");
if (result == WizardController.WizardResult.Finish)
    MessageBox.Show("Hello, " + t.Text);

This time, in lieu of a handler for the next button, we provided a strategy (in the form of a boolean delegate) to determine if the Next button should be enabled. But we needed one more piece:

t.KeyUp += (object sender, KeyEventArgs keyArgs) => {
   myStep.StateUpdated();
};

We need to know when to recheck the strategy so that the buttons accurately reflect the validity of the user's input. To do this, we fire TemplateStep's StateUpdated method whenever something in our UI has changed.

That does it for this lesson. Please leave any questions or feedback in the comments, we'll do our best to answer.

2 comments:

  1. Yev - these posts are great and I truly hope you keep them up.

    A truly minor quibble, just in case this makes it into someone's production code. Wouldn't you want to subscribe to t.TextChanged rather than t.KeyUp, for circumstances such as pasting via right-click, etc?

    Otherwise, this is well written and just the kind of information I was looking for. You seem to know your audience well.

    -Scott

    ReplyDelete
  2. Yes, I would. I seem to remember having issues with TextChanged not firing under some circumstances, but after some experimentation, I think the former might have been user error on my part. In any case, no one ever died from subscribing to both. :-)

    ReplyDelete