Question

Je charge dynamiquement les contrôles utilisateur en les ajoutant à la collection Controls du formulaire Web.

Je souhaite masquer les contrôles utilisateur s'ils provoquent une exception non gérée lors du rendu.

J'ai donc essayé de m'accrocher à l'événement Error de chaque UserControl, mais il semble que cet événement ne se déclenche jamais pour les UserControls comme pour la classe Page.

J'ai fait quelques recherches sur Google et cela ne semble pas prometteur.Des idées ici ?

Était-ce utile?

La solution

mmilic, faisant suite à votre réponse à mon idée précédente..

Aucune logique supplémentaire requise !C'est là le point, vous ne faites rien aux classes en question, vous les enveloppez simplement dans du papier bulle d'instanciation !:)

OK, j'allais justement parler d'un point, mais je voulais voir ce travail par moi-même, alors j'en ai bricolé quelques-uns. très code approximatif mais le concept est là et il semble fonctionner.

EXCUSES POUR LE LONG POST

Le chargeur sécurisé

Ce sera essentiellement la « bulle » que j’ai mentionnée.Il obtiendra les contrôles HTML, détectant toutes les erreurs qui se produisent lors du rendu.

public class SafeLoader
{
    public static string LoadControl(Control ctl)
    {
        // In terms of what we could do here, its down
        // to you, I will just return some basic HTML saying
        // I screwed up.
        try
        {
            // Get the Controls HTML (which may throw)
            // And store it in our own writer away from the
            // actual Live page.
            StringWriter writer = new StringWriter();
            HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
            ctl.RenderControl(htmlWriter);

            return writer.GetStringBuilder().ToString();
        }
        catch (Exception)
        {
            string ctlType = ctl.GetType().Name;
            return "<span style=\"color: red; font-weight:bold; font-size: smaller;\">" + 
                "Rob + Controls = FAIL (" + 
                ctlType + " rendering failed) Sad face :(</span>";
        }
    }
}

Et quelques contrôles..

Ok, je viens de me moquer de deux contrôles ici, l'un lancera l'autre rendra indésirable.Pointez ici, je m'en fous.Ceux-ci seront remplacés par vos contrôles personnalisés.

MauvaisContrôle

public class BadControl : WebControl
{
    protected override void Render(HtmlTextWriter writer)
    {
        throw new ApplicationException("Rob can't program controls");
    }
}

BonContrôle

public class GoodControl : WebControl
{
    protected override void Render(HtmlTextWriter writer)
    {
        writer.Write("<b>Holy crap this control works</b>");
    }
}

La page

OK, regardons donc la page "test".Ici, j'instancie simplement les contrôles, récupère leur code HTML et le publie, je suivrai avec des réflexions sur le support des concepteurs, etc.

Code de page derrière

    protected void Page_Load(object sender, EventArgs e)
    {
        // Create some controls (BadControl will throw)
        string goodHtml = SafeLoader.LoadControl(new BadControl());
        Response.Write(goodHtml);

        string badHtml = SafeLoader.LoadControl(new GoodControl());
        Response.Write(badHtml);
    }

Pensées

OK, je sais ce que vous pensez : "ces contrôles sont instanciés par programme, qu'en est-il du support du concepteur ?J'ai passé des heures fous à rendre ces commandes agréables pour le concepteur, maintenant vous jouez avec mon mojo".

OK, donc je n'ai pas encore vraiment testé cela (ce sera probablement le cas dans une minute !) mais l'idée ici est de remplacer la méthode CreateChildControls pour la page, de prendre l'instance de chaque contrôle ajouté sur le formulaire et de l'exécuter via SafeLoader.Si le code réussit, vous pouvez l'ajouter à la collection Controls comme d'habitude, sinon vous pouvez créer des littéraux erronés ou quelque chose du genre, à vous de décider, mon ami.

Enfin..

Encore une fois, désolé pour le long post, mais je voulais obtenir le code ici afin que nous puissions en discuter :) J'espère que cela aide à démontrer mon idée :)

Mise à jour

Testé en plaçant un contrôle sur le concepteur et en remplaçant la méthode CreateChildControls par ceci, fonctionne bien, peut nécessiter un peu de nettoyage pour rendre les choses plus belles, mais je vous laisse cela ;)

protected override void CreateChildControls()
{
    // Pass each control through the Loader to check
    // its not lame
    foreach (Control ctl in Controls)
    {
        string s = SafeLoader.LoadControl(ctl);
        // If its bad, smack it downnnn!
        if (s == string.Empty)
        {
            ctl.Visible = false; // Prevent Rendering
            string ctlType = ctl.GetType().Name;
            Response.Write("<b>Problem Occurred Rendering " + 
                ctlType + " '" + ctl.ID + "'.</b>");
        }
    }
}

Apprécier!

Autres conseils

C'est un problème intéressant..Je suis encore assez frais en ce qui concerne les contrôles personnalisés, etc., mais voici mes réflexions (n'hésitez pas à commenter/corriger les personnes !).(Je réfléchis/écris un peu à voix haute ici !)

  • Si une erreur survient lors du rendu, dans certains cas, ne serait-il pas trop tard ?(puisque certains des contrôles HTML peuvent avoir déjà été envoyés au Writer et sortis).
  • Par conséquent, ne serait-il pas préférable d'envelopper la méthode Render du contrôle utilisateur, mais plutôt que de lui transmettre la référence au HtmlTextWriter "Live", vous transmettez la vôtre, piègez toutes les exceptions levées dans cette petite "bulle" de sécurité, si tout se passe bien. , vous transmettez ensuite votre HTML résultant au véritable HtmlTextWriter?
  • Cette logique pourrait probablement être associée à une classe wrapper générique que vous utiliseriez pour charger/restituer dynamiquement les contrôles au moment de l'exécution.
  • Si des erreurs surviennent, vous avez toutes les informations nécessaires à votre disposition !(c'est-à-dire les références de contrôle, etc.).

Juste mes pensées, enflammez-vous !:D ;)

Selon l'endroit où se produisent vos erreurs, vous pouvez faire quelque chose comme...

public abstract class SilentErrorControl : UserControl
{
    protected override void Render( HtmlTextWriter writer )
    {
        //call the base's render method, but with a try catch
        try { base.Render( writer ); }
        catch ( Exception ex ) { /*do nothing*/ }
    }
}

Héritez ensuite de SilentErrorControl au lieu de UserControl.

Global.asax et Application_Error ?

http://www.15seconds.com/issue/030102.htm

Ou l'événement Page_Error sur une page individuelle uniquement :

http://support.microsoft.com/kb/306355

void Page_Load(object sender, System.EventArgs e)
{
    throw(new ArgumentNullException());
}

public void Page_Error(object sender,EventArgs e)
{
    Exception objErr = Server.GetLastError().GetBaseException();
    string err =    "<b>Error Caught in Page_Error event</b><hr><br>" + 
                    "<br><b>Error in: </b>" + Request.Url.ToString() +
                    "<br><b>Error Message: </b>" + objErr.Message.ToString()+
                    "<br><b>Stack Trace:</b><br>" + 
                      objErr.StackTrace.ToString();
    Response.Write(err.ToString());
    Server.ClearError();
}

De plus, Karl Seguin (Salut Karl !) a publié un article sur l'utilisation de HttpHandler :

http://codebetter.com/blogs/karlseguin/archive/2006/06/12/146356.aspx

(Je ne sais pas quelle est l'autorisation de le reproduire, mais si vous souhaitez rédiger une réponse, vous avez obtenu mon vote positif ☺)

Que diriez-vous d'ajouter une nouvelle sous-classe de UserControl qui gère les erreurs de ses méthodes de rendu et de chargement (afin qu'elles se cachent comme vous le souhaitez), puis d'en hériter pour vos contrôles utilisateur ?

Je ne suis pas sûr de comprendre votre réponse..Comment chargez-vous vos contrôles et les ajoutez-vous à votre collection de contrôles ?

C'était tout l'intérêt du bit ajouté dans la section "Mise à jour".Vous avez la possibilité d'utiliser le Chargeur sécurisé où bon vous semble.

Je ne sais pas pourquoi vous pensez ne pas avoir accès/contrôler le HTML ?Le but du SafeLoader est que vous ne se soucier ce qu'est le code HTML, vous essayez simplement de "sortir" le contrôle (dans la "bulle") et de déterminer s'il se charge correctement dans son état actuel.

Si c'est le cas (c'est-à-direle code HTML est renvoyé), vous pouvez alors en faire ce que vous voulez, afficher le code HTML, ajouter le contrôle à la collection de contrôles, peu importe !

Sinon, vous pouvez faire ce que vous voulez, afficher un message d'erreur, lancer une exception personnalisée.Le choix t'appartient!

J'espère que cela vous aidera à clarifier les choses, sinon, criez :)

J'ai utilisé l'approche de @Keith, mais le problème est que le contrôle est rendu jusqu'à ce que l'exception soit levée, ce qui peut entraîner des balises HTML ouvertes.Je restitue également les informations d'exception dans le contrôle si en mode débogage.

  protected override void Render(System.Web.UI.HtmlTextWriter writer)
  {
     try
     {
        // Render the module to a local a temporary writer so that if an Exception occurs
        // the control is not halfway rendered - "it is all or nothing" proposition
        System.IO.StringWriter sw = new System.IO.StringWriter();
        System.Web.UI.HtmlTextWriter htw = new System.Web.UI.HtmlTextWriter(sw);
        base.Render(htw);

        // We made it!  Copy the Control Render over
        writer.Write(sw.GetStringBuilder().ToString());
     }
     catch (System.Exception ex)
     {
        string message = string.Format("Error Rendering Control {0}\n", ID);
        Log.Error(message, ex);
        if (Page.IsDebug)
           writer.Write(string.Format("{0}<br>Exception:<br><pre>{1}\n{2}</pre>", message, ex.Message, ex.StackTrace));
     }
  }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top