Cree una página Aspx de instancia de control Ascx en una clase de back-end sin cargar FilePath

StackOverflow https://stackoverflow.com/questions/1325101

  •  19-09-2019
  •  | 
  •  

Pregunta

Pregunta: ¿Es posible en el código back-end? (no en el código subyacente sino en una clase de back-end real) ¿Cargar y representar una página o control definido en .aspx o .ascx sin tener que usar Load(path) y en su lugar simplemente crear una instancia de la clase de página/control?

Quiero poder hacer esto (desde una clase de back-end, NO desde un código subyacente):

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

en lugar de esto

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

Detalles:En una aplicación web Asp.net, ocasionalmente necesito representar un control de usuario (.ascx) o una página (.aspx) como una cadena HTML.Cuando una página o control hereda de un código subyacente, su clase aparece en Intellisense en mi código back-end y puedo crear una instancia y establecer propiedades sin obtener errores de tiempo de compilación o de ejecución.Sin embargo, cuando intento representar la página o el control, siempre obtengo una cadena vacía y, al inspeccionarla, la página o el control muestra errores de representación internos suprimidos a menos que cargue la página o el control utilizando su ruta de archivo física.

Creo que la cuestión clave tiene que ver con cuándo y cómo se compilan en tiempo de ejecución los archivos .aspx/.ascx.No quiero crear una biblioteca de clases precompilada de controles de usuario porque eso haría que el proceso de diseño fuera incómodo y realmente me gustan las funciones de diseño que ofrecen las páginas .aspx/.ascx, por lo que me encantaría encontrar una manera de haga que las páginas se compilen en la solución para que se puedan utilizar como cualquier otra clase de back-end pero que aún se puedan crear con el diseñador.Quiero lo mejor de ambos mundos (1) poder editar páginas y controles en el diseñador y (2) crear instancias y establecer sus propiedades usando clases de back-end.

¿Fue útil?

Solución

Este es un enfoque que puede ayudar en situaciones como esta.

El código "back-end" puede no saber dónde se encuentra el control de usuario, pero el control de usuario no saber dónde está.

Por lo tanto, en el control de usuario, agregar un método estático como sigue:

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

(El CustomDto se incluye para ilustrar cómo los datos inicial se puede pasar al control de usuario. Si no necesita hacer eso, llevarlo a cabo!)

Con esto, el código que carga el control de usuario hace no necesita saber la ruta en la que el control de usuario se encuentra físicamente. Si ese lugar cambia nunca, a continuación, actualizar éste ubicación. Todo otro código que utiliza este control de usuario no se ha modificado.

En el código de fondo, o en cualquier otro lugar, se puede hacer algo así:

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

Otros consejos

En términos generales: ninguna.

Por lo que yo sé, ASP.NET hereda de sus clases para combinar los / Ascx plantilla .aspx con su código. Es por esto que los controles aparecen vacíos: el código de combinar la plantilla con el código no se encuentra. Esto se hace generalmente por ASP.NET la primera vez que acceda a un control de página o usuario (que es precisamente el motivo por el primer golpe es un poco lento: esto es en realidad la generación y compilación de la conexión de código).

Para los sitios web precompilados ASP.NET genera este código como parte de .dll de su sitio web precompilado con antelación, por lo que dichos sitios se cargan más rápido. Sin embargo, IIRC usted todavía necesita para crear instancias de los generada clases en lugar de sus clases originales.

Es una petición bastante común, pero hasta ahora la EM no ha proporcionado las herramientas para hacer esto.

Editar:. Aunque no veo por qué te gustaría para hacer un control a una cadena en memoria, que podría tener una solución a los problemas de construcción

Si usted se pega a la no-compilado .ascx archivos (utilizando el modelo de sitio web y no el modelo de aplicación web), en realidad se puede desarrollar de forma separada físicamente por su inclusión en la subcarpeta de su proyecto principal, y tratarlos como archivos de contenido solamente. A continuación, se puede hacer un proyecto independiente con esta subcarpeta como la carpeta raíz. Sólo es necesario para el tratamiento de los archivos de esta subcarpeta como los archivos del sitio web, el principal proyecto todavía puede ser una aplicación web. (En realidad recomendado, porque no desea que los archivos Csproj incluidas en el proyecto principal.)

Sin embargo, el código compartido (es decir, compartida entre el proyecto y el proyecto controles principal) debe ser puesto en un proyecto de biblioteca independiente, por lo que se puede compilar cada proyecto por separado sin interdependencias.

El uso de LoadControl dentro del proyecto principal compilará sobre la marcha (código detrás es posible); si es necesario establecer las propiedades, debe sin embargo definir interfaces en el proyecto compartido, implementarlas en los controles de usuario adecuados y echó el control creado por LoadControl a la interfaz apropiada.

Desarrollé una solución que resuelve mi problema en VS 2008:

  1. Crear solución del sitio principal: Cree una solución de sitio web MVC 1 en VS 2008
  2. Crear biblioteca de clases de modelo: Agregar una biblioteca de clases para el código del modelo
  3. Crear código de vista: Agregue un "sitio web vacío" para contener las páginas .ascx y agregue una referencia a la biblioteca de modelos
  4. Crear sitio de implementación: Agregue un proyecto de implementación que compile el "sitio web vacío", vaya a la "página de propiedades" y Controlar:"Fusionar todas las salidas en un solo ensamblaje" y "Tratar como componente de biblioteca" y asegúrese de Desmarcar:"Permitir que este sitio precompilado sea actualizable"
  5. Salida de implementación de referencia: En el proyecto principal, agregue una referencia al resultado del sitio de implementación.
  6. ÁSPID.- Controles compilados: Los controles aparecen debajo del ASP.espacio de nombres y se nombran de dos maneras A.si la página .ascx/aspx no declaró un "Nombre de clase", entonces se nombran usando su carpeta y nombre de archivo con guiones bajos, por ejemplo.< %@ Control lenguaje = "c#" classname = "admin_index" %> B.Si declararon un nombre de clase, entonces ese es su nombre.

  7. Elemento de lista

Uso: El código de ejemplo está debajo

Aquí hay un ejemplo de uso.

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

        }
    }

Yo he llegado con una solución más simple en la línea de consejos de Ruben. Ha funcionado sin problemas durante un mes:

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

        }
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top