【问题标题】:Create Instance Aspx Page of Ascx Control In a Back End Class without Loading FilePath在后端类中创建 Ascx 控件的实例 Aspx 页面而不加载 FilePath
【发布时间】:2009-08-24 22:22:37
【问题描述】:

问题:是否可以在后端代码中(不是在后面的代码中,而是在实际的后端类中)渲染在 .aspx 或 .ascx 中定义的页面或控件,而无需使用 Load(path),而只需创建页面/控件类的实例?

我希望能够做到这一点(来自后端类而不是背后的代码):

MyControl myCtl = new MyApp.Views.Shared.MyControl();
String html = Util.ControlToString(myCtl); //I get an empty string & hidden errors

而不是这个

public static string ControlToString(string path)
{
    Page pageHolder = new Page();
    MyControl myCtl = (MyControl)pageHolder.LoadControl(path);
    pageHolder.Controls.Add(myCtl);
    StringWriter output = new StringWriter();
    HttpContext.Current.Server.Execute(pageHolder, output, false);
    return output.ToString();
}

详情: 在 Asp.net WebApp 中,我偶尔需要将用户控件 (.ascx) 或页面 (.aspx) 呈现为 HTML 字符串。当页面或控件从后面的代码继承时,它的类在我的后端代码中以智能感知显示,我可以创建一个实例并设置属性,而不会出现编译时或运行时错误。但是,当我尝试渲染页面或控件时,我总是得到一个空字符串,并且在检查时页面或控件显示抑制的内部渲染错误,除非我使用其物理文件路径加载页面或控件。

我认为关键问题与运行时编译 .aspx / .ascx 文件的时间和方式有关。我不想创建用户控件的预编译类库,因为这会使设计过程变得尴尬,我真的很喜欢 .aspx / .ascx 页面提供的设计器功能,所以我很想找到一种方法来使页面在解决方案中编译,以便它们像任何其他后端类一样可用,但仍然可以使用设计器创建。我希望两全其美 (1) 能够在设计器中编辑页面和控件,以及 (2) 创建实例并使用后端类设置它们的属性。

【问题讨论】:

    标签: asp.net loadcontrol


    【解决方案1】:

    这是一种可能在这种情况下有所帮助的方法。

    “后端”代码可能不知道用户控件的位置,但用户控件确实知道它在哪里。

    所以,在用户控件中,添加一个像这样的静态方法:

    public partial class MyControl : UserControl
    {
      ...
      public static MyControl LoadControl(CustomDto initialData)
      {
        var myControl = 
            (MyControl) 
            ((Page) HttpContext.Current.Handler)
            .LoadControl("~\\UserControlsSecretPath\\MyControl.ascx");
        myControl._initialData = initialData;
        return myControl;
      }
      ...
      private CustomDto _initialData;
    }
    

    (包含CustomDto 是为了说明如何将初始数据传递给用户控件。如果您不需要这样做,请将其取出!)

    这样,加载用户控件的代码确实不需要知道用户控件物理位置的路径。如果该位置发生变化,请更新此位置。使用此 UserControl 的所有其他代码均未更改。

    在您的后端代码或其他任何地方,您可以执行以下操作:

    var control = MyControl.LoadControl(customDto);
    PlaceHolder1.Controls.Add(control);
    

    【讨论】:

    • 我喜欢这种方法,因为它将魔术文件路径字符串放在靠近它们使用的位置,并且在 LoadControl 调用中明确初始化要求。我一直在使用泛型间接加载,然后设置属性,这使得错误更容易,并将魔术文件路径字符串放置在离需要它们的实际位置更远的地方。
    【解决方案2】:

    一般来说:没有。

    据我所知,ASP.NET 从您的类中继承,以将 .aspx/.ascx 模板与您的代码结合起来。这就是您的控件显示为空的原因:缺少将模板与您的代码组合的代码。这通常在您第一次访问页面或用户控件时由 ASP.NET 完成(这正是第一次访问有点慢的原因:它实际上是在生成和编译连接代码)。

    对于预编译的网站,ASP.NET 会提前将此代码生成为预编译网站的 .dll 的一部分,这就是此类网站加载速度更快的原因。然而,IIRC 你仍然需要实例化生成的类而不是你的原始类。

    这是一个很常见的请求,但到目前为止,MS 尚未提供执行此操作的工具。

    编辑:虽然我不明白您为什么要将控件呈现为内存中的字符串,但我可能有解决构建问题的方法。

    如果您坚持使用未编译的 .ascx 文件(使用网站模型而不是 Web 应用程序模型),您实际上可以通过将它们物理放置在主项目的子文件夹中来单独开发它们,并将它们视为内容文件只要。然后,您可以将此子文件夹作为根文件夹创建一个单独的项目。您只需将此子文件夹中的文件视为网站文件,主项目仍然可以是 Web 应用程序。 (实际上是推荐的,因为您不希望 .csproj 文件包含在主项目中。)

    但是,共享代码(即控件项目和主项目之间共享)应该放在一个单独的库项目中,这样你就可以单独编译每个项目而不需要相互依赖。

    在主项目中使用LoadControl 将即时编译它们(可以使用后面的代码);但是,如果您需要设置属性,则必须在共享项目中定义接口,在适当的用户控件上实现它们并将LoadControl 创建的控件转换为适当的接口。

    【讨论】:

    • 我担心这就是答案。我正在考虑使用已编译的控件设置工作流程。我对控件的编译类库允许我做我想做的事是对的吗?关于在大型应用程序中使用此类库的最佳实践有什么建议吗?谢谢!
    • 我目前在 VisualStudio 中的做法是在包含我的 WebApp 和测试的解决方案中添加一个“EmptyWebSite”。我在那个 EmptyWebsite 中创建我的页面和控件,然后在需要实例化它们的主站点中引用已发布的 DLL。整个事情太麻烦了,因为我必须手动发布更新类库 DLL。我正在考虑只使用 load(controlPath) 来简化事情
    • 不确定它是否有帮助,但也许我已经为构建问题找到了解决方案。 (原始答案已编辑。)
    • 糟糕。我现在明白了。如果我制作的解决方案既包含单独的控件类库又包含已编译的 Web 应用程序。如何设置 ClassLibrary 自动发布 dll 和 WebApp 自动获取更新的编译控件 dll?现在我正在尝试这个,我必须手动更新 Bin 文件,即使它们在同一个解决方案中。
    • 我不知道该怎么做,抱歉。
    【解决方案3】:

    我开发了一个解决方案来解决我在 VS 2008 中的问题:

    1. 创建主站点解决方案: 创建一个 MVC 1 网站解决方案 VS 2008
    2. 创建模型类库:为模型代码添加类库
    3. 创建查看代码:添加一个“空网站”来保存 .ascx 页面,并添加对模型库的引用
    4. 创建部署站点:添加一个部署项目,编译“空网站”转到“属性页面”和检查:“将所有输出合并到一个程序集中”和“处理作为库组件”并确保UnCheck:“允许此预编译站点可更新”
    5. 参考部署输出:在主项目中添加对部署站点输出的引用。
    6. 平均售价。 - 编译控件: 控件显示在 ASP 下。命名空间并以两种方式命名 A. 如果 .ascx / aspx 页面没有声明“ClassName”,那么它们将使用带有下划线的文件夹和文件名来命名 ex。 B. 如果他们确实声明了一个类名,那么这就是他们的名字

    7. 列表项

    用法:示例代码如下

    这是一个示例用法

    public ActionResult Index()
    {
        var ctl = new ASP.Directory_FirmProfile();  //create an instance
        ctl.Setup(new MyDataModel);                 //assign data
    
        //string test = CompiledControl.Render(ctl); //output to string
        return new HtmlCtl.StrongView(ctl);         //output to response
    }    
    
    
    
       public class CompiledControl
        {
            public static string Render(Control c)
            {
                Page pageHolder = new Page();
                pageHolder.Controls.Add(c);
                StringWriter output = new StringWriter();
                HttpContext.Current.Server.Execute(pageHolder, output, false);
                return output.ToString();
            }
    
            public static void Render(Control c, StringWriter output)
            {
                Page pageHolder = new Page();
                pageHolder.Controls.Add(c);
                HttpContext.Current.Server.Execute(pageHolder, output, false);
            }
    
            public static void Render(Control c, HttpResponseBase r)
            {
                Page pageHolder = new Page();
                pageHolder.Controls.Add(c);
                HttpContext.Current.Server.Execute(pageHolder, r.Output, false);
            }
    
    
        }
    
    
        public class StrongView : ActionResult
        {
            private Control ctl;
            public StrongView(Control ctl)
            {
                this.ctl = ctl;
            }
    
            public string VirtualPath{get;set;}
    
    
            public override void ExecuteResult(ControllerContext context)
            {
                if (context == null)
                    throw new ArgumentNullException("context");
    
                HtmlCtl.CompiledControl.Render(ctl, context.HttpContext.Response);
    
            }
        }
    

    【讨论】:

    • 在使用我的解决方案几周后,我建议不要使用它。额外的编译步骤和偶尔出现的错误大大超过了这种方法的小好处。
    【解决方案4】:

    我按照鲁本的建议提出了一个更简单的解决方案。 它已经运行了大约一个月没有问题:

    //Example usage
    
    //reference the control
    var emailCTL = new HtmlCtl.ControlOnDisk<MyControlType>(@"~\Views\EmailTemplates\MyControlType.ascx");
    
    //if you have a code behind you will get intellisense allowing you to set these properties
    // and re-factoring support works most places except the template file. 
    emailCTL.c.title = "Hello World "; //title is a property in the code behind
    emailCTL.c.data = data; //data is a property in the code behind
    
    string emailBody = emailCTL.RenderStateless(); 
    
    
    
    //Helper Class
        public class ControlOnDisk<ControlType> where ControlType : UserControl
        {
            public ControlType c;
            Page pageHolder = new Page();
            public ControlOnDisk(string path)
            {
                var o = pageHolder.LoadControl(path);
                c = (ControlType)o;
                pageHolder.Controls.Add(c);
            }
    
            public string RenderStateless()
            {
    
                StringWriter output = new StringWriter();
    
                // set up dumby context for use in rendering to email stream
                StringBuilder emailMessage = new StringBuilder();
                TextWriter tw = new StringWriter(emailMessage);
                HttpResponse dumbyResponse = new HttpResponse(tw);
                HttpRequest dumbyRequest = new HttpRequest("", "http://InsertURL.com/", ""); //dummy url requierd for context but not used
                HttpContext dumbyContext = new HttpContext(dumbyRequest, dumbyResponse);
                //HttpContextBase dumbyContextBase = new HttpContextWrapper2(dumbyContext);
    
                dumbyContext.Server.Execute(pageHolder, output, false);
                return output.ToString();
    
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-08
      • 2012-08-16
      • 1970-01-01
      • 1970-01-01
      • 2014-11-26
      相关资源
      最近更新 更多