在后端类中创建 Ascx 控件的实例 Aspx 页面而不加载 FilePath
-
19-09-2019 - |
题
问题: 后端代码可以实现吗 (不是在后面的代码中,而是在实际的后端类中) 加载和呈现 .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)创建实例并使用后端类设置其属性。
解决方案
下面是一种方法,其可以在这样的情况下帮助。
在“后端”的代码可能不知道在用户控制的位置,但用户控制不知道它在哪里。
所以,在用户控制,添加一个静态方法是这样的:
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
来说明数据如何初始可以被传递到用户控件。如果不需要做的是,取出!)
由此,加载用户控件的代码执行的不强>需要知道的路径,其中用户控制的物理位置。如果该位置都没有改变,然后更新这一个位置。使用该用户控件的所有其他码保持不变。
在你的后端代码,或任何其他地方,你可以做一些这样的:
var control = MyControl.LoadControl(customDto);
PlaceHolder1.Controls.Add(control);
其他提示
一般而言:没有。
据我所知,ASP.NET继承的从类的到.aspx /模板名为.ascx与您的代码相结合。这就是为什么你的控件显示空:该代码与您的代码模板丢失结合。这通常是通过ASP.NET完成的第一次访问一个页面或用户控件(这正是为什么第一主打是有点慢:它实际上是生成和编译联播代码)。
有关预编译的ASP.NET网站生成的代码作为预编译网站的.dll文件的一部分提前,这就是为什么这样的网站加载速度更快。然而,IIRC你仍然需要实例化的生成的类,而不是你原来的班。
这是一个相当普遍的要求,但到目前为止MS还没有提供的工具来做到这一点。
编辑:虽然我不明白你为什么会想呈现一个控制内存中的串,我可能有一个解决的构建问题。
如果你坚持非编.ascx文件(使用的网站模式,而不是Web应用程序模型),你其实可以单独通过将其放置在物理上您的主项目的子文件夹中开发它们,并把它们作为内容文件只要。然后,你可以用这个子文件夹的根文件夹一个单独的项目。你只需要把该子文件夹的网站文件的文件,主要项目仍然是一个Web应用程序。 (实际上推荐的,因为你不希望包含在主体工程中的.csproj文件。)
但是,共享的代码(即,控制项目和主项目之间共享)应放在一个单独的库项目,这样可以单独编译每个项目,而不相互依赖性。
主项目中使用LoadControl
将编译它们在飞行中(代码后面是可能的);如果需要设置属性,则必须定义然而在共享项目中的接口,实现它们在适当的用户的控制和投通过LoadControl
创建到适当的接口控制。
我开发了一个解决方案来解决我在 VS 2008 中的问题:
- 创建主站点解决方案: 在VS 2008中创建MVC 1网站解决方案
- 创建模型类库: 为模型代码添加类库
- 创建视图代码: 添加一个“空网站”来保存 .ascx 页面,并添加模型库的引用
- 创建部署站点: 添加一个编译“空网站”的部署项目,转到“属性页面”并 查看:“将所有输出合并到一个程序集中”和“视为库组件”,并确保 取消选中:“允许此预编译站点可更新”
- 参考部署输出: 在主项目中添加对部署站点输出的引用。
ASP。- 编译的控件: 控件显示在 ASP 下。名称空间,并以两种方式命名。如果 .ascx / aspx 页面未声明“ClassName”,则使用带有下划线 ex 的文件夹和文件名来命名它们。<%@ control语言=“ c#” className =“ admin_index”%> B。如果他们确实声明了类名,那么那就是他们的名字
项目清单
用法: 示例代码如下
这是一个示例用法
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);
}
}
我拿出沿鲁文的建议通过一项简单的解决方案。 它没有问题工作了大约一个月:
//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();
}
}