ASP.NET MVC程序设计教程(第3版)
上QQ阅读APP看书,第一时间看更新

1.3 本章示例的布局和创建办法

在后续的章节中,各种布局页、导航页以及每个例子的运行和调试办法都非常相似,这一节我们先学习如何创建本书各章示例公用的布局页,然后再以本章为例,介绍各章示例的创建和运行办法。

1.3.1 创建多个区域公用的布局页

在ASP.NET MVC中,控制器文件和视图文件默认分别保存在Controllers和Views文件夹下,这种默认的项目结构可满足大多数Web应用项目的需求。但是,当应用程序具有大量控制器,而每个控制器又可能与若干个视图关联时,默认的项目结构可能不实用,比如不同的模块无法仅仅通过一个_ViewStart.cshtml文件来引用不同的默认布局页、各模块之间无法有效分离等。为了解决这些问题,可通过在项目中添加区域(Area)的办法,将大型Web应用程序划分为各自独立的模块。

在Mvc5Examples解决方案中,我们可以将每一章的示例都看作一个独立的模块,这样就把整个应用程序划分为多个区域,区域名分别定义为Chapter01、Chapter02、……、Chapter10。这样做的好处是:由于每个区域都有自己的Controllers、Models和Views文件夹以及_ViewStart.cshtml文件,因此,这样做既可以让模块功能各自独立,又可以让这些不同的模块共享相同的资源(如图像文件、.css文件、js文件等),同时还能在某个模块中调用其他模块的功能。

所有区域默认都保存在项目根目录的Areas子文件夹下。

1.添加区域

下面以添加Chapter01为例,说明添加区域的办法。

在【解决方案资源管理器】中,鼠标右击项目名,单击【添加】→【区域】命令,在弹出的窗口中,输入区域名称为“Chapter01”,单击【添加】按钮。

此时系统将自动检查项目根目录下是否已经存在Areas文件夹,如果不存在Areas文件夹,则自动创建该文件夹,然后在该文件夹下创建Chapter01区域;如果已经存在Areas文件夹,则自动在Areas文件夹下创建Chapter01区域。

在Chapter01区域中,系统会自动创建Controllers子文件夹、Models子文件夹、Views子文件夹以及web.config文件和其他相关的文件。

有一点需要注意,项目根目录下的Web.config文件首字母是大写字母(保存整个项目的配置),而区域中Views子文件夹下的web.config文件首字母是小写字母(保存对应区域下的模块配置)。

添加区域后,系统会自动在Global.asax文件中,通过AreaRegistration类注册所有添加的区域。打开项目中的Global.asax文件,可看到下面的代码:

  AreaRegistration.RegisterAllAreas();

正是由于这行代码的作用,MVC才能正确找到项目中添加的所有区域。

2.添加分部页(_AreasPartialRef.cshtml文件)

_AreasPartialRef.cshtml文件用于保存布局页引用的CSS和脚本,这样可避免在每个布局页中都重复定义它。

在Shared子文件夹下添加一个文件名为“_AreasPartialRef.cshtml”的分部页文件,然后将该文件改为下面的内容:

  @Styles.Render("~/Content/themes/base/jquery-ui")
  @*
      下面的代码是将文件直接拖放到此处添加的,不是手工键入的。
      另外,不要用Styles.Render实现,否则所有3D例子加载时都会出现界面短暂停顿的现象
  *@
  <link href="~/Content/bootstrap.css" rel="stylesheet" />
  <link href="~/Content/bootstrap-theme.css" rel="stylesheet" />
  <style>
      body { margin-top: 2px; margin-bottom: 2px; }
      #demo { margin-right: -15px; }
          #demo .list-group-item { font-size: 14px; margin-left: -33px;
                     margin-right: -33px; padding-top: 7px; padding-bottom: 7px; }
              #demo .list-group-item:first-child { margin-top: -15px; }
              #demo .list-group-item:last-child { margin-bottom: -15px; }
  </style>
  
  @*
      由于子视图(分部视图)中不能使用@section指定脚本放置的位置,为了能在视图以及分部视图中都
      能调用布局页引用的脚本,需要将这些引用放到head块内,而不是放到body块的末尾。
  *@
  @Scripts.Render("~/bundles/modernizr")
  @Scripts.Render("~/bundles/jquery")
  @Scripts.Render("~/bundles/jqueryui")
  @*Ajax帮助器是利用jQuery的Ajax来实现的,所以需要添加下面的引用*@
  @Scripts.Render("~/bundles/jquery/unobtrusive-ajax")
  @*输入验证需要添加下面的引用(也可以用到时再在相应页面中添加,而不是添加到此处)*@
  @Scripts.Render("~/bundles/jquery/validate")
  @*将下面的引用放在最后,是为了确保在调用Bootstrap之前先调用jQuery*@
  @Scripts.Render("~/bundles/bootstrap")

在这个文件的代码中,需要关注以下两个方面。

(1)下面的2行代码

  <link href="~/Content/bootstrap.css" rel="stylesheet" />
  <link href="~/Content/bootstrap-theme.css" rel="stylesheet" />

是通过将文件直接从【解决方案资源管理器】中拖放到_AreasHeadPartial.cshtml文件中而得到的,而不是直接键入这些代码。另外,之所以通过<link.../>来实现,而不是通过@Style.Render(...)来实现,是为了避免加载3D页面的例子时界面出现短暂停顿的现象。

(2)该文件中的所有代码都全部存放在布局页的<head>与<head/>之间,这是因为本书的示例有些是通过视图实现的,有些则是通过分部视图实现的,由于子视图或者分部视图中不能使用@section指定脚本放置的位置,为了能在视图以及分部视图中都能调用布局页引用的脚本,因此需要将这些引用全部都放到head块内,而不是放到body块的末尾。

3.添加分部页(_AreasPartialAjax.cshtml文件)

_AreasPartialAjax.cshtml文件用于保存各章示例导航页调用的Ajax和jQuery UI的Accordion方法,这样可避免在每个导航文件中都重复定义它。

鼠标右击项目根目录Views文件夹下的Shared子文件夹,选择【添加】→【新建项】命令,在弹出的窗口中选择【MVC 5分部页(Razor)】模板,将名称改为“_AreasPartialAjax.cshtml”,单击【添加】按钮,然后将该文件改为下面的内容:

  @*
      将Ajax功能保存到一个单独文件中是因为有些页面并不需要它,
      比如呈现三维图形的主界面就不需要这个文件。另外,这样也容易看出相关的代码
  *@
  <style>
      body { margin-top: 2px; margin-bottom: 2px; }
      .accordionDemo { margin-right: -15px; padding-bottom: 0; }
      .accordionDemo :first-child { margin-top: 0; }
      .accordionDemo .list-group { margin-bottom: 0; border-radius:0; }
      .accordionDemo .list-group-item { font-size: 14px; margin-left: -34px;
                         margin-right: -34px; padding-top: 7px; padding-bottom: 7px;}
      .accordionDemo .list-group-item:first-child { margin-top: -15px; }
      .accordionDemo .list-group-item:last-child { margin-bottom: -15px; }
      .accordionDemo .list-group-item:hover { background-color: #fbe77a; color: 
red; }
      .accordionDemo .list-group-item:focus{ color: red; }
  </style>
  @{
      var ajaxOptions = new AjaxOptions
      {
          LoadingElementId = "loading",
          UpdateTargetId = "bodyContent",
          OnFailure = "OnFailure"
      };
      TempData["AjaxOptions"] = ajaxOptions;
  }
  <script>
      function OnFailure(xhr, textStatus, errorThrown) {
          $("body").html(xhr.responseText);
      }
  </script>

4.添加布局页(_AreasLayout.cshtml文件)

_AreasLayout.cshtml是本书所有区域(Areas)中的示例共同使用的布局页。

所有区域共用的布局页一般保存在项目根目录下的Views/Shared文件夹下,某个区域专用的布局页则保存在该区域内的Views/Shared文件夹下。在某个区域中创建的视图页,既可以引用整个项目公用的布局页,也可以引用本区域内定义的布局页。

用前面介绍的添加布局页的办法,在项目根目录下的Views/Shared文件夹下添加一个文件名为“_AreasLayout.cshtml”的文件,并将其改为下面的内容:

  <!DOCTYPE html>
  <html>
  <head>
      <meta charset="utf-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>@ViewBag.Title</title>
      @Html.Partial("~/Views/Shared/_AreasPartialRef.cshtml")
      @Html.Partial("~/Views/Shared/_AreasPartialAjax.cshtml")
  </head>
  <body>
      @{
          int chapter = ViewContext.ViewBag.Chapter;
      }
      <div class="container">
          <div class="panel panel-default" style="min-height: 350px;">
              <div class="panel-heading">
                  <span>第@(chapter)章</span>
                  <span class="pull-right">@Html.ActionLink("返回主页", "MainIndex",
                         "Home", new { area = "" }, null)</span>
                  <span id="loading" class="pull-right"
   style="display: none; color: red; margin-right:20px;">(正在加载,请稍等...)</span>
              </div>
              <div class="panel-body">
                  <div class="row">
                      <div class="col-md-3">
                          @Html.Partial(string.Format("ch{0:d2}Demos", chapter))
                      </div>
                      <div id="bodyContent" class="col-md-9">
                          @RenderBody()
                      </div>
                  </div>
              </div>
          </div>
      </div>
  </body>
  </html>

在后续的章节中,我们还会详细介绍布局页中代码的含义和具体用法,这里只需要关注如何链接到各章示例的默认页面即可。

1.3.2 创建本章示例使用的布局页和导航页

创建Chapter01区域之后,就可以在该区域中编写第1章的示例代码了。

1.添加本章示例使用的默认页面

当首次转到某一章的示例页面时,当用户还没有选择某个例子时,可以先显示一个默认的页面,如在该页面中介绍本章应该掌握的内容等。

Chapter01区域中ch01NavDemos子文件夹下的ch01Index.cshtml文件用于实现该区域的默认页面。

在【解决方案资源管理器】中,鼠标右击Chapter01区域下的Controllers文件夹,选择【添加】→【控制器】命令,此时系统会首先弹出如图1-16所示的基架(Scalffolder)模板,让开发人员选择使用哪种基架模板来添加控制器。

图1-16 选择基架模板

选择【MVC 5控制器—空】模板,在后续的弹出窗口中,将控制器名称改为“ch01NavDemosController”,单击【添加】按钮,如图1-17所示。

图1-17 添加控制器

注意:控制器名称必须带Controller后缀,这是ASP.NET MVC的命名规定。

此时,系统就会自动在Controllers文件夹下添加一个文件名为“ch01NavDemosController.cs”的文件,并自动在该文件中添加一个Index操作方法。同时,系统还会自动在该区域的Views文件夹下创建一个名为“ch01NavDemos”的子文件夹(ch01NavDemosController去掉Controller后缀得到的名称)。

将ch01NavDemosController.cs文件中的Index方法改为下面的内容:

  public class ch01NavDemosController : Controller
  {
      public ActionResult Index(string id)
      {
          return View(id);
      }
  }

鼠标右击Index操作方法,选择【添加视图】,在弹出的窗口中,将视图名称改为“ch01Index”,勾选“使用布局页”,如图1-18所示,单击【添加】按钮。

图1-18 添加视图

此时,系统就会自动在ch01NavDemos文件夹下添加一个文件名为“ch01Index.cshtml”的文件。双击该文件,将其改为下面的内容:

  @{
      ViewBag.Title = "ch01Index";
  }
  <div class="jumbotron">
      <h2 class="text-danger">第1章 概述</h2>
      <p class="text-primary">本章重点:MVC项目的创建和配置,布局页的创建和代码设计。</p>
  </div>

2.修改本章布局页

在区域中首次添加视图时,系统会自动在对应区域的Shared文件夹下创建一个_Layout.cshtml文件,将该文件换名为“_ch01Layout.cshtml”(这样做的目的仅仅是为了能明确看出是哪一章使用的布局页,但这并不是必需的步骤),然后将代码改为下面的内容:

  <!DOCTYPE html>
  <html>
  <head>
      <meta charset="utf-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>@ViewBag.Title</title>
      <link href="~/Content/bootstrap.css" rel="stylesheet" />
      @Scripts.Render("~/bundles/modernizr")
      @Scripts.Render("~/bundles/jquery")
      @Scripts.Render("~/bundles/bootstrap")
  </head>
  <body>
      <div class="container">
          <nav class="navbar navbar-default">
              <div class="navbar-header">
                  <div class="navbar-brand">第1章</div>
              </div>
              <div class="navbar-collapse">
                  @Html.Partial("ch01NavDemos")
                  <ul class="nav navbar-nav navbar-right">
                      <li>@Html.ActionLink("返回主页", "MainIndex", "Home",
                           new { area = "" }, new { @class = "navbar-link" })</li>
                  </ul>
              </div>
          </nav>
      </div>
      <div class="container">
          <div class="panel panel-primary">
              <div class="panel-body">
                  @RenderBody()
              </div>
          </div>
      </div>
  </body>
  </html>

3.修改本章的_ViewStart.cshtml文件

打开Chapter01区域中的_ViewStart.cshtml文件,将其改为下面的内容:

  @{
      Layout = "~/Areas/Chapter01/Views/Shared/_ch01Layout.cshtml";
  }

这样一来,Chapter01区域内的所有视图除非明确指定引用的是那个布局页,否则默认都会将_ch01Layout.cshtml作为它所引用的布局页。

4.添加本章示例导航页

用前面介绍的添加分部页的办法,在Chapter01区域的Shared子文件夹下添加一个文件名为ch01NavDemos.cshtml的文件,然后将其改为下面的内容:

  <ul class="nav navbar-nav">
      <li>@Html.ActionLink("例1-各章布局示意", "Index", "ch01NavDemos",
           new { id = "LayoutDemo" }, null)</li>
      <li>@Html.ActionLink("例2-获取Web服务器信息", "Index", "ch01NavDemos",
           new { id = "ServerInfo" }, null)</li>
  </ul>

5.观察本章导航页和默认页面的运行效果

在快捷工具栏中,选择【Internet Explorer】选项,然后按<F5>键运行应用程序(调试模式),或者按<Ctrl>+<F5>键运行应用程序(非调试模式),通过主页的菜单导航到本章后,就会在IE 11.0浏览器中看到程序运行的效果,如图1-19所示。

图1-19 本章示例导航的运行效果

完成这些步骤以后,就可以在ch01NavDemos文件夹下添加本章示例的代码了。

1.3.3 添加本章示例代码

本章的例子有两个用途:一是让读者观察本书各章示例的布局,二是让读者观察呈现当前网页的Web服务器配置。

1.观察各章示例的主页布局

本书所有章节的示例源程序都采用以下的布局形式(根据需要,可以同时用两个导航区,也可以只使用其中的一个导航区)。

Demos导航区(左侧导航区):利用区域中的超链接导航到主窗口,即在主窗口中显示基本用法示例的运行结果。

NavDemos导航区(上方导航区):其结果也是在“主窗口”中显示对应示例的运行效果。另外,在该导航区的右侧,有一个可返回到主页的链接。

为了让示例导航的代码修改起来更方便,我们将在导航区显示的所有链接内容都保存在独立的分部页中。例如,第1章只在上方显示导航区,导航文件名为“ch01NavDemos.cshtml”;第2章仅在左侧显示导航区,导航文件名为“ch02Demos.cshtml”;而第3章则演示了同时显示两个导航区的情况,导航文件名分别为“ch03Demos.cshtml”和“ch03NavDemos.cshtml”。

下面通过例子演示各章布局示意图。

【例1-1】演示各章示例的布局,运行效果如图1-20所示。

图1-20 LayoutDemo.cshtml文件的运行效果

该例子的源程序请参看ch01NavDemos文件夹下的LayoutDemo.cshtml文件,此处不再列出源代码。

2.观察承载当前网页的Web服务器环境信息

下面的例子演示如何通过System.Web.Helps命名空间下的ServerInfo方法,显示当前网页使用的ASP.NET Web Pages版本以及该页面请求的Web服务器的环境信息。

【例1-2】显示承载当前网页的Web服务器环境信息,在IE 11.0浏览器中运行的效果,如图1-21所示。

图1-21 ServerInfo.cshtml文件的运行效果

该例子的源程序见ServerInfo.cshtml文件,在这个文件中只有下面2行代码:

  <h3 class="text-center">获取承载当前网页的Web服务器环境信息</h3>
  @ServerInfo.GetHtml()