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:
- Knowledge of ASP.NET MVC5.
- Knowledge of HTML.
- Knowledge of JavaScript.
- Knowledge of Bootstrap.
- Knowledge of jQuery.
- 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.
- Create a new MVC Web project and name it MultiModelSingleView.
- Create a file LoginObj.cs under Models folder and replace the code given below in it
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
-
- namespace MultiModelSingleView.Models
- {
- public class LoginObj
- {
- public int Id { get; set; }
-
- public string Username { get; set; }
-
- public string Password { get; set; }
- }
- }
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
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations;
-
- namespace MultiModelSingleView.Models
- {
- public class AccountViewModel
- {
- [Display(Name = "Id")]
- public int Id { get; set; }
-
- [Display(Name = "Enter Username")]
- public string Username { get; set; }
-
- [Display(Name = "Enter Password")]
- public string Password { get; set; }
- }
- }
Replace the code given below in ResultSetViewModel.cs file i.e.
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations;
-
- namespace MultiModelSingleView.Models
- {
- public class ResultSetViewModel
- {
- [Display(Name = "Result")]
- public List<LoginObj> ResultSet { get; set; }
- }
- }
Replace the code given below in CommonViewModel.cs file i.e.
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations;
-
- namespace MultiModelSingleView.Models
- {
- public class CommonViewModel
- {
- public AccountViewModel AccountVM { get; set; }
- public ResultSetViewModel ResultSetVM { get; set; }
- }
- }
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
-
-
-
-
-
-
-
- namespace MultiModelSingleView.Controllers
- {
- using System;
- using System.Globalization;
- using System.Linq;
- using System.Security.Claims;
- using System.Threading.Tasks;
- using System.Web;
- using System.Web.Mvc;
- using Microsoft.AspNet.Identity;
- using Microsoft.AspNet.Identity.Owin;
- using Microsoft.Owin.Security;
- using MultiModelSingleView.Models;
- using System.Collections.Generic;
- using System.IO;
- using System.Reflection;
-
- public class AccountController : Controller
- {
-
- #region Register method
-
- #region GET: /Account/Register
-
-
-
- [AllowAnonymous]
- public ActionResult Register()
- {
-
- CommonViewModel model = new CommonViewModel();
- model.AccountVM = new AccountViewModel();
- model.ResultSetVM = new ResultSetViewModel();
-
-
- model.ResultSetVM.ResultSet = this.LoadData();
-
- return View(model);
- }
-
- #endregion
-
- #region POST: /Account/Register
-
-
-
- [HttpPost]
- [AllowAnonymous]
- [ValidateAntiForgeryToken]
- public ActionResult Register(CommonViewModel model)
- {
- if (ModelState.IsValid)
- {
-
- this.StoreData(model.AccountVM.Username, model.AccountVM.Password);
-
-
- model.ResultSetVM = new ResultSetViewModel();
- model.ResultSetVM.ResultSet = this.LoadData();
- }
-
-
- return View(model);
- }
-
- #endregion
-
- #endregion
-
- #region Helpers
-
- #region Load Data
-
-
-
-
-
- private List<LoginObj> LoadData()
- {
-
- List<LoginObj> lst = new List<LoginObj>();
-
- try
- {
-
- string line = string.Empty;
- string srcFilePath = "content/files/login_list.txt";
- var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
- var fullPath = Path.Combine(rootPath, srcFilePath);
- string filePath = new Uri(fullPath).LocalPath;
- StreamReader sr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read));
-
-
- while ((line = sr.ReadLine()) != null)
- {
-
- LoginObj infoObj = new LoginObj();
- string[] info = line.Split(',');
-
-
- infoObj.Id = Convert.ToInt32(info[0].ToString());
- infoObj.Username = info[1].ToString();
- infoObj.Password = info[2].ToString();
-
-
- lst.Add(infoObj);
- }
-
-
- sr.Dispose();
- sr.Close();
- }
- catch (Exception ex)
- {
-
- Console.Write(ex);
- }
-
-
- return lst;
- }
-
- #endregion
-
- #region Store Data
-
-
-
-
- private void StoreData(string username, string password)
- {
-
- LoginObj obj = new LoginObj();
-
- try
- {
-
- int idVal = this.LoadData().Select(p => p).ToList().Count > 0
- ? (this.LoadData().OrderByDescending(p => p.Id).Select(p => p.Id).FirstOrDefault()) + 1
- : 1;
-
-
- string line = string.Empty;
- string srcFilePath = "content/files/login_list.txt";
- var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
- var fullPath = Path.Combine(rootPath, srcFilePath);
- string filePath = new Uri(fullPath).LocalPath;
- StreamWriter sw = new StreamWriter(new FileStream(filePath, FileMode.Append, FileAccess.Write));
-
-
- string content = idVal.ToString() + "," + username + "," + password;
- sw.WriteLine(content);
-
-
- sw.Dispose();
- sw.Close();
- }
- catch (Exception ex)
- {
-
- Console.Write(ex);
- }
- }
-
- #endregion
-
- #endregion
- }
- }
Let's break down the code given above, method by method
- #region Load Data
-
-
-
-
-
- private List<LoginObj> LoadData()
- {
-
- List<LoginObj> lst = new List<LoginObj>();
-
- try
- {
-
- string line = string.Empty;
- string srcFilePath = "content/files/login_list.txt";
- var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
- var fullPath = Path.Combine(rootPath, srcFilePath);
- string filePath = new Uri(fullPath).LocalPath;
- StreamReader sr = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read));
-
-
- while ((line = sr.ReadLine()) != null)
- {
-
- LoginObj infoObj = new LoginObj();
- string[] info = line.Split(',');
-
-
- infoObj.Id = Convert.ToInt32(info[0].ToString());
- infoObj.Username = info[1].ToString();
- infoObj.Password = info[2].ToString();
-
-
- lst.Add(infoObj);
- }
-
-
- sr.Dispose();
- sr.Close();
- }
- catch (Exception ex)
- {
-
- Console.Write(ex);
- }
-
-
- return lst;
- }
-
- #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.
- #region Store Data
-
-
-
-
- private void StoreData(string username, string password)
- {
-
- LoginObj obj = new LoginObj();
-
- try
- {
-
- int idVal = this.LoadData().Select(p => p).ToList().Count > 0
- ? (this.LoadData().OrderByDescending(p => p.Id).Select(p => p.Id).FirstOrDefault()) + 1
- : 1;
-
-
- string line = string.Empty;
- string srcFilePath = "content/files/login_list.txt";
- var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
- var fullPath = Path.Combine(rootPath, srcFilePath);
- string filePath = new Uri(fullPath).LocalPath;
- StreamWriter sw = new StreamWriter(new FileStream(filePath, FileMode.Append, FileAccess.Write));
-
-
- string content = idVal.ToString() + "," + username + "," + password;
- sw.WriteLine(content);
-
-
- sw.Dispose();
- sw.Close();
- }
- catch (Exception ex)
- {
-
- Console.Write(ex);
- }
- }
-
- #endregion
Now, the code given above creates a StoreData(...) method, which will store the data into the file, as we register any new account.
- #region GET: /Account/Register
-
-
-
- [AllowAnonymous]
- public ActionResult Register()
- {
-
- CommonViewModel model = new CommonViewModel();
- model.AccountVM = new AccountViewModel();
- model.ResultSetVM = new ResultSetViewModel();
-
-
- model.ResultSetVM.ResultSet = this.LoadData();
-
- return View(model);
- }
-
- #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.
- #region POST: /Account/Register
-
-
-
- [HttpPost]
- [AllowAnonymous]
- [ValidateAntiForgeryToken]
- public ActionResult Register(CommonViewModel model)
- {
- if (ModelState.IsValid)
- {
-
- this.StoreData(model.AccountVM.Username, model.AccountVM.Password);
-
-
- model.ResultSetVM = new ResultSetViewModel();
- model.ResultSetVM.ResultSet = this.LoadData();
- }
-
-
- return View(model);
- }
-
- #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
- @model MultiModelSingleView.Models.CommonViewModel
- @{
- ViewBag.Title = "Register";
- }
-
- <h2>@ViewBag.Title.</h2>
-
- @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
- {
- @Html.AntiForgeryToken()
- <h4>Create a new account.</h4>
- <hr />
- @Html.ValidationSummary("", new { @class = "text-danger" })
- <div class="form-group">
- @Html.LabelFor(m => m.AccountVM.Username, new { @class = "col-md-2 control-label" })
- <div class="col-md-10">
- @Html.TextBoxFor(m => m.AccountVM.Username, new { @class = "form-control" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(m => m.AccountVM.Password, new { @class = "col-md-2 control-label" })
- <div class="col-md-10">
- @Html.PasswordFor(m => m.AccountVM.Password, new { @class = "form-control" })
- </div>
- </div>
-
- <div class="form-group">
- <div class="col-md-offset-2 col-md-10">
- <input type="submit" class="btn btn-default" value="Register" />
- </div>
- </div>
- }
-
- <h2>Result List</h2>
-
- @if (Model.ResultSetVM.ResultSet != null)
- {
- for (int i = 0; i < Model.ResultSetVM.ResultSet.Count; i++)
- {
- <div class="row">
- <div class="col-md-2">
- <p>@Model.ResultSetVM.ResultSet[i].Id</p>
- </div>
- <div class="col-md-2">
- <p>@Model.ResultSetVM.ResultSet[i].Username</p>
- </div>
- <div class="col-md-2">
- <p>@Model.ResultSetVM.ResultSet[i].Password</p>
- </div>
- </div>
- }
- }
-
- @section Scripts {
- @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.
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>@ViewBag.Title</title>
- @Styles.Render("~/Content/css")
- @Scripts.Render("~/bundles/modernizr")
-
- <!-- Font Awesome -->
- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" />
- </head>
- <body>
- <div class="navbar navbar-inverse navbar-fixed-top">
- <div class="container">
- <div class="navbar-header">
- <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- </div>
- </div>
- </div>
- <div class="container body-content">
- @RenderBody()
- <hr />
- <footer>
- <center>
- <p><strong>Copyright © @DateTime.Now.Year - <a href="http://www.asmak9.com/">Asma's Blog</a>.</strong> All rights reserved.</p>
- </center>
- </footer>
- </div>
-
- @Scripts.Render("~/bundles/jquery")
- @Scripts.Render("~/bundles/bootstrap")
-
-
- @RenderSection("scripts", required: false)
- </body>
- </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.