Click here to Skip to main content
Click here to Skip to main content

WPF Wizard (MVVM) With a Time Consuming Operation

, 10 Apr 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
How to implement a Time Consuming Operation when using WPF Wizard.

Introduction

The article aims to demonstrate how to implement a Time Consuming Operation when using WPF Wizard, based on the MVVM design pattern (i.e., using the Setting Home Network Connection wizard in Windows).

I must add that the attached code is only for demonstration purpose, it is far from being complete for production. However, it can be a good starting point if you need to perform a consuming operation while running a WPF wizard.

Searching the web for WPF Wizard, I came across a great article Creating an Internationalized Wizard in WPF (by Josh Smith, Karl Shifflett), which gave me a great starting point, though I needed to implement also a scenario were one of the wizard steps need to run more than a few seconds, meaning perform a long operation. I couldn’t find an example on the web, I had to get my hands dirty and implement it myself.

If you don’t have a background in WPF or MVVM you must first read the article Creating an Internationalized Wizard in WPF where I took the code from. Those who have some background can probably jump right in.

I stripped off the Creating an Internationalized Wizard in WPF code to the minimum, so I could concentrate on my main purpose: a working WPF Wizard which has a time consuming operation. At this point I have no interest in a fancy UI, each page displaying its title is perfectly sufficient.

For the time consuming operation I wanted to add a progress bar, I really like the round progress bars which are mostly common on the web. I found in CodeProject a nice and straightforward implementation: Better WPF Circular Progress Bar (by Sacha Barber), which saved me a lot of time.

In this post I am only going to concentrate on how to add a time consuming operation to the WPF Wizard, rather than how the WPF MVVM Wizard works (this you can find in the above link).

Saying that I will explain in detail at the end how you can use the code for easily building your own Wizard.

I believe there are other ways to achieve the task, but I found the suggested solution pretty straightforward to understand and to implement.

I tried to reduce the code to the minimum, allowing the main goal of this post to be presented clearly, thus I am only going to explain the main points. If you want to understand how things work you will have to dig into the code. The code at the beginning might be confusing, especially if you are new to WPF, but I think it will be worthwhile at the end.

The code was built in VS 2010 SP1.

Hope you find it useful Smile | :)

Using the code

We all know that Wizard has three buttons:  Back, Next/Finished, and Cancel. The Next button navigates to the Next wizard’s page up to the final page (most wizards actually jump to the summary page without any user action).

Let’s assume that our wizard has three steps (pages):

  1. Welcome
  2. Configure the user system (a time consuming operation)
  3. Summary

In the above scenario, when the user clicks Next in the Welcome page, he/she is actually accepting the second step “Configure the user system”, the step that we need to do some work (i.e., checking system requirement, downloading some files….).

The long operation only happens in stage 2 so we don’t want to add to the code next[i].DoWork(), causing some operations to happen on each page (actually we can, but it looks….not great, agree?). We want however to bind the long operation only to the second page.

In MVVM every View Page has a corresponding class in the ViewModel namespace; therefore we can bind the page’s Loaded event to a class Command, which will cause, at the right moment, to launch the long operation, in our example – the Configure operation.

Binding the page’s Loaded event to a class Command:

<i:Interaction.Triggers>
 <i:EventTrigger EventName="Loaded">
   <i:InvokeCommandAction  Command="{Binding Path=RunOperation}"/> <= binding command
 </i:EventTrigger>
</i:Interaction.Triggers>

Binding between the two is great but we also need to launch the operation. Of course, we cannot launch it on the UI thread causing the UI to freeze, we must launch it on a different thread, allowing for example to cancel the Operation in the middle (if it possible).

Using BackgroundWorker is a good option; it will allow integrating the operation with the Binding Command. As follows:

protected BackgroundWorker _operationWorkerThread = null;
protected ICommand _operationWorkCommand = null;
// the command which bind to the relevant view. RunOperation in the example
………

this._operationWorkCommand = new CRRelayCommand(() => 
  this._operationWorkerThread.RunWorkerAsync()); //Start the thread on binding

this._operationWorkerThread = new BackgroundWorker();
this._operationWorkerThread.DoWork += this.DoWork; // The working thread in action
this._operationWorkerThread.RunWorkerCompleted += this.RunWorkerCompleted; //the working thread completed

In DoWork I am actually calling a virtual function DoOperation(), which the inherited classes must implement.

Main Classes

  • MainWindow – the Wizard main window.
  • CRViewModelWizardMain – the Wizard UI manager.
  • CRViewWizardMain – the Wizard main presenter.
  • CRViewModelPageBase – the ViewModel base class. All the Wizard pages must inherit from this class.
  • CRViewModelOperationPageBase - the ViewModel Operation base class, inherit from CRViewModelPageBase. All the operational classes must inherit from this class.

It is important to remember the connection between the Page view and the ViewModel object. In CRViewWizardMain.xaml we need to declare:

<DataTemplate DataType="{x:Type viewModel:CRViewModelXXXPage}">
<view:CRViewXXXPage/>
</DataTemplate>

How to build your own Wizard

  1. Design our Wizard layout (CRViewWizardMain) and the Wizard’s pages.
  2. For each View Page add a corresponding class in the ViewModel namespace.
  3. If a page is only a presenter inherit from CRViewModelPageBase; If a page is operational inherit from CRViewModelOperationPageBase.
  4. Operational classes must override DoOperation() to handle the page operation.
  5. Run and try.

Enjoy!

Points of Interest

History

  • 10.4.2013 V1.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ziv Ron
Software Developer (Senior)
Israel Israel
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmembermark_ian10-Jul-13 3:55 
GeneralRe: My vote of 5 PinmemberZiv Ron10-Jul-13 7:24 
GeneralMy vote of 5 Pinmemberilanbu11-Apr-13 8:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141216.1 | Last Updated 10 Apr 2013
Article Copyright 2013 by Ziv Ron
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid