I've just updated my Visual Studio 2017 ASP.NET MVC 5 application from Bootstrap v3 to v4. I'm finding when I add a new edit partial view using scaffolding, it is still using the Bootstrap v3 CSS class names for the form. Is there a way to update the scaffolding to use BS v4?
Edit
There seems to be some confusion about what I'm talking about.
In Visual Studio 2017, in an MVC project, in Solution Explorer, right click the Views folder > Add > View... > MVC 5 View > Click Add
.
This brings up the Add View dialog. I type my View name, choose the Edit Template and choose, for this example, LoginVm
as the Model class. Visual Studio generates this markup. This process is called scaffolding.
@model Demo.ViewModels.LoginVm
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>LoginVm</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.UserName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.UserName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Notice the Bootstrap 3 classes in use such as form-label
and col-md-offset-2
. These were removed in Bootstrap 4. Similarly, if you were to create a new Layout page, it would generate a Bootstrap 3 Navbar which is invalid in Bootstrap 4.
So I'm asking if there is a way (short of writing a custom scaffolder) to update Visual Studio to stop outputting Bootstrap 3 specific markup and, ideally, output Bootstrap 4 markup instead?
To add a scaffold, right-click on Controllers folder in the Solution Explorer and select Add → New Scaffolded Item. It will display the Add Scaffold dialog.
Upgrade Bootstrap Files Now open the project in Visual Studio and right-click on the project file and select Add > Client-Side Library. The dialog that shows will allow you to search for client-side libraries to include in your application. In this case, we are looking for twitter-bootstrap.
This tutorial teaches you the basics of building an ASP.NET MVC 5 web app using Visual Studio 2017.
In the VS Code File menu, select Preferences then Settings. Expand Extensions then find Docs Scaffolding Extension Configuration.
An update is not yet available, however to support the edit view scaffolding with Bootstrap 4 in Visual Studio 2017, You have to edit the file Edit.cs.t4 in "%ProgramFiles%\Microsoft Visual Studio\2017\Community\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates\MvcView"
<#@ template language="C#" HostSpecific="True" #>
<#@ output extension=".cshtml" #>
<#@ include file="Imports.include.t4" #>
@model <#= ViewDataTypeName #>
<#
// "form-control" attribute is only supported for all EditorFor() in System.Web.Mvc 5.1.0.0 or later versions, except for checkbox, which uses a div in Bootstrap
string boolType = "System.Boolean";
Version requiredMvcVersion = new Version("5.1.0.0");
bool isControlHtmlAttributesSupported = MvcVersion >= requiredMvcVersion;
// The following chained if-statement outputs the file header code and markup for a partial view, a view using a layout page, or a regular view.
if(IsPartialView) {
#>
<#
} else if(IsLayoutPageSelected) {
#>
@{
ViewBag.Title = "<#= ViewName#>";
<#
if (!String.IsNullOrEmpty(LayoutPageFile)) {
#>
Layout = "<#= LayoutPageFile#>";
<#
}
#>
}
<h2><#= ViewName#></h2>
<#
} else {
#>
@{
Layout = null;
}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title><#= ViewName #></title>
</head>
<body>
<#
PushIndent(" ");
}
#>
<#
if (ReferenceScriptLibraries) {
#>
<#
if (!IsLayoutPageSelected && IsBundleConfigPresent) {
#>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")
<#
}
#>
<#
else if (!IsLayoutPageSelected) {
#>
<script src="~/Scripts/jquery-<#= JQueryVersion #>.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<#
}
#>
<#
}
#>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<h4><#= ViewDataTypeShortName #></h4>
<hr />
<#
if (isControlHtmlAttributesSupported) {
#>
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<#
} else {
#>
@Html.ValidationSummary(true)
<#
}
#>
<#
foreach (PropertyMetadata property in ModelMetadata.Properties) {
if (property.Scaffold && !property.IsAssociation) {
if (property.IsPrimaryKey) {
#>
@Html.HiddenFor(model => model.<#= property.PropertyName #>)
<#
} else if (!property.IsReadOnly) {
bool isCheckbox = property.TypeName.Equals(boolType);
#>
<div class="form-group">
<#
if (property.IsForeignKey) {
#>
@Html.LabelFor(model => model.<#= property.PropertyName #>, "<#= GetAssociationName(property) #>", htmlAttributes: new { @class = "col-form-label col-lg-2" })
<#
} else if (!isCheckbox) {
#>
@Html.LabelFor(model => model.<#= property.PropertyName #>, htmlAttributes: new { @class = "col-form-label col-lg-2" })
<#
}
#>
<div class="col-lg-10">
<#
if (property.IsForeignKey) {
#>
<#
if (isControlHtmlAttributesSupported) {
#>
@Html.DropDownList("<#= property.PropertyName #>", null, htmlAttributes: new { @class = "form-control" })
<#
} else {
#>
@Html.DropDownList("<#= property.PropertyName #>", String.Empty)
<#
}
#>
<#
} else if (isControlHtmlAttributesSupported) {
if (isCheckbox) {
#>
<div class="custom-control custom-checkbox">
<#
PushIndent(" ");
#>
@Html.EditorFor(model => model.<#= property.PropertyName #>, new { htmlAttributes = new { @class = "custom-control-input" } })
@Html.LabelFor(model => model.<#= property.PropertyName #>, htmlAttributes: new { @class = "custom-control-label" })
<#
} else if (property.IsEnum && !property.IsEnumFlags) {
#>
@Html.EnumDropDownListFor(model => model.<#= property.PropertyName #>, htmlAttributes: new { @class = "form-control" })
<#
} else {
#>
@Html.EditorFor(model => model.<#= property.PropertyName #>, new { htmlAttributes = new { @class = "form-control" } })
<#
}
} else {
#>
@Html.EditorFor(model => model.<#= property.PropertyName #>)
<#
}
#>
<#
if (isControlHtmlAttributesSupported) {
#>
@Html.ValidationMessageFor(model => model.<#= property.PropertyName #>, "", new { @class = "text-danger" })
<#
} else {
#>
@Html.ValidationMessageFor(model => model.<#= property.PropertyName #>)
<#
}
#>
<#
if (isCheckbox && isControlHtmlAttributesSupported) {
PopIndent();
#>
</div>
<#
}
#>
</div>
</div>
<#
}
}
}
#>
<div class="form-group">
<div class="col-lg-10">
<input type="submit" value="Save" class="btn btn-primary">
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
<#
if(IsLayoutPageSelected && ReferenceScriptLibraries && IsBundleConfigPresent) {
#>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
<#
}
#>
<#
else if(IsLayoutPageSelected && ReferenceScriptLibraries) {
#>
<script src="~/Scripts/jquery-<#= JQueryVersion #>.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<#
}
#>
<#
// The following code closes the tag used in the case of a view using a layout page and the body and html tags in the case of a regular view page
#>
<#
if(!IsPartialView && !IsLayoutPageSelected) {
ClearIndent();
#>
</body>
</html>
<#
}
#>
<#@ include file="ModelMetadataFunctions.cs.include.t4" #>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With