通用继承的 ViewPage<>和新物业 [英] Generic Inherited ViewPage<> and new Property

查看:21
本文介绍了通用继承的 ViewPage<>和新物业的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

设置:

  • 自定义视图引擎
  • 自定义控制器基础
  • CustomViewPage Base(在此基础中,添加了一个新属性MyCustomProperty")

问题:

当视图是强类型的时,例如:<@ Page Inherits="CustomViewPage<MyCustomObject" MyCustomProperty="Hello">,我收到一个编译器Parser"错误,指出 MyCustomProperty 是不是 System.Web.Mvc.ViewPage 的公共属性

我进行了无数次试验和错误(见下文)以查看导致此错误的原因,并得出以下结论:

  • 仅当我在视图的 @Page 指令中声明MyCustomProperty"或任何其他属性时才会发生错误.
  • 错误将始终显示System.Web.Mvc.ViewPage"而不是声明的 inherits=".." 类.

解决方案

更新:看起来像 Technitium另一种看起来更容易的方法,至少在较新版本的 ASP.NET MVC 上是这样.(在下面复制了他的评论)

<块引用>

我不确定这在 ASP.NET MVC 3 中是否是新的,但是当我交换从引用 C# 语法中的泛型到 CLR 继承属性语法,标准 ViewPageParserFilter 正确解析了泛型——不需要 CustomViewTypeParserFilter.使用贾斯汀的例子,这意味着交换

<%@ Page Language="C#" MyNewProperty="来自@Page 指令!"Inherits="JG.ParserFilter.CustomViewPage

<块引用>

<%@ Page Language="C#" MyNewProperty="来自@Page 指令!"`Inherits="JG.ParserFilter.CustomViewPage`1[MvcApplication1.Models.FooModel]>

原始答案如下:

好的,我解决了这个问题.这是一项引人入胜的练习,解决方案并不简单,但一旦您第一次使用它就不会太难.

这是潜在的问题:ASP.NET 页面解析器不支持泛型作为页面类型.

ASP.NET MVC 解决这个问题的方法是欺骗底层页面解析器,让其认为该页面不是通用的.他们通过构建一个自定义的PageParserFilter 来做到这一点和自定义 FileLevelPageControlBuilder.解析器过滤器查找泛型类型,如果找到,则将其替换为非泛型 ViewPage 类型,以便 ASP.NET 解析器不会阻塞.然后,在页面编译生命周期的后期,他们的自定义页面构建器类将通用类型换回.

这是有效的,因为泛型 ViewPage 类型派生自非泛型 ViewPage,并且所有在 @Page 指令中设置的有趣属性都存在于(非泛型)基类中.因此,当在@Page 指令中设置属性时,真正发生的事情是针对非通用 ViewPage 基类验证这些属性名称.

无论如何,这在大多数情况下都很有效,但在您的情况下则不然,因为他们将 ViewPage 硬编码为页面过滤器实现中的非通用基类型,并且没有提供简单的方法来更改它.这就是您一直在错误消息中看到 ViewPage 的原因,因为错误发生在 ASP.NET 交换 ViewPage 占位符和它在编译之前交换回通用 ViewPage 之间.

修复方法是创建您自己的以下版本:

  1. 页面解析器过滤器 - 这几乎是 MVC 源代码中 ViewTypeParserFilter.cs 的精确副本,唯一的区别是它指的是您的自定义 ViewPage 和页面构建器类型,而不是 MVC 的
  2. 页面构建器 - 这与 MVC 源代码中的 ViewPageControlBuilder.cs 相同,但它将类放在您自己的命名空间中,而不是他们的命名空间中.
  3. 直接从 System.Web.Mvc.ViewPage(非通用版本)派生您的自定义视图页面类.在这个新的非泛型类上粘贴任何自定义属性.
  4. 从#3 派生一个泛型类,从 ASP.NET MVC 源代码的 ViewPage 实现中复制代码.
  5. 如果您还需要用户控件指令的自定义属性,请对用户控件 (@Control) 重复 #2、#3 和 #4.

然后您需要更改视图目录中的 web.config(不是主应用程序的 web.config)以使用这些新类型而不是 MVC 的默认类型.

我附上了一些代码示例来说明这是如何工作的.非常感谢 Phil Haack 的文章帮助我理解这一点,尽管我也不得不对 MVC 和 ASP.NET 源代码进行大量研究才能真正理解它.

首先,我将从您的 web.config 中所需的 web.config 更改开始:

现在,这是页面解析器过滤器(上面的#1):

命名空间 JG.ParserFilter {使用系统;使用 System.Collections;使用 System.Web.UI;使用 System.Web.Mvc;内部类 CustomViewTypeParserFilter : PageParserFilter{私有字符串_viewBaseType;private DirectiveType _directiveType = DirectiveType.Unknown;私有 bool _viewTypeControlAdded;公共覆盖无效 PreprocessDirective(字符串指令名称,IDictionary 属性){base.PreprocessDirective(指令名称,属性);字符串 defaultBaseType = null;//如果我们识别出指令,请跟踪它是什么.如果我们不认识//指令然后停止.开关(指令名称){案例页面":_directiveType = DirectiveType.Page;defaultBaseType = typeof(JG.ParserFilter.CustomViewPage).FullName;//JG:在此处注入自定义类型休息;案例控制":_directiveType = DirectiveType.UserControl;defaultBaseType = typeof(JG.ParserFilter.CustomViewUserControl).FullName;//JG:在此处注入自定义类型休息;案例主人":_directiveType = DirectiveType.Master;defaultBaseType = typeof(System.Web.Mvc.ViewMasterPage).FullName;休息;}如果(_directiveType == DirectiveType.Unknown){//如果我们正在处理一个未知指令(例如寄存器指令),停止处理返回;}//寻找一个继承属性字符串继承=(字符串)属性[继承"];如果(!String.IsNullOrEmpty(继承)){//如果它看起来不像一个泛型类型,不要做任何特别的事情,//并让解析器进行正常处理如果(IsGenericTypeString(继承)){//删除继承属性,这样解析器就不会崩溃属性[继承"] = defaultBaseType;//记住完整的类型字符串,以便我们稍后将其提供给 ControlBuilder_viewBaseType = 继承;}}}private static bool IsGenericTypeString(string typeName) {//检测 C# 和 VB 通用语法//评论:其他语言呢?return typeName.IndexOfAny(new char[] { '<', '(' }) >= 0;}公共覆盖无效 ParseComplete(ControlBuilder rootBuilder) {base.ParseComplete(rootBuilder);//如果它是我们的页面 ControlBuilder,给它基本类型字符串CustomViewPageControlBuilder pageBuilder = rootBuilder as JG.ParserFilter.CustomViewPageControlBuilder;//JG:在此处注入自定义类型如果(页面构建器!= null){pageBuilder.PageBaseType = _viewBaseType;}CustomViewUserControlControlBuilder userControlBuilder = rootBuilder as JG.ParserFilter.CustomViewUserControlControlBuilder;//JG:在此处注入自定义类型如果 (userControlBuilder != null) {userControlBuilder.UserControlBaseType = _viewBaseType;}}public override bool ProcessCodeConstruct(CodeConstructType codeType, string code) {if (codeType == CodeConstructType.ExpressionSnippet &&!_viewTypeControlAdded &&_viewBaseType != null &&_directiveType == DirectiveType.Master) {//如果我们正在处理需要设置其基本类型的母版页,请在此处执行.//这是通过添加 ViewType 控件来完成的,该控件具有设置基本类型的构建器.//该代码当前假定有问题的文件包含一个代码片段,因为//这是我们为了知道何时添加 ViewType 控件而关闭的项目.Hashtable attribs = new Hashtable();attribs["typename"] = _viewBaseType;AddControl(typeof(System.Web.Mvc.ViewType), attribs);_viewTypeControlAdded = true;}返回 base.ProcessCodeConstruct(codeType, code);}//此类中的所有其他内容都与我们的继承"处理无关.//由于 PageParserFilter 默认阻塞一切,我们需要解除阻塞公共覆盖 bool AllowCode {得到 {返回真;}}公共覆盖 bool AllowBaseType(Type baseType) {返回真;}公共覆盖 bool AllowControl(Type controlType, ControlBuilder builder) {返回真;}public override bool AllowVirtualReference(string referenceVirtualPath, VirtualReferenceType referenceType) {返回真;}公共覆盖 bool AllowServerSideInclude(string includeVirtualPath) {返回真;}公共覆盖 int NumberOfControlsAllowed {得到 {返回-1;}}公共覆盖 int NumberOfDirectDependenciesAllowed {得到 {返回-1;}}公共覆盖 int TotalNumberOfDependenciesAllowed {得到 {返回-1;}}私有枚举 DirectiveType {未知,页,用户控件,掌握,}}}

这是页面构建器类(上面的#2):

命名空间 JG.ParserFilter {使用 System.CodeDom;使用 System.Web.UI;内部密封类 CustomViewPageControlBuilder : FileLevelPageControlBuilder {公共字符串 PageBaseType {得到;放;}公共覆盖 void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit,CodeTypeDeclaration baseType,CodeTypeDeclaration 派生类型,CodeMemberMethod 构建方法,CodeMemberMethod dataBindingMethod) {//如果我们找到了一个基类字符串,就使用它如果(PageBaseType != null){derivedType.BaseTypes[0] = new CodeTypeReference(PageBaseType);}}}}

这里是自定义视图页面类:非泛型基类(上面的#3)和泛型派生类(上面的#4):

使用系统;使用 System.Collections.Generic;使用 System.Linq;使用 System.Web;使用 System.Web.UI;使用 System.Diagnostics.CodeAnalysis;使用 System.Web.Mvc;命名空间 JG.ParserFilter{[FileLevelControlBuilder(typeof(JG.ParserFilter.CustomViewPageControlBuilder))]公共类 CustomViewPage : System.Web.Mvc.ViewPage//, IAttributeAccessor{公共字符串 MyNewProperty { 获取;放;}}[FileLevelControlBuilder(typeof(JG.ParserFilter.CustomViewPageControlBuilder))]公共类 CustomViewPage: 自定义视图页面其中 TModel :类{//从 ViewPage 源代码复制的代码私有 ViewDataDictionary_viewData;公开新的 AjaxHelper<TModel>阿贾克斯{得到;放;}公共新 HtmlHelperhtml{得到;放;}公开新的 TModel 模型{得到{返回 ViewData.Model;}}[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]公共新 ViewDataDictionary查看数据{得到{如果(_viewData == null){SetViewData(new ViewDataDictionary());}返回_viewData;}放{设置视图数据(值);}}公共覆盖无效 InitHelpers(){base.InitHelpers();Ajax = new AjaxHelper(ViewContext, this);Html = new HtmlHelper(ViewContext, this);}protected override void SetViewData(ViewDataDictionary viewData){_viewData = new ViewDataDictionary(viewData);base.SetViewData(_viewData);}}}

这里是用户控件的相应类(上面的#5):

命名空间 JG.ParserFilter{使用 System.Diagnostics.CodeAnalysis;使用 System.Web.Mvc;使用 System.Web.UI;[FileLevelControlBuilder(typeof(JG.ParserFilter.CustomViewUserControlControlBuilder))]公共类 CustomViewUserControl : System.Web.Mvc.ViewUserControl{公共字符串 MyNewProperty { 获取;放;}}公共类 CustomViewUserControl:CustomViewUserControl 其中 TModel:类{私有 AjaxHelper_ajaxHelper;私有 HtmlHelper_htmlHelper;私有 ViewDataDictionary_viewData;公开新的 AjaxHelper<TModel>阿贾克斯{得到 {如果(_ajaxHelper == null){_ajaxHelper = new AjaxHelper(ViewContext, this);}返回_ajaxHelper;}}公共新 HtmlHelperhtml {得到 {如果(_htmlHelper == null){_htmlHelper = new HtmlHelper(ViewContext, this);}返回 _htmlHelper;}}公共新 TModel 模型 {得到 {返回 ViewData.Model;}}[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]公共新 ViewDataDictionary查看数据{得到 {确保视图数据();返回_viewData;}放 {设置视图数据(值);}}protected override void SetViewData(ViewDataDictionary viewData) {_viewData = new ViewDataDictionary(viewData);base.SetViewData(_viewData);}}}命名空间 JG.ParserFilter {使用 System.CodeDom;使用 System.Web.UI;内部密封类 CustomViewUserControlControlBuilder : FileLevelUserControlBuilder {内部字符串 UserControlBaseType {得到;放;}公共覆盖 void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit,CodeTypeDeclaration baseType,CodeTypeDeclaration 派生类型,CodeMemberMethod 构建方法,CodeMemberMethod dataBindingMethod) {//如果我们找到了一个基类字符串,就使用它if (UserControlBaseType != null) {derivedType.BaseTypes[0] = new CodeTypeReference(UserControlBaseType);}}}}

最后,这是一个示例视图,显示了这一点:

<%@ Page Language="C#" MyNewProperty="来自@Page 指令!"Inherits="JG.ParserFilter.CustomViewPage"%><%=Model.SomeString %><br/><br/>this.MyNewProperty = <%=MyNewProperty%></asp:内容>

Setup:

  • CustomViewEngine
  • CustomController Base
  • CustomViewPage Base (in this base, a new property is added "MyCustomProperty")

Problem:

When a view is strongly typed such as: <@ Page Inherits="CustomViewPage<MyCustomObject" MyCustomProperty="Hello">, I get a compiler "Parser" error stating that MyCustomProperty is not a public property of System.Web.Mvc.ViewPage

I have done numerous trial and errors (see below) to see whats causing this error and have come to the following conclusions:

  • The error only occurs when I declare "MyCustomProperty" or any other property in the @Page directive of the view.
  • The error will always display "System.Web.Mvc.ViewPage" rather than the declared inherits=".." class.

解决方案

Update: Looks like Technitium found another way to do this that looks much easier, at least on newer versions of ASP.NET MVC. (copied his comment below)

I'm not sure if this is new in ASP.NET MVC 3, but when I swapped the Inherits attribute from referencing the generic in C# syntax to CLR syntax, the standard ViewPageParserFilter parsed generics correctly -- no CustomViewTypeParserFilter required. Using Justin's examples, this means swapping

<%@ Page Language="C#" MyNewProperty="From @Page directive!"
    Inherits="JG.ParserFilter.CustomViewPage<MvcApplication1.Models.FooModel>

to

<%@ Page Language="C#" MyNewProperty="From @Page directive!"` 
    Inherits="JG.ParserFilter.CustomViewPage`1[MvcApplication1.Models.FooModel]>

Original answer below:

OK, I solved this. Was a fascinating exercise, and the solution is non-trivial but not too hard once you get it working the first time.

Here's the underlying issue: the ASP.NET page parser does not support generics as a page type.

The way ASP.NET MVC worked around this was by fooling the underlying page parser into thinking that the page is not generic. They did this by building a custom PageParserFilter and a custom FileLevelPageControlBuilder. The parser filter looks for a generic type, and if it finds one, swaps it out for the non-generic ViewPage type so that the ASP.NET parser doesn't choke. Then, much later in the page compilation lifecycle, their custom page builder class swaps the generic type back in.

This works because the generic ViewPage type derives from the non-generic ViewPage, and all the interesting properties that are set in a @Page directive exist on the (non-generic) base class. So what's really happening when properties are set in the @Page directive is that those property names are being validated against the non-generic ViewPage base class.

Anyway, this works great in most cases, but not in yours because they hardcode ViewPage as the non-generic base type in their page filter implementation and don't provide an easy way to change it. This is why you kept seeing ViewPage in your error message, since the error happens in between when ASP.NET swaps in the ViewPage placeholder and when it swaps back the generic ViewPage right before compilation.

The fix is to create your own version of the following:

  1. page parser filter - this is almost an exact copy of ViewTypeParserFilter.cs in the MVC source, with the only difference being that it refers to your custom ViewPage and page builder types instead of MVC's
  2. page builder - this is identical to ViewPageControlBuilder.cs in the MVC source, but it puts the class in your own namespace as opposed to theirs.
  3. Derive your custom viewpage class directly from System.Web.Mvc.ViewPage (the non-generic version). Stick any custom properties on this new non-generic class.
  4. derive a generic class from #3, copying the code from the ASP.NET MVC source's implementation of ViewPage.
  5. repeat #2, #3, and #4 for user controls (@Control) if you also need custom properties on user control directives too.

Then you need to change the web.config in your views directory (not the main app's web.config) to use these new types instead of MVC's default ones.

I've enclosed some code samples illustrating how this works. Many thanks to Phil Haack's article to help me understand this, although I had to do a lot of poking around the MVC and ASP.NET source code too to really understand it.

First, I'll start with the web.config changes needed in your web.config:

<pages
    validateRequest="false"
    pageParserFilterType="JG.ParserFilter.CustomViewTypeParserFilter"
    pageBaseType="JG.ParserFilter.CustomViewPage"
    userControlBaseType="JG.ParserFilter.CustomViewUserControl">

Now, here's the page parser filter (#1 above):

namespace JG.ParserFilter {
    using System;
    using System.Collections;
    using System.Web.UI;
    using System.Web.Mvc;

    internal class CustomViewTypeParserFilter : PageParserFilter
    {

        private string _viewBaseType;
        private DirectiveType _directiveType = DirectiveType.Unknown;
        private bool _viewTypeControlAdded;

        public override void PreprocessDirective(string directiveName, IDictionary attributes) {
            base.PreprocessDirective(directiveName, attributes);

            string defaultBaseType = null;

            // If we recognize the directive, keep track of what it was. If we don't recognize
            // the directive then just stop.
            switch (directiveName) {
                case "page":
                    _directiveType = DirectiveType.Page;
                    defaultBaseType = typeof(JG.ParserFilter.CustomViewPage).FullName;  // JG: inject custom types here
                    break;
                case "control":
                    _directiveType = DirectiveType.UserControl;
                    defaultBaseType = typeof(JG.ParserFilter.CustomViewUserControl).FullName; // JG: inject custom types here
                    break;
                case "master":
                    _directiveType = DirectiveType.Master;
                    defaultBaseType = typeof(System.Web.Mvc.ViewMasterPage).FullName;
                    break;
            }

            if (_directiveType == DirectiveType.Unknown) {
                // If we're processing an unknown directive (e.g. a register directive), stop processing
                return;
            }


            // Look for an inherit attribute
            string inherits = (string)attributes["inherits"];
            if (!String.IsNullOrEmpty(inherits)) {
                // If it doesn't look like a generic type, don't do anything special,
                // and let the parser do its normal processing
                if (IsGenericTypeString(inherits)) {
                    // Remove the inherits attribute so the parser doesn't blow up
                    attributes["inherits"] = defaultBaseType;

                    // Remember the full type string so we can later give it to the ControlBuilder
                    _viewBaseType = inherits;
                }
            }
        }

        private static bool IsGenericTypeString(string typeName) {
            // Detect C# and VB generic syntax
            // REVIEW: what about other languages?
            return typeName.IndexOfAny(new char[] { '<', '(' }) >= 0;
        }

        public override void ParseComplete(ControlBuilder rootBuilder) {
            base.ParseComplete(rootBuilder);

            // If it's our page ControlBuilder, give it the base type string
            CustomViewPageControlBuilder pageBuilder = rootBuilder as JG.ParserFilter.CustomViewPageControlBuilder; // JG: inject custom types here
            if (pageBuilder != null) {
                pageBuilder.PageBaseType = _viewBaseType;
            }
            CustomViewUserControlControlBuilder userControlBuilder = rootBuilder as JG.ParserFilter.CustomViewUserControlControlBuilder; // JG: inject custom types here
            if (userControlBuilder != null) {
                userControlBuilder.UserControlBaseType = _viewBaseType;
            }
        }

        public override bool ProcessCodeConstruct(CodeConstructType codeType, string code) {
            if (codeType == CodeConstructType.ExpressionSnippet &&
                !_viewTypeControlAdded &&
                _viewBaseType != null &&
                _directiveType == DirectiveType.Master) {

                // If we're dealing with a master page that needs to have its base type set, do it here.
                // It's done by adding the ViewType control, which has a builder that sets the base type.

                // The code currently assumes that the file in question contains a code snippet, since
                // that's the item we key off of in order to know when to add the ViewType control.

                Hashtable attribs = new Hashtable();
                attribs["typename"] = _viewBaseType;
                AddControl(typeof(System.Web.Mvc.ViewType), attribs);
                _viewTypeControlAdded = true;
            }

            return base.ProcessCodeConstruct(codeType, code);
        }

        // Everything else in this class is unrelated to our 'inherits' handling.
        // Since PageParserFilter blocks everything by default, we need to unblock it

        public override bool AllowCode {
            get {
                return true;
            }
        }

        public override bool AllowBaseType(Type baseType) {
            return true;
        }

        public override bool AllowControl(Type controlType, ControlBuilder builder) {
            return true;
        }

        public override bool AllowVirtualReference(string referenceVirtualPath, VirtualReferenceType referenceType) {
            return true;
        }

        public override bool AllowServerSideInclude(string includeVirtualPath) {
            return true;
        }

        public override int NumberOfControlsAllowed {
            get {
                return -1;
            }
        }

        public override int NumberOfDirectDependenciesAllowed {
            get {
                return -1;
            }
        }

        public override int TotalNumberOfDependenciesAllowed {
            get {
                return -1;
            }
        }

        private enum DirectiveType {
            Unknown,
            Page,
            UserControl,
            Master,
        }
    }
}

Here's the page builder class (#2 above):

namespace JG.ParserFilter {
    using System.CodeDom;
    using System.Web.UI;

    internal sealed class CustomViewPageControlBuilder : FileLevelPageControlBuilder {
        public string PageBaseType {
            get;
            set;
        }

        public override void ProcessGeneratedCode(
            CodeCompileUnit codeCompileUnit,
            CodeTypeDeclaration baseType,
            CodeTypeDeclaration derivedType,
            CodeMemberMethod buildMethod,
            CodeMemberMethod dataBindingMethod) {

            // If we find got a base class string, use it
            if (PageBaseType != null) {
                derivedType.BaseTypes[0] = new CodeTypeReference(PageBaseType);
            }
        }
    }
}

And here's the custom view page classes: the non-generic base (#3 above) and the generic derived class (#4 above):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Diagnostics.CodeAnalysis;
using System.Web.Mvc;

namespace JG.ParserFilter
{
    [FileLevelControlBuilder(typeof(JG.ParserFilter.CustomViewPageControlBuilder))]
    public class CustomViewPage : System.Web.Mvc.ViewPage //, IAttributeAccessor 
    {
        public string MyNewProperty { get; set; }
    }

    [FileLevelControlBuilder(typeof(JG.ParserFilter.CustomViewPageControlBuilder))]
    public class CustomViewPage<TModel> : CustomViewPage
        where TModel : class
    {
        // code copied from source of ViewPage<T>

        private ViewDataDictionary<TModel> _viewData;

        public new AjaxHelper<TModel> Ajax
        {
            get;
            set;
        }

        public new HtmlHelper<TModel> Html
        {
            get;
            set;
        }

        public new TModel Model
        {
            get
            {
                return ViewData.Model;
            }
        }

        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public new ViewDataDictionary<TModel> ViewData
        {
            get
            {
                if (_viewData == null)
                {
                    SetViewData(new ViewDataDictionary<TModel>());
                }
                return _viewData;
            }
            set
            {
                SetViewData(value);
            }
        }

        public override void InitHelpers()
        {
            base.InitHelpers();

            Ajax = new AjaxHelper<TModel>(ViewContext, this);
            Html = new HtmlHelper<TModel>(ViewContext, this);
        }

        protected override void SetViewData(ViewDataDictionary viewData)
        {
            _viewData = new ViewDataDictionary<TModel>(viewData);

            base.SetViewData(_viewData);
        }

    }
}

And here are the corresponding classes for user controls (#5 above) :

namespace JG.ParserFilter
{
    using System.Diagnostics.CodeAnalysis;
    using System.Web.Mvc;
    using System.Web.UI;

    [FileLevelControlBuilder(typeof(JG.ParserFilter.CustomViewUserControlControlBuilder))]
    public class CustomViewUserControl : System.Web.Mvc.ViewUserControl 
    {
        public string MyNewProperty { get; set; }
    }

    public class CustomViewUserControl<TModel> : CustomViewUserControl  where TModel : class
    {
        private AjaxHelper<TModel> _ajaxHelper;
        private HtmlHelper<TModel> _htmlHelper;
        private ViewDataDictionary<TModel> _viewData;

        public new AjaxHelper<TModel> Ajax {
            get {
                if (_ajaxHelper == null) {
                    _ajaxHelper = new AjaxHelper<TModel>(ViewContext, this);
                }
                return _ajaxHelper;
            }
        }

        public new HtmlHelper<TModel> Html {
            get {
                if (_htmlHelper == null) {
                    _htmlHelper = new HtmlHelper<TModel>(ViewContext, this);
                }
                return _htmlHelper;
            }
        }

        public new TModel Model {
            get {
                return ViewData.Model;
            }            
        }

        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public new ViewDataDictionary<TModel> ViewData {
            get {
                EnsureViewData();
                return _viewData;
            }
            set {
                SetViewData(value);
            }
        }

        protected override void SetViewData(ViewDataDictionary viewData) {
            _viewData = new ViewDataDictionary<TModel>(viewData);

            base.SetViewData(_viewData);
        }
    }
}

namespace JG.ParserFilter {
    using System.CodeDom;
    using System.Web.UI;

    internal sealed class CustomViewUserControlControlBuilder : FileLevelUserControlBuilder {
        internal string UserControlBaseType {
            get;
            set;
        }

        public override void ProcessGeneratedCode(
            CodeCompileUnit codeCompileUnit,
            CodeTypeDeclaration baseType,
            CodeTypeDeclaration derivedType,
            CodeMemberMethod buildMethod,
            CodeMemberMethod dataBindingMethod) {

            // If we find got a base class string, use it
            if (UserControlBaseType != null) {
                derivedType.BaseTypes[0] = new CodeTypeReference(UserControlBaseType);
            }
        }
    }
}

Finally, here's a sample View which shows this in action:

<%@ Page Language="C#" MyNewProperty="From @Page directive!"  Inherits="JG.ParserFilter.CustomViewPage<MvcApplication1.Models.FooModel>" %>
    <%=Model.SomeString %>
    <br /><br />this.MyNewPrroperty = <%=MyNewProperty%>
</asp:Content>

这篇关于通用继承的 ViewPage&lt;&gt;和新物业的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆