Créer une Instance Aspx Page de Ascx de Contrôle Dans l'Arrière de la Fin de la Classe sans Chargement FilePath

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

  •  19-09-2019
  •  | 
  •  

Question

Question: Est-il possible en fin de code (pas dans le code derrière, mais dans un véritable retour en fin de classe) pour charger et afficher une page ou de contrôle définies dans un .aspx ou .ascx sans avoir à utiliser la Charge(chemin d'accès) et au lieu de cela il suffit de créer une instance de la page/de contrôle de la classe?

Je veux être capable de faire cela (à partir d'une extrémité arrière de la classe de ne PAS un code derrière):

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

au lieu de cela

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

Détails: Dans un Asp.net WebApp j'ai parfois besoin de rendre un contrôle utilisateur (.ascx) ou page (.aspx) comme une chaîne HTML.Lorsque une page ou le contrôle hérite d'un code de la catégorie s'affiche dans intellisense dans mon dos, le code de fin et je peux créer une instance et de définir les propriétés sans se moment de la compilation ou de l'exécution des erreurs.Cependant, lorsque j'essaie d'afficher la page ou le contrôle, je reçois toujours une chaîne vide et lors de l'inspection de la page ou de contrôle montre supprimé interne des erreurs de rendu, à moins que je charge la page ou le contrôle à l'aide de son physique chemin d'accès au fichier.

Je pense que la clé du problème a à voir avec quand et comment le .aspx / .ascx fichiers d'exécution de la compilation.Je ne veux pas créer un pré classe compilée de la bibliothèque de l'utilisateur, les contrôles car cela rendrait le processus de conception maladroite et j'aime vraiment le concepteur des fonctionnalités offertes par le .aspx / .ascx pages et donc j'aimerais trouver un moyen de rendre les pages de la compilation dans la solution de sorte qu'ils sont utilisables comme n'importe quel autre extrémité arrière de la classe, mais qui peuvent encore être créés à l'aide de la designer.Je veux le meilleur des deux mondes (1) pour être en mesure de modifier des pages et des contrôles dans le concepteur et (2) créer des instances et de définir leurs propriétés à l'aide du dos de la fin des classes.

Était-ce utile?

La solution

Voici une approche qui peut aider dans des situations comme celle-ci.

Le code « back-end » ne peut pas savoir où le contrôle de l'utilisateur se trouve, mais le contrôle de l'utilisateur ne sait où il est.

Ainsi, dans le contrôle de l'utilisateur, ajoutez une méthode statique comme ceci:

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

(Le CustomDto est inclus pour illustrer la façon dont les données initiales peuvent être transmises au contrôle de l'utilisateur. Si vous n'avez pas besoin de faire cela, prenez-le!)

Avec cela, le code qui charge le contrôle de l'utilisateur fait pas besoin de connaître le chemin où le contrôle de l'utilisateur est physiquement situé. Si cet emplacement ne change jamais, puis mettre à jour ce un endroit. Tout autre code qui utilise cette UserControl ne change pas.

Dans votre code back-end, ou nulle part ailleurs, vous pouvez faire quelque chose à ceci:

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

Autres conseils

D'une manière générale: non.

Pour autant que je sache, ASP.NET hérite de vos classes pour combiner les .aspx / .ascx modèle avec votre code. Ceci est la raison pour laquelle vos commandes apparaissent vide: le code de combiner le modèle avec votre code est manquant. Cela se fait habituellement par ASP.NET la première fois que vous accédez à un contrôle de page ou de l'utilisateur (c'est précisément la raison pour laquelle le premier coup est un peu lent: il est en fait générer et compiler le branchement code).

Pour les sites Web précompilés ASP.NET génère ce code dans le cadre de votre .dll site précompilé à l'avance, ce qui explique pourquoi ces sites se chargent plus rapidement. Cependant, IIRC vous aurez toujours besoin d'instancier les générés cours plutôt que vos classes d'origine.

Il est une demande assez commun, mais jusqu'à présent, MS n'a pas fourni les outils pour le faire.

Edit:. Bien que je ne vois pas pourquoi vous voulez rendre un contrôle à une chaîne en mémoire, je pourrais avoir une solution aux problèmes de construction

Si vous vous en tenez à des fichiers non compilé .ascx (en utilisant le modèle de site web plutôt que le modèle d'application Web), vous pouvez réellement les développer séparément en les plaçant physiquement dans un sous-dossier de votre projet principal, et les traiter comme des fichiers de contenu seulement. Ensuite, vous pouvez faire un projet séparé avec ce sous-dossier que le dossier racine. Il vous suffit de traiter les fichiers dans ce sous-dossier sous forme de fichiers de site Web, le principal projet peut encore être une application web. (En fait recommandé, parce que vous ne voulez pas les fichiers .csproj inclus dans le projet principal.)

Cependant, le code partagé (qui est partagé entre le projet de contrôle et le projet principal) devrait être mis dans un projet de bibliothèque séparée, de sorte que vous pouvez compiler chaque projet séparément sans interdépendances.

Utilisation LoadControl dans le projet principal les compiler à la volée (le code est derrière possible); si vous avez besoin de définir les propriétés, vous devez toutefois définir des interfaces dans le projet commun, les mettre en œuvre sur les contrôles utilisateur appropriés et lancer le contrôle créé par LoadControl à l'interface appropriée.

J'ai développé une solution qui résout mon problème dans VS 2008:

  1. Créer Site Principal De La Solution: Créer un MVC 1 solution de Site internet dans VS 2008
  2. Création De Modèle De Bibliothèque De Classe: Ajouter une Bibliothèque de classes pour le Modèle de Code
  3. Créer Afficher Le Code: Ajouter un Site Vide" maintenir la .ascx pages, et d'ajouter une référence à la bibliothèque de modèles de
  4. Créer Site De Déploiement: Ajouter un projet de déploiement qui compile le "Site Vide" goto "propriétés de la page" et Vérifier:"Fusionner Toutes les sorties en une assemblée unique" et "Traiter en tant que composant de la bibliothèque" et assurez-vous de Décochez la case:"Permettre à ce site précompilé à être mis à jour"
  5. Référence Déploiement De Sortie: Dans le projet principal ajouter une référence à la sortie du site de Déploiement.
  6. ASP.- Compilé Contrôles: Contrôles affichent en vertu de l'ASP.espace de noms et sont nommés dans deux façons A.si l' .ascx / page aspx ne pas déclarer un "ClassName", alors qu'ils sont nommés à l'aide de leur dossier et un nom de fichier avec des traits de soulignement ex.<%@ Control Language="C#" ClassName="Admin_Index" %> B.si ils l'ont fait de déclarer un nom de classe alors que c'est leur nom

  7. Élément de la liste

Utilisation: L'exemple de code est ci-dessous

Voici un exemple d'utilisation

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

        }
    }

Je suis venu avec une solution plus simple le long des lignes de l'avis de Ruben. Il a travaillé sans problème pendant environ un mois:

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

        }
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top