Tratar un dato dentro de un gridview

Leyendo el post Eval in gridview de los foros de ASP.NET en castellano (a los que a todo el mundo aconsejo), y dado que esta duda también me surgió en su momento y también me resolvió el señor Fredrik Normén en este artículo, paso a comentar un poquito la problemática.

Cuando estamos trabajando con controles de ASP.NET que leen de la base de datos y muestran un listado de resultados, como por ejemplo el gridview, el datalist o el repeater, en ocasiones nos hace falta tratar algún que otro dato.

Por ejemplo, queremos mostrar el listado de temas de un foro con el título de los temas y los 20 primeros caracteres de su texto con un gridview.

Algo tan sencillo como leer esos primeros 20 caracteres puede resultar muy complejo!!!

El truquito es llamar desde el Item del gridview a una función que hayamos creado en nuestro código. Siguiendo el ejemplo, tendríamos:

ejemplo.aspx
<asp:GridView ID="gv_Mensajes" runat="server" AutoGenerateColumns="False" AllowPaging="True" PageSize="20" OnRowDataBound="gv_Mensajes_RowDataBound" OnDataBinding="gv_Mensajes_DataBinding">
   <Columns>
      <asp:TemplateField>
         <ItemTemplate>   
            El título es:
            <asp:Label ID="lb_Titulo" runat="server" Text='<%# Eval("Titulo") %>'></asp:Label>  
         </ItemTemplate>
      </asp:TemplateField>      
      <asp:TemplateField>
         <ItemTemplate>   
            <asp:Label ID="lb_Texto" runat="server" Text='<%# acorta(Eval("Texto")) %>'></asp:Label>  
         </ItemTemplate>
     </asp:TemplateField>
   </Columns>
</asp:GridView>


Como veis en este (feo) gridview, el título lo dejamos tal cual está, pero al texto lo mandamos a la función acorta para que nos dé el valor de la Label.

En cuanto a la función acorta, tenemos:

ejemplo.aspx.cs
protected string acorta(object texto)
{
   return texto.ToString().Substring(0, 20);
}


Es muy importante darse cuenta de que la función acorta debe ser protected o public, pero no private, porque entonces nos dará un error diciéndonos que no se puede acceder a acorta dado su nivel de protección.

Por otra parte, también aconsejo, o al menos yo me he acostumbrado, a parsear la variable desde el gridview, de modo que acorta no tenga que recibir una variable object, sino, en este caso, un string.

De modo que quedaría:

<%# acorta((string)Eval("Texto")) %>

protected string acorta(string texto)


El motivo es que de ese modo, podemos acceder a la función acorta desde otros sitios que no sean el gridview sin necesidad de obligarle a tratar a texto como una variable object cuando sabemos que es un string. Esto puede parecer poco importante cuando estamos hablando de una simple variable de texto, pero sí es muy importante cuando por ejemplo estamos trabajando con una lista genérica con 1000 items.



Expresiones regulares. Cómo trabajarlas con ASP.NET.

Como parte segunda del artículo sobre expresiones regulares, recordarte que en la primera parte "Expresiones regulares. Introducción", se dio una pequeña entrada a lo que eran las expresiones regulares, para qué podían servir y algunos enlaces buenos para profundizar en el tema. Ahora, lo que vamos a ver es cómo con ASP.NET se pueden tratar esas expresiones regulares de un modo muy sencillo.

Supongamos que queremos recoger todas las palabras de un texto que estén en negrita, es decir que estén entre los tags <b> y </b> (ya, ya sé que lo sabías ).

Por tanto, nuestra expresión regular sería: (?<=<b>).+(?=</b>). Es decir, que tenga como prefijo <b>, como sufijo </b> y entre ambos uno o más caracteres.

Como este simple código lo obtendríamos

busconegrita.aspx.cs
public MatchCollection buscarNegrito(string texto)
{
   string pattern = "(?<=<b>).+(?=</b>)";
   Regex myReg = new Regex(pattern, RegexOptions.IgnoreCase);

   if (myReg.IsMatch(texto))
   {
      MatchCollection resultadosBusqueda = myReg.Matches(texto);
   }
   else
   {
      return null;
   }

   return resultadosBusqueda;
}


Donde lo primero que hacemos es definir la expresión regular; podríamos habérsela pasado como atributo de la función, por lo que ésta nos serviría para cualquier expresión regular que definamos.

Seguidamente creamos la variable de tipo Regex, inicializándola con la propia expresión regular y con un segunda atributo muy útil y que nos ahorra algún que otro dolor de cabeza, que le dice a la variable Regex que no distinga entre mayúsculas y minúsculas.

A continuación comprobamos con myReg.IsMatch(texto) si nos va a devolver algún resultado. En este caso no es algo demasiado útil, pero en otras ocasiones podría servir.

Si es cierto que se nos va a devolver algún resultado, los recogemos todos en un MatchCollection, que no es más que una colección de resultados que más tarde podremos iterar. Como alternativa, podríamos haber cogido un único resultado con: 

   Match myMatch = myReg.Match(texto);
 
y luego ir iterando con:

   myMatch.NextMatch();

pero personalmente, si el eficiencia no es extremadamente necesaria, prefiero las colecciones, pues es mucho más cómodo e intuitivo trabajar con ellas.

Si por ejemplo la frase fuera:

El hombre es una <b>fracción</b>, donde el <b>numerador</b> es lo que es y el <b>denominador</b> lo que dice ser.

... myMatch tendría tres elementos: fracción, numerador y denominador.

Por tanto, ya sabemos inicializar una variable Regex, así como a ver si nos daría algún resultado. Pero lo más importante es ver que es muy sencillo recoger dichos resultados en una colección. Para no dejarse nada en el tintero, veamos cómo se puede trabajar con esa colección resultante:

trataResultados.aspx.cs
private bool trata(string texto)
{
   MatchCollection resultados = buscarnegrita(texto);
   foreach (Match resultado in resultados)
   {
      if (Match.Value == "subgurim")
      {
         return true;
      }
   }
   return false;
}


Donde simplemente buscamos lo que esté en negrita dentro de un texto, y si alguno de los resultados es "subgurim" nos devuelve verdadero, y si no es así, falso.

Así pues, con esto ya podríamos trabajar, pero ASP.NET sigue dándonos más opciones tremendamente útiles.

Por ejemplo, si trabajamos con grupos de expresiones regulares (si no sabes lo que son los grupos de expresiones regulares, te recomiendo que le eches un vistazo a tutoriales como "The 30 minutes regex tutorial" o en www.regular-expressiones.info), podemos trabajarlas muy cómodamente con:

- GetGroupNames(): devuelve un array de strings con los nombres que le hemos dado a los grupos.
- GetGroupNumbers(): nos devuelve un array de enteros con los número que se corresponden a los grupos
- GroupNameFromNumber(0): donde le damos un entero y nos devuelve un string con el nombre del grupo cuyo índice se corresponde con el entero.
- GroupNumberFromName("nombredelgrupo"): al contrario de la anterior, le damos el nombre del grupo y nos devuelve el entero.

Para finalizar, hacer mención a otras dos funciones muy útiles y a las que se les puede sacar muchísimo provecho, son:
- Replace(...).
- Split(...).

Pongamos un ejemplo de ambas:

ejemploReplaceRegex.aspx.cs
public void reemplazar(string texto)
{
   string pattern = "(?<=<b>).+(?=</b>)";
   Regex myReg = new Regex(pattern, RegexOptions.IgnoreCase);

   texto = myReg.Replace(texto, "estoy en negrita");
}


Donde, como en el ejemplo anterior, definimos que nuestro pattern es "todo lo que esté en negrita sin incluir los tags <b> y </b>", y con el replace, reemplazamos de la variable texto los resultados de ese pattern por el texto "estoy en negrita".

De modo que la frase

"¿Quieres ser <b>rico</b>? Pues <b>no te afanes</b> en aumentar tus bienes sino en <b>disminuir tu codicia</b>. Epicuro."

... pasaría a ser...

"¿Quieres ser <b>estoy en negrita</b>? Pues <b>estoy en negrita</b> en aumentar tus bienes sino en <b>estoy en negrita</b>. Epicuro.".

¡¡¡Imaginaos la que habría que armar para hacer esto con el método Replace(...) de las variables string!!!

La misma función Replace de Regex, admine un par de parámetros más, el típico "count" en el que le dices el máximos de reemplazos que debe hacer, y "startat", en el que le indicas a partir de dónde quiere que hagas los reemplazos.


En cuanto a Split(...), es algo más sencillo:

ejemploSplitRegex.aspx.cs
public string[] mysplit(string texto)
{
   string pattern = "<br>|<br />";
   Regex myReg = new Regex(pattern, RegexOptions.IgnoreCase);

   return myReg.Split(texto);
}


Donde en este caso, el pattern indica que queremos encontrar todas las referencias a "<br>" y a "<br/>". La función devuelve un array de strings, donde el delimitador ha sido cualquiera de esas dos variables.

Esto mismo también se podría hacer con la función Split(...) de las variables string, pero el límite del Split de Regex sólo lo pone tu imaginación, y el del Split() de string lo pone tu paciencia .


Un último apunte, también se pueden hacer todas estas operaciones apoyándonos en las funciones estáticas del propio tipo Regex, por lo que no haría falta inicializar ninguna variable.

Por ejemplo, podríamos hacer

Regex.(texto, "<br>|<br />", RegexOptions.IgnoreCase);

en lugar de todo el código del último ejemplo.

El hacerlo de un modo u otro depende de muchas cosas, como el modo de proceder habitual de cada uno o la situación de código en la que te encuentres.


Como conclusión, os aconsejo no perdáis nunca de vista a Regex y que lo utilicéis tanto como podáis si la situación lo requiere.




Asignar foco a un cuadro de texto

En ocasiones puede resultarnos muy útil que nada más se cargue una página, el cursor esté sobre el cuadro de texto que nosotros queramos, incluso si nos interesa elegir ese cuadro de texto de forma dinámica.

Así pues, ASP.NET nos lo vuelve a poner muy fácil, pues no hay más que añadir este texto, por ejemplo en el evento Page_Load:

AsignarFoco.aspx.cs
    protected void Page_Load(object sender, EventArgs e)
    {
        this.SetFocus("TextBox1");
    }

En este caso, a SetFocus le pasamos la ID que le hemos dado al control, pero también se le puede pasar el control entero. Por ejemplo, y tal y como cuenta (el gran) Fredrik Normén en su artículo Set Focus to the UserName textbox of the Login control, podemos asignar el foco al cuadro de texto de "Nombre de usuario" del control Login:

AsignarFoco.aspx.cs
    TextBox userName = Login1.FindControl("UserName") as TextBox;
    if (userName != null)
       Page.SetFocus(userName);


Como veis, lo que se hace es recoger el control UserName que está dentro de Login con FindControl(IdDelControl) y se convierte a TextBox (debe convertirse porque FindControl devuelve un Control, padre de TextBox). Por tanto es fácil observar que el control se puede recoger de cualquier sitio, por ejemplo dentro de un DetailsView, dentro de un Formview o dentro de la propia Page.

Si rizamos un poco más el rizo, podemos tener algo cómo:

AsignarFoco2.aspx.cs
        int iFocus = Convert.ToInt32(Request.QueryString["iFocus"]);
        switch (iFocus)
        {
            case 1:
                this.SetFocus("TextBox2");
                break;
            case 2:
                TextBox tb_Nuevo = new TextBox();
                PlaceHolder1.Controls.Add(tb_Nuevo);
                this.SetFocus(tb_Nuevo);
                break;
            default:
                break;
                // Si no vale ni 1 ni 2, no ponemos el cursor en ningún sitio.
        }


1. Recogemos una variable de (por ejemplo el querystring). También podríamos cogerla de una BBDD, de una variable de sesión, una cookie...
2. Según su valor, hacemos una acción u otra.
3. Si iFocus vale 2, creamos un TextBox nuevo, la añadimos a un PlaceHolder (que en este caso debe estar en la página .aspx) y le colocamos el cursor sobre él.




Asignar un botón por defecto

Seguro que todos hemos pasado por el típico problema en nuestros inicios con ASP.NET, y es que si tenemos varios botones en una misma página, cada uno con su acción determinada, cuando el cliente presiona return ni tal siquiera sabemos qué acción se va a realizar.

Pongamos un ejemplo sencillito. Imaginemos que tenemos esta página:

DefaultButton.aspx
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
    <br />
    <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
    <asp:Button ID="Button2" runat="server" Text="Button" OnClick="Button2_Click" />
    <br />
    <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></div>


DefaultButton.aspx.cs
    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = TextBox1.Text;
    }

    protected void Button2_Click(object sender, EventArgs e)
    {
        Label1.Text = TextBox2.Text;
    }


Bajo este escenario, si el usuario se encuentra rellenando el TextBox2 y presiona enter, se ejecutará el evento Button1_Click, y no el Button2_Click como sería deseable, por lo que el texto de la Label1 pasaría a ser el contenido del Textbox1, y no el del TextBox2 que es el que el usuario quería.

ASP.NET nos ofrece la opción de asignar un botón por defecto a toda la página entera, si es que quisiéramos que siempre que un usuario apriete enter (esté donde esté), se ejecute el evento asociado a ese botón.
Esto se consigue asignando el botón en el propio tag del formulario:

    <form id="form1" runat="server" defaultbutton="Button1">

o dinámicamente mediante un simple:

    form1.DefaultButton = "Button2";

Sin embargo, aunque es bueno saber que es posible porque en otros escenarios puede ser útil, a nosotros no nos interesa porque lo que queremos es que:
- Si el usuario está en el TextBox1 y presiona enter, se ejecute el evento asociado al Button1.
- Si el usuario está en el TextBox2 y presiona enter, se ejecute el evento asociado al Button2.

Afortunadamente esto también es posible a la par que muy fácil. No hay más que agrupar cada TextBoxX - ButtonX en un Panel, y hacer uso del atributo "DefaultButton" de cada Panel. Es decir, nos quedaría algo como:

DefaultButton.aspx
        <asp:Panel ID="Panel1" runat="server" Height="50px" Width="125px" DefaultButton="Button1">
            <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
            <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
        </asp:Panel>
        <br />
        <asp:Panel ID="Panel2" runat="server" Height="50px" Width="125px" DefaultButton="Button2">
            <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
            <asp:Button ID="Button2" runat="server" Text="Button" OnClick="Button2_Click" />
        </asp:Panel>
        <br />
        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></div>


Obviamente, esto sirve tanto para un simple TextBox con un Button, como con una larga lista de TextBox, DropdownList, RadioButton, etc.

Como os podréis imaginar, todo esta también se puede hacer con javascript. Si queréis averiguar cómo, os recomiendo que le echéis un vistazo al artículo Insertar javascript dinámicamente y luego a Set Focus to an ASP.NET Control.

Ojo! Daos cuenta que en dicho artículo se utiliza Page.RegisterClientScriptBlock, algo que en ASP.NET 2.0 ya está "deprecated", por tanto os vuelvo a insistir en que leais el artículo Insertar javascript dinámicamente, donde se explica el nuevo modo de trabajar con javascript en ASP.NET.



Insertar javascript dinámicamente

Muchas veces puede resultar necesario generar código javascript dinámicamente e insertarlo en nuestra página. Con ASP.NET es muy sencillo hacerlo, y para ello se ofrecen dos alternativas: RegisterStartupScript y RegisterClientScriptBlock.

Las diferencias entre ambos es que el primero conecta el script cuando se carga la página, y el segundo lo hace cuando le llamamos desde cualquier control.

Ojo, porque en ASP.NET 1.1 se podían usar desde Page.RegisterStartupScript(...), pero en ASP.NET 2.0 esas funciones son ya deprecated: siguen funcionando pero no se aconseja. En cambio, ahora tenemos que trabajar con el ClientScriptManager que podemos obtener desde Page.ClientScript.

Pero vamos con un ejemplo y así quedará más claro:

Type cstype = this.GetType();
string nombreScript = "alertar";
ClientScriptManager cs = Page.ClientScript;
StringBuilder sb = new StringBuilder();
sb.AppendLine("function quieroAlertar()");
sb.AppendLine("{");
sb.AppendLine("alert('Estoy alertando');");
sb.AppendLine("}");

/*if (!cs.IsClientScriptBlockRegistered(nombreScript))
{
   cs.RegisterClientScriptBlock(cstype, nombreScript, sb.ToString(), true);
}*/

if (!cs.IsStartupScriptRegistered(nombreScript))
{
   cs.RegisterStartupScript(cstype, nombreScript, sb.ToString(), true);
}


Lo primero que hacemos es coger el tipo de allá donde estemos (luego veremos el porqué) y le damos un nombre que identifique nuestro script. Posteriormente recogemos el ClientScriptManager desde Page.ClientScript y trabajaremos sobre él.

Creamos nuestro script con un StringBuilder, del que ya se habló en el artículo "El StringBuilder", y ahora viene la parte interesante.

El motivo de haberle dado un nombre identificativo a nuestro script (nombre que debe ser único) es para poder asegurarnos de no sobreescribir dos scripts diferentes, motivo por el que hacemos la comprobación previa "IsxxxRegistered(nombreScript)".

Finalmente, procedemos a registrar el script, y nos encontramos con cuatro parámetros:
1.- El Type de donde estamos y que recogemos previamente con un this.GetType()
2.- El nombre identificativo del script.
3.- El texto propio del script, que habíamos fabricado en el StringBuilder.
4.- Un parámetro booleano en el que decimos si queremos que nuestro script vaya precedido por los típicos tags de inicio y final de javascript "<script language="xxx">" y "</script>". Este última parámetro podemos no ponerlo (por defecto es false).

Y ya está, es así de simple. Ya sabemos cómo insertar código javascript dinámicamente, contruyendo el javascript como nos convenga en cada momento.




Expresiones regulares. Introducción.

Expresiones regulares, regular expressions o simplemente regex. Como casi todo en .NET, no se trata de un concepto exclusivo del framework de Microsoft (ni mucho menos), pero éste nos ofrece unas herramientas de trabajo muy potentes y que nos hacen la vida muy fácil.

Esta artículo consta de dos partes. En la primera se dará una pequeña introducción de lo que son las expresiones regulares, así como algunos ejemplos y referencias a tutoriales completos y programas, y en la segunda parte se hará más enfasis en cómo con ASP.NET se puede trabajar de forma muy sencilla, elegante y eficaz con las expresiones regulares.

¿Qué son las expresiones regulares?

Las expresiones regulares son una de esas cosas que tardarías diez minutos en explicar cuando bastaba un simple ejemplo para aprender. La idea es que queremos encontrar "cierto patrón" dentro de un texto.

A continuación tenéis algunos ejemplos, desde los muy simples hasta los más complejos. Dentro de un texto dado, queremos encontrar cosas como...
- ... todas las referencias a la palabra "programación".
- ... todas las palabras que empiecen por 'Val'.
- ... todas las palabras que acaben por 'ar', 'er' o 'ir' para identificar los verbos regulares
- ... las palabras que contengan la letra 'q' y cuya longitud sea menor de 10 caracteres.
- ... las palabras que empiezan por 'pre', pero no quiero que 'pre' entre dentro del valor de referencia devuelto. Por ejemplo, encontraríamos la palabra 'previsión', pero se nos devolvería la palabra 'visión'.

Algunos ejemplos...
No es objetivo de este artículo el explicar el formato de esos patrones que van a definir nuestra búsqueda (eso vendrá en un futuro artículo), sino el explicar cómo ASP.NET trabaja con los resultados de la búsqueda.

Pero para que nos entendamos un poco mejor, a continuación muestro algunos ejemplos de patrones:

1. programa. Este es el patrón más sencillo de todos, pues simplemente nos devuelve todas las referencias al conjunto de caracteres seguidos p r o g r a m a, por tanto, nos devolverá todas las palabras 'programa', pero también devolverá 'programación' o 'programando'.

2. \bprogramación\b
. Dado que es posible/probable que no nos interese la palabra programando, sino la palabra programa, podemos usar '\b', que es un código especial que delimita el inicio o el final de una palabra (según donde se coloque).

3. \b\w+\b.
Esta expresión recoge cualquier palabra entera, donde '\w' representa a caracteres alfanuméricos y '+' indica "una o más repeticiones".

4. \bprograma\b.*\btarde\b
. Ahora queremos encontrar una serie de palabras empezando por programa y terminando por tarde, haya lo que haya entre la palabra programa y la palabra frase. Por ejemplo, nos devolvería 'programa hasta muy tarde'. El caracter especial '.' se refiere a "cualquier caracter", y el caracter especial '*' se refiere a 0 o más repeticiones. Es interesante observar que en este caso '*' nunca serán 0 caracteres porque tanto a programa como a tarde les imponemos que sean palabras enteras (y no lo serían si no tuvieran espacio entre ellas).

5. (?<=<u>).{1,20}(?=</u>)
. Ahora cogemos todo lo que vaya subrayado y que tenga menos de 20 caracteres, pero sin coger los tags que indican el subrayado. Es decir, en el texto "Estoy en <u>subrayado</u>", se encontraría "<u>subrayado</u>", pero se devolvería únicamente "subrayado". (?<=miexpresion) indica que se buscan coincidencias con ese prefijo, pero que no se devuelve ese prefijo, y análogamente (?=miexpresion) para el sufijo. {1,20} indica entre 1 y 20 repeticiones de la expresión anterior.

6. (?<=<\s*u\s*>).{1,20}(?=<\s*/\s*u\s*>)
. Upload/expresso.jpgAlgo más complejito, y sólo a título lectivo, ahora devuelve lo mismo de antes, pero permitiendo que los tags tengan espacios intermedios (algunas editores de HTML dan ese error). Es decir, tal y como sucede antes, devolvería "subrayado" dentro de "Estoy en <   u    >subrayado<  /    u>"

Repito que no es objetivo de este artículo la explicación de cómo se forman las expresiones regulares. Podríamos seguir con 3000 ejemplos, pero para hacernos a una idea de lo que queremos, no está mal la cosa, ¿no?

Para los que queráis hacer más hincapié, deciros que de todos los tutoriales sobre expresiones regulares que he leído, el que más me ha aclarado las ideas ha sido "The 30 minutes regex tutorial". Además hace referencia a un programa llamado Expresso... sinceramente es el único que he pobado, pero no creo que pruebe ninguno más porque éste nos da todo lo que hace falta. Desde el análisis paso por paso del patrón de expresión regular que queremos tratar, hasta los resultados de ese patrón en el texto que le especifiquemos, pasando un montón de ejemplos y mucho más.

También es interesante esta librería de expresiones regulares, donde se puede encontrar prácticamente de todo, aunque destacan sobretodo las expresiones regulares complejas, por lo que sólo es aconsejable cuando se ha cogido algo de práctica (sino, uno puede hasta deprimirse).

Finalmente, echadle un vistazo a un tutorial de expresiones regulares algo más técnico.

En la parte II, se hablará sobre cómo ASP.NET trabajo con las expresiones regulares.



El StringBuilder

En el mundo web, es muy común el tener que trabajar con largas cadenas de texto.

Habitualmente se trabaja(ba) concatenando diferentes strings o reescribiendo el mismo string una y otra vez. Ambos modos son malos para el rendimiento de nuestra aplicación, dado que cada vez que lo hacemos se crea una instancia string bastante molesta.

Con StringBuilder ya no sólo se mejora el rendimiento considerablemente, sino que tenemos una manera más elegante y más "humana" para trabajar con largos strings.

Pero vayamos con un ejemplo simple con los dos modos malos (hay que importar el namespace System.Text):

    public string ejemploStrings1()
    {
        string s1 = "No es más fuerte el que no se cae, ";
        string s2 = "sino el que al caerse ";
        string s3 = "vuelve a levantarse con más fuerza ";
        string s4 = "y la lección aprendida";

        return s1 + s2 + s3 + s4;
    }


    public string ejemploStrings2()
    {
        string s = "No es más fuerte el que no se cae, ";
        s += "sino el que al caerse ";
        s += "vuelve a levantarse con más fuerza ";
        s += "y la lección aprendida";

        return s;
    }


El modo de hacerlo con un StringBuilder sería el siguiente:

    public string ejemploStringBuilder1()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("No es más fuerte el que no se cae, ");
        sb.Append("sino el que al caerse ");
        sb.Append("vuelve a levantarse con más fuerza ");
        sb.Append("y la lección aprendida");

        return sb.ToString();
    }


Como veis es un ejemplo terriblemente sencillo. Fijaos que para devolver el string que alamcena el StringBuilder debe hacerse un .ToString().

Cuando le cojais el gustillo, probad otras cosillas como sb.AppendFormat(...) o sb.AppendLine(...), donde el primero permite darle un formato al string que se inserte y el segundo inserta una línea (el equivalente de \n).

Además, sb.Append(variable) no sólo no obliga a que variable sea un string, sino que permite un montón de tipos de datos: chars, arrays de chars, double, int, bool, etc.

Ahora es cuestión de vuestra imaginación saber aprovechar esta herramienta, personalmente yo ya la uso para todo lo que sea el trabajo con más de un string



El archivo App_Offline.htm

A todos los webmasters nos es muy común la actualización de nuestras webs, desde solucionar algún pequeño bug o hasta actualizar gran parte de la web.

En todo caso, queda muy feo, sobretodo si tienes webs bastante visitadas, que el usuario vea errores causados porque los archivos están a mitad de subida, un archivo depende de otro, y ya sabéis, nada funciona bien hasta que se han subido todos los archivos.
Es más, es hasta probable que una vez todo actualizado, empiecen a saliros errores y urgentemente vayais corrigiendo pequeños olvidos del tipo "ups, debería haber subido esta carpeta en lugar de esta otra..."

Recuerdo mis tiempos de ASP 3.0, cuando para evitar esos errores ponía en algunos de los includes comunes a todas las páginas el típico response.end que, bien es cierto, hacía su función y no hacía otra cosa más que parar la ejecución ahí y mostrar por pantalla sólo lo que se hubiera procesado hasta ese momento.

Ahora, con ASP.NET todo es mucho más fácil (para variar, jeje). Cada vez que recibe la petición de servir una página, el motor de ASP.NET realiza un comprobación de la existencia del archivo App_Offline.htm. Si lo encuentra, no sirve la página que le hemos pedido, sino ese mismo archivo App_Offline.htm.

De modo que ya tenemos una forma muy elegante de informar al usuario que estamos actualizando el sistema, colocar nuestro App_Offline.htm en el directorio raíz. Ojo, no sirve ponerlo en un subdirectorio para que no muestre nada de ese subdirectirio, sólo vale ponerlo en el directorio raíz.

Lo mejor es que es un método totalmente independiente de nuestro código y estamos limitados únicamente por nuestra imaginación, ya que App_Offline.htm puede ser tan complejo como queramos (todo lo complejo que puede ser una página .htm).