Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update Visual Studio 2017 MVC View Scaffolding to Bootstrap 4

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, LoginVmas 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?

like image 776
xr280xr Avatar asked Apr 03 '18 21:04

xr280xr


People also ask

How do I add scaffold in Visual Studio 2017?

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.

How do I update bootstrap in Visual Studio?

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.

What version of MVC is in Visual Studio 2017?

This tutorial teaches you the basics of building an ASP.NET MVC 5 web app using Visual Studio 2017.

How do you add a scaffold code in Visual Studio?

In the VS Code File menu, select Preferences then Settings. Expand Extensions then find Docs Scaffolding Extension Configuration.


1 Answers

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"

  • Rename the file to "Edit.cs.backup.t4"
  • Create a new file "Edit.cs.t4" and add the following code, this will support new Bootstrap 4 classes and form controls (Bootstrap custom forms and controls):

<#@ 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" #>
like image 158
Max Avatar answered Oct 07 '22 01:10

Max