带你读开源—ASP.NET_MVC(十一)

        上篇说到HtmlHelper,今天继续这个话题。

        我们在自己建立的MVC项目中,随便打开一个.cshtml文件,找到一个HtmlHelper调用,例如:@Html.TextBox("CustomerName"),把光标放在Html上,按F12进入其定义,即代码段1中的语句【public new HtmlHelper<TModel> Html { get; set; }】。可见,@Html是HtmlHelper<TModel>类的一个实例,并且它是泛型类WebViewPage<TModel>的一个属性,由于.cshtml文件中的类继承于泛型类WebViewPage<TModel>,所以我们在Razor中可以直接使用@Html。

#region 程序集System.Web.Mvc.dll, v4.0.30319
// C:\Program Files(x86)\Microsoft ASP.NET\ASP.NET MVC 4\Assemblies\System.Web.Mvc.dll
#endregion
 
using System;
 
namespace System.Web.Mvc
{
    // 摘要:
    //    表示呈现使用 ASP.NET Razor 语法的视图所需的属性和方法。
    //
    // 类型参数:
    //  TModel:
    //    视图数据模型的类型。
    public abstract classWebViewPage<TModel> : WebViewPage
    {
        // 摘要:
        //    初始化 System.Web.Mvc.WebViewPage<TModel> 类的新实例。
        protected WebViewPage();
 
        // 摘要:
        //    获取或设置 System.Web.Mvc.AjaxHelper 对象,该对象用于使用 Ajax 呈现 HTML 标记。
        //
        // 返回结果:
        //    用于使用 AJAX 呈现 HTML 标记的System.Web.Mvc.AjaxHelper 对象。
        public AjaxHelper<TModel> Ajax { get; set; }
        //
        // 摘要:
        //    获取或设置 System.Web.Mvc.HtmlHelper 对象,该对象用于呈现 HTML 元素。
        //
        // 返回结果:
        //    用于呈现 HTML 元素的 System.Web.Mvc.HtmlHelper 对象。
        public HtmlHelper<TModel> Html {get; set; }
        //
        // 摘要:
        //    获取关联的 System.Web.Mvc.ViewDataDictionary 对象的 Model属性。
        //
        // 返回结果:
        //    关联的 System.Web.Mvc.ViewDataDictionary 对象的 Model属性。
        public TModel Model { get; }
        //
        // 摘要:
        //    获取或设置一个字典,其中包含在控制器和视图之间传递的数据。
        //
        // 返回结果:
        //    一个字典,其中包含在控制器和视图之间传递的数据。
        public ViewDataDictionary<TModel>ViewData { get; set; }
 
        // 摘要:
        //    初始化 System.Web.Mvc.AjaxHelper、System.Web.Mvc.HtmlHelper和 System.Web.Mvc.UrlHelper
        //    类。
        public override void InitHelpers();
        //
        // 摘要:
        //    设置视图数据。
        //
        // 参数:
        //  viewData:
        //    视图数据。
        protected override void SetViewData(ViewDataDictionaryviewData);
    }
}


代码段 1

        好了,现在我们进入泛型类HtmlHelper<TModel>的定义(代码段2),在这里可以看到两个大家肯定会感兴趣的属性,即ViewData和ViewBag。没错,这就是我们用于保持不同Http请求之间状态的那两个东东。

    public class HtmlHelper<TModel> :HtmlHelper
    {
        private DynamicViewDataDictionary_dynamicViewDataDictionary;
        privateViewDataDictionary<TModel> _viewData;
 
        public HtmlHelper(ViewContextviewContext, IViewDataContainer viewDataContainer)
            : this(viewContext,viewDataContainer, RouteTable.Routes)
        {
        }
 
        public HtmlHelper(ViewContextviewContext, IViewDataContainer viewDataContainer, RouteCollectionrouteCollection)
            : base(viewContext,viewDataContainer, routeCollection)
        {
            _viewData = new ViewDataDictionary<TModel>(viewDataContainer.ViewData);
        }
 
        public new dynamic ViewBag
        {
            get
            {
                if (_dynamicViewDataDictionary== null)
                {
                    _dynamicViewDataDictionary= new DynamicViewDataDictionary(() => ViewData);
                }
 
                return_dynamicViewDataDictionary;
            }
        }
 
        public newViewDataDictionary<TModel> ViewData
        {
            get { return _viewData; }
        }
    }


代码段 2

        大家看ViewData和ViewBag属性的定义中用了new关键字,这是为什么?我们知道,关键字new除了常用来创建对象,还有一个作用是覆写父类的同名成员。由此可知,在泛型类HtmlHelper<TModel>的父类中,肯定也存在一个叫ViewData的成员。(注:本篇文章第二自然段中的语句【public newHtmlHelper<TModel> Html { get; set; }】也是这个意思。)我们进入泛型类HtmlHelper<TModel>的父类HtmlHelper,果然不出所料,确实有ViewData属性和ViewBag属性,见代码段3,其中ViewData是一个字典,ViewBag是一个动态类型(dynamic)。怎么样?这就是ViewData和ViewBag的庐山真面目。

  

      public ViewDataDictionary ViewData
        {
            get { returnViewDataContainer.ViewData; }
        }
        public dynamic ViewBag
        {
            get
            {
                if (_dynamicViewDataDictionary== null)
                {
                    _dynamicViewDataDictionary= new DynamicViewDataDictionary(() => ViewData);
                }
                return _dynamicViewDataDictionary;
            }
        }

代码段 3

        我们在HtmlHelper类中并没有找到常用的ActionLink、TextBox等Helper,回想一下上篇文章中关于ActionLink的分析,可以知道MVC框架内置的HtmlHelper是以扩展方法的形式进行定义的,这些扩展方法存在于MVC源码中的“System.Web.MVC/Html”路径下,名称包含Extention的类中,例如InputExtensions、LabelExtensions、ChildActionExtensions、FormExtensions、NameExtensions、PartialExtensions、SelectExtensions、TextAreaExtensions等等。

        下面我们着重说一下FormExtensions类。在这个类中,我们可以找到常用的@Html.BeginForm的定义,没错,这就是Razor中用来定义Web表单的Helper。用过这个Helper的人都会注意到一个细节,就是BeginForm的用法是【@using (Html.BeginForm()){}】,和普通的Helper不同。这种用法可以让我们不用调用EndForm方法,但这是为什么呢?

        见到using关键字,我们都知道在这里它的作用是在代码块结束之前,自动调用Dispose方法,当然,using代码块中的对象必须实现了IDisposable接口。找到BeginForm的定义(代码段4),发现这个方法的返回值是一个MvcForm对象。

        public static MvcForm BeginForm(thisHtmlHelper htmlHelper)
        {
            // generates <formaction="{current url}" method="post">...</form>
            string formAction =htmlHelper.ViewContext.HttpContext.Request.RawUrl;
            return FormHelper(htmlHelper, formAction,FormMethod.Post, new RouteValueDictionary());
        }


代码段 4

        跟踪进入MvcForm类的定义,可以看到这个类果然实现了IDisposable接口,狂喜!在MvcForm类的定义中,找到Dispose方法的实现(代码段5),豁然开朗吧?原来在Dispose方法中调用了FormExtensions.EndForm方法。

        public void Dispose()
        {
            Dispose(true /* disposing */);
            GC.SuppressFinalize(this);
        }
 
        protected virtual void Dispose(booldisposing)
        {
            if (!_disposed)
            {
                _disposed = true;
                FormExtensions.EndForm(_viewContext);
            }
        }


代码段 5

        继续进入FormExtensions.EndForm方法的定义(代码段6),更加豁然开朗吧?继续狂喜!EndForm方法的作用正是向浏览器发送Web表单的结束标记</form>。

        internal static voidEndForm(ViewContext viewContext)
        {
           viewContext.Writer.Write("</form>");
           viewContext.OutputClientValidation();
            viewContext.FormContext = null;
        }


代码段 6

你可能感兴趣的:(.net,mvc,开源,asp.net,开源代码)