Pregunta

Estoy cargando dinámicamente controles de usuario agregándolos a la colección Controles del formulario web.

Me gustaría ocultar los controles de usuario si provocan una excepción no controlada durante el procesamiento.

Entonces, intenté conectarme al evento Error de cada Control de usuario, pero parece que este evento nunca se activa para los Controles de usuario como lo hace para la clase Página.

Busqué un poco en Google y no parece prometedor.¿Alguna idea aquí?

¿Fue útil?

Solución

mmilic, siguiendo desde tu respuesta para mi idea previa..

¡No se requiere lógica adicional!Ese es el punto, ¡no estás haciendo nada con las clases en cuestión, simplemente envolviéndolas en un plástico de burbujas de creación de instancias!:)

Vale, iba a enumerar algunas viñetas, pero quería ver cómo funcionaba esto por mí mismo, así que improvisé algunas muy Código aproximado, pero el concepto está ahí y parece funcionar.

DISCULPAS POR EL LARGO PUBLICACIÓN

El cargador seguro

Esta será básicamente la "burbuja" que mencioné.Obtendrá los controles HTML, detectando cualquier error que ocurra durante el renderizado.

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

Y algunos controles...

Ok, acabo de burlarme de dos controles aquí, uno arrojará y el otro generará basura.Punto aquí, me importa una mierda.Estos serán reemplazados por sus controles personalizados.

Mal control

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

Buen control

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

La página

Bien, echemos un vistazo a la página de "prueba".Aquí simplemente creo una instancia de los controles, tomo su html y lo genero, seguiré con ideas sobre el soporte del diseñador, etc.

Código subyacente de página

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

Pensamientos

Bien, sé lo que estás pensando: "estos controles se crean instancias mediante programación, ¿qué pasa con el soporte del diseñador?Pasé horas de locura haciendo que estos controles fueran agradables para el diseñador, ahora estás jugando con mi encanto".

Bien, realmente no he probado esto todavía (¡probablemente lo haré en un minuto!), pero la idea aquí es anular el método CreateChildControls para la página, tomar la instancia de cada control agregado en el formulario y ejecutarlo a través de SafeLoader.Si el código se aprueba, puedes agregarlo a la colección de Controles como de costumbre; si no, puedes crear literales erróneos o algo así, eso depende de ti, amigo mío.

Finalmente..

Nuevamente, perdón por la larga publicación, pero quería obtener el código aquí para que podamos discutir esto :) Espero que esto ayude a demostrar mi idea :)

Actualizar

Probado colocando un control en el diseñador y anulando el método CreateChildControls con esto, funciona bien, puede que necesite un poco de limpieza para que las cosas se vean mejor, pero eso se lo dejo a usted;)

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

¡Disfrutar!

Otros consejos

Este es un problema interesante...Todavía estoy bastante fresco en lo que respecta a controles personalizados, etc., pero aquí están mis pensamientos (¡siéntete libre de comentar/corregir a las personas!).(¡Estoy pensando/escribiendo en voz alta aquí!)

  • Si ocurre un error durante el renderizado, en algunos casos, ¿no sería demasiado tarde?(dado que es posible que algunos de los controles HTML ya se hayan enviado al escritor y a la salida).
  • Por lo tanto, ¿no sería mejor ajustar el método Render del control de usuario, sino que en lugar de pasarle la referencia al HtmlTextWriter "Live", pase el suyo propio y atrape cualquier excepción generada en esta pequeña "burbuja" de seguridad, si todo va bien? , luego pasa el HTML resultante al HtmlTextWriter real.
  • Esta lógica probablemente podría incluirse en una clase contenedora genérica que usaría para cargar/presentar dinámicamente los controles en tiempo de ejecución.
  • Si se produce algún error, ¡tienes toda la información que necesitas a tu disposición!(es decir, referencias de control, etc.).

¡Solo mis pensamientos, llamas!:D ;)

Dependiendo de dónde se produzcan los errores, puede hacer algo como...

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*/ }
    }
}

Luego herede SilentErrorControl en lugar de UserControl.

¿Global.asax y Application_Error?

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

O el evento Page_Error solo en una página individual:

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

Además, Karl Seguin (¡Hola Karl!) publicó una publicación sobre el uso de HttpHandler:

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

(No estoy seguro de cuál es el permiso para reproducirlo, pero si quieres escribir una respuesta, tienes mi voto a favor ☺)

¿Qué tal agregar una nueva subclase de UserControl que maneje sus métodos de procesamiento y carga (para que se oculten como desee) y luego heredar eso para sus controles de usuario?

No estoy seguro de entender tu respuesta..¿Cómo carga sus controles y los agrega a su colección de controles?

Ese fue el objetivo del bit agregado en la sección "Actualizar".Tienes la flexibilidad de utilizar el Cargador seguro donde quieras.

No estoy seguro de por qué cree que no tiene acceso/control sobre el HTML.El objetivo de SafeLoader es que usted no cuidado cuál es el html, simplemente intente "generar" el control (dentro de la "burbuja") y determinar si se carga correctamente en su estado actual.

Si es así (es decir,se devuelve el html) entonces puedes hacer lo que quieras con él, generar el html, agregar el control a la colección de controles, ¡lo que sea!

Si no es así, puede hacer lo que quiera, mostrar un mensaje de error, lanzar una excepción personalizada...¡La decisión es tuya!

Espero que esto te ayude a aclararte las cosas, si no, grita :)

Utilicé el enfoque de @Keith, pero el problema es que el control se representa hasta que se genera la excepción, lo que potencialmente resulta en etiquetas HTML abiertas.También estoy representando la información de excepción en el Control si estoy en modo de depuración.

  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));
     }
  }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top