4.1 View详解
View(视图)的职责是向用户提供界面,负责根据提供的模型数据生成准备提供给用户的格式界面,并提供用户和系统交互的入口。
MVC支持多种视图引擎(Razor和ASPX视图引擎是官方默认给出的,其实还支持N种视图引擎,甚至都可以是自己写的视图引擎)。
4.1.1 View和Action之间数据传递的方式
View和Action之间前后台数据传递的方式有如下几种。
● 弱类型ViewData[""]
● 动态型ViewBag //dynamic
● 强类型Model
● 临时存储TempData[""]
● 后台:return View(data); //存入ViewData.Model
● 前台:Model //其实就是WebViewPage.Model
下面我们通过一个实例来进行讲解。
打开VS2012,选择“文件→新建项目→Web→ASP.NET MVC4 Web应用程序”,并将项目命名为MvcViewApp,选择“基本”项目模板,然后单击“确定”按钮。
在Controllers目录中添加Home控制器、Models目录中添加User模型类:
public class User { public string Name { get; set; } } public ActionResult Index() { ViewData["One"] = "天机老人孙老头"; ViewBag.Two = "子母龙凤环上官金虹"; var _user = new User{Name="小李飞刀李寻欢" }; TempData["Four"] = "嵩阳铁剑郭嵩阳"; return View(_user); //等于ViewData.Model = _user; }
添加Index视图,这里使用强类型视图,用法为“@model类型”,写在View最上面:@ViewData.Model(一般直接简写为@Model)、@Html.xxFor(x=>x.**)。
使用强类型视图的好处:提供智能提示,实现了编译时错误检查,防止写错字段。
@model MvcViewApp.Models.User @{ ViewBag.Title = "Index"; } <h2>百晓生兵器谱排名</h2> <p>第一名:@ViewData["One"]</p> <p>第二名:@ViewBag.Two</p> <p>第三名:@Model.Name</p> <p>第四名:@TempData["Four"] </p>
运行结果如图4-1所示。
图4-1
4.1.2 TempData、ViewData和ViewBag的区别
ViewData是字典型的(Dictionary), ViewBag不再是字典的键值对结构,而是dynamic(动态)型,会在程序运行的时候动态解析。ViewData为object型,而ViewBag为dynamic型。dynamic型与object型的区别是在使用时它会自动根据数据类型转换,而object型则需要我们自己去强制转换。
通过反编译工具查看System.Web.Mvc.dll的源码(当然也可以直接下载System.Web.Mvc4的源码,本书云盘中提供了MVC4的源码Chapter04 \MVC4SourceCode.zip),代码如下所示。
public abstract class WebViewPage : WebPageBase, IViewDataContainer, IViewStartPageChild { [Dynamic] public object ViewBag { [return: Dynamic] get; } public ViewContext ViewContext { get; set; } public ViewDataDictionary ViewData { get; set; } …… }
这里ViewBag只有get方法,没有set方法,但是我们在上面却给ViewBag赋值了。通过反编译发现ViewBag的代码如下所示。
[Dynamic] public object ViewBag { [return: Dynamic] get { Func<ViewDataDictionary> viewDataThunk = null; if (this._dynamicViewDataDictionary == null) { if (viewDataThunk == null) { viewDataThunk = () => this.ViewData; //创建一个委托对象,内部方法返回ViewData } this._dynamicViewDataDictionary = new DynamicViewDataDictionary(viewDataThunk); } return this._dynamicViewDataDictionary; } }
不难看出ViewBag返回的是_dynamicViewDataDictionary,继续跟踪发现_dynamicViewData Dictionary属于DynamicViewDataDictionary类,代码如下:
internal sealed class DynamicViewDataDictionary : DynamicObject { // Fields private readonly Func<ViewDataDictionary> _viewDataThunk; // Methods public DynamicViewDataDictionary(Func<ViewDataDictionary> viewDataThunk); public override IEnumerable<string> GetDynamicMemberNames(); public override bool TryGetMember(GetMemberBinder binder, out object result); public override bool TrySetMember(SetMemberBinder binder, object value); // Properties private ViewDataDictionary ViewData { get; } }
TryGetMember和TrySetMember方法如下:
public override bool TryGetMember(GetMemberBinder binder, out object result) { result = this.ViewData[binder.Name]; return true; } public override bool TrySetMember(SetMemberBinder binder, object value) { this.ViewData[binder.Name] = value; return true; }
ViewBag其实就是ViewData,只是多了一层Dynamic控制,可以说它是另一种访问ViewData的方式。理论上ViewBag要比ViewData慢一点点,但是几乎可以忽略,所以使用何种方式完全取决于个人的爱好。
TempData的使用同ViewData和ViewBag一样,TempData也可以用来向视图传递数据,只是ViewData和ViewBag的生命周期和View相同,它们只对当前View有用。TempData则可以在不同的Action中进行传值,类似Webform里的Session。有一点需要注意,TempData的值在取了一次后会自动删除。TempData用来在一次请求中同时执行的多个Action方法之间共享数据。
从源码中可以看到TempData是一个TempDataDictionary类型。TempDataDictionary类型中有一个this索引器:
public TempDataDictionary TempData { get; set; } public object this[string key] { get { object obj2; if (this.TryGetValue(key, out obj2)) { this._initialKeys.Remove(key); return obj2; } return null; } set { this._data[key] = value; this._initialKeys.Add(key); } }
每次获取数据之后,就马上将该数据从键值对列表_initialKeys中移除了。
public class TempDataDictionary : IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable { // Fields private Dictionary<string, object> _data; private HashSet<string> _initialKeys; //注意,就是往这里面存数据 private HashSet<string> _retainedKeys; internal const string TempDataSerializationKey = "__tempData"; …… }