Criar instância aspx de controle ASCX Em um End Class Voltar sem carregar FilePath

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

  •  19-09-2019
  •  | 
  •  

Pergunta

Pergunta: É possível no código de back-end (não no código por trás, mas em uma classe de back-end real) para carregar e processar uma página ou controle definido em um .aspx ou .ascx sem ter que usar Load (path) e ao invés de apenas criar uma instância da classe de página / controle?

Eu quero ser capaz de fazer isso (a partir de uma classe de back-end não um atrás de código):

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

Em vez disso

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

Detalhes: Em um Asp.net WebApp eu ocasionalmente precisamos para processar um controle de usuário (.ascx) ou página (.aspx) como uma string HTML. Quando uma página ou controle herda de uma por trás de códigos, suas mostras de classe-se em intellisense no meu código de back-end e eu posso criar uma instância e definir propriedades sem ficar tempo ou tempo de execução erros de compilação. No entanto, quando tento processar a página ou controlar Eu sempre obter uma seqüência vazia e após a inspeção da página ou controle mostra suprimida erros de processamento interno, a menos que eu carregar a página ou controle usando seu caminho de arquivo físico.

Eu acho que a questão-chave tem a ver com quando e como o aspx / ascx arquivos são runtime compilado. Eu não quero criar uma biblioteca de classes pré compilado de controles de usuário porque isso tornaria o processo de design estranho e eu realmente gosto do designer de recursos oferecidos pelo .aspx / páginas .ascx e por isso eu gostaria de encontrar uma maneira de tornar as páginas compilar na solução de modo que eles são utilizáveis ??como qualquer outra classe de back-end, mas ainda pode ser criado usando o designer. Eu quero o melhor dos dois mundos (1) para ser capaz de editar páginas e controles no designer e (2) criar instâncias e definir suas propriedades usando classes back-end.

Foi útil?

Solução

Aqui é uma abordagem que pode ajudar em situações como esta.

O código de "back-end" pode não saber onde o controle de usuário está localizado, mas o User Control sabe onde ele está.

Assim, no User Control, adicionar um método estático como este:

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

(A CustomDto está incluído para ilustrar como os dados inicial pode ser passada para o User Control. Se você não precisa fazer isso, levá-lo para fora!)

Com isso, o código que carrega o controle de usuário faz não necessidade de saber o caminho para onde o controle de usuário está localizado fisicamente. Se esse local nunca muda, então atualizar esta localização. Todos os outros códigos que usa esse UserControl é alterada.

Em seu código de back-end, ou em qualquer outro lugar, você pode fazer algo com isto:

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

Outras dicas

De um modo geral: não.

Tanto quanto eu sei, herda ASP.NET a partir de suas classes para combinar o aspx / ascx modelo com seu código. É por isso que os controles aparecer vazio: o código para combinar o modelo com o seu código está faltando. Isso geralmente é feito por ASP.NET a primeira vez que você acessar uma página ou controle de usuário (que é precisamente por isso que o primeiro hit é um pouco lento: é realmente gerar e compilar o código-conexão).

Para sites de pré-compilados ASP.NET gera esse código como parte de .dll do seu site pré-compilado com antecedência, que é por isso que esses sites carregar mais rápido. No entanto, IIRC você ainda vai precisar instanciar os gerado aulas em vez de suas classes originais.

É um pedido bastante comum, mas até agora MS não forneceu as ferramentas para fazer isso.

Editar:. Embora eu não vejo por que você iria querer processar um controle para uma seqüência na memória, eu poderia ter uma solução para os problemas de construção

Se você furar a arquivos não-compilado .ascx (usando o modelo de web site, em vez do modelo de aplicativo web), você pode realmente desenvolvê-las separadamente, colocando-os fisicamente na subpasta do seu projeto principal, e tratá-los como arquivos de conteúdo só. Então, você pode fazer um projeto separado com este subpasta da pasta raiz. Você só precisa tratar os arquivos nesta subpasta como arquivos do site web, o projeto principal ainda pode ser uma aplicação web. (Na verdade recomendado, porque você não quer que os arquivos csproj incluídos no projeto principal.)

código No entanto, compartilhado (isto é, compartilhado entre os controles de projeto e do projeto principal) deve ser colocado em um projeto de biblioteca separada, assim você pode compilar cada projeto separadamente sem interdependências.

Usando LoadControl dentro do projeto principal será compilá-los em tempo real (por trás do código é possível); se você precisa definir propriedades, você deve, contudo, definir interfaces no projeto compartilhado, implementá-las nos controles de usuário apropriados e lançou o controle criado por LoadControl para a interface apropriada.

Eu desenvolveu uma solução que resolve meu problema no VS 2008:

  1. Criar Principal Solution Site: Criar uma solução site MVC 1 em VS 2008
  2. Criar Biblioteca de Classes Modelo: Adicionar uma biblioteca de classes para o Código Modelo
  3. Criar View Code: Adicionar um "Website Empty" para manter as páginas .ascx, e adicione uma referência à biblioteca modelo
  4. Criar implantação do site: Adicionar um projeto de implantação que compila o "Website vazio" goto a página "Propriedades" e Verificar : "Mesclar Todas as saídas em um único assembly" e "Tratar como componente de biblioteca" e certifique-se de desmarque : "Permitir que este site pré-compilado seja atualizável"
  5. Saída de Implantação Referência: No projeto principal adicionar uma referência para a saída do local de implantação.
  6. ASP. - Controles compilado: Controles aparecer sob o ASP. namespace e são nomeados em duas maneiras A. Se a página ascx / aspx não declarar um "ClassName", então eles são nomeados com sua pasta eo nome do arquivo com sublinhados ex. <% @ Control Language = "C #" ClassName = "admin_index" %> B. se eles fizeram declarar um nome de classe, em seguida, que é o seu nome

  7. item da lista

Uso: código Exemplo abaixo é

Aqui está um exemplo 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);

        }
    }

Eu vim com uma solução mais simples ao longo das linhas de conselho de Ruben. Ele tem trabalhado sem problemas por cerca de um mês:

//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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top