Lesson 5 - Form handling in ASP.NET Core MVC
In the previous lesson, First web application in ASP.NET Core MVC, we tried the MVC architecture in practice
and learned how to pass data from the model to the view. We said that we use a
special collection (mostly ViewBag
) to do so. But there's also a
second way and that's to connect the model directly to the View
.
This technique is called model binding. This is very useful
when working with forms and we're gonna try it in today's ASP.NET Core tutorial.
We'll program a simple calculator.
Let's create a new ASP.NET Core Web Application named
MVCCalculator
. Even though we could start with an empty template,
we'll now choose the MVC template.

This way, the folders for the MVC components will be generated for us, together with the routes and configurations that we've done manually last time. A sample project including several sliders and even the famous EU cookie message will be also generated. You can try to run it. We didn't use it last time in order to better understand how the MVC works and so it didn't distract us unnecessarily.

We won't need this project and therefore we'll remove the contents of the
Models/
, Controllers/
, and Views/
folders
in the Solution Explorer, but keep the _ViewImports.cshtml
file,
otherwise, tag helpers (see below) won't work properly. If we started with an
empty project like last time, we'd have to add this file manually. Don't use
only Calculator
as the project name, as it'd conflict with the name
of our class.
Let's show how our finished calculator will look like:

Model
Let's start with the model again which will be the Calculator
class. Add it to the Models/
folder. We'll add several public
properties to the model, particularly, two input numbers, the selected
operation, and the result. The last property will be a list of the
SelectListItem
type that will include possible operations for the
view. It'll be rendered as the <select>
HTML element later.
We'll fill the list straight in the constructor. Don't forget to add
using Microsoft.AspNetCore.Mvc.Rendering
.
public class Calculator { public int Number1 { get; set; } public int Number2 { get; set; } public double Result { get; set; } public string Operation { get; set; } public List<SelectListItem> PossibleOperations { get; set; } public Calculator() { PossibleOperations = new List<SelectListItem>(); PossibleOperations.Add(new SelectListItem { Text = "Add", Value = "+", Selected = true }); PossibleOperations.Add(new SelectListItem { Text = "Subtract", Value = "-" }); PossibleOperations.Add(new SelectListItem { Text = "Multiply", Value = "*" }); PossibleOperations.Add(new SelectListItem { Text = "Divide", Value = "/" }); } }
The Text
property of the SelectListItem
class is
the label of the option the user can see. The Value
is the value
that is sent to the server (it shouldn't contain any non-alphanumeric characters
except for dashes or underscores). We can also set the Selected
property to indicate whether the item should be selected when the page is
loaded.
The only thing left is a method with some logic that, according to the
selected Operation
and values in Number1
and
Number2
, calculates the Result
:
public void Calculate() { switch (Operation) { case "+": Result = Number1 + Number2; break; case "-": Result = Number1 - Number2; break; case "*": Result = Number1 * Number2; break; case "/": Result = Number1 / Number2; break; } }
The result is stored to the Result
property after calling the
method. We could also return the result, as we did in the previous project with
the random numbers. But for our further intentions with binding, this will be
more useful.
We have the model ready, let's add the controller.
Controller
We'll have only one controller in our application. You surely remember that
the controller wires up the model (logic) and view (HTML template). We'll add a
new Empty Controller and name it HomeController
. So it'll be called
when we open the homepage of our application. Let's move to its code and edit
the Index()
method as follows:
public IActionResult Index() { Calculator calculator = new Calculator(); return View(calculator); }
When we open the page, the Index()
method is called, we already
know that. At this time, we create a new model instance, which is still the same
thing we did in the previous lesson. However, this time, we pass the model to
the view as a parameter. Don't forget to add
using MVCCalculator.Models
again.
View
We'll generate the view for the Index()
action. We'll do this as
always by clicking anywhere in the method by the right mouse button and choosing
Add View. As the Template, choose Create, and set the Model class to
Calculator
.

The template allows us to pre-generate some code right to the view, this technique is called scaffolding. The Create template generates a view with a form for the properties of the selected model, wired to this model to create a new model instance. Now when we run the app it looks like this:
We can see that Visual Studio generated a total of 4 inputs for the numbers,
result, and operation. However, we'd like to specify the operation using the
<select>
element and print the result into the HTML
<p>
paragraph instead of a form field.
For this reason, let's move to Index.cshtml
and change it to the
following form:
@model MVCCalculator.Models.Calculator @{ ViewData["Title"] = "Calculator"; } <head> <title>@ViewData["Title"]</title> </head> <body> <h2>Index</h2> <h4>Calculator</h4> <hr /> <div class="row"> <div class="col-md-4"> <form asp-action="Index"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Number1" class="control-label"></label> <br /> <input asp-for="Number1" class="form-control" /> <span asp-validation-for="Number1" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Number2" class="control-label"></label> <br /> <input asp-for="Number2" class="form-control" /> <span asp-validation-for="Number2" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Operation" class="control-label"></label> <br /> @Html.DropDownListFor(model => model.Operation, new SelectList(Model.PossibleOperations, "Value", "Text")) <span asp-validation-for="Operation" class="text-danger"></span> </div> <div class="form-group"> <input type="submit" value="Calculate" class="btn btn-default" /> </div> <p style="font-size: 2em;">@Model.Result</p> </form> </div> </div> @section Scripts { @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} } </body>
We've made only minimum changes compared to the original template. At the
very beginning of the template, we set the type of the model to which the view
is bound. Next, we set the page title and the subtitle. Note that since we don't
insert the template into a layout, we added the <head>
and
<body>
elements into it. Next, there's a form generated by
Visual Studio and we've only edited it.
We add individual editing fields for the model properties the following way:
<div class="form-group"> <label asp-for="PropertyName" class="control-label"></label> <br /> <input asp-for="PropertyName" class="form-control" /> <span asp-validation-for="PropertyName" class="text-danger"></span> </div>
The asp-for
attributes are called tag helpers by which ASP.NET
Core can generate an appropriate control element for our property. E.g. a
DatePicker is rendered for dates and so on. The asp-validation-for
attributes insert a space for the error message in the case when the user fills
the field incorrectly. This is again detected from the property data type and
everything is completely automatized. A minor disadvantage is that we pass
properties to the helpers as strings, which you've certainly noticed.
Fortunately, Visual Studio is still able to verify such code.
You can see that we combine tag helpers with the older approach of inserting
controls using IHtmlHelper
(@Html
). Not all the
controls are currently supported by tag helpers, sometimes we can't avoid this
solution. However, we prefer to wire form elements to the model properties using
tag helpers and asp-for
rather than at-signs. We want the HTML
template to look as much as HTML code as possible
To make the tag helpers work in your project, you need to have a file called
_ViewImports.cshtml
in it with the following contents. If you have
followed the tutorial, the file is already included in the project. If you
accidentally deleted this file or started with an empty project, you can create
it now:
@using MVCCalculator @using MVCCalculator.Models @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
At the bottom of the page, we'll print the Result
property of
the model into an HTML <p>
paragraph, so we can display it to
the user.
Our form now looks like this:

Once the form is submitted, nothing happens yet. We'll continue next time.
In the next lesson, Data processing and validations in ASP.NET Core MVC, we'll finish the app. If you've made a mistake somewhere, you can also download the complete project code in the next lesson.