Update Visual Studio 2017 MVC View Scaffolding to

2020-03-01 12:14发布

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?

3条回答
甜甜的少女心
2楼-- · 2020-03-01 12:29

The templates used by the scaffolding engine in VS are fixed. They are located in your VS install dir (e.g. %programfiles%\Microsoft Visual Studio\2017\Community\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates\MvcView).

So the Bootstrap 3 classes are fixed in the T4-files provided by MS (current standard is BS3 which is added by default currently when creating a new MVC web project). Just have a look at "Edit.cs.t4" in the dir mentioned above. You will find deprecated BS3 classes like "btn-default" (which is btn-secondary in BS4) in there.

You can create your own custom T4-templates if you like. The MS ref for this task would be: https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates

查看更多
我命由我不由天
3楼-- · 2020-03-01 12:43

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" #>

查看更多
Animai°情兽
4楼-- · 2020-03-01 12:44

To fix/update the ASP .NET MVC 5 project navbar to Bootstrap 4 you have to update your code manually as follows:

  • Views -> Shared -> _Layout.cshtml.

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container">
    @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
    <button type="button" class="navbar-toggler" data-toggle="collapse" data-target=".navbar-collapse" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="navbar-collapse collapse">
      <ul class="nav navbar-nav mr-auto">
          <li class="nav-item">@Html.ActionLink("Home", "Index", "Home", null, new { @class = "nav-link" })</li>
          <li class="nav-item">@Html.ActionLink("About", "About", "Home", null, new { @class = "nav-link" })</li>
          <li class="nav-item">@Html.ActionLink("Contact", "Contact", "Home", null, new { @class = "nav-link" })</li>
      </ul>
      @Html.Partial("_LoginPartial")
    </div>
  </div>
</nav>

Then if you don’t use the Login partial, you can remove it. Otherwise, change the _LoginPartial.cshtml to:

@using Microsoft.AspNet.Identity
@if (Request.IsAuthenticated)
{
  using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" }))
  {
  @Html.AntiForgeryToken()
  <ul class="nav navbar-nav">
    <li class="nav-item">
        @Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage", @class = "nav-link" })
    </li>
    <li class="nav-item"><a class="nav-link" href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
  </ul>
  }
}
else
{
  <ul class="nav navbar-nav">
    <li class="nav-item">@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink", @class = "nav-link" })</li>
    <li class="nav-item">@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink", @class = "nav-link" })</li>
  </ul>
}

And last just remove the next lines from Content/Site.css:

/*delete this*/
body {
    padding-top: 50px;
    padding-bottom: 20px;
}

查看更多
登录 后发表回答