此教程将讨论ASP.NET MVC 4 Web应用程序里的移动特性。对于此教程,可以使用 Visual Studio Express 2012 或者 Visual Web Developer 2010 Express Service Pack 1 ("Visual Web Developer 或者 VWD"). 如果你已经有了专业版本的 Visual Studio 你也可以使用。
开始之前,确保你已经安装了以下列出的必需项。
- Visual Studio Express 2012 (推荐) 或者 Visual Studio Web Developer Express SP1。 Visual Studio 2012 包含了 ASP.NET MVC 4。如果你用的是 Visual Web Developer 2010,你还得安装 ASP.NET MVC 4 。
你还需要一个移动浏览器模拟器。下面哪个都行:
- Windows 7 Phone Emulator. (本教程里大多数截图都是用的这个模拟器)
- 更改用户代理字符串模拟iPhone。见 这篇 博客。
- Opera Mobile Emulator
- Apple Safari ,必须已经为iPhone设置了用户代理字符串。如何在Safari里为"iPhone"设置用户代理字符串,见David Alison的博客 How to let Safari pretend it's IE 。
本文配套的C#源代码的Visual Studio 工程文件见下:
要生成什么东西
在初学者工程中提供的简单会议列表程序里添加移动特性。 下面的截图演示了在Windows 7 Phone Emulator里看到的完整程序的页签。要简化键盘输入,见 Keyboard Mapping for Windows Phone Emulator 。
你可以通过设置用户代理字符串在IE9或10,FireFox或Chrome里开发你的移动应用程序。下图演示使用IE来模拟iPhone的完整教程。可以用IE F-12开发者工具和 Fiddler tool 来帮你调试程序。
将学到的技能
以下是你将要学到的东西:
-
ASP.NET MVC 4 模板是如何使用 HTML5的
viewport
属性和自适应呈现来改善移动设备上的显示效果。 - 如何创建移动专用视图。
- 如何创建视图开关让用户在应用程序的移动视图和桌面视图间进行切换。
开始吧
点击此链接下载初学者工程的会议列表程序。然后在Windows资源管理器中,右击 MvcMobile.zip 文件并选择属性。在 MvcMobile.zip 属性对话框中,点击“解除锁定”按钮。(解除锁定防止你在使用从网上下载的.zip文件时弹出安全警告)
解压 MvcMobile.zip 文件并在Visual Studio中打开 MvcMobile.sln 文件。
按下 CTRL+F5 运行程序,它将显示在桌面浏览器中。启动移动浏览器模拟器,复制会议程序的URL到模拟器中,然后点击 Browse by tag 链接。如果你用的是 Windows Phone 模拟器,在URL框中点击,按下暂停(Pause)键获取键盘访问。下图演示 AllTags 视图 (点击了 Browse by tag)。
这个页面在移动设备上非常具备可读性。点击 ASP.NET 链接。
ASP.NET 标签页非常杂乱。例如Date列就很难看。接下来的教程里会教大家创建专门为移动浏览器定制的版本,那个看起来就好看多了。
注意: 目前移动缓存引擎里有个Bug。在运行产品程序前,要先安装 Fixed DisplayModes nugget 包。Bug的修复详见 ASP.NET MVC 4 Mobile Caching Bug Fixed 。
CSS 媒体查询
CSS 媒体查询 是对媒体类型的CSS扩展。它们能让你为特定的浏览器(用户代理)重写默认的CSS规则。一个常见的用于移动浏览器的CSS规则是定义屏幕的最大大小。 创建完ASP.NET MVC 4 Internet工程后,Content\Site.css 文件包含了以下媒体查询:
@media only screen and (max- 850px) {
如果浏览器窗口宽度是 850 像素或更小,那它将会使用这个媒体块中的CSS规则。像这样使用CSS媒体查询为小屏幕浏览器(如移动浏览器)提供更好的HTML显示内容,默认的CSS规则用于桌面浏览器的较宽显示设计。
Viewport 元标签
大多数移动浏览器都定义了虚拟的浏览器窗口宽度(视口viewport),这个宽度比移动设备的真是宽度要大得多。这使得移动浏览器在虚拟显示内适应整个网页。用户就可以在感兴趣的内容上放大。但是,如果将视口宽度设为实际的设备宽度的话,是不需要放大的,因为内容已经适应移动浏览器了。
ASP.NET MVC 4布局文件中的viewport <meta>
标签将视口设置成设备的宽度。
<meta name="viewport" content="width=device-width">
测试CSS媒体查询和Viewport Meta标签的效果
在编辑器中打开 Views\Shared\_Layout.cshtml 文件,把 viewport <meta>
标签注释掉。
@*<meta name="viewport" content="width=device-width">*@
在编辑器中打开 MvcMobile\Content\Site.css 文件,将媒体查询中的最大宽度改为0像素。这将会防止CSS规则用在移动浏览器中。
@media only screen and (max- 0px) { ...
保存设置并在移动浏览器模拟器中浏览会议程序。下图中的小文字就是移除viewport <meta>
标签后的结果。没有 viewport <meta>
标签,浏览器缩小到默认视口宽度
(大多数移动浏览器是850 像素或更宽)
撤销更改——在布局文件中取消注释 viewport <meta>
标签并将Site.css文件中的媒体查询恢复为850像素。保存更改,刷新移动浏览器验证一下移动友好显示是否已恢复。
viewport <meta>
标签和 CSS 媒体查询并不是ASP.NET MVC 4特有的,你可以在任何web程序中利用这些特性。不过在创建新的ASP.NET
MVC 4工程时,它们现在已经嵌入到生成的文件中了。
要了解更多关于 viewport <meta>
标签,参见 A
tale of two viewports — part two.
下一节,你将看到如何提供移动浏览器特有视图。
重写视图,布局和部分视图
ASP.NET MVC 4的一个重要新特性是可以为通用的移动浏览器、个别移动浏览器或者任何特定浏览器重写任何视图(包括布局和部分视图)的机制。要提供特定移动视图,可以复制一个视图文件并在文件名中添加.Mobile。例如,要创建一个移动索引视图,可以复制 Views\Home\Index.cshtml 到 Views\Home\Index.Mobile.cshtml。
本节将创建一个特定移动布局文件。
开始前,先复制 Views\Shared\_Layout.cshtml 到 Views\Shared\_Layout.Mobile.cshtml。打开 _Layout.Mobile.cshtml 并把标题从 MVC4 Conference 改为 Conference (Mobile).
在每一个 Html.ActionLink
调用处,移除每一个ActionLink链接里的 "Browse by" 。下面代码演示完整的移动布局文件体。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> <div id="title"> <h1> Conference (Mobile)</h1> </div> <div id="menucontainer"> <ul id="menu"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("Date", "AllDates", "Home")</li> <li>@Html.ActionLink("Speaker", "AllSpeakers", "Home")</li> <li>@Html.ActionLink("Tag", "AllTags", "Home")</li> </ul> </div> @RenderBody() @Scripts.Render("~/bundles/jquery") @RenderSection("scripts", required: false) </body> </html>
复制 Views\Home\AllTags.cshtml 文件到 Views\Home\AllTags.Mobile.cshtml。打开新文件并将<h2>
元素从“Tags”更改为
"Tags (M)":
<h2>Tags (M)</h2>
用桌面浏览器和移动浏览器模拟器分别浏览该标签页。移动浏览器模拟器显示出了这两处更改。
相反,桌面显示没任何变化。
特定浏览器视图
除了特定移动和特定桌面视图,还可以创建个别浏览器视图。例如,可以创建专用于iPhone浏览器的视图。本节将创建一个用于iPhone浏览器和AllTags视图的iPhone版本布局。
打开 Global.asax 文件添加以下代码到 Application_Start
方法。
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone") { ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf ("iPhone", StringComparison.OrdinalIgnoreCase) >= 0) });
这段代码定义了一个新的显示模式,名为iPhone,它将匹配每一个传入的请求。如果传入的请求与你定义的条件相匹配(也就是,如果用户代理包含了字符串”iPhone“), ASP.NET MVC 将寻找名字包含”iPhone“后缀的视图。
在代码里,右击 DefaultDisplayMode
,选择”解析“,然后选择 using
System.Web.WebPages;
. 将添加 System.Web.WebPages
命名空间,就是 DisplayModes
和DefaultDisplayMode
类定义的地方。
另外,可以手工添加下面的 using
部分。
using System.Web.WebPages;
完整的 Global.asax 文件内容见下:
using System; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using System.Web.WebPages; namespace MvcMobile { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone") { ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf ("iPhone", StringComparison.OrdinalIgnoreCase) >= 0) }); AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } } }
保存更改。复制 MvcMobile\Views\Shared\_Layout.Mobile.cshtml 文件到 MvcMobile\Views\Shared\_Layout.iPhone.cshtml。打开新文件并把 h1
头从 Conference
(Mobile)
改为 Conference (iPhone)。
复制 MvcMobile\Views\Home\AllTags.Mobile.cshtml 文件到 MvcMobile\Views\Home\AllTags.iPhone.cshtml。在新文件里,把 <h2>
元素里的
"Tags (M)" 改为 "Tags (iPhone)".
运行程序。运行移动浏览器模拟器,确保用户代理设为”iPhone“,浏览到 AllTags 视图。下面的截图显示Safari浏览器里呈现的 AllTags 视图。你可以在这里下载 Safari for Windows 。
本节我们看到了如何创建移动布局和视图和如何创建特定设备的布局视图,如iPhone。下节来讨论jQuery Mobile如何控制移动视图。
使用jQuery Mobile
jQuery Mobile 类库提供了一个可以运行在所有主要移动浏览器的用户接口框架。 jQuery Mobile 应用了支持CSS和JavaScript的逐步增强。逐步增强允许所有浏览器显示网页的基本内容,同时允许更多强大的浏览器和设备具有更丰富的显示。jQuery Mobile包括的JavaScript 和CSS 文件不用更改任何标记就能使得元素具有适应移动浏览器的样式。
本节将安装 jQuery.Mobile.MVC NuGet 包, 它将安装 jQuery Mobile 和 视图开关小工具。
开始之前,先删除之前创建的 Shared\_Layout.Mobile.cshtml 和 Shared\_Layout.iPhone.cshtml 文件。
重命名 Views\Home\AllTags.Mobile.cshtml 和 Views\Home\AllTags.iPhone.cshtml 文件为 Views\Home\AllTags.iPhone.cshtml.hide 和 Views\Home\AllTags.Mobile.cshtml.hide.。由于文件已经不再有.cshtml 扩展名,所以他们不会被 ASP.NET MVC 运行时使用来呈现 AllTags 视图。
按如下说明安装 jQuery.Mobile.MVC NuGet 包:
-
从”工具“菜单,选择”库程序包管理器“,然后选择”程序包管理器控制台“。
-
在”程序包管理器控制台“中,输入
Install-Package jQuery.Mobile.MVC -version 1.0.0
下图显示MvcMobile工程里由NuGet jQuery.Mobile.MVC包增加和更改的文件。增加的文件在文件名后有一个 [add] 。图里没有显示Content\images文件夹里新增的GIF 和 PNG 文件。
jQuery.Mobile.MVC NuGet 包安装了以下的东西:
- App_Start\BundleMobileConfig.cs 文件,用来引用新增的 jQuery JavaScript 和 CSS 文件。请遵照接下来的指示引用这个文件定义的移动bundle。
- jQuery Mobile CSS 文件。
-
一个
ViewSwitcher
控制器小工具 (Controllers\ViewSwitcherController.cs)。 - jQuery Mobile JavaScript 文件。
- 一个jQuery Mobile样式的布局文件 (Views\Shared\_Layout.Mobile.cshtml)。
- 一个视图开关部分视图 (MvcMobile\Views\Shared\_ViewSwitcher.cshtml) ,在每个页面顶部提供一个链接以在桌面视图和移动视图之间转换。
- 几个.png 和.gif 图片文件,位于 Content\images 目录。
打开 Global.asax 文件,在 Application_Start
方法最后加上这句代码:
BundleMobileConfig.RegisterBundles(BundleTable.Bundles);
以下是完整的 Global.asax 文件。
using System; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using System.Web.WebPages; namespace MvcMobile { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone") { ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf ("iPhone", StringComparison.OrdinalIgnoreCase) >= 0) }); AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); BundleMobileConfig.RegisterBundles(BundleTable.Bundles); } } }
打开 MvcMobile\Views\Shared\_Layout.Mobile.cshtml 文件,在Html.Partial调用之后直接加上下面的标记:
<div data-role="header" align="center"> @Html.ActionLink("Home", "Index", "Home") @Html.ActionLink("Date", "AllDates") @Html.ActionLink("Speaker", "AllSpeakers") @Html.ActionLink("Tag", "AllTags") </div>
完整的 MvcMobile\Views\Shared\_Layout.Mobile.cshtml 文件:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>@ViewBag.Title</title> <meta name="viewport" content="width=device-width" /> @Styles.Render("~/Content/Mobile/css", "~/Content/jquerymobile/css") @Scripts.Render("~/bundles/jquery", "~/bundles/jquerymobile") </head> <body> <div data-role="page" data-theme="a"> @Html.Partial("_ViewSwitcher") <div data-role="header" align="center"> @Html.ActionLink("Home", "Index", "Home") @Html.ActionLink("Date", "AllDates") @Html.ActionLink("Speaker", "AllSpeakers") @Html.ActionLink("Tag", "AllTags") </div> <div data-role="header"> <h1>@ViewBag.Title</h1> </div> <div data-role="content"> @RenderSection("featured", false) @RenderBody() </div> </div> </body> </html>
生成程序,在浏览器模拟器里浏览 AllTags 视图,如图:
注意:可以通过 设置用户代理字符串 为IE或Chrome调试移动特定代码,然后使用 F-12开发工具。如果移动浏览器不显示 Home, Speaker, Tag, 和 Date 链接的话,那么 jQuery Mobile 脚本和 CSS 文件可能没有正确的引用。
除了样式变了之外,还可以看到显示了移动视图和一个能让你从移动视图换成桌面视图的链接。选择Desktop视图链接,则桌面视图将显示。
桌面视图并不是直接回退到移动视图。现在就修复它吧。打开 Views\Shared\_Layout.cshtml 文件,在页面的 body
元素下,增加以下代码,呈现视图开关小工具。
@Html.Partial("_ViewSwitcher")
在移动浏览器中刷新 AllTags 视图。现在就可以在桌面和移动视图间切换了。
else { @:Not Mobile/Get }再在 Views\Shared\_Layout.cshtml 文件里加上这个头:
<h1> Non Mobile Layout MVC4 Conference </h1>
在桌面浏览器里浏览 AllTags 页面。 视图开关小工具并不在桌面浏览器中显示,因为它只在移动布局页面才会添加。待会儿你将看到如何将视图开关小工具加到桌面视图中。
改善演讲者列表
在移动浏览器中,选择 Speakers 链接。由于没有移动视图(AllSpeakers.Mobile.cshtml),所以默认的演讲者显示界面 (AllSpeakers.cshtml) 用移动布局视图来呈现 (_Layout.Mobile.cshtml)。
可以在Views\_ViewStart.cshtml文件中通过设置 RequireConsistentDisplayMode
为 true
来全局地禁用默认(非移动)视图呈现在移动布局中。如:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
DisplayModeProvider.Instance.RequireConsistentDisplayMode = true;
}
当 RequireConsistentDisplayMode
设置为 true
,移动布局
(_Layout.Mobile.cshtml) 只用于移动视图。 (也就是说,视图文件名形如 ViewName.Mobile.cshtml.)
如果移动布局在非移动视图中显示不好,你可能就要设置 RequireConsistentDisplayMode
为 true
。下面的截图演示当 RequireConsistentDisplayMode
设为 true
时Speakers页的样子。
可以在视图文件里通过设置 RequireConsistentDisplayMode
为 false
来禁用视图中的连续显示模式。下面的标记位于 Views\Home\AllSpeakers.cshtml 文件中, RequireConsistentDisplayMode
设为 false:
@model IEnumerable<string> @{ ViewBag.Title = "All speakers"; DisplayModeProvider.Instance.RequireConsistentDisplayMode = false; }
创建移动演讲者视图
就像刚看到的,演讲者视图可读性很强,但是链接很小,在移动设备上很难点到。本节将创建移动特定的演讲者视图,让它看起来像是个时髦的移动程序——显示大的,容易点击超链接并包含一个搜索框,可以快速找到演讲者。
复制 AllSpeakers.cshtml 到 AllSpeakers.Mobile.cshtml。打开 AllSpeakers.Mobile.cshtml 文件并将 <h2>
头元素移除掉。
在 <ul>
标签里, 添加 data-role
属性并把它的值设为 listview。
像其他的 data-*
属性, data-role="listview"
使得大列表项更容易点击。完整的标记如下:
@model IEnumerable<string> @{ ViewBag.Title = "All speakers"; } <ul data-role="listview"> @foreach(var speaker in Model) { <li>@Html.ActionLink(speaker, "SessionsBySpeaker", new { speaker })</li> } </ul>
刷新移动浏览器。更新后的视图如下:
虽然移动视图改善了,但这么长的演讲者列表用起来还是很费劲。要修复这个缺陷,在 <ul>
标签里, 添加 data-filter
属性并设为 true
。下面的代码演示了 ul
标记。
<ul data-role="listview" data-filter="true">
下图演示了data-filter属性作用下的页面顶部的搜索过滤框。
在搜索框里输入每一个字母, jQuery Mobile 都会过滤显示列表,如下图:
改善标签列表
正如默认的演讲者视图,标签视图也是可读性挺强的,但在移动设备上链接太小不容易点。本节将修复标签视图,就像修复演讲者视图一样。
将 Views\Home\AllTags.Mobile.cshtml.hide 文件的“hide”后缀去掉,变为Views\Home\AllTags.Mobile.cshtml。打开重命名后的文件删掉 <h2>
元素。
添加 data-role
和 data-filter
属性到 <ul>
标签中,如下:
<ul data-role="listview" data-filter="true">
下图演示标签页以字母J过滤的结果:
改善日期列表
可以像改善演讲者和标签视图那样改善日期视图,这样在移动设备上更易用。
复制 Views\Home\AllDates.cshtml 文件到 Views\Home\AllDates.Mobile.cshtml。打开新文件并删掉<h2>
元素。
增加 data-role="listview"
到 <ul>
标签中,如下:
<ul data-role="listview">
下图演示 data-role
属性下的日期页面:
用下面的代码替换 Views\Home\AllDates.Mobile.cshtml文件中的内容:
@model IEnumerable<DateTime> @{ ViewBag.Title = "All dates"; DateTime lastDay = default(DateTime); } <ul data-role="listview"> @foreach(var date in Model) { if (date.Date != lastDay) { lastDay = date.Date; <li data-role="list-divider">@date.Date.ToString("ddd, MMM dd")</li> } <li>@Html.ActionLink(date.ToString("h:mm tt"), "SessionsByDate", new { date })</li> } </ul>
这段代码将所有会议按日期分组。它为每一个新的日期创建一个分隔符,并在每一个分隔符下列出当天的所有会议。下面是代码运行后的样子:
改善会议表视图
本节将创建一个浏览器特定的会议视图。这里所做的更改将比之前做的更具扩展性。
在移动浏览器中,点击Speaker按钮,然后在搜索框中输入 Sc
。
点击 Scott Hanselman 链接。
正如所见,显示的内容在移动浏览器中很难看。日期列很难看,标签列跑出视图外边了。要修复此缺陷,复制 Views\Home\SessionsTable.cshtml 到 Views\Home\SessionsTable.Mobile.cshtml,然后用下面的代码替换文件中的内容:
@using MvcMobile.Models @model IEnumerable<Session> <ul data-role="listview"> @foreach(var session in Model) { <li> <a href="@Url.Action("SessionByCode", new { session.Code })"> <h3>@session.Title</h3> <p><strong>@string.Join(", ", session.Speakers)</strong></p> <p>@session.DateText</p> </a> </li> } </ul>
这段代码去掉了会议室和标签列,还垂直显示格式化后的标题,演讲者和日期,使得所有信息在移动浏览器中都有很强的可读性。下图演示代码改后的样子:
改善 SessionByCode 视图
最后,创建一个移动特定的SessionByCode视图。在移动浏览器里,点击Speaker按钮,然后再搜索框输入 Sc
。
点击 Scott Hanselman 链接, Scott Hanselman的会议就显示出来了。
选择 An Overview of the MS Web Stack of Love 链接。
桌面视图看起来也还好,但还可以改善。
复制 Views\Home\SessionByCode.cshtml 到 Views\Home\SessionByCode.cshtml 并用下面的代码替换 Views\Home\SessionByCode.Mobile.cshtml 文件中的内容:
@model MvcMobile.Models.Session @{ ViewBag.Title = "Session details"; } <h2>@Model.Title</h2> <p> <strong>@Model.DateText</strong> in <strong>@Model.Room</strong> </p> <ul data-role="listview" data-inset="true"> <li data-role="list-divider">Speakers</li> @foreach (var speaker in Model.Speakers) { <li>@Html.ActionLink(speaker, "SessionsBySpeaker", new { speaker })</li> } </ul> <p>@Model.Description</p> <h4>Code: @Model.Code</h4> <ul data-role="listview" data-inset="true"> <li data-role="list-divider">Tags</li> @foreach (var tag in Model.Tags) { <li>@Html.ActionLink(tag, "SessionsByTag", new { tag })</li> } </ul>
代码里用 data-role
属性来改善视图的布局。
刷新移动浏览器。下图演示代码修改后的结果:
总结与复习
本文介绍了ASP.NET MVC 4的移动特性。包括:
- 重写布局,视图和部分视图的能力,包括全局地和个别的视图。
-
用
RequireConsistentDisplayMode
属性控制布局。 - 可以在桌面视图和移动视图间进行切换的开关小工具。
- 支持特定浏览器,如iPhone浏览器。