Обновление Visual Studio 2017 MVC View Scaffolding до Bootstrap 4

Я только что обновил приложение Visual Studio 2017 ASP.NET MVC 5 с Bootstrap v3 до v4. Я нахожу, что когда я добавляю новое частичное представление редактирования с использованием скаффолдинга, он все еще использует имена классов CSS Bootstrap v3 для формы. Есть ли способ обновить строительные леса для использования BS v4?

редактировать

Кажется, есть некоторая путаница в том, о чем я говорю. В Visual Studio 2017 в проекте MVC в обозревателе решений щелкните правой кнопкой мыши папку "Представления"> "Добавить"> "Показать"> "Представление MVC 5"> нажмите "Добавить". Это вызывает диалог Добавить вид. Я ввожу свое имя просмотра, выбираю Редактировать шаблон и выбираю, для этого примера, LoginVmкак модельный класс. Visual Studio создает эту разметку. Этот процесс называется строительные леса.

@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>

Обратите внимание на используемые классы Bootstrap 3, такие как form-label а также col-md-offset-2, Они были удалены в Bootstrap 4. Точно так же, если бы вы создали новую страницу Layout, она бы сгенерировала панель навигации Bootstrap 3, которая недопустима в Bootstrap 4. Поэтому я спрашиваю, есть ли способ (если не считать пользовательский скаффолдер)) обновить Visual Studio, чтобы остановить вывод конкретной разметки Bootstrap 3 и, в идеале, вместо этого выводить разметку Bootstrap 4?

1 ответ

Обновление пока недоступно, однако для поддержки лесов представления редактирования с помощью Bootstrap 4 в Visual Studio 2017 необходимо отредактировать файл Edit.cs.t4 в "%ProgramFiles%\Microsoft Visual Studio\2017\Community\Common7\IDE". \Extensions\Microsoft\Web\Mvc\ Строительные леса \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" #>

Шаблоны, используемые механизмом скаффолдинга в VS, исправлены. Они находятся в каталоге установки VS (например,%programfiles%\Microsoft Visual Studio\2017\Community\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates\MvcView).

Таким образом, классы Bootstrap 3 зафиксированы в T4-файлах, предоставляемых MS (текущий стандарт - BS3, который в настоящее время добавляется по умолчанию при создании нового веб-проекта MVC). Просто посмотрите на "Edit.cs.t4" в директории, упомянутой выше. Там вы найдете устаревшие классы BS3, такие как "btn-default" (который является btn-вторичным в BS4).

Вы можете создавать свои собственные T4-шаблоны, если хотите. Ссылка MS для этой задачи будет следующей: https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates

Чтобы исправить / обновить навигационную панель проекта ASP .NET MVC 5 до Bootstrap 4, вам необходимо обновить код вручную следующим образом:

  • Просмотры -> Общие -> _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>

Затем, если вы не используете частичный вход в систему, вы можете удалить его. В противном случае измените _LoginPartial.cshtml на:

@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>
}

И наконец, просто удалите следующие строки из Content/Site.css:

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