ASP MVC3 - 如何从数据库加载页面的自定义用户定义布局?

时间:2011-10-28 12:56:02

标签: asp.net-mvc asp.net-mvc-3 razor

我在ASP.NET MVC3中使用Razor视图进行了在线表单构建器应用程序。 它与此类似 - https://examples.wufoo.com/forms/workshop-registration/

我需要用户能够自定义页面设计。 不仅要上传自定义css,还要自定义HTML页面模板。 假设用户应该完全控制Layout的自定义webform页面的HTML。用户应该能够编辑页面上的任何HTML,包括在布局中的表单旁边。

我不确定如何使用Razor和ASP.NET MVC 3。 有可能:

  1. 从数据库的某处加载布局为字符串或其他
  2. 将“FORM1_INCLUDE”等自定义标签替换为 @ Html.Partial(“some_non_customizable_layout_for_form1”)
  3. 将结果用作用户表单页面的有效布局文件
  4. 也许1-3不是做我需要的最好方法。 对于使用Razor视图的ASP.NET MVC 3中的这种用户定义的页面布局方法,您有什么建议?

    更新1 使用VirtualPathProvider我能够从数据库加载View,但它只返回如下文本:

    @inherits System.Web.Mvc.WebViewPage
    <body>
    @Html.EditorFor(z => z.Customer)
    </body>
    

    并且根本不处理任何Razor语法。 它可能有什么问题?

    已解决:

    需要将此行作为Application_Start()方法中的第一行:

    HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
    

    更新2 Custom View Provider在Global.asax.cs中注册为:

    protected void Application_Start()
            {
                HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
    
                AreaRegistration.RegisterAllAreas();
    
                RegisterGlobalFilters(GlobalFilters.Filters);
                RegisterRoutes(RouteTable.Routes);
            }
    

    MyVirtualPathProvider代码是:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Hosting;
    using System.IO;
    using System.Text;
    
    namespace new_frontend
    {
        public class MyVirtualPathProvider : VirtualPathProvider
        {
            public override bool FileExists(string virtualPath)
            {
                var td = FindTemplate(virtualPath);
                if (td == null)
                {
                    return true;
                    //return base.FileExists(virtualPath);
                }
                else
                {
                    return true;
                }
            }
    
            public override VirtualFile GetFile(string virtualPath)
            {
                var td = FindTemplate(virtualPath);
                if (td == null)
                {
                    return new MyVirtualFile(virtualPath, "");
                    //return base.GetFile(virtualPath);
                }
                else
                {
                    return new MyVirtualFile(virtualPath, td.ContentStep1);
                }
            }
    
            private Facade.Dto.TemplateData FindTemplate(string virtualPath)
            {
                string prefix = "Template#";
    
                int id = 0;
                Facade.Dto.TemplateData td = null;
    
                string fileName = System.IO.Path.GetFileNameWithoutExtension(virtualPath);
                if (fileName.StartsWith(prefix))
                    Int32.TryParse(fileName.Substring(prefix.Length), out id);
    
                if (id > 0)
                    td = Facade.FrontEndServices.GetTemplate(id);
    
                return td;
            }
        }
    
        public class MyVirtualFile : VirtualFile
        {
            private byte[] data;
    
            public MyVirtualFile(string virtualPath, string body)
                : base(virtualPath)
            {                      // 'System.Web.WebPages.ApplicationStartPage
                string _body = /*body +*/ @"
    
    @inherits System.Web.Mvc.WebViewPage
    @using (Ajax.BeginForm(""Submit"", new AjaxOptions { UpdateTargetId = ""main"" }))
    {
    }
    <!-- <PERSONAL_INFO> -->
    <div id=""personal_info"" class=""op2-block"">
    
    </div>
    <!-- <PERSONAL_INFO> -->";
    
                this.data = Encoding.UTF8.GetBytes(_body);
            }
    
            public override System.IO.Stream Open()
            {
                return new MemoryStream(data);
            }
        }
    }
    

    现在对于上面定义为字符串的Razor视图代码,我得到了这个例外:

    “编译器错误消息:CS1061:'System.Web.Mvc.AjaxHelper'不包含'BeginForm'的定义,也没有扩展方法'BeginForm'接受类型'System.Web.Mvc'的第一个参数可以找到.AjaxHelper'(你错过了使用指令或汇编引用吗?)“

    当我将Razor View代码更改为:

    string _body = /*body +*/ @"
    
    @using System.Web.WebPages;
    @using System.Web.Mvc;
    @using System.Web.Mvc.Ajax;
    @using System.Web.Mvc.Html;
    @using System.Web.Routing;
    
    @inherits System.Web.Mvc.WebViewPage<dynamic>
    
    @using (Ajax.BeginForm(""Submit"", new AjaxOptions { UpdateTargetId = ""main"" }))
    {
    }
    <!-- <PERSONAL_INFO> -->
    <div id=""ppg_op2_personal_info"" class=""op2-block"">
    
    </div>
    <!-- <PERSONAL_INFO> -->";
    

    我得到了一个不同的错误:

    类型'ASP._Page__appstart_cshtml'不会继承自'System.Web.WebPages.ApplicationStartPage'

    当我改变

    @inherits System.Web.Mvc.WebViewPage

    @inherits System.Web.WebPages.ApplicationStartPage

    要解决上面的错误,我会得到一个新错误:

    “编译器错误消息:CS0103:当前上下文中不存在名称'Ajax'”

    UPDATE3: 我尝试使用base.XXX:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Hosting;
    using System.IO;
    using System.Text;
    
    namespace new_frontend
    {
        public class MyVirtualPathProvider : VirtualPathProvider
        {
            public override bool FileExists(string virtualPath)
            {
                var td = FindTemplate(virtualPath);
                if (td == null)
                {
                    //return true;
                    return base.FileExists(virtualPath);
                }
                else
                {
                    return true;
                }
            }
    
            public override VirtualFile GetFile(string virtualPath)
            {
                var td = FindTemplate(virtualPath);
                if (td == null)
                {
                    //return new MyVirtualFile(virtualPath, "");
                    return base.GetFile(virtualPath);
                }
                else
                {
                    return new MyVirtualFile(virtualPath, td.ContentStep1);
                }
            }
    
            private Facade.Dto.TemplateData FindTemplate(string virtualPath)
            {
                string prefix = "Template#";
    
                int id = 0;
                Facade.Dto.TemplateData td = null;
    
                string fileName = System.IO.Path.GetFileNameWithoutExtension(virtualPath);
                if (fileName.StartsWith(prefix))
                    Int32.TryParse(fileName.Substring(prefix.Length), out id);
    
                if (id > 0)
                    td = Facade.FrontEndServices.GetTemplate(id);
    
                return td;
            }
        }
    
        public class MyVirtualFile : VirtualFile
        {
            private byte[] data;
    
            public MyVirtualFile(string virtualPath, string body)
                : base(virtualPath)
            {                      // 'System.Web.WebPages.ApplicationStartPage
                string _body = /*body +*/ @"
    
    @inherits System.Web.Mvc.WebViewPage<PPG.Facade.Dto.NewOrderPageData>
    @using (Ajax.BeginForm(""Submit"", new AjaxOptions { UpdateTargetId = ""main"" }))
    {
    }
    <!-- <PERSONAL_INFO> -->
    <div id=""personal_info"" class=""op2-block"">
    
    </div>
    <!-- <PERSONAL_INFO> -->";
    
                this.data = Encoding.UTF8.GetBytes(_body);
            }
    
            public override System.IO.Stream Open()
            {
                return new MemoryStream(data);
            }
        }
    }
    

    在这种情况下,我得到的视图根本没有被解析,这是我在网络浏览器中得到的:

    @using System.Web.WebPages;    
    @using System.Web.Mvc;    
    @using System.Web.Mvc.Ajax;    
    @using System.Web.Mvc.Html;    
    @using System.Web.Routing;    
    @inherits System.Web.Mvc.WebViewPage<PPG.Facade.Dto.NewOrderPageData>
    @using (Ajax.BeginForm("Submit", new AjaxOptions { UpdateTargetId = "main" }))
    {
    }
    <!-- <PERSONAL_INFO> -->
    <div id="ppg_op2_personal_info" class="op2-block">
    </div>
    <!-- <PERSONAL_INFO> -->
    

4 个答案:

答案 0 :(得分:7)

您应该创建一个虚拟路径提供程序,从数据库中提取自定义视图。

这里有几个问题。只需搜索VirtualPathProvider

更新(从我的评论到问题讨论)

  • 必须使用HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());

  • 在Application_Start中注册VirtualPathProvider
  • 必须为当前无法提供服务的所有文件调用基类。这是必需的,因为只能有一个VirtualPathProvider。 (否则你会看到许多奇怪的错误)

  • @model指令在您提供的文件中不起作用。您必须改为使用@inherits System.Web.Mvc.WebViewPage<YourNameSpace.YourModelName>

  • IIRC您还需要覆盖GetCacheDependency并为自己的资源返回null

答案 1 :(得分:0)

取决于您希望给予的灵活性和定制程度,这可能是非常简单到非常耗时的任务。

如果您希望用户只是在页面上自定义HTML,那么您可以使用WYSIWYG编辑器并将原始html存储在数据库中。

在视图中,使用

@Html.Raw(Model.body) // Where body is the field containing the wysiwyg content

这将按原样呈现标记。

要包含自定义标记/替换,您必须定义可在使用WYSIWYG编辑器时插入的字符串常量列表。然后可以在显示时替换它们。

例如,在您的控制器或型号中:

model.body.replace("$[form1]", "<form action='something' method='post' name='form1'></form>");

当然,根据您的应用程序的性质,您可能希望将其重新分解为某种标签=&gt;标记转换,允许您添加更多自定义标记及其各自的实际HTML标记。

希望这有帮助,干杯!

答案 2 :(得分:0)

答案 3 :(得分:0)

我做过类似的事情。你可能会认为XML非常有用。将布局定义作为XML保存到DB。这意味着它可以直接操作,也可以通过序列化/反序列化操作到对象模型中。

如果要显示页面,请使用XSLT将XML转换为HTML,将样式等应用于输出。