This is the seventh article in my series about one way to implement MVVM in client side Blazor. If you would like to read all of the articles from the beginning please start here:

All of the code for this article is located in my Github repository. The completed code of the first version of the Wizard will be accessible as either the ArticleSeven branch or as the 4.7 release.

In this article we are going to take a look at building a Wizard in client side Blazor. This is a common enough task that I have had to do it in almost every company I have worked for. We will be using MVVM but the focus will be on building a working Wizard so we are only going to have a single Model and a single ViewModel. The code for this article became much longer than previous examples so 100% of the code is not displayed in this article but it is all available in the repository.

Framingham Risk Score

For this article we are going build a Wizard that asks a users a series of questions and then calculates the chance of developing cardiovascular disease in the next ten years. If you are interested in learning more about this or seeing step by step how it is calculated you can read about it on Wikipedia:

The algorithm takes the answers to a series of questions and uses them to calculate a number of points. At the end the number of points are added together and the total is used to determine the ten year risk. The points assigned for each question as well as the final calculation are all different for men and women.

To encapsulate all of the logic needed to store the answers and calculate the risk we will create a new Model named CvdRisk_Model.cs . The class and interface for this Model resulted in almost 800 lines of code so I will link to it instead of embedding the entire class in the article.

First we will take a look at the interface for this Model which actually does fit on the screen.

Looking at the interface there is a Func delegate that can be called to retrieve the risk score at the end of the process, a CalulateRiskScore() method which totals up the points from all of the answered questions and then seven methods which are each called to set the answer to each one of the asked questions related to the Model.

Most of the methods in the Model are filled with Switch statements which assign a number of points based on the answer to each question. As we discussed earlier the number of points and how they are measured is different for men and women so the model was build to take that into account. If we look at lines 34 through 65 of the model this is what we see:

For every method that evaluates an answer where the values are different for men and women we have declared a Func<T> . The very first question we ask the user will be the gender and we will then use this to assign the correct method to the delegate for each question. This will allow the rest of the code we work on to not have to be concerned about the user’s gender.

Starting the Wizard

Now that we have a fully coded Model lets start building up a Wizard to populate it with the required data we will need to calculate the risk score. First create CvdRisk_ViewModel.cs in the ViewModels folder. We want our Wizard to display a progress bar as well as letting the user know the current step they are on and give them the ability to navigate between steps. Lets start off by putting some code in the ViewModel we can bind to with that in mind.

We’ve injected our Model and started off our Wizard on step 1 at 0% completion. We also are all set to start our user off with previous and next buttons disabled since they will be on the first step and have not answered the question yet. With these public properties pulled up to the interface we can now use the ViewModel to start building our View.

Create a new View in our Views folder named CvdRisk.razor . Update NavMenu.razor in the Shared folder to make the new View accessible from navigation. We will start off the View by injecting our ViewModel and setting up the Bootstrap progress bar:

We now have our progress bar bound to the public property in our ViewModel so as long as we properly update that value we now have a visual indicator for our user that shows how close to done they are.

To make the building of our Wizard as simple as possible we are going to use a switch statement in our View to display the correct step in our Wizard. We are going to put the full markup in each step so this will add some length and repetitive markup to our View. In the next article we will look into ways to refactor our Wizard to make it more elegant.

We will use Bootstrap cards to add some layout to our Wizard. When we add our switch statement and the first step of our Wizard to our View this is what we’ll see:

We have set up a card displaying information for our user that indicates where they are in the process and gives the first instruction. It also has previous and next buttons and an event that will allow navigation to step two if the button is enabled. All we need is for the user to be able to provide their gender via some input control.

Building a radio button component

Radio buttons seem like a good choice to let the user select their gender. They have to select either Female or Male and cannot select both. The Bootstrap radio button however has a minor hurdle we need to clear to be able to use it in our Wizard. To set the initial state of the radio button Bootstrap simply uses the checked property and that property’s value can only be set to empty or checked which both result in the button being checked. Because of this we cannot bind a value to that property so we can’t have the ViewModel directly tell the radio button group which button should be checked if we navigate away from the Wizard step and then come back to it. How do we work around this? We are going to make a component that renders our radio button group for us and properly displays the checked button.

In our Components folder make a new component RadioButtonList.razor . We are going to pass in all of the data our button group needs to render as parameters as well as a delegate method that we will use as an EventCallback . This will ensure that our Wizard will properly update its display when actions take place in the component. Here is the code for our component:

We have passed in all of the choices as a List<string> and are using a foreach loop to write out all of the radio buttons in our button group. We are using some conditional logic to only have the checked property present on the radio button if the current choice in the loop matches what has already been previously selected. MakeChoice will be the delegate for the method which gets called when we click on one of the radio buttons in the group.

To make use of the parameters we will need some more properties in our ViewModel. Add these members to the ViewModel and pull up the public members to the interface:

We will also need to update our constructor to make the appropriate choices available to pass in to the component:

We can now update case 1 in our View to use this new component:

If we launch our application we will now be able to see step 1 of our Wizard in the browser:

Adding the second step to the Wizard

Now we can let our users move on the the next step once they have selected their gender. We have already seen that once the user clicks a radio button that the gender is saved to the ViewModel and also passed to the Model. After that happens we make a call to SetNavButtons . That method doesn’t do anything yet but now we need some code in it.

The purpose of SetNavButtons is to make sure that the previous and next buttons are enabled and disabled at the proper times. This means that Previous Step is disabled on Step 1 and Next Step is disabled when the calculator is done as well as when no input has been received on the current step. Lets add a switch statement to that method to do this for us:

That added logic now enables the Next Step button when we select a gender an allows us to move onto Step 2.

Step 2 required the user to enter their age so we will add members to the ViewModel to deal with age as an int.

The formula starts at 20 and goes up to 79 so we are enforcing that restriction on the user. With that added to the ViewModel we can add Case 2 to our View. Since we are grabbing an integer we will use a number input this time:

With the View updated we can add more logic to SetNavButtons() to handle the second step of the Wizard. Just like we did in Step 1 we only enable the Next Step button if we have received valid input.

If we launch our application again we can now enter our gender, navigate to Step 2 and enter our age. We will even see that the progress bar is updating. We can also navigate back and forth from Stage 2 to Stage 1.

Step 3 through Step 7 are all exactly the same as Step 1 and 2 were. Depending on the input we are using a radio button list or a number input to get the required information from the user. All of the code for these steps are in the GitHub repository.

Finishing the Wizard

Once a user has answered all of the questions we need to show them the calculated risk percentage from the Model. We want the user to be able to go back and change numbers and have the risk be recalculated if they want so we are going to call the calculation in SetNavButtons() . First we will add the needed members to hold the result

then update SetNavButtons() so that navigating to Step 8 always recalculates the CVD risk.

With the risk score available we can add the final case to our View to complete our Wizard:

With the full code of the ViewModel and the View now in place we can launch our application once more and answer all of the questions and have a risk score calculated.

We can also go back and change our answers and have the results be recalculated each time we get to the end.

Wrapping Up

In this article we looked at building a Wizard in client side Blazor using MVVM. We created a self contained Model that implemented the Framingham Risk Score algorithm. We then created a Wizard in a Razor View using a switch statement and built the necessary ViewModel to wire everything up. We also build a self contained radio button component to meet our Wizard requirements.

We have a lot of cut and paste duplicated markup in our View. In the next article we will look at a way to improve our View.