ASP.NET MVC使用Bootstrap系列(3)——使用Bootstrap 组件
Bootstrap为我们提供了十几种的可复用组件,包括字体图标、下拉菜单、导航、警告框、弹出框、输入框组等。在你的Web Application中使用这些组件,将为用户提供一致和简单易用的用户体验。
Bootstrap组件本质上是结合了各种现有Bootstrap元素以及添加了一些独特Class来实现。Bootstrap元素我在上一篇文章中涉及到,具体可以参考《ASP.NET MVC使用Bootstrap系列(2)——使用Bootstrap CSS和HTML元素》。
在这篇博客中,我将继续探索Bootstrap丰富的组件以及将它结合到ASP.NET MVC项目中。
Bootstrap 导航条
Bootstrap导航条作为"明星组件"之一,被使用在大多数基于Bootstrap Framework的网站上。为了更好的展示Bootstrap导航条,我在ASP.NET MVC的_Layout.cshtml布局页创建一个fixed-top导航条,当然它是响应式的——在小尺寸、低分辨率的设备上打开时,它将会只展示一个按钮并带有3个子菜单,当点击按钮时垂直展示他们。在网页上显示如下:
在移动设备上显示如下:
在ASP.NET MVC默认的_Layouts.cshtml布局页中已经帮我们实现了上述功能,打开它对其稍作修改,如下代码片段所示:
<div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink("Northwind Traders", "Index", "Home", null, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse"> @Html.Partial("_BackendMenuPartial") @Html.Partial("_LoginPartial") </div> </div> </div>
其中class为.navbar-fixed-top可以让导航条固定在顶部,还可包含一个 .container 或 .container-fluid 容器,从而让导航条居中,并在两侧添加内补(padding)
注意,我使用了2个局部视图(_BackendMenuPartial和LoginPartial)来生成余下的导航条(使用.navbar-collapse类在低分辨率设备中折叠),其中局部视图逻辑是基于当前访问的用户是否登陆来控制是否显示。
首先,添加如下代码在_BackendMenuPartial视图中,这将会在导航条中产生一个搜索框:
@using (Html.BeginForm("Index", "Search", FormMethod.Post, new { @class = "navbar-form navbar-left", role = "search" })) { <div class="form-group"> @Html.TextBox("searchquery", "", new { @id = "searchquery", @class = "form-control input-sm", placeholder = "Search" }) @Html.Hidden("fromcontroller", @ViewContext.RouteData.Values["controller"], new { @id = "fromcontroller" }) </div> <button type="submit" class="btn btn-default btn-xs">GO</button> }
因为Bootstrap导航条作为整个网站的公共部分,要实现快速搜索那么必须要知道当前所处于哪个Controller,这样才能提高检索效率。所以上述代码中,增加了一个Id为fromcontroller隐藏字段,代表当前访问的Controller。
当点击搜索时,发送HTTP POST请求到Index Action下。然后根据传递过来的fromcontroller来swith到具体的Action来执行搜索,具体的搜索逻辑代码如下:
public ActionResult Index(string searchquery, string fromcontroller) { switch (fromcontroller) { case "Products": return RedirectToAction("SearchProductsResult", new { query = searchquery }); case "Customers": return RedirectToAction("SearchCustomersResult", new { query = searchquery }); case "Employees": return RedirectToAction("SearchEmployeesResult", new { query = searchquery }); } return View(); }
具体搜索的Action如下:
public ActionResult SearchProductsResult(string query) { ViewBag.SearchQuery = query; var results = _context.Products.Where(p => p.ProductName.Contains(query)).ToList(); return View(results); } public ActionResult SearchCustomersResult(string query) { ViewBag.SearchQuery = query; var results = _context.Customers.Where(p => p.CompanyName.Contains(query) || p.ContactName.Contains(query) || p.City.Contains(query) || p.Country.Contains(query)).ToList(); return View(results); } public ActionResult SearchEmployeesResult(string query) { ViewBag.SearchQuery = query; var results = _context.Employees.Where(p => p.FirstName.Contains(query) || p.LastName.Contains(query) || p.Notes.Contains(query)).ToList(); return View(results); }
列表组
列表组是灵活又强大的组件,不仅能用于显示一组简单的元素,还能结合其他元素创建一组复杂的定制内容。上面的搜索为我们重定向到Result视图,在此视图中,它为我们显示了搜索结果,为了更好的展示结果,我们可以使用列表组来显示搜索到的产品,视图中的代码如下所示:
@model IEnumerable<Bootstrap.Data.Models.Products> @{ ViewBag.Title = "搜索产品"; } <div class="container"> <div class="page-header"> <h1>产品结果 <small>搜索条件: "@ViewBag.SearchQuery"</small></h1> </div> <ul class="list-group"> @foreach (var item in Model) { <a href="@Url.Action("Edit","Products", new { id=@item.ProductID})" class="list-group-item">@item.ProductName <span class="badge">@item.UnitsInStock</span></a> } </ul> </div>
在上述代码中,为无序列表(<ul>)的class设置为list-group,并且每一个<li>的class为list-group-item,这是一个最简单的列表组。
徽章
徽章用来高亮条目,可以很醒目的显示新的或者未读的条目数量,为一个元素设置徽章仅仅只需要添加<span>元素并设置它的class为badge。所以,在上述代码的基础上稍作修改,添加徽章,表示库存个数,如下HTML所示:
<a href="@Url.Action("Edit","Products", new { id=@item.ProductID})" class="list-group-item"> @item.ProductName <span class="badge">@item.UnitsInStock</span> </a>
显示的结果为如下截图:
媒体对象
媒体对象组件被用来构建垂直风格的列表比如博客的回复或者推特。在Northwind数据库中包含一个字段ReportTo表示Employee向另一个Employee Report。使用媒体对象可以直观来表示这种关系。在视图中的代码如下所示:
<div class="container"> <div class="page-header"> <h1>员工搜索结果: <small>搜索条件: "@ViewBag.SearchQuery"</small></h1> </div> @foreach (var item in Model) { <div class="media"> <a class="pull-left" href="@Url.Action("Edit", "Employees", new { id = @item.EmployeeID })"> <img class="media-object" src="@Url.Content("~/Images/employees/" + @item.EmployeeID + ".png")" alt="@item.FirstName" width="64" height="64"> </a> <div class="media-body"> <h4 class="media-heading">@item.FirstName @item.LastName</h4> @item.Notes @foreach (var emp in @item.ReportingEmployees) { <div class="media"> <a href="#" class="pull-left"> <img alt="@emp.FirstName" src="@Url.Content("~/Images/employees/" + @emp.EmployeeID + ".png")" class="media-object" width="64" height="64"> </a> <div class="media-body"> <h4 class="media-heading">@emp.FirstName @emp.LastName</h4> @emp.Title </div> </div> } </div> </div> } </div>
显示结果如下:
可以看到,媒体对象组件是由一系列class为media、media-heading、media-body、media-object的元素组合而成,其中media-object用来表示诸如图片、视频、声音等媒体对象。
注:.pull-left 和 .pull-right 这两个类以前也曾经被用在了媒体组件上,但是,从 v3.3.0 版本开始,他们就不再被建议使用了。.media-left 和 .media-right 替代了他们,不同之处是,在 html 结构中, .media-right 应当放在 .media-body 的后面。
页头
当用户访问网页时,Bootstrap页头可以为用户提供清晰的指示。Bootstrap页头本质上是一个<h1>元素被封装在class为page-header的<div>元素中。当然你也可以利用<small>元素来提供额外的关于页面的信息,同时Bootstrap为页头添加了水平分隔线用于分隔页面,如下HTML即为我们构建了页头:
<div class="page-header"> <h1>员工搜索结果: <small>搜索条件: "@ViewBag.SearchQuery"</small></h1> </div>
路径导航
路径导航(面包屑)在Web 设计中被用来表示用户在带有层次的导航结构中当前页面的位置。类似于Windows资源管理器。如下HTML所示:
<ol class="breadcrumb"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("Manage", "Index", "Manage")</li> <li class="active">Products</li> </ol>
在上面HTML代码中,通过指定有序列表(<ol>)的class为breadcrumb,每一个子路径用<li>来表示,其中通过设置<li>的class为active代表当前所处的位置。
各路径间的分隔符已经自动通过 CSS 的 :before 和 content 属性添加了。
分页
分页用来分隔列表内容,特别是显示大量数据时通过分页可以有效的减少服务器压力和提高用户体验,如下截图使用分页来显示产品列表:
要完成上述的分页,需要安装PagedList.Mvc程序包,在NuGet控制台中安装即可:Install-PackagePagedList.Mvc
然后修改Action,它需要接受当然的页码,它是一个可空的整数类型变量,然后设置PageSize等于5,表示每页显示5条记录,如下代码所示:
public ActionResult Index(int? page) { var models = _context.Products.Project().To<ProductViewModel>().OrderBy(p => p.ProductName); int pageSize = 5; int pageNumber = (page ?? 1); return View(models.ToPagedList(pageNumber, pageSize)); }
在View中,使用PagedList动态生成分页控件:
<div class="container"> <div class="page-header"> <h1>Products <small>Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount</small></h1> </div> <ol class="breadcrumb"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("Manage", "Index", "Manage")</li> <li class="active">Products</li> </ol> <table class="table table-striped table table-hover"> <thead> <tr> <th> Product Name </th> <th> Unit Price </th> <th> Units In Stock </th> <th> Discontinued </th> <th></th> </tr> </thead> <tbody> @foreach (var item in Model) { <tr class="@item.Status"> <td> @Html.ActionLink(item.ProductName, "Edit", new { id = item.ProductID }) </td> <td> @Html.DisplayFor(modelItem => item.UnitPrice) </td> <td> @Html.DisplayFor(modelItem => item.UnitsInStock) </td> <td> @Html.DisplayFor(modelItem => item.Discontinued) </td> <td> @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ }) </td> </tr> } </tbody> </table> @Html.PagedListPager(Model, page => Url.Action("Index", new { page }), PagedListRenderOptions.ClassicPlusFirstAndLast) <p> <a href="@Url.Action("Create","Products")" class="btn btn-sm btn-primary" role="button">Create New</a> </p> </div>
输入框组
输入框组为用户在表单输入数据时可以提供更多的额外信息。Bootstrap的输入框组为我们在Input元素的前面或者后面添加指定class的块,这些块可以是文字或者字体图标,如下所示:
<div class="form-group"> <div class="col-sm-2 input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-phone-alt"></span> </span> @Html.TextBox("txtPhone","1194679215",new { @class = "form-control" }) </div> </div>
上面的输入框组合中,在Textbox的左边放置了一个带有字体图标Phone的灰色块,结果如下所示:
不仅可以使用字体图标,还可以使用纯文本来显示信息,如下所示在Textbox右边放置了固定的邮箱域名:
<div class="form-group"> <div class="col-sm-4 input-group"> @Html.TextBox("txtEmail","1194679215", new { @class = "form-control" }) <span class="input-group-addon">@@qq.com</span> </div> </div>
当然也可以在Input元素的两边同时加上块,如下代码所示:
<div class="form-group"> <div class="col-sm-2 input-group"> <span class="input-group-addon">¥</span> @Html.TextBox("txtMoney","100",new { @class = "form-control" }) <span class="input-group-addon">.00</span> </div> </div>
按钮式下拉菜单
按钮式下拉菜单顾名思义,一个按钮可以执行多种action,比如既可以Save,也可以Save之后再打开一个新的Form继续添加记录,如下所示:
<div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <div class="btn-group"> <button type="submit" class="btn btn-primary btn-sm">Save</button> <button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown"> <span class="caret"></span> <span class="sr-only">Toggle Dropdown</span> </button> <ul class="dropdown-menu" role="menu"> <li><a href="#" id="savenew">Save & New</a></li> <li class="divider"></li> <li><a href="#" id="duplicate">Duplicate</a></li> </ul> </div> </div> </div>
警告框
Bootstrap警告组件通常被用作给用户提供可视化的反馈,比如当用户Save成功后显示确认信息、错误时显示警告信息、以及其他的提示信息。
Bootstrap提供了4中不同风格的警告,如下所示:
<div class="container"> <div class="page-header"> <h1>Alerts </h1> </div> <ol class="breadcrumb"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>Bootstrap</li> <li class="active">Alerts</li> </ol> <div class="alert alert-success"><strong>Success. </strong></div> <div class="alert alert-info"><strong>Info.</strong></div> <div class="alert alert-warning"><strong>Warning!</strong></div> <div class="alert alert-danger"><strong>Danger!</strong></div> </div>
可关闭的警告框可以让用户点击右上角的X来关闭,你可以使用alter-dismissible 类:
<div class="alert alert-warning alert-Dismissible" role="alert"> <button type="button" class="close" data-dismiss="alert"> <span aria-hidden="true">×</span><span class="sr-only">Close</span> </button> <strong>Alert!</strong>这是可关闭的Alter </div>
进度条
进度条在传统的桌面应用程序比较常见,当然也可以用在Web上。通过这些简单、灵活的进度条,可以为当前工作流程或动作提供实时反馈。Bootstrap为我们提供了许多样式的进度条。
-
基本进度条
基本进度条是一种纯蓝色的进度条,添加一个class 为sr-only 的<span>元素在进度条中是比较好的实践,这样能让屏幕更好的读取进度条的百分比。
<div class="row"> < h4>基本进度条</h4> <div class="progress"> <div class="progress-bar" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width: 80%;"> <span class="sr-only">80%完成</span> </div> </div> </div>
-
上下文情景变化进度条
上下文情景变化进度条组件使用与按钮和警告框相同的类,根据不同情境展现相应的效果。
- progress-bar-success
- progress-bar-info
- progress-bar-warning
- progress-bar-danger
-
条纹动画效果进度条
为了让进度条更加生动,可以为其添加条纹效果,在进度条<div>中添加class为progress-striped。当然让进度条看起来有动画,可以再为其指定active的class在<div>上,如下所示:
<div class="progress progress-striped active"> <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 40%"> <span class="sr-only">40% 完成 (success)</span> </div> </div>
-
使用SignalR动态更新进度条
SignalR是ASP.NET的库,可以用来双向实时通信,在ASP.NET MVC项目中使用SignalR:1.首先通过NuGet来安装SignalR Nuget Package:
Install-Package Microsoft.AspNet.SignalR
2.在项目里新建一个名为Hubs的文件夹,并添加ProgressbarHub类,如下代码所示:
public class ProgressbarHub : Hub { public void SendProgress() { for (int i = 0; i <= 100; i++) { Thread.Sleep(50); Clients.Caller.sendMessage(i + "%"); } } }
SendProgress方法很简单,向客户端发送消息(通过自增变量输出0—100)
3.在布局页添加对SignalR虚拟路径的引用:
<script src="~/signalr/hubs"></script>
4.在打包文件中,包含 SignalR.js
bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js", "~/Scripts/jquery.signalR-2.2.0" ));
5.在客户端建立与服务端的双向通信:
@section scripts { <script type="text/javascript" language="javascript"> $(document).ready(function () { //$(".progress-bar").width('0%'); }); $("#start").click(function () { $(".progress-bar").width('0%'); var progressNotifier = $.connection.progressbarHub; progressNotifier.client.sendMessage = function (message) { updateProgress(message); }; $.connection.hub.start().done(function () { progressNotifier.server.sendProgress(); }); }); function updateProgress(message) { $(".progress-bar").width(message); $("#progressbarTitle").html(message + ' Complete'); } </script> }
你可以看到,上述代码先初始化和服务端的连接,然后设置被服务端调用的客户端方法updateProgress。
最后,我们建立了和服务端的连接并且启动了服务端的操作,你可以看到,我们调用了sendProgress方法——这已经在ProgressbarHub类中定义好了。这样当点击ID为start的按钮时动态为进度条更新了0-100的数值。
小结
在这篇博客中,探索了Bootstrap中丰富的组件,并将它结合到ASP.NET MVC项目中。通过实例可以发现,这类组件本质上是结合了各种现有Bootstrap元素以及添加了一些独特Class来实现。
更多的Bootstrap组件请参见:http://v3.bootcss.com/components/