在不加载FilePath的后端类中创建Ascx控件的实例Aspx页面

时间:2009-08-24 22:22:37

标签: asp.net loadcontrol

问题:是否可以在后端代码 (不在后面的代码中,但在实际的后端类中) 来加载和渲染.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字符串。当页面或控件从后面的代码继承时,它的类在我的后端代码中以intellisense显示,我可以创建实例并设置属性而不会产生编译时或运行时错误。但是,当我尝试渲染页面或控件时,我总是得到一个空字符串,并在检查时页面或控件显示抑制内部渲染错误,除非我使用其物理文件路径加载页面或控件。

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

4 个答案:

答案 0 :(得分: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);

答案 1 :(得分:0)

一般来说:不。

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

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

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

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

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

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

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

答案 2 :(得分:0)

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

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

  7. 列出项目

  8. 用法:示例代码位于

    之下

    以下是一个示例用法

    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);
    
            }
        }
    

答案 3 :(得分:0)

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

//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();

        }
    }