top button
Flag Notify
    Connect to us
      Site Registration

Site Registration

Your First MVC 6 And EF 7 Application (Dependency Injection) : Part 3

+5 votes
351 views

In Part 1 and Part 2 of this series you developed a simple database driven application that displays a list of customers and also allows you to modify the customer details. Although the application is working as expected, it relies on the local instances of the NorthwindDbContext to get its job done. In this article we will use the Dependency Injection (DI) features of MVC 6 to inject the NorthwindDbContext into the controller class. Later we will also add repository support in the application.

Let's begin!

Open the same project in the Visual Studio and also open the Startup class in the IDE. Modify the ConfigureServices() method of the Startup class as shown below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<NorthwindDbContext>
             (options => options.UseSqlServer
             (AppSettings.ConnectionString));
}

Notice the code marked in bold letters. We have chained the AddDbContext() method at the end of t he AddSqlServer() method call. The AddDbContext() is a generic method that allows you to specify the type of the DbContext that you wish to inject into the controllers. Moreover, you can also specify the database connection string while adding the DbContext. In this case the AppSettings.ConnectionString property holds the database connection string and is passed to the UseSqlServer() method.

Now open the NorthwindDbContext class and remove the OnConfiguring() overridden method completely.

public class NorthwindDbContext:DbContext
{
    public DbSet<Customer> Customers { get; set; }
    
    // no more OnConfiguring() method
}

We do this because connection string is now being supplied from the ConfigureServices() method.

Finally, open the HomeController and add a public constructor to it as shown below:

public class HomeController : Controller
{
    private NorthwindDbContext db;
    private CustomerRepository repository;

    public HomeController(NorthwindDbContext context)
    {
        this.db = context;
        this.repository = repository;
    }
...
...
}

The HomeController class declares a NorthwindDbContext variable at the top. This variable is assigned in the constructor. The constructor takes a parameter of type NorthwindDbContext. This parameter will be supplied by the DI framework of MVC 6. Once received we store its reference in the db variable for further use.

We need to modify the actions to use this class level variable instead of locally instantiating a DbContext. So, we need remove the using blocks from the existing code. The modified actions are shown below:

public IActionResult Index()
{
    List<Customer> data = db.Customers.ToList();
    return View(data);
}

public IActionResult Edit(string id)
{
    Customer data = db.Customers.Where(i => 
                    i.CustomerID == id).SingleOrDefault();
    var query = (from c in db.Customers
                    orderby c.Country ascending
                    select new SelectListItem() 
                    { Text = c.Country, Value 
                      = c.Country }).Distinct();
    List<SelectListItem> countries = query.ToList();
    ViewBag.Countries = countries;
    return View(data);
}

public IActionResult Save(Customer obj)
{
    var query = (from c in db.Customers
                    orderby c.Country ascending
                    select new SelectListItem() 
                    { Text = c.Country, Value = 
                      c.Country }).Distinct();
    List<SelectListItem> countries = query.ToList();
    ViewBag.Countries = countries;

    if (ModelState.IsValid)
    {
        db.Entry(obj).State = EntityState.Modified;
        db.SaveChanges();
    }
    return View("Edit", obj);
}

Run the application by setting a break point in the constructor. You will find that the DI framework supplies an instance of NorthwindDbContext configured to use the specified connection string.

So far so good. Now let's go ahead and use a repository for the database operations.

To do so, add CustomerRepository class in the Classes folder and write five methods - SelectAll(), SelectByID(), Insert(), Update() and Delete() - in it as shown below:

public class CustomerRepository
{
    public NorthwindDbContext Context {get; private set;}
    public CustomerRepository(NorthwindDbContext context)
    {
        this.Context = context;
    }

    public List<Customer> SelectAll()
    {
        return Context.Customers.ToList();
    }

    public Customer SelectByID(string id)
    {
        return Context.Customers.Where(i => 
               i.CustomerID == id).SingleOrDefault();
    }

    public void Insert(Customer obj)
    {
        Context.Entry(obj).State = EntityState.Added;
        Context.SaveChanges();
    }

    public void Update(Customer obj)
    {
        Context.Entry(obj).State = EntityState.Modified;
        Context.SaveChanges();
    }

    public void Delete(string id)
    {
        Customer obj = Context.Customers.Where(i => 
                   i.CustomerID == id).SingleOrDefault();
        Context.Entry(obj).State = EntityState.Deleted;
        Context.SaveChanges();
    }
}

The CustomerRepository class encapsulates all the logic to perform CRUD operations on the Customers table. Notice that the CustomerRepository doesn't create any local instance of NorthwindDbContext class. It receives it through the constructor. We do this because we want the DI framework to inject the DbContext in the repository just as it did for the controller. The received instance is stored in a public Context property. The Context property has private setter so that the DbContext can't be assigned from outside, at the same time allowing access to the DI supplied instance.

Once the CustomerRepository is ready you need to register it with the DI framework. This can be done as follows:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<NorthwindDbContext>
             (options => options.UseSqlServer
             (AppSettings.ConnectionString));
    services.AddScoped<CustomerRepository>();
}

Notice the code marked in bold letters. It uses the AddScoped() generic method to register CustomerRepository class with the DI framework. The AddScope() creates an object instance whose scope is the current request. So, now CustomerRepository can also be injected into the controller class.

Although the above class doesn't do that, you can create a repository interface and then implement that interface on the repository class. If your repository is based on some interface you can also specify the interface in the AddScoped() generic method. For example:

services.AddScoped<ICustomerRepository, CustomerRepository>();

The following code shows how the injected repository instance can be used.

public class HomeController : Controller
{
    private CustomerRepository repository;

    public HomeController(CustomerRepository repository)
    {
        this.repository = repository;
    }
...
...
}

This code should look familiar to you because you used it while injecting the NorthwindDbContext. In this case, however, you declare a variable of type CustomerRepository and assign it in the constructor.

Once injected you can use the CustomerRepository in all the actions instead of using the DbContext directly. The following code shows the modified actions of the HomeController.

public IActionResult Index()
{
    List<Customer> data = repository.SelectAll();
    return View(data);
}

public IActionResult Edit(string id)
{
    Customer data = repository.SelectByID(id);
    var query = (from c in repository.Context.Customers
                    orderby c.Country ascending
                    select new SelectListItem()
                    { Text = c.Country, Value = c.Country })
                    .Distinct();
    List<SelectListItem> countries = query.ToList();
    ViewBag.Countries = countries;
    return View(data);
}

public IActionResult Save(Customer obj)
{
    var query = (from c in repository.Context.Customers
                    orderby c.Country ascending
                    select new SelectListItem()
                    { Text = c.Country, Value = c.Country })
                    .Distinct();
    List<SelectListItem> countries = query.ToList();
    ViewBag.Countries = countries;

    if (ModelState.IsValid)
    {
        repository.Update(obj);
    }
    return View("Edit", obj);
} 

For the sake of simplicity the above code creates List of SelectListItem objects by writing a query using the Context property. You could have added some method in the repository that returned the desired countries. Run the application again. If all goes well, you should be able to see the customer list and will be able to modify the customer details.

posted Oct 12, 2016 by Shivaranjini

  Promote This Article
Facebook Share Button Twitter Share Button LinkedIn Share Button


Related Articles

In Part 1 of this series you created and configured a basic project using ASP.NET MVC 6. Although the project runs as expected and outputs a message in the browser, it isn't database driven yet. In this part we will add database support to the project in the following ways:

  • Display a list of customers from the Northwind database
  • Allow for modification of existing customers
  • Provide basic validation capabilities

In this part we will modify the Index view to look like this:

image

As you can see, the page displays a list of customers from the Customers table of the Northwind database. Each table row has an Edit link. Clicking on the Edit link takes you to the Edit view so that the customer details can be modified. (see below).

image

Once you modify the details and click on the Save button the modified details are saved in the database. In case there are any validation errors they are displayed on the page like this:

image

Ok. Let's begin our development!

Open the same project that we created in Part 1 and add Customer class to the Models folder using the Add New Items dialog. The complete code of this class is shown below:

[Table("Customers")]
public class Customer
{
    [Required]
    [StringLength(5)]
    public string CustomerID { get; set; }
    [Required]
    public string CompanyName { get; set; }
    [Required]
    public string ContactName { get; set; }
    [Required]
    public string Country { get; set; }
}

The Customer class is mapped to the Customers table using the [Table] attribute and contains four public properties namely CustomerID, CompanyName, ContactName and Country. Basic validations such as [Required] and [StringLength] are also added to these properties.

Then add NorthwindDbContext class to the Classes folder and write the following code to it.

public class NorthwindDbContext:DbContext
{
    public DbSet<Customer> Customers { get; set; }

    protected override void OnConfiguring
      (DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(AppSettings.ConnectionString);
    }
}

The NorthwindDbContext class represents our data context and hence inherits from the DbContext base class. It consists of a single DbSet - Customers. Notice how the OnConfiguring() method has been overridden to specify the database connection string.

The overridden OnConfiguring() method supplies optionsBuilder parameter. The UseSqlServer() method accepts a database connection string. Recollect that we have stored the database connection string in the ConnectionString static property of the AppSettings class during startup of the application.

Now modify the Index() action of the HomeController as shown below:

public IActionResult Index()
{
    using (NorthwindDbContext db = new NorthwindDbContext())
    {
        List<Customer> data = db.Customers.ToList();
        return View(data);
    }
}

The Index() action simply instantiates the NorthwindDbContext and fetches all the customers in the form of a List. This List is supplied to the Index view as its model.

Now, open the Index view and modify it as shown below:

@model List<MVC6Demo.Models.Customer>

<html>
<head>
    <title>My First MVC 6 Application</title>
</head>
<body>
    <h1>List of Customers</h1>
    <table border="1" cellpadding="10">
        @foreach (var item in Model)
        {
            <tr>
                <td>@item.CustomerID</td>
                <td>@item.CompanyName</td>
                <td><a asp-action="Edit" 
                     asp-controller="Home" 
                     asp-route-id="@item.CustomerID">
                    Edit</a></td>
            </tr>
        }
    </table>
</body>
</html>

The Index view displays a list of customers in a table. This code is quite similar to MVC 5.x except the Edit link. In MVC 5.x you use ActionLink() HTML helper to render hyperlinks. The above code uses an anchor Tag Helper to achieve the same. The asp-action and asp-controller attributes points to the action method and the controller. The asp-route-id attribute specifies the ID route parameter to a CustomerID. This way the Edit links will take this form:

/home/edit/ALFKI

To get the Tag Helper intellisense you need to add _ViewImports.cshtml file to the Views folder (you can do that using Add New Items dialog). Once added place the following code in it:

@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"

The @addTagHelper directive tells the framework to use Tag Helpers from the specified assembly. You will now get various tag helper related attributes in the Visual Studio intellisense.

Ok. Next, add Edit() action to the HomeController as shown below:

public IActionResult Edit(string id)
{
    using (NorthwindDbContext db = new NorthwindDbContext())
    {
        Customer data = db.Customers.Where(i => 
                 i.CustomerID == id).SingleOrDefault();
        var query = (from c in db.Customers
                        orderby c.Country ascending
                        select new SelectListItem() 
                    { Text = c.Country, Value = c.Country })
                    .Distinct();
        List<SelectListItem> countries = query.ToList();
        ViewBag.Countries = countries;
        return View(data);
    }
}

The Edit action receives a CustomerID as its parameter. Inside, the code fetches a Customer matching the supplied ID and passes it to the Edit view. The Edit view also needs a list of countries for the Country column. So, a List of SelectListItem (Microsoft.AspNet.Mvc.Rendering namespace) is created and filled with unique countries from the Customers table. This List is passed to the view through the Countries ViewBag property.

Then add Edit view under Views/Home folder and key-in the following markup in it:

@model MVC6Demo.Models.Customer

<html>
<head>
    <title>My First MVC 6 Application</title>
    <style>
        .field-validation-error
        {
            color:red;
        }
        .validation-summary-errors
        {
            color:red;
        }
    </style>
</head>
<body>
<h1>Edit Customer</h1>
<form asp-controller="Home" asp-action="Save" method="post">
<table border="1" cellpadding="10">
<tr>
<td>
<label asp-for="CustomerID">Customer ID :</label>
</td>
<td>
<input asp-for="CustomerID" readonly="readonly" />
<span asp-validation-for="CustomerID"></span>
</td>
</tr>
<tr>
<td>
<label asp-for="CompanyName">Company Name :</label>
</td>
<td>
<input asp-for="CompanyName" />
<span asp-validation-for="CompanyName"></span>
</td>
</tr>
<tr>
<td>
<label asp-for="ContactName">Contact Name :</label>
</td>
<td>
<input asp-for="ContactName" />
<span asp-validation-for="ContactName"></span>
</td>
</tr>
<tr>
<td>
<label asp-for="Country">Country :</label>
</td>
<td>
<select asp-for="Country" 
asp-items="@ViewBag.Countries"></select>
<span asp-validation-for="Country"></span>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save" />
</td>
</tr>
</table>
</form>
<div asp-validation-summary="ValidationSummary.All"></div>
<br />
<a asp-action="Index" asp-controller="Home">Go Back</a>
</body>
</html>

The above markup uses the following Tag Helpers:

  • form
  • label
  • input
  • select
  • field validation and validation summary

The asp-action and asp-controller attributes of the form tag helper are set to Save and Home respectively. This way the form will be POSTed to the Save() action of the HomeController. The asp-for attribute of the label and the input tag helpers specify a model property that is bound with the label and the input field respectively. The asp-items attribute of the select tag helper specify that the <option> elements be generated from the Countries ViewBag property.

The field level validations are displayed by adding <span> elements and setting their asp-validation-for attribute to the appropriate model property. This way the model validation errors will be displayed in the <span> elements. The validation summary is displayed in a <div> element by setting its asp-validation-summary attribute to ValidationSummary.All. The look and feel of the validation tag helpers is controlled through CSS classes - .field-validation-error and .validation-summary-errors (see top of the markup).

Now add Save() action to the HomeController and write the following code to it:

public IActionResult Save(Customer obj)
{
    using (NorthwindDbContext db = 
                  new NorthwindDbContext())
    {
        var query = (from c in db.Customers
                        orderby c.Country ascending
                        select new SelectListItem() 
         { Text = c.Country, Value = c.Country }).Distinct();
        List<SelectListItem> countries = query.ToList();
        ViewBag.Countries = countries;

        if (ModelState.IsValid)
        {
            db.Entry(obj).State = EntityState.Modified;
            db.SaveChanges();
        }
        return View("Edit", obj);
    }
}

The code that fills the Countries ViewBag property is same as before. Then the code checks whether the ModelState is valid using the IsValid property. If the model state is valid the modified Customer details are saved to the database. This is done by setting the State property to Modified and then calling SaveChanges() method.

That's it! Run the application and check if the customer details can be modified successfully.

In this part you instantiated NorthwindDbContext locally. In the next part we will use MVC 6 dependency injection to inject it into the HomeController. Till then keep coding!

READ MORE

If you are tracking the progress of ASP.NET 5, you are probably aware that RC 1 of ASP.NET 5 is now available. Although MVC 5.x is not going away anytime soon, it's good time to get a feel of how MVC 6 application development is going to be. To that end this article explains how a simple MVC 6 application can be developed from ground up using an Empty Project Template. This article is based on the following software:

  • Visual Studio 2015
  • ASP.NET 5 RC 1
  • Entity Framework 7 RC 1

So, make sure you have them installed correctly before you proceed any further.

Let's begin!

Open Visual Studio 2015 and select File > New > Project menu option. In the New Project dialog select ASP.NET Web Application, give some project name and click OK button.

image

In the project template dialog, select Empty under ASP.NET 5 Preview Templates section.

image

The newly created project looks like this in the Solution Explorer:

image

Now, open Project.json file in the Visual Studio editor. The Project.json file is a JSON file and contains all the project configuration such as project dependencies, target frameworks and more. For this example, you need to add certain NuGet packages required to develop and run the application. You can add project dependencies in two ways:

  • Right click on the References node and select Manage NuGet Packages. This is quite similar to MVC 5.x projects. Once added the NuGet package entries are automatically added to the Project.json file.
  • Add NuGet package dependencies directly in the Project.json file.

Let's use the later approach. Modify the dependencies section of Project.json as shown below:

image

Add the project dependencies as shown above and save the Project.json file. At this point Visual Studio automatically installs the dependencies (downloading from NuGet, if necessary) for you. Look for the message like this that confirms this process:

image

We won't go into the details of each and every package in this article. It is suffice to mention that:

  • Microsoft.AspNet.Mvc is the core MVC 6 package
  • Microsoft.AspNet.Mvc.TagHelpers contains tag helpers (discussed later)
  • EntityFramework.MicrosoftSqlServer provides Entity Framework 7 support

Next, right click on the project and select Add > New Item to open the Add New Item dialog.

image

Select ASP.NET Configuration File from the list, name it AppSettings.json and click on Add to add the file to project root folder.

The AppSettings.json file contains all the application specific configuration (no more web.config). Remember the difference between Project.json and AppSettings.json. The former file stores project configuration that is necessary to compile and run the project. The later stores application configuration utilized in the code (connection strings, application settings and so on).

Change the default JSON markup from AppSettings.json to the following:

image

As you might have guessed, we stored database connection string for the Northwind database under Data:DefaultConnection:ConnectionString key.

Now add four folders under the project root namely Models, Views, Controllers and Classes. The purpose of the first three folders is quite obvious. The Classes folder will be used for storing other classes needed in the application such as a DbContext class. You are free to give any other name to this folder (or you may even isolate them in a Class Library).

Then add a class called AppSettings in the Classes folder and write the following code in it:

public class AppSettings
{
    public static string ConnectionString { get; set; }
}

The AppSettings class contains a single static property - ConnectionString - that is intended to hold the database connection string. This property is assigned from the Startup class as discussed shortly. Once assigned you can access the database connection string from anywhere in the application.

Now open Startup.cs file. The code from the Startup class is invoked whenever the application runs. You will find that the Startup class consists of two methods - ConfigureServices() and Configure(). Both of them have special meaning to the framework and hence their name must be kept unchanged.

Modify the Startup file to include a few namespaces and a public constructor.

...
...
using Microsoft.AspNet.Hosting;
using Microsoft.Dnx.Runtime;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Data.Entity;
using MVC6Demo.Classes;

namespace WebApplication1
{
    public class Startup
    {
        public Startup(IHostingEnvironment env,
               IApplicationEnvironment app)
        {
        }

        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure(IApplicationBuilder app)
        {
        }
    }
}

Notice the namespaces at the top. They are required by the code that you will fill in these methods shortly. Also notice that the constructor accepts two parameters - IHostingEnvironment and IApplicationEnvironment. These three methods are executed by the framework in the same order - Startup(), ConfigureServices() and Configure().

Next, add the following code to the Startup() constructor.

public Startup(IHostingEnvironment env,
               IApplicationEnvironment app)
{
    ConfigurationBuilder builder = new ConfigurationBuilder();
    builder.SetBasePath(app.ApplicationBasePath);
    builder.AddJsonFile("appsettings.json");
    IConfigurationRoot config = builder.Build();
    AppSettings.ConnectionString = config.Get<string>
           ("Data:DefaultConnection:ConnectionString");
}

The Startup constructor basically reads the AppSettings.json file and stores the database connection string into the AppSettings class. The ConfigurationBuilder class is responsible for reading and loading the configuration information. The SetBasePath() method sets the base path where the configuration file(s) is located. The ApplicationBasePath means the physical path of the project's root folder.

The AddJsonFile() adds AppSettings.json file to the list of configuration files to be processed. You can have multiple configuration files per project. The Build() method reads the configuration and returns it as an instance of IConfigurationRoot.

To read the configuration settings, you can use Get<T>() method on the IConfigurationRoot object. Notice how the setting to be read is specified using the : notation. Now the database connection string can be accessed from anywhere in the application in typed manner (and without reloading the config files).

Then modify the ConfigureServices() method as shown below:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddEntityFramework().AddSqlServer();
}

The ConfigureServices() method specifies the features that are available to your application. In this case your application is made available the services of the MVC framework and the Entity Framework.

Now modify the Configure() method as shown below:

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

The Configure() method tells the framework that you want to use static files feature and MVC features. The static files feature allows you to access static files such as .htm and .html. If you don't specify this feature and attempt to access the static files you will get an error. Of course, if you don't need this feature you can skip this line.

The UseMvc() call is important because it not only tells the framework that your application is using MVC but also configures the routing system.

 Ok. Now add HomeController to the Controllers folder using the Add New Item dialog. The default HomeController looks like this:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        ViewBag.Message = "Hello from MVC 6!";
        return View();
    }
}

Notice that the Index() action returns IActionResult. The Index() action is quite straightforward and simply sets Message property on the ViewBag.

Then add Home subfolder under Views folder. Right click on the Home folder and open the Add New Item dialog again to add Index view to the project.

Add the following HTML markup to the Index view.

<html>
<head>
    <title>My First MVC 6 Application</title>
</head>
<body>
    <h1>@ViewBag.Message</h1>
</body>
</html>

The Index view simply outputs the Message ViewBag property on the response stream.

Run the application by pressing F5. Your browser should look like this:

image

Although our application is working as expected we didn't use AppSettings class and Entity Framework features. The second part of this article discusses those details. Till then keep coding!

READ MORE

In Part 1 and Part 2 of this article series you developed a wizard in an ASP.NET MVC application using full page postback and Ajax helper respectively. In this final part of this series you will develop a client side wizard using jQuery. The navigation between various wizard steps (Next, Previous) happens without any postback (neither full nor partial). The only step that causes form submission to the server is clicking on the Finish wizard button.

The jQuery version of the wizard uses only one view - Index - that contains all the three steps as three <div> elements. Using jQuery code you show only one <div> at a time giving a wizard appearance to the view.

The HomeController class now contains two overloads of Index() action method as shown below:

public class HomeController : Controller
{
  public ActionResult Index()
  {
    return View();
  }

  [HttpPost]
  public ActionResult Index(Customer obj)
  {
    if (ModelState.IsValid)
    {
      NorthwindEntities db = new NorthwindEntities();
      db.Customers.Add(obj);
      db.SaveChanges();
      return View("Success");
    }
    return View();
  }
}

As you can see the second Index() method is marked with [HttpPost] attribute and accepts Customer parameter. Inside it checks whether there are any model state errors or not. In case of any errors Index view is returned otherwise data is added to the database and the Success view is returned.

Currently, there are no model validations on Customer class.  To add then using data annotations you can create a metadata class as shown below:

public class CustomerMetadata
{
  [Required]
  public string CustomerID { get; set; }
  [Required]
  public string CompanyName { get; set; }
  [Required]
  public string Address { get; set; }
  [Required]
  public string City { get; set; }
  [Required]
  public string Country { get; set; }
  [Required]
  public string PostalCode { get; set; }
  [Required]
  public string ContactName { get; set; }
  [Required]
  public string Phone { get; set; }
  [Required]
  public string Fax { get; set; }
}

[MetadataType(typeof(CustomerMetadata))]
public partial class Customer
{
}

As you can see the CustomerMetadata class defines all the properties that are accepted through the wizard. All the properties are marked with [Required] attribute. The CustomerMetadata class is linked with the Customer model class by creating a partial class and then decorating it with [MetadataType] attribute.

Now, add the Index view to the project. The Index view that contains all these <div> elements looks like this:

@model WizardInMVC.Models.Customer

@{
Layout = null;
}

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
<div id="divBasic">
<h1>Step 1 : Basic Details</h1>
@Html.LabelFor(m=>m.CustomerID)<br />
@Html.TextBoxFor(m=>m.CustomerID)
@Html.ValidationMessageFor(m=>m.CustomerID)<br />
@Html.LabelFor(m=>m.CompanyName)<br />
@Html.TextBoxFor(m=>m.CompanyName)
@Html.ValidationMessageFor(m=>m.CompanyName)
<br />
<input type="button" name="nextBtn" value='Next' />
</div>
<div id="divAddress">
<h1>Step 2 : Address Details</h1>
@Html.LabelFor(m=>m.Address)<br />
@Html.TextBoxFor(m=>m.Address)
@Html.ValidationMessageFor(m=>m.Address)
<br />
@Html.LabelFor(m=>m.City)<br />
@Html.TextBoxFor(m=>m.City)
@Html.ValidationMessageFor(m=>m.City)
<br />
@Html.LabelFor(m=>m.Country)<br />
@Html.TextBoxFor(m=>m.Country)
@Html.ValidationMessageFor(m=>m.Country)
<br />
@Html.LabelFor(m=>m.PostalCode)<br />
@Html.TextBoxFor(m=>m.PostalCode)
@Html.ValidationMessageFor(m=>m.PostalCode)
<br />
<input type="button" name="prevBtn" value='Previous' />
<input type="button" name="nextBtn" value='Next' />
</div>
<div id="divContact">
<h1>Step 3 : Contact Details</h1>
@Html.LabelFor(m=>m.ContactName)<br />
@Html.TextBoxFor(m=>m.ContactName)
@Html.ValidationMessageFor(m=>m.ContactName)
<br />
@Html.LabelFor(m=>m.Phone)<br />
@Html.TextBoxFor(m=>m.Phone)
@Html.ValidationMessageFor(m=>m.Phone)
<br />
@Html.LabelFor(m=>m.Fax)<br />
@Html.TextBoxFor(m=>m.Fax)
@Html.ValidationMessageFor(m=>m.Fax)
<br />
<input type="button" name="prevBtn" value='Previous' />
<input type="submit" name="nextBtn" value='Finish' />
</div>
}
</body>
</html>

As you can see there are three <div> elements namely divBasic, divAddress and divContact. They contain various form fields for the respective wizard step. The markup shown above is essentially the summation of the markups in individual views from the Part 1 and Part 2 examples. This markup is quite straightforward. There is a minor change though - the Previous and Next buttons are of type button rather than submit. This is because these buttons no longer cause form submission. To turn the above markup into a wizard you need to write some jQuery code as shown below:

$(document).ready(function () {
  $("div").hide();
  $("div:first").show();
  $(":button").click(function () {
    var parentDiv = $(this).parent();
    $("div").hide();
    if($(this).val()=="Previous")
    {
      var prevDiv = parentDiv.prev();
      prevDiv.show();
    }
    if ($(this).val() == "Next") {
      var nextDiv = parentDiv.next();
      nextDiv.show();
    }
  });
});

As you can see the above jQuery code first hides all the <div> elements from the view. This is done using the element selector and hide() method. Then the first <div> element is made visible using :first selector and show() method. This way initially when the page loads only the first wizard step will be visible.

The code then wires event handlers for the click event of the Previous and Next buttons. This is done by selecting these buttons using :button selector and then using click() method. Notice that inside the click event handler nowhere we hardcode the IDs of various <div> elements. This way adding or removing a wizard step doesn't need any change in the jQuery code. Inside the click event handler, the code retrieves a reference to the parent <div> element of the button being clicked. This is done using parent() method called on this. The code then hides all the <div> elements. It then checks the button that was clicked - Previous or Next. If Previous button is clicked a reference to the previous <div> element is retrieved using prev() method called on parentDiv variable. Similarly if Next button is clicked, a reference to the next <div> element is retrieved using next() method called on parentDiv. Accordingly, either previous <div> or next <div> is displayed using show() method.

That's it! Test the wizard by running the application and then navigating between wizard steps.

READ MORE
...