0

问题:是否可以在后端代码中(不是在后面的代码中,而是在实际的后端类中)加载和呈现在 .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)使用后端类创建实例并设置它们的属性。

4

4 回答 4

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);
于 2010-03-09T20:58:05.067 回答
0

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

  1. 创建主站点解决方案:在 VS 2008 中创建 MVC 1 网站解决方案
  2. 创建模型类库:为模型代码添加类库
  3. 创建查看代码:添加一个“空网站”来保存 .ascx 页面,并添加对模型库的引用
  4. 创建部署站点:添加一个编译“空网站”的部署项目转到“属性页面”并检查:“将所有输出合并到一个程序集中”和“视为库组件”并确保取消选中:“允许这个预编译网站可更新”
  5. 参考部署输出:在主项目中添加对部署站点输出的引用。
  6. ASP。- 编译控件:控件显示在 ASP 下。命名空间并以两种方式命名 A. 如果 .ascx / aspx 页面未声明“ClassName”,则使用其文件夹和文件名命名,并带有下划线 ex。<%@ Control Language="C#" ClassName="Admin_Index" %> 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);

        }
    }
于 2009-09-01T13:39:15.237 回答
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();

        }
    }
于 2010-01-05T01:21:06.410 回答
0

一般来说:没有。

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

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

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

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

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

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

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

于 2009-08-24T22:37:53.473 回答