ASP.NET MVC 5 - Multiple View Models In Single View

This article is about implementing multiple view models in a single view via ASP.NET MVC5 platform.

There is a common scenario, which is being asked quite a lot in ASP.NET MVC platforms. Thus, I have decided to demonstrate my take on this particular scenario. The scenario is as follows; i.e., create a single page, which will display the data from two different view models. Now, the question that arises here is if the ASP.NET MVC platform only attaches a single model to a single view, how can this be achieved? Let me demonstrate how this is done.

Today, I shall be demonstrating about how to utilize multiple view models into a single view in an ASP.NET MVC5 platform.

Following are some prerequisites before you proceed further in this tutorial:

  1. Knowledge of an ASP.NET MVC5.
  2. Knowledge of HTML.
  3. Knowledge of JavaScript.
  4. Knowledge of Bootstrap.
  5. Knowledge of jQuery.
  6. Knowledge of C# programming.

You can download the complete source code for this tutorial or you can follow the step by step discussion below. The sample code is being developed in Microsoft Visual Studio 2015 Enterprise.

Before we jump into the technical working of this scenario, let's build a little conceptual understanding of how will we achieve this scenario in an ASP.NET MVC5 platform. To do achieve this, we will proceed in our code, as shown below i.e.

Step 1 

We have two independent models Register Model & ResultSet Model 

Step 2

Since we know the constraint on ASP.NET MVC platform i.e. we can only attach a single model to a single view. So, here we will create a parent place holder kind of model; i.e., Common Model, and make our actual view models the child to this model, as shown below 
Step 3

After combining our two separate models into a single model, we then can easily attach our common place holder model to our single view, as shown below.

 

Now, let's see, how can we code this.

  1. Create a new MVC Web project and name it MultiModelSingleView.
  2. Create a file LoginObj.cs under Models folder and replace the code given below in it
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Web;  
  5.   
  6. namespace MultiModelSingleView.Models  
  7. {  
  8.     public class LoginObj  
  9.     {  
  10.         public int Id { getset; }  
  11.   
  12.         public string Username { getset; }  
  13.   
  14.         public string Password { getset; }  
  15.     }  

Now, create our view models files -- AccountViewModel.cs, ResultSetViewModel.cs & CommonViewModel.cs -- under Models folder and replace the code given below in each file accordingly, as shown below

Replace the code given below in AccountViewModel.cs file

  1. using System.Collections.Generic;  
  2. using System.ComponentModel.DataAnnotations;  
  3.   
  4. namespace MultiModelSingleView.Models  
  5. {  
  6.     public class AccountViewModel  
  7.     {  
  8.         [Display(Name = "Id")]  
  9.         public int Id { getset; }  
  10.   
  11.         [Display(Name = "Enter Username")]  
  12.         public string Username { getset; }  
  13.   
  14.         [Display(Name = "Enter Password")]  
  15.         public string Password { getset; }  
  16.     }  

Replace the code given below in ResultSetViewModel.cs file i.e.

  1. using System.Collections.Generic;  
  2. using System.ComponentModel.DataAnnotations;  
  3.   
  4. namespace MultiModelSingleView.Models  
  5. {  
  6.     public class ResultSetViewModel  
  7.     {  
  8.         [Display(Name = "Result")]  
  9.         public List<LoginObj> ResultSet { getset; }  
  10.     }  

Replace the code given below in CommonViewModel.cs file i.e.

  1. using System.Collections.Generic;  
  2. using System.ComponentModel.DataAnnotations;  
  3.   
  4. namespace MultiModelSingleView.Models  
  5. {  
  6.     public class CommonViewModel  
  7.     {  
  8.         public AccountViewModel AccountVM { getset; }  
  9.         public ResultSetViewModel ResultSetVM { getset; }  
  10.     }  

In all the snippets given above, we have created our view models according to the conceptual understanding, which we have developed.

Create a new controller file AccountController.cs under Controllers folder and replace the code given below in it

  1. //-----------------------------------------------------------------------  
  2. // <copyright file="HomeController.cs" company="None">  
  3. //     Copyright (c) Allow to distribute this code.  
  4. // </copyright>  
  5. // <author>Asma Khalid</author>  
  6. //-----------------------------------------------------------------------  
  7.   
  8. namespace MultiModelSingleView.Controllers  
  9. {  
  10.     using System;  
  11.     using System.Globalization;  
  12.     using System.Linq;  
  13.     using System.Security.Claims;  
  14.     using System.Threading.Tasks;  
  15.     using System.Web;  
  16.     using System.Web.Mvc;  
  17.     using Microsoft.AspNet.Identity;  
  18.     using Microsoft.AspNet.Identity.Owin;  
  19.     using Microsoft.Owin.Security;  
  20.     using MultiModelSingleView.Models;  
  21.     using System.Collections.Generic;  
  22.     using System.IO;  
  23.     using System.Reflection;  
  24.   
  25.     public class AccountController : Controller  
  26.     {  
  27.   
  28.         #region Register method  
  29.  
  30.         #region GET: /Account/Register  
  31.   
  32.         //  
  33.         // GET: /Account/Register  
  34.         [AllowAnonymous]  
  35.         public ActionResult Register()  
  36.         {  
  37.             // Initialization.  
  38.             CommonViewModel model = new CommonViewModel();  
  39.             model.AccountVM = new AccountViewModel();  
  40.             model.ResultSetVM = new ResultSetViewModel();  
  41.   
  42.             // Get Result  
  43.             model.ResultSetVM.ResultSet = this.LoadData();  
  44.   
  45.             return View(model);  
  46.         }  
  47.  
  48.         #endregion  
  49.  
  50.         #region  POST: /Account/Register  
  51.   
  52.         //  
  53.         // POST: /Account/Register  
  54.         [HttpPost]  
  55.         [AllowAnonymous]  
  56.         [ValidateAntiForgeryToken]  
  57.         public ActionResult Register(CommonViewModel model)  
  58.         {  
  59.             if (ModelState.IsValid)  
  60.             {  
  61.                 // Inserting.  
  62.                 this.StoreData(model.AccountVM.Username, model.AccountVM.Password);  
  63.   
  64.                 // Get Result  
  65.                 model.ResultSetVM = new ResultSetViewModel();  
  66.                 model.ResultSetVM.ResultSet = this.LoadData();  
  67.             }  
  68.   
  69.             // If we got this far, something failed, redisplay form  
  70.             return View(model);  
  71.         }  
  72.  
  73.         #endregion  
  74.  
  75.         #endregion  
  76.  
  77.         #region Helpers  
  78.  
  79.         #region Load Data  
  80.   
  81.         /// <summary>  
  82.         /// Load data method.  
  83.         /// </summary>  
  84.         /// <returns>Returns - Data</returns>  
  85.         private List<LoginObj> LoadData()  
  86.         {  
  87.             // Initialization.  
  88.             List<LoginObj> lst = new List<LoginObj>();  
  89.   
  90.             try  
  91.             {  
  92.                 // Initialization.  
  93.                 string line = string.Empty;  
  94.                 string srcFilePath = "content/files/login_list.txt";  
  95.                 var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);  
  96.                 var fullPath = Path.Combine(rootPath, srcFilePath);  
  97.                 string filePath = new Uri(fullPath).LocalPath;  
  98.                 StreamReader sr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read));  
  99.   
  100.                 // Read file.  
  101.                 while ((line = sr.ReadLine()) != null)  
  102.                 {  
  103.                     // Initialization.  
  104.                     LoginObj infoObj = new LoginObj();  
  105.                     string[] info = line.Split(',');  
  106.   
  107.                     // Setting.  
  108.                     infoObj.Id = Convert.ToInt32(info[0].ToString());  
  109.                     infoObj.Username = info[1].ToString();  
  110.                     infoObj.Password = info[2].ToString();  
  111.   
  112.                     // Adding.  
  113.                     lst.Add(infoObj);  
  114.                 }  
  115.   
  116.                 // Closing.  
  117.                 sr.Dispose();  
  118.                 sr.Close();  
  119.             }  
  120.             catch (Exception ex)  
  121.             {  
  122.                 // info.  
  123.                 Console.Write(ex);  
  124.             }  
  125.   
  126.             // info.  
  127.             return lst;  
  128.         }  
  129.  
  130.         #endregion  
  131.  
  132.         #region Store Data  
  133.   
  134.         /// <summary>  
  135.         /// Store data method.  
  136.         /// </summary>  
  137.         private void StoreData(string username, string password)  
  138.         {  
  139.             // Initialization.  
  140.             LoginObj obj = new LoginObj();  
  141.   
  142.             try  
  143.             {  
  144.                 // Setting.  
  145.                 int idVal = this.LoadData().Select(p => p).ToList().Count > 0  
  146.                             ? (this.LoadData().OrderByDescending(p => p.Id).Select(p => p.Id).FirstOrDefault()) + 1  
  147.                             : 1;  
  148.   
  149.                 // Initialization.  
  150.                 string line = string.Empty;  
  151.                 string srcFilePath = "content/files/login_list.txt";  
  152.                 var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);  
  153.                 var fullPath = Path.Combine(rootPath, srcFilePath);  
  154.                 string filePath = new Uri(fullPath).LocalPath;  
  155.                 StreamWriter sw = new StreamWriter(new FileStream(filePath, FileMode.Append, FileAccess.Write));  
  156.   
  157.                 // Write file.  
  158.                 string content = idVal.ToString() + "," + username + "," + password;  
  159.                 sw.WriteLine(content);  
  160.   
  161.                 // Closing.  
  162.                 sw.Dispose();  
  163.                 sw.Close();  
  164.             }  
  165.             catch (Exception ex)  
  166.             {  
  167.                 // info.  
  168.                 Console.Write(ex);  
  169.             }  
  170.         }  
  171.  
  172.         #endregion  
  173.  
  174.         #endregion  
  175.     }  

Let's break down the code given above, method by method

  1. #region Load Data  
  2.   
  3. /// <summary>  
  4. /// Load data method.  
  5. /// </summary>  
  6. /// <returns>Returns - Data</returns>  
  7. private List<LoginObj> LoadData()  
  8. {  
  9.     // Initialization.  
  10.     List<LoginObj> lst = new List<LoginObj>();  
  11.   
  12.     try  
  13.     {  
  14.         // Initialization.  
  15.         string line = string.Empty;  
  16.         string srcFilePath = "content/files/login_list.txt";  
  17.         var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);  
  18.         var fullPath = Path.Combine(rootPath, srcFilePath);  
  19.         string filePath = new Uri(fullPath).LocalPath;  
  20.         StreamReader sr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read));  
  21.   
  22.         // Read file.  
  23.         while ((line = sr.ReadLine()) != null)  
  24.         {  
  25.             // Initialization.  
  26.             LoginObj infoObj = new LoginObj();  
  27.             string[] info = line.Split(',');  
  28.   
  29.             // Setting.  
  30.             infoObj.Id = Convert.ToInt32(info[0].ToString());  
  31.             infoObj.Username = info[1].ToString();  
  32.             infoObj.Password = info[2].ToString();  
  33.   
  34.             // Adding.  
  35.             lst.Add(infoObj);  
  36.         }  
  37.   
  38.         // Closing.  
  39.         sr.Dispose();  
  40.         sr.Close();  
  41.     }  
  42.     catch (Exception ex)  
  43.     {  
  44.         // info.  
  45.         Console.Write(ex);  
  46.     }  
  47.   
  48.     // info.  
  49.     return lst;  
  50. }  
  51.  
  52. #endregion 

The code given above creates a LoadData() method, which will load the data from a file, if the file contains any data, initially, the file is empty, since we have not registered any account.

  1. #region Store Data  
  2.   
  3. /// <summary>  
  4. /// Store data method.  
  5. /// </summary>  
  6. private void StoreData(string username, string password)  
  7. {  
  8.     // Initialization.  
  9.     LoginObj obj = new LoginObj();  
  10.   
  11.     try  
  12.     {  
  13.         // Setting.  
  14.         int idVal = this.LoadData().Select(p => p).ToList().Count > 0  
  15.                     ? (this.LoadData().OrderByDescending(p => p.Id).Select(p => p.Id).FirstOrDefault()) + 1  
  16.                     : 1;  
  17.   
  18.         // Initialization.  
  19.         string line = string.Empty;  
  20.         string srcFilePath = "content/files/login_list.txt";  
  21.         var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);  
  22.         var fullPath = Path.Combine(rootPath, srcFilePath);  
  23.         string filePath = new Uri(fullPath).LocalPath;  
  24.         StreamWriter sw = new StreamWriter(new FileStream(filePath, FileMode.Append, FileAccess.Write));  
  25.   
  26.         // Write file.  
  27.         string content = idVal.ToString() + "," + username + "," + password;  
  28.         sw.WriteLine(content);  
  29.   
  30.         // Closing.  
  31.         sw.Dispose();  
  32.         sw.Close();  
  33.     }  
  34.     catch (Exception ex)  
  35.     {  
  36.         // info.  
  37.         Console.Write(ex);  
  38.     }  
  39. }  
  40.  
  41. #endregion 

Now, the code given above creates a StoreData(...) method, which will store the data into the file, as we register any new account.

  1. #region GET: /Account/Register  
  2.   
  3.         //  
  4.         // GET: /Account/Register  
  5.         [AllowAnonymous]  
  6.         public ActionResult Register()  
  7.         {  
  8.             // Initialization.  
  9.             CommonViewModel model = new CommonViewModel();  
  10.             model.AccountVM = new AccountViewModel();  
  11.             model.ResultSetVM = new ResultSetViewModel();  
  12.   
  13.             // Get Result  
  14.             model.ResultSetVM.ResultSet = this.LoadData();  
  15.   
  16.             return View(model);  
  17.         }  
  18.  
  19.         #endregion 

The code given above creates our view method Register() HTTP GET, which will simply load our data to ResultSetViewModel. Notice here that now in our view; instead of returning an individual model to the view, we are returning our common view model, since we have attached that with our View.

  1. #region  POST: /Account/Register  
  2.   
  3.         //  
  4.         // POST: /Account/Register  
  5.         [HttpPost]  
  6.         [AllowAnonymous]  
  7.         [ValidateAntiForgeryToken]  
  8.         public ActionResult Register(CommonViewModel model)  
  9.         {  
  10.             if (ModelState.IsValid)  
  11.             {  
  12.                 // Inserting.  
  13.                 this.StoreData(model.AccountVM.Username, model.AccountVM.Password);  
  14.   
  15.                 // Get Result  
  16.                 model.ResultSetVM = new ResultSetViewModel();  
  17.                 model.ResultSetVM.ResultSet = this.LoadData();  
  18.             }  
  19.   
  20.             // If we got this far, something failed, redisplay form  
  21.             return View(model);  
  22.         }  
  23.  
  24.         #endregion 

The code given above creates our view method Register() HTTP POST, which will simply load our data to ResultSetViewModel after storing the register account details received via account view model. Notice here again that now in our view instead of returning an individual model to the view, we are returning our common view model, since we have attached that with our view.

Now, create a new view file Register.cshtml under Views\Account folder and replace the code given below in it

  1. @model MultiModelSingleView.Models.CommonViewModel  
  2. @{  
  3.     ViewBag.Title = "Register";  
  4. }  
  5.   
  6. <h2>@ViewBag.Title.</h2>  
  7.   
  8. @using (Html.BeginForm("Register""Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))  
  9. {  
  10.     @Html.AntiForgeryToken()  
  11.     <h4>Create a new account.</h4>  
  12.     <hr />  
  13.     @Html.ValidationSummary(""new { @class = "text-danger" })  
  14.     <div class="form-group">  
  15.         @Html.LabelFor(m => m.AccountVM.Username, new { @class = "col-md-2 control-label" })  
  16.         <div class="col-md-10">  
  17.             @Html.TextBoxFor(m => m.AccountVM.Username, new { @class = "form-control" })  
  18.         </div>  
  19.     </div>  
  20.     <div class="form-group">  
  21.         @Html.LabelFor(m => m.AccountVM.Password, new { @class = "col-md-2 control-label" })  
  22.         <div class="col-md-10">  
  23.             @Html.PasswordFor(m => m.AccountVM.Password, new { @class = "form-control" })  
  24.         </div>  
  25.     </div>  
  26.   
  27.     <div class="form-group">  
  28.         <div class="col-md-offset-2 col-md-10">  
  29.             <input type="submit" class="btn btn-default" value="Register" />  
  30.         </div>  
  31.     </div>  
  32. }  
  33.   
  34. <h2>Result List</h2>  
  35.   
  36. @if (Model.ResultSetVM.ResultSet != null)  
  37. {  
  38.     for (int i = 0; i < Model.ResultSetVM.ResultSet.Count; i++)  
  39.     {  
  40.         <div class="row">  
  41.             <div class="col-md-2">  
  42.                 <p>@Model.ResultSetVM.ResultSet[i].Id</p>  
  43.             </div>  
  44.             <div class="col-md-2">  
  45.                 <p>@Model.ResultSetVM.ResultSet[i].Username</p>  
  46.             </div>  
  47.             <div class="col-md-2">  
  48.                 <p>@Model.ResultSetVM.ResultSet[i].Password</p>  
  49.             </div>  
  50.         </div>  
  51.     }  
  52. }  
  53.   
  54. @section Scripts {  
  55.     @Scripts.Render("~/bundles/jqueryval")  

In the code given above, we have created a single view, which is attached to our common model and displays data from the result set view model and sends register account request via account view model.

Create a our standard view layout file _Layout.cshtml under Views\Shared folder. Replace the code given below in it i.e.

  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8" />  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  6.     <title>@ViewBag.Title</title>  
  7.     @Styles.Render("~/Content/css")  
  8.     @Scripts.Render("~/bundles/modernizr")  
  9.   
  10.     <!-- Font Awesome -->  
  11.     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" />  
  12. </head>  
  13. <body>  
  14.     <div class="navbar navbar-inverse navbar-fixed-top">  
  15.         <div class="container">  
  16.             <div class="navbar-header">  
  17.                 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">  
  18.                     <span class="icon-bar"></span>  
  19.                     <span class="icon-bar"></span>  
  20.                     <span class="icon-bar"></span>  
  21.                 </button>  
  22.             </div>  
  23.         </div>  
  24.     </div>  
  25.     <div class="container body-content">  
  26.         @RenderBody()  
  27.         <hr />  
  28.         <footer>  
  29.             <center>  
  30.                 <p><strong>Copyright © @DateTime.Now.Year - <a href="http://www.asmak9.com/">Asma's Blog</a>.</strong> All rights reserved.</p>  
  31.             </center>  
  32.         </footer>  
  33.     </div>  
  34.   
  35.     @Scripts.Render("~/bundles/jquery")  
  36.     @Scripts.Render("~/bundles/bootstrap")  
  37.   
  38.   
  39.     @RenderSection("scripts", required: false)  
  40. </body>  
  41. </html> 

Now, execute the project and register some accounts. You will be able to see the output, as shown below.



Conclusion

In this article, you learned about mapping multiple view models to a single UI view in an ASP.NET MVC5 platform. You also learned about conceptual understanding behind this scenario along with how to achieve this scenario from the coding perspective.