Introduction
This article aims to show how a multi-step procedure for collecting user input could be implemented in Silvelight 2.0 Beta.
Background
In developing business applications, you soon realise that users need simple and self-guiding UIs in order to complete even really simple tasks. When a multitude of fields must be completed and not all of that can fit in a single form/window, we are forced to split those by some sort of criteria and place them in different forms. Sometimes, some of the input will effect the choices available for other fields, so those fields are pushed further down the UI. MFC has the classes CPropertySheet
and CPropertyPage
to aid in resolving such issues. Combining those two classes, we could create a modal window with a set of buttons that allows users to flip through pages of input field, and we would put the dependent fields in pages that are further away.
Silverlight does not provide such a feature, and nor do the .NET standard libraries. I will shortly describe how to do just that.
Here is how my implementation looks like, with some extra demo UI added for completeness:
How it works
The main player is the Wizard
(UserControl
); it lays out the familiar UI for the user, with two buttons at the right bottom, a title bar, and a pages area. This is achieved using a grid with 3X3 cells. The middle cell 1,1 is reserved for pages. This cell is assigned a SwitcherControl
(UserControl
) that has no UI content of its own. The SwitchPage(UserControl oControl)
function is used to switch pages on the SwitcherControl
.
The Wizard
class is added a Pages
attached property that enables us to bind to it and to also add pages through XAML.
Whenever one of the two buttons is clicked, the wizard fires a predefined PageEvent
that enables the host page to monitor wizard activity. The WizardEventArgs
passes the current page index, the new page index, button action (previous, next, and finish) and an optional Cancel
property. This event is fired before the page is changed; setting the Cancel
property to true
will stop the intended transition.
I have tried to encapsulate as much functionality as necessary to demonstrate the point in the Wizard
class while following the OO design paradigm, and as little as not to clutter it too much with unnecessary code.
In this project, I have decided to involve as much of Binding as possible, as it makes the implementation cleaner and easier to follow.
Functionality provided by the Wizard class
- Setting the wizard title.
- Switching between pages available.
- Previous button is disabled when showing the first page.
- Next button is changed to Finish when on the last page.
- A
PageEvent
is fired after every button (previous, next, and finish) click.
WizardEventArgs
class contains the
- current pages index,
- the index of the page intending to move to,
- the action taken (previous, next, and finish),
- the option to cancel the switch.
Useful features to add
We could also easily add the following;
- Property to get/set the current page.
- In the
PageEvent
, an option to specify a page to go to instead of the one in the sequence.
- Properties to specify other visual aspects of the wizard.
- Making the wizard into a modal popup window - that is shown on my article: Modal window in Silverlight.
Using the code
Start by creating your own page (MainPage
); import the wizard namespace in XAML. In the code below, I have alias-ed the namespace as "sl
". Within the section/container you wish to place the wizard, add the following XAML;
<sl:Wizard x:Name="ctrlWizard" Title="Test Wizard" Grid.Row="0" />
The code above specifies that we are placing the wizard in grid row 0; we have given it an instance name of "ctrlWizard
", and we have set the title property to "Test Wizard".
Adding three pages to the wizard through XAML is shown below:
<sl:Wizard x:Name="ctrlWizard" Title="Test Wizard" Grid.Row="0" >
<sl:Wizard.Pages>
<sl:WizardPage1 x:Name="Page1" />
<sl:WizardPage2 x:Name="Page2" />
<sl:WizardPage3 x:Name="Page3" />
</sl:Wizard.Pages>
</sl:Wizard>
We can do the same in code; e.g., in the in Loaded
event of the MainPage
.
ctrlWizard.Pages.Add(new WizardPage1());
ctrlWizard.Pages.Add(new WizardPage2());
ctrlWizard.Pages.Add(new WizardPage3());
Below is the implementation of the PageEvent
handler:
void OnPageEvent(Wizard sender, WizardEventArgs e)
{
_txtMsg.Text = string.Format("Action: {0}, Current: {1}, New: {2}",
e.Action, e.CurrentPageIndex, e.NewPageIndex);
}
Points of interest
- Wizard pages can be any control derived from
UserControl
. Pages are completely oblivious to how they are used.
- The
Wizard
class is just as much oblivious to what pages/controls are shown and their purpose.