El Blog de Óscar Sotorrío Sánchez - MCP Application Development Foundation - C#

Todo lo que vaya aprendiendo de .NET, C#, ASP.NET, SQL Server, etc, lo compartiré con vosotros.

El autor:

Óscar Sotorrío Sánchez
Contacto Send mail

Encuéntralo

Álguien dijo...


La venganza es el manjar más sabroso condimentado en el infierno.

Walter Scott

Cambiar la ubicación de una base de datos con SQL Server 2008

Es posible que en alguna ocasión nos interese mover de ubicación una base de datos. Los motivos pueden ser aprovechar un mayor espacio de almacenamiento adicional en la nueva ubicación, o cambiar a otro equipo de nuestra red con mayor rendimiento y capacidades.

Para realizar esta operación, sin tener problemas de asociación entre los archivos que componen la base de datos y la instancia actual de SQL Server, debemos seguir tres pasos principales que consisten en separar, mover y adjuntar los archivos de base de datos con la instancia de SQL Server.

 

Seperar la base de datos

Utilizando la herramienta SQL Server Management Studio buscamos en el Explorador de Objetos la base de datos que queremos mover. Seleccionamos la base de datos, en este caso PruebaDB, y con el botón secundario seleccionamos Tareas / Separar.

 

 
 
En el cuadro de diálogo que se abre, aceptamos directamente los valores por defecto. 
 
 
 
 
Si nos fijamos ahora en el Explorador de Objetos de Management Studio, nos daremos cuenta que la base de datos ya no está vinculada a la instancia actual de SQL Server. Esta acción nos ha permitido cerrar todos los archivos de la base de datos así como los posibles bloqueos del sistema operativo. Ahora la base de datos PruebaDB ya no está vinculada en modo alguno con la instacia actual de SQL Sever.
 
 
Mover los archivos de la base de datos
 
Realizada la operación anterior, podemos copiar con el Explorador de Windows los archivos de la base de datos en otra ubicación distinta. En este paso es muy importante mover todos los archivos relacionados con nuestra base de datos, es decir, archivos primarios (.mdf), archivos secuandarios (.ndf), archivo de registro de transacciones (.ldf), archivos de texto completo, y filestream.
 
 
Adjuntar la base de datos
 
Copiados los archivos en la nueva ubicación debemos vincular otra vez la base de datos con la instancia de SQL Server desde la nueva ubicación. En el Explorador de Objetos de Management Studio seleccionamos el nodo principal Base de Datos y con el botón secundario seleccionamos Adjuntar.
 
 
 
 
En el cuadro de diálogo que se abre pulsamos sobre el botón Agregar.
 
 
 
 
En el árbol de carpetas buscamos y seleccionamos el archivo principal (.mdf) de la base de datos en la nueva ubicación.
 
 
 
 
Al pulsar en Aceptar podremos observar en el Explorador de Objetos de Management Studio que nuestra base de datos ya está asociada desde la nueva ubicación con la instancia actual de SQL Server.

Posted by Oscar.SS on sábado, 10 de julio de 2010 23:52
Permalink | Comentarios (0) | Post RSSRSS comment feed

La intención no es lo que cuenta

Precisamente esto es lo que le ocurre al libro que os presento hoy. Es un libro con muy buena intención, muy completo, relativamente breve en su contenido y bien organizado. Pero lamentablemente han fallado en la traducción.

 

 

ASP.NET 3.5

Título: ASP.NET 3.5
Autor:George Shepherd
Editorial:Anaya Multimedia
I.S.B.N.: 978-84-415-2439-2
Fecha publicación: 2008.
Nº páginas: 688.
Nº capítulos: 24.
Precio: 58.60 euros.

  

Elogios:

Prácticamente ya lo he comentado antes. Los capítulos del libro están muy bien organizados y su contenido es muy completo. Comienza por lo conceptos básicos de toda aplicación web para continuar con los conceptos básicos de las aplicaciones web ASP.NET.

A continuación, dedica algunos capítulos a los controles web y a los Web Parts. Configuración de aplicaciones ASP.NET y seguridad en el inicio se sesión. Enlace a datos, personalización, estado de sesión y caché. Depuración y trazas, manejadores ashx, servicios web y WCF, AJAX, WPF y para finalizar publicación de sitios ASP.NET.

Como podéis ver, el libro contiene prácticamente todo lo que hay que saber para construir sitios consistentes con ASP.NET.

 

Críticas:

La nefasta traducción hace muy difícil la comprensión de los conceptos más duros. Por no hablar, del aburrimiento y desmotivación que supone leer frases que más se parecen al habla de "los indios" en los westerns americanos. Realmente han fallado con la versión española de esta publicación. 

Mencionar también, lo que ocurre siempre con este tipo de libros, al intentar abarcar tanto, hay capítulos que quedan muy cortos y tienes que buscar una segunda lectura para profundizar. Especialmente esto me ha disgustado en el capítulo dedicado a AJAX.

 

Resumiendo:

Hace algunos días, me escribía un lector de este blog pidiéndome consejo sobre un libro de C#. Su caso es el de una persona que está empezando en esto de la programación y se había comprando un libro de la misma editorial, concretamente "Visual C# 2008", y literalmente me comentaba que el libro era para tirarlo porque la tradución era horrible. Curiosamente, ambos libros son de la misma colección de Anaya Multimedia, "Paso a paso".

En fin, que si el idioma no es un problema para vosotros os recomiendo que lo compréis en ingles porque el libro tiene muy buena intención pero en este caso no fué suficiente.


Categories: Formación
Posted by Oscar.SS on jueves, 17 de junio de 2010 22:03
Permalink | Comentarios (0) | Post RSSRSS comment feed

Ajedrez aleatorio de Fischer en ASP.NET

Supongo que esto es una frikada de las buenas pero...¡que leches!...me ha apetecido hacerlo. Además, me consta que muchos compañeros de profesión son también aficionados al ajedrez.

Por lo tanto, me he currado una pequeña aplicación que genera posiciones iniciales aleatorias de ajedrez siguiendo las reglas del conocido como Fischer Random Chess o FRC, y también Fischer 960. Aquí podéis ver una de esas posiciones.

 

 

En el generador de posiciones podéis encontrar una breve explicación, así como enlaces relacionados, sobre esta interesante modalidad de ajedrez.

 

También, si lo queréis, podéis obtener el código fuente de la aplicación escribiéndome en la zona de contacto. No es el mejor ejemplo de código del mundo pero no está mal para haberlo hecho en una tarde y...¡de vacaciones tomándome unas birras en la terraza!.

Solo os pido 3 cosillas a cambio:

  • Vuestro nombre, para dirigirme a vosotros.
  • El país y región.
  • Una breve explicación contándome para que queréis la aplicación.
 
 
Podéis reescribir el código, podéis publicarlo donde queráis, podéis utilizarlo cómo y dónde queráis. ¡¡Es totalmente FREE!!
 
 

 


Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on viernes, 11 de junio de 2010 18:31
Permalink | Comentarios (0) | Post RSSRSS comment feed

Enviar datos entre páginas ASP.NET

Existen distintas formas de enviar información o datos entre páginas y cada una tiene sus ventajas e inconvenientes. Por lo tanto, debemos evaluar estas consideraciones para determinar como implementar esta funcional en cada caso concreto.

En este artículo vamos a a hablar de un método que estaba disponible en las páginas ASP de antaño y que dejó de estarlo en las versiones ASP.NET 1.x. Con la llegada de ASP.NET 2.0 Microsoft puso a nuestra disposición otra vez la técnica bautizada como Cross Page PostBack (¡telita con el nombre!).

La técnica consiste en hacer que un formulario no se envíe sobre si mismo, es decir, que haga un Submit sobre otra página. Esto se consigue por medio de la propiedad PostBackUrl que implementan los siguientes controles.

        <asp:Button ID="Button1" runat="server" Text="Button" PostBackUrl="~/PaginaDestino.aspx" />
        <asp:ImageButton ID="ImageButton1" ImageUrl="~/img/enviar.png" runat="server" PostBackUrl="~/PaginaDestino.aspx" />
        <asp:LinkButton ID="LinkButton1" runat="server" PostBackUrl="~/PaginaDestino.aspx">LinkButton</asp:LinkButton>

Estos controles estarán insertados en nuestra página de origen y como vemos la propiedad PostBackUrl apuntará a la página de destino sobre la que se quiere enviar los datos de la página actual.

Suponiendo que en la página de origen tenemos un campo de texto sobre el que el usuario escribe un texto que será enviado a la página de destino, en el código de esta, obtendremos el valor del campo de texto y lo mostraremos de la siguiente forma.

            if (PreviousPage != null)
            {
                TextBox ControlEncontrado 
(TextBox)PreviousPage.FindControl("TextBox1");

                if 
(ControlEncontrado != null)
                {
                    sptexto.InnerText 
ControlEncontrado.Text;
                
}
            }

La propiedad PreviousPage de la página de destino nos devuelve una referencia a la página de origen y mediante su método FindControl() buscaremos el control sobre el que escribió el usuario.

Es muy importante, como hemos visto, hacer las comprobaciones oportunas sobre la propiedad PreviousPage y el método FindControl() para evitar sorpresas del tipo System.NullReferenceException.

Existe otra forma de utilizar esta técnica que es recomendable si tenemos pensado diseñar la página de origen exclusivamente para compartir información con otras páginas. Consiste en exponer los datos que se quieren compartir o enviar como propiedades públicas. Es decir, en la página de origen haremos algo como esto:

 

        public string NombrePropiedadTexto
        {
            
get 
            
{
                
return TextBox1.Text;
            
}
        }

        
public TextBox NombrePropiedadControl
        {
            
get
            
{
                
return TextBox1;
            
}
        }

En primer lugar, en la página de destino deberemos escribir la directiva PreviousPageType apuntando a la página de origen

<%@ PreviousPageType VirtualPath="~/PaginaOrigen.aspx" %> 

O también así.

<%@ PreviousPageType TypeName="EspacioDeNombres.PaginaOrigen" %>

 

Al colocar esta directiva en nuestra página de destino, obtenemos una referencia con establecimiento inflexible de tipos (¡bonito palabro!) a nuestra página de origen. Esta directiva tiene dos posibles atributos que son mutuamente excluyentes, VitualPath y TypeName. Es decir, solo podemos utilizar uno de ellos al mismo tiempo.

Ahora, en el code behind de la página de destino tendremos a nuestra disposición la propiedades anteriormente implementadas de forma muy natural y que recuperaremos respectivamente de la siguiente forma dependiendo como las hemos implementado.

            if (PreviousPage != null)
            {
                
if (PreviousPage.IsCrossPagePostBack)
                {
                    sptexto.InnerText 
PreviousPage.NombrePropiedadTexto;
                
}
            }


            
if (PreviousPage != null)
            {
                
if (PreviousPage.IsCrossPagePostBack)
                {
                    sptexto.InnerText 
PreviousPage.NombrePropiedadControl.Text;
                
}
            }

Ni que decir tiene, que no es necesario que las propiedades que exponen los datos a compartir deban necesariamente devolver un control de servidor como hemos visto en este ejemplo. Podemos implementar una propiedad del tipo que necesitemos y recuperarla igualmente por medio de la propiedad PreviousPage.

Veamos ahora una pequeña comparativa de los métodos más usuales para enviar datos entre páginas ASP.NET y tener una visión más general que nos permita elegir adecuadamente en cada caso.

 

  • Compartir datos mediante estado de sesión.
- Cuando utilizamos la sesión para compartir los datos, estos están disponibles para todas las páginas de nuestra aplicación.
- Debemos tener en cuenta que esto supone un consumo relativo de la memoria del servidor.
- Los datos permanecen en memoria hasta que el usuario cierra el explorador y quizás no necesitemos tanta disponibilidad. 
  • Enviar datos como parámetros en la URL
- Permite enviar datos entre páginas de distintas aplicaciones e incluso entre distintas plataformas.
- Se pueden derivar algunos problemas de seguridad al ser siempre visible la información a los usuarios.
- Cantidad de datos limitada por la longitud máxima de la cadena URL.
- Se puede utilizar tanto con controles de servidor como controles HTML.
- Los datos pueden ser enviados programativamente por medio del método Redirect(). 
  • Cross Page PostBack
- Solo se puede utilizar para enviar información a páginas de nuestra aplicación.
- La información enviada no es visible para los usuarios.
- Se pueden enviar grandes cantidades de datos contenidas en propiedades o en controles, por ejemplo un GridView complento.
- Solo es accesible para controles se servidor.
- Programativamente se pueden transferir los datos mediante el método Transfer().
 
 

 


Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on sábado, 08 de mayo de 2010 13:32
Permalink | Comentarios (0) | Post RSSRSS comment feed

Ponerle puertas al campo

La verdad, no se me ocurría otro título para lo que trata de hacer el gobierno (y como siempre insisto en que me da igual que sea este u otro gobierno) con la Disposición de la Ley de Economía Sostenible referente a la protección de la Propiedad Intelectual y el cierre de las páginas web con contenidos sobre esta supuesta propiedad intelectual. No entiendo como no se da cuenta el gobierno que no se puede contener lo incontenible.

 

 

Recuerdo cuando mi hermano Roberto y yo jugábamos de pequeños en playas del Cantábrico intentando detener las olas. Olas que eran en muchas ocasiones más altas que nosotros. Ahí estábamos los dos, esperando que llegara la próxima ola, preparados para el impacto, gritábamos y nos lanzábamos contra ella con los brazos abiertos para abarcar todo lo posible. El resultado siempre era el mismo, la ola siempre llegaba a la playa y nosotros acabamos con el pecho rojo del golpe y arrastrados por el impacto en el mejor de los casos.

Este pequeño recuerdo de la infancia me viene siempre a la cabeza cuando pienso en el Ministerio de Cultura y su hermano pequeño la SGAE, intentando detener la ola de la nueva tecnología, la ola de la nueva forma de pensar de 40 millones de españoles, la ola desmedida e imparable de Internet.

Como es lógico no voy a entrar en puntos de vista jurídicos, no soy la persona apropiada para ello, ni el contenido de esta web es el indicado. Pero si es cierto, que leyendo por la web, en sitios más especializados, se comentan varias cosas que me han llamado la atención para mi pobre comprensión.

 

  • Con la Ley de la Propiedad Intelectual se ha comparado (o elevado) jurídicamente el derecho de la propiedad intelectual con otros derechos fundamentales como la libertad de expresión. Esto me parece un absurdo, mañana podemos elevar también a derecho fundamental el derecho de los constructores a que sigan especulando con el terreno. Esta claro que la ley de la oferta y la demanda les ha permitido este derecho de especulación pero...¡de ahí a que sea un derecho fundamental de las personas!.

  • La defensa legal de un autor afectado será resuelta en 4 días. Tiene gracia la cosa. Resulta que si eres propietario de una vivienda, la cual alquilas a una familia, si ellos un buen día deciden dejar de pagar la renta, se han dado casos en los que los inquilinos habitan tu casa hasta un año sin pagar nada de nada. Y encima no puedes entrar en tu casa, no puedes cortar el suministro de luz ni de agua porque te denuncian. Y todo ello porque la justicia es lenta, pero para lo que interesa a unos pocos...¡ale!...resoluciones judiciales a la carta...¡que me lo quitan de las manos!...¡sentencias bonitas y baratas!.

  • Según algunos expertos, todo el proceso administrativo para cerrar una página web no ofrece las debidas garantías jurídicas.

 

Después de este pequeño inciso, lo que realmente me interesa comentar son los aspectos filosóficos y lógicos desde el punto de vista de un humilde usuario de la red. Porqué no nos engañemos, ¡todos somos internet!.

¿Que pretende el gobierno con esta ley?. Es muy sencillo, cerrar todas aquellas páginas, previa denuncia de los autores, que contengan contenido que atente contra la propiedad intelectual de aquellos.

Esta claro que la filosofía de los billones de internautas es utilizar, conseguir y exprimir la web de forma totalmente gratuita y más en los tiempos que corren. ¿Entonces porque ir en contra de billones de usuarios?. Supuestamente la disculpa es proteger a los autores y su obra, pero lo cierto, es que como siempre hay un interés económico. En el mundo editorial, por ejemplo, los autores tan solo se llevan el 7% de lo que recauda su obra. Lo que de verdad están protegiendo es el otro 93% que va a parar a la saca de personas muy influyentes y con altos contactos en los círculos de la política.

 

 

¿Es lógico cerrar una web de esta forma?. Desde un punto de vista puramente práctico es evidente que no. Si el servidor que aloja la web está en España le obligarán a eliminar el contenido de la web. Pero el dueño de la misma duplicará y duplicará su contenido con otros dominios. Y si el servidor se encuentra en una empresa extranjera no podrán obligar a su cierre. Así que todo el mundo a contratar servicios de hosting extranjeros. ¡Mal asunto para las empresas españolas!.

Como no podrán cerrar las paginas alojadas en el extranjero, obligarán a las operadoras a evitar que los españoles puedan navegar a estas URLs. Es decir, que la página será publica para el planeta entero menos para los españoles. ¡Buen asunto para las web que ofrecen servicios de proxy!. 

En fin, que sea como sea, Internet seguirá su curso imparable igual que lo hacen las olas del mar o el curso de los ríos. ¡No se pueden poner puertas al campo!.

Con todo esto no estoy diciendo...¡viva la piratería!. Al contrario, tengo claro que los autores de la cultura merecen ganarse la vida dignamente igual que todo el mundo. Pero son ellos los que tienen que entender que la forma de comunicarse con el mundo, la forma de mostrar su obra a los demás, ha cambiado radicalmente.

Por lo tanto, son ellos los que tienen que encontrar nuevos modelos de negocio para sus obras. El concepto y el valor de la copia ha cambiado gracias a que los ordenadores y las redes sociales permiten duplicar y difundir en el acto a escala mundial cualquier contenido. Son ellos lo que deben mandar a paseo el actual y obsoleto modelo de negocio.

Es necesario un nuevo modelo de negocio, basado en la misma naturaleza intrínseca de Internet, y esto es, muy barato o gratuito para el usuario, billones de copias repartidas y compartidas por toda la red y expansión pública prácticamente instantánea. Esto es Internet, esto es la red y esto es lo que queremos todos

Y hablando de derechos fundamentales de las personas...el derecho de acceso libre al conocimiento y la cultura si me parece un derecho fundamental que debe estar debidamente protegido. Quizás no nos damos cuenta pero con el actual y arcaico modelo no tenemos acceso a todos los autores y su creatividad.

Siempre hay una persona en una editorial, o en una discográfica que se encarga de cribar por puros intereses económicos que obra se hará publica. Por lo tanto, hasta ahora, solo hemos tenido acceso a una pequeña fracción de toda la oferta cultural. Solo hemos tenido acceso a aquella parte que unos pocos interesados han determinado que sería rentable para ellos.

Pero Internet, nos ofrece acceso a todo el mundo, cualquier persona, autor o no, puede ser escritor, poeta, músico, etc. Luego seremos nosotros los que determinemos que nos interesa o nos deja de interesar. Pero en ningún caso decidirá por nosotros un señor con los bolsillos llenos que pretende llenarlos aún más. Esto modelo me parece más justo y se acerca más a la forma de pensar actual de todo el mundo, ya sean consumidores o creadores de cultura. Todos queremos tener acceso a todos.

Señores y señoras del gobierno...la ola llegará a la playa...¡esto es imparable!

 


Categories: Internet | Personal
Posted by Oscar.SS on lunes, 05 de abril de 2010 17:20
Permalink | Comentarios (0) | Post RSSRSS comment feed

GridView: asignar dinámicamente el texto de CommandField

En ocasiones debemos mostrar informes en controles GridView en los que una, o varias, de sus columnas nos permitan seleccionar una fila para realizar una determinada acción en nuestro código. De forma predeterminada el control GridView nos permite añadir columnas de tipo BoundFile, ButtonField, CheckBoxField, CommandField, HyperLinkField, etc.

El problema con estas columnas es que el texto mostrado por pantalla siempre es el mismo en cada fila del GridView. Por ejemplo, "Seleccionar" para una columna CommnadField de tipo ShowSelectButton.

Pero esto no es lo que queremos conseguir. Se trata de que cada fila en una columna determinada por nosotros muestre el texto de los datos de la fuente de datos.

De esta forma cada registro permite seleccionar esa fila para realizar una acción en función de los datos que contiene. ¡Veamos como!. 

Tenemos el siguiente GridView en nuestro formulario:

         <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 

            ondatabound="GridView1_DataBound" 
            onselectedindexchanged
="GridView1_SelectedIndexChanged">
            
<Columns>
                
<asp:CommandField ShowSelectButton="true" ButtonType="Link" HeaderText="Proyecto" />
                <
asp:BoundField DataField="Responsable" HeaderText="Responsable" ReadOnly="True" 
                    SortExpression
="Responsable" />
                <
asp:BoundField DataField="Analista" HeaderText="Analista" 
                    SortExpression
="Analista" />
                <
asp:BoundField DataField="FechaInicio" HeaderText="Fecha Inicio" 
                    SortExpression
="FechaInicio" />
                <
asp:BoundField DataField="FechaFin" HeaderText="Fecha Fin" 
                    SortExpression
="FechaFin" />
            </
Columns>
        
</asp:GridView>


Ahora cargamos el GridView con un DataTable por ejemplo en el evento load.

    protected void Page_Load(object sender, EventArgs e)
    {
        
if (!IsPostBack)
        {
            dt 
= new DataTable();
            
dt.Columns.Add("Proyecto");
            
dt.Columns.Add("Responsable");
            
dt.Columns.Add("Analista");
            
dt.Columns.Add("FechaInicio");
            
dt.Columns.Add("FechaFin");

            int 
contador 0;
            foreach 
(DataColumn colum in dt.Columns)
            {
                DataRow dr 
dt.NewRow();
                
dr["Proyecto""000" + contador.ToString();
                
dr["Responsable""Responsable " + contador.ToString();
                
dr["Analista""Analista " + contador.ToString();
                
dr["FechaInicio""Inicio " + contador.ToString();
                
dr["FechaFin""Fin " + contador.ToString();
                
dt.Rows.Add(dr);
                
contador++;
            
}

            GridView1.DataSource 
dt;
            
GridView1.DataBind();
        
}
    }

En el evento OnDataBound asignamos dinámicamente los datos de la columna CommnadField.

    protected void GridView1_DataBound(object sender, EventArgs e)
    {
        
for (int 0i < dt.Rows.Counti++)
        {
            LinkButton lb 
GridView1.Rows[i].Controls[0].Controls[0as LinkButton;
            
lb.Text dt.Rows[i]["Proyecto"].ToString();
        
}
    }

En este caso la fuente de datos era un DataTable que hemos creado a mano pero podíamos haber utilizado cualquier origen de datos. Por ejemplo, si hubiéramos utilizado un objeto SqlDataSource podríamos recuperar los datos de la forma siguiente.

        DataView dv =  SqlDataSource1.Select(new DataSourceSelectArguments()) as DataView;
        
DataTable dt dv.ToTable();

 

Ahora solo nos queda recoger los datos sobre la celda que pulsa el usuario. Utilizaremos para ello el evento OnSelectedIndexChanged.

    protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
    {
        GridView gv 
(GridView)sender;
        int 
filaSeleccionada gv.SelectedIndex;
        
LinkButton lb gv.Rows[filaSeleccionada].Cells[0].Controls[0as LinkButton;
    
}

Posted by Oscar.SS on domingo, 14 de marzo de 2010 14:09
Permalink | Comentarios (0) | Post RSSRSS comment feed

Peleándome con el UpdatePanel

Cuando empezamos a trabajar con los controles ScriptManager y UpdatePanel normalmente nos quedamos encantados de lo rápido, sencillo y bonito que es todo. Pero cuando estamos en el trabajo,en aplicaciones reales, cuando se nos pide implementar cierta funcionalidad, nos damos cuenta que todo este automatismo tan bonito es poco elástico. Y que para conseguir la funcionalidad requerida por el cliente o por tu jefe, no nos queda más remedio que...¡pelearnos con el UpdatePanel!.

Vamos a ver un ejemplo de esta "pelea". Y para entrar en harina nada mejor que ir paso a paso chocando con todos lo problemas. Os recomiendo que copies el código en vuestro Visual Studio y ejecutéis todos los pasos que aquí se explican para daros de morros con todos lo problemas que surjan.

Supongamos que tenemos que implementar la siguiente funcionalidad.

  1. Mostrar un informe con los 4 primeros clientes de la tabla "Customers" de la conocida base de datos Northwind.
  2. Solo mostraremos los datos de los campos siguientes: CompanyName, ContactName, Address y Phone.
  3. En cada fila o registro del informe, debe haber un botón que muestre la fila selecionada escribiendo en el objeto HttpResponse. Es decir, que cada botón de cada fila debe hacer un PostBack para mostrar la fila seleccionada. Atentos porque esta es la verdadera dificultad.
  4. Otro botón, externo al informe, debe permitir mostrar los 10 primeros clientes y esta operación debe hacerse con AJAX.
Ahora nosotros como desarrolladores nos ponemos a pensar..¡que peligro!.
 
Y pensando, y pensando, llegamos a la conclusión de vamos a utilizar un control DataList para mostrar el informe. Y que este control lo meteremos dentro de un UpdatePanel registrando el botón externo al informe como disparador asíncrono para mostrar los 10 primeros resultados por AJAX. Y también registraremos el botón de cada fila del informe como disparador síncrono para que muestre la fila seleccionada con un PostBack.
 
Ya lo tenemos todo claro, y estamos seguros de que esto es muy fácil. ¡Veamos que pasa.!
 
            <asp:Button ID="Button1" runat="server" Text="Mostrar 10 resultados" onclick="Button1_Click" />
            <
asp:DataList ID="DataList1" runat="server" DataSourceID="SqlDataSource1" 
                onitemcommand
="DataList1_ItemCommand">
                
<ItemTemplate>
                    
<br />
                    <
br />
                    CompanyName:
                    <
asp:Label ID="CompanyNameLabel" runat="server" 
                        Text
='<%# Eval("CompanyName") %>' />
                    <
br />
                    ContactName:
                    <
asp:Label ID="ContactNameLabel" runat="server" 
                        Text
='<%# Eval("ContactName") %>' />
                    <
br />
                    Address:
                    <
asp:Label ID="AddressLabel" runat="server" Text='<%# Eval("Address") %>' />
                    <
br />
                    Phone:
                    <
asp:Label ID="PhoneLabel" runat="server" Text='<%# Eval("Phone") %>' />
                    <
br />
                    <
asp:Button ID="Button2" runat="server" Text="Mostrar Fila" />
                </
ItemTemplate>
            
</asp:DataList>
            
<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
                ConnectionString
="<%$ ConnectionStrings:NorthwindConnectionString %>" 
                SelectCommand
="SELECT TOP 4 [CompanyName], [ContactName], [Address], [Phone] FROM [Customers]">
            
</asp:SqlDataSource>
 
Añadimos este código a nuestro formulario. En primer lugar definimos el botón que mostrará los 10 primeros registros de la tabla "Customers". Después definimos el DataList que al cargarse la página gracias al SqlDataSource mostrará los 4 primeros resultados. Como vemos, en cada item o fila hemos definido el botón que mostrará la fila seleccionada. Solo nos falta escribir el código de cada botón.
 
    protected void DataList1_ItemCommand(object source, DataListCommandEventArgs e)
    {
        Response.Write(
"Fila seleccionada = " + (e.Item.ItemIndex + 1));
    
}

    
protected void Button1_Click(object sender, EventArgs e)
    {
        SqlDataSource1.SelectCommand 
"SELECT TOP 10 [CompanyName], [ContactName], [Address], [Phone] FROM [Customers]";
    
}
 
Podemos ejecutar la aplicación para comprobar que todo funciona perfectamente. Solo hay un problema, que cuando añadimos los 10 primeros resultados no lo hacemos por AJAX como se nos ha especificado. Así que manos a la obra.
 
Incluimos un control ScriptManager y un UpdatePanel que contenga el control DataList que muestra el informe. Y definimos los disparadores de control UpdatePanel...
 
                <Triggers>
                    
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
                    <
asp:PostBackTrigger ControlID="Button2" />
                </
Triggers>
  
Como vemos hemos registrado el control Button1 para que por AJAX muestre los 10 primeros registros. Y también hemos registrado el Button2 para muestre la fila seleccionada pero sin que se ejecute con AJAX. El código de nuestro formulario quedaría de la siguiente forma:
 
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
        
<div>
            
<asp:Button ID="Button1" runat="server" Text="Mostrar 10 resultados" onclick="Button1_Click" />
            <
asp:UpdatePanel ID="UpdatePanel1" runat="server">
                
<ContentTemplate>
                    
<asp:DataList ID="DataList1" runat="server" DataSourceID="SqlDataSource1" 
                        onitemcommand
="DataList1_ItemCommand">
                        
<ItemTemplate>
                            
<br />
                            <
br />
                            CompanyName:
                            <
asp:Label ID="CompanyNameLabel" runat="server" 
                                Text
='<%# Eval("CompanyName") %>' />
                            <
br />
                            ContactName:
                            <
asp:Label ID="ContactNameLabel" runat="server" 
                                Text
='<%# Eval("ContactName") %>' />
                            <
br />
                            Address:
                            <
asp:Label ID="AddressLabel" runat="server" Text='<%# Eval("Address") %>' />
                            <
br />
                            Phone:
                            <
asp:Label ID="PhoneLabel" runat="server" Text='<%# Eval("Phone") %>' />
                            <
br />
                            <
asp:Button ID="Button2" runat="server" Text="Mostrar Fila" />
                        </
ItemTemplate>
                    
</asp:DataList>
                
</ContentTemplate>
                
<Triggers>
                    
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
                    <
asp:PostBackTrigger ControlID="Button2" />
                </
Triggers>
            
</asp:UpdatePanel>
            
<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
                ConnectionString
="<%$ ConnectionStrings:NorthwindConnectionString %>" 
                SelectCommand
="SELECT TOP 4 [CompanyName], [ContactName], [Address], [Phone] FROM [Customers]">
            
</asp:SqlDataSource>
    
</div>
 
 
Ejecutemos la aplicación para ver que sucede.
 
 
 
 
Recibimos el error: No se pudo encontrar un control con el id. 'Button2' para el desencadenador de UpdatePanel 'UpdatePanel1'. 
 
 
¿Pero porque?. En realidad es lógico, el botón de cada fila se va a generar dinámicamente. Se crearán tantos botones como registros o filas mostremos en el control DataList. Y naturalmente cada botón tendrá un Id de cliente diferente, por lo tanto el UpdatePanel no tiene ni idea de a que disparador nos referimos cuando le decimos que registre el Button2, porque no existe un solo botón.
 
 
Eliminamos de nuestro código el registro de este botón maldito. Es decir, quitamos lo siguiente y ejecutamos la aplicación.
 
            <asp:PostBackTrigger ControlID="Button2" /> 
 
 
Parece que todo funciona correctamente. Pulsamos el botón para mostrar los 10 primeros registros y todo va como la seda. Pero que ocurre si pulsamos un botón de una de las filas.
 
 
 
 
¿Pero que es esto?. Al pulsar el botón estamos intentado escribir en el objeto HttpResponse de nuestra página. Y como estamos dentro del esquema de trabajo del UpdatePanel nuestra página ya no es la encargada de devolver el HTML al cliente. Ahora toman el mando de la respuesta los controles ScriptManager y UpdatePanel. 
 
 
¿Y como narices solucionamos esto?. Ejecutemos otra vez la aplicación pero no hagamos nada. Simplemente veamos el código fuente que se genera la primera vez que se carga la página.
 
    <script type="text/javascript">
    
//<![CDATA[
    
Sys.WebForms.PageRequestManager._initialize('ScriptManager1'document.getElementById('form1'));
    
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], ['Button1'], [], 90);
    
//]]>
    
</script>
 
Al principio del código fuente nos encontramos con este script autogenerado. ¿Y que hace este script?. 
 
En la primera linea instancia la clase cliente PageRequestManager y registra nuestro ScriptManager en nuestro formulario. Pero fijaros en la segunda linea. Fijaros que el botón que hemos definido para que muestre los 10 primeros resultados esta entre corchetes. Y fijaros también en que hay otros corchetes que no contienen nada.
 
Pues bien, esta linea lo que hace es registrar en el UpdatePanel cuales serán los controles disparadores que provocarán la actualización parcial del contenido del UpdatePanel. El primer corchete es para los controles que provocarán una llamada AJAX, y el segundo, como es lógico, serán los controles que provocarán un PostBack en toda regla.
 
He hablado de controles en plural porque entre estos corchetes podemos definir la colección de todos los disparadores del UpdatePanel. 
 
    Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], ['btAjax1','btAjax2',...],
 ['btPostBack1','btPostBack2',...], 90); 
 
 
Ahora que sabemos que este script es el encargado de registrar los disparadores para el UpdatePanel es muy fácil imaginar que también podremos hacerlo nosotros desde el código cliente. ¡Veamos como!.
 
 
Creamos nuestro propio script que registre solamente los botones que se generan dinámicamente, es decir, cada botón de cada fila. Y lo vamos hacer bajo demanda del usuario. Este sería el script que habría que escribir justo debajo de la definición de nuestro ScriptManager.
  
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <script type
="text/javascript" language="javascript">
        
function RegistrarTriggers(control) {
            
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], [], [control.id], 90);
        
}
    </script>
Ahora solo nos queda colocar la llamada a esta función en el botón que se generará para cada fila del informe.
 
<asp:Button ID="Button1" OnClientClick="RegistrarTriggers(this)" runat="server" Text="Mostrar Fila" />
 
¡Y ya esta todo!. Comprobarlo ejecutando la aplicación y veréis como cada botón de las filas del informe muestra la fila seleccionada escribiendo en el objeto HttpResponse provocando un PostBack.
 
Quizás alguien se ha preguntado porque este ejemplo tan raro. Es una simplificación de una aplicación real, en la que se pretendía mostrar un informe más complejo en el que cada fila tuviera un botón que exportara a Excel el contenido de la fila pulsada. Este Excel, se enviaba desde el servidor en la cabecera del objeto HttpResponse. Por este motivo, los botones de cada fila deberían generar un PostBack y permitir escribir en el objeto HttpResponse de la página.
 
 
Agradecimientos: Este artículo no podría haberse escrito sin la colaboración de un buen compañero. ¡Gracias Carlos!. 
 
 

Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on sábado, 06 de marzo de 2010 11:57
Permalink | Comentarios (0) | Post RSSRSS comment feed

Eventos del ciclo de vida cliente en ASP.NET AJAX

Como ya hemos comentado alguna vez, ASP.NET con AJAX provoca en el servidor los mismo eventos que durante un PostBack en el ciclo de vida de una página. Pero además, cuando tenemos habilitado AJAX en nuestra página, provoca eventos en el ciclo de vida cliente. El conocer estos eventos y su ciclo es muy importante para tener el mayor control y manejo posible de las solicitudes asíncronas.
 
Estos eventos en el cliente son provocados por las clases de la librería de AJAX .NET. Esta librería y las instancias de sus clases están a nuestra disposición automáticamente al incluir un control ScriptManager al inicio de nuestras páginas.
 
Son dos las clases principales que provocan estos eventos durante el ciclo de vida de la página en el cliente. Tenemos la clase Sys.Application que para entendernos es similar a la clase Page del objeto de servidor. Y tenemos la clase Sys.WebForms.PageRequestManager.
 
Veamos por partes como ocurre todo tanto durante un petición síncrona (PostBack), como una petición asíncrona (AJAX).
 
 
Ciclo de eventos cliente durante un PostBack
 
Cuando arrancamos por primera vez nuestra aplicación, o cuando hacemos un viaje al servidor de nuevo, el ciclo de eventos que se provocan en el cliente son los siguientes.
  • Window.load(). Este evento del DOM como ya sabemos se dispara cuando se carga nuestra página.
  • PageRequestManager.pageLoaded(). Este evento se dispara tanto durante una llamada asíncrona como una síncrona cuando se ha actualizado todo el contenido de la página. Es el evento ideal para determinar en el código cliente si la devolución de datos fué realizada por AJAX o por un PostBack.
  • Application.init(). Se dispara cuando se terminan de cargar todos los script de cliente pero antes de que se cree ningún objeto de estos. Es decir, que si intentamos acceder durante este evento a cualquiera de los objetos declarados en nuestros script recibiremos un error. Otra cosa muy importante a tener en cuenta en este evento es que se dispara una única vez por cada devolución de datos síncrona, nunca durante una devolución asíncrona.
  • Application.load(). Este evento se dispara cuando se han cargado todos los objetos de los script de la página.
  • Application.unload(). Entra en acción antes de eliminar todos los objetos script de la página.
  • Window.unload(). Este evento del DOM como se dispara cuando se cierra nuestra ventana del explorador.

 
Ciclo de eventos cliente durante AJAX en .NET
 
Ahora viene lo que realmente debemos tener en cuenta. No olvidemos que estamos trabajando bajo el esquema de un control ScriptManager y un control UpdatePanel en nuestra página. Esto significa que cuando el servidor nos devuelve los datos, se realiza una actualización parcial de la página, en concreto, solo obtenemos el HTML necesario para repintar el contenido de los elementos del interior del UpdatePanel.

  • PageRequestManager.initializeRequest(). Este es el primero de los eventos que se disparan cuando provocamos una llamada AJAX. En realidad, durante este evento aún no ha comenzado la solicitud, por lo tanto, es el evento ideal para cancelar la llamada desde nuestro código cliente.
  • PageRequestManager.beginRequest(). Se provoca antes de que se envíen los datos al servidor. Es el último evento que se produce en el cliente. Es el momento ideal para llamar a un script personal que inicie una animación que notifique al usuario que se está procesando la devolución de datos. El control UpdateProgress hace uso de este evento para mostrar su contenido.
  • PageRequestManager.pageLoading(). Se dispara después de recibir la respuesta del servidor durante una llamada AJAX, pero antes de actualizar cualquier contenido en la página. Es una oportunidad perfecta para realizar cualquier acción personalizada, antes de ser pintados, sobre los datos recibidos desde el servidor, como filtrarlos o formatearlos. También es el evento usado para enviar datos a los controles que no están incluidos en el updatepanel.
  • PageRequestManager.pageLoaded(). Este evento se dispara tanto durante una llamada asíncrona como una síncrona cuando se ha actualizado todo el contenido de la página. Es el evento ideal para determinar en el código cliente si la devolución de datos fué realizada por AJAX o por un PostBack.
  • Application.load(). Este evento se dispara cuando se han cargado todos los objetos de los script de la página.
  • PageRequestManager.endRequest(). Se dispara después del repintado parcial de la página y se devuelve el control al explorador. También se dispara este evento cuando se ha producido un error durante la devolución de datos asíncrona. Si se produce un error, la página no se actualiza por lo tanto los datos devueltos por el servidor no son mostrados. Se puede utilizar este evento para proporcionar una notificación de error personalizada a los usuarios o para registrar los errores. También se utilizar para ejecutar script personalizados al terminar la devolución de datos asíncrona.
 
 
Pues muy bien, mucho rollo pero...¿como se declaran estos eventos en el cliente?. Pongamos todo esto en práctica.
<script type="text/javascript" language="javascript">

        
//Eventos del objeto Application

        
Sys.Application.add_init(applicationInitHandler);

        function 
applicationInitHandler() {
        
//Código para este evento
        
}


        
//Eventos del objeto PageRequestManager

        
Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(prmInitializeRequest);

        function 
prmInitializeRequest() {
        
//Código para este evento
        
}
 </script>
La clase Sys.Application se instacia directamente, no así la clase Sys.WebForms.PageRequestManager que para intstanciarla debemos utilizar el método getInstance().
 
Los eventos de ambas clases se declaran al estilo de los delegados de C#, y siempre siguen la misma sintaxis: add_nombreEvento (nombreFuncion)
 
Podéis descargaros un pequeño ejemplo para jugar con todo esto: EventosClienteAJAX.rar (3,52 kb)
 
 

Posted by Oscar.SS on domingo, 21 de febrero de 2010 19:53
Permalink | Comentarios (2) | Post RSSRSS comment feed

Ejemplo de CAPTCHA en ASP.NET

Este sencillo ejemplo mostrará como implementar un controlador de peticiones HTTP personalizado para servir una imagen a modo de CAPTCHA. Naturalmente esto es solo un punto de partida muy simple y se podría complicar más para, por ejemplo, construir un control de servidor tipo CAPTCHA.

Para implementar nuestro controlador de peticiones HTTP personalizado debemos utilizar la interfaz IHttpHandler. Esta interfaz define el contrato que deben tener todos los tipos que se utilicen para manejar las peticiones HTTP. De esta forma podemos decidir qué y cómo se devuelve la solicitud desde el servidor.

A continuación dejo el código de la clase que implementa esta funcionalidad.

public class CaptchaHandler : IHttpHandler
{
    
public static string RndTexto { get; set; }
    
private char[] Abc;

    public 
CaptchaHandler()
    {
        Abc 
= new char[]{
            
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','Ñ','O','P','Q','R','S',
            
'T','U','V','W','X','Y','Z'};
        
InitRndTexto();
    
}
    //Creamos un texto aleatorio de 6 letras.
    
private void InitRndTexto()
    {
        Random Rnd 
= new Random();
        
System.Text.StringBuilder Sb = new System.Text.StringBuilder();

        for 
(int 0i < 6i++)
        {
            Sb.Append(Abc[Rnd.Next(
027)]);
        
}

        RndTexto 
Sb.ToString();
    
}

    
#region Miembros de IHttpHandler

    
public bool IsReusable
    {
        
get return false; }
    }

    
public void ProcessRequest(HttpContext context)
    {
        Bitmap Imagen 
= new Bitmap(18080, PixelFormat.Format16bppRgb555);
        
Graphics Grafico Graphics.FromImage(Imagen);

        
//Escribimos el texto.
        
Font Fuente = new Font(new FontFamily("Chiller"), 35, FontStyle.Italic);
        
Grafico.DrawString(RndTexto, Fuente, Brushes.Red, 1015);

        
//Dibujamos dos lineas.
        
for (int 0i < 2i++)
        {
            Point A 
= new Point(5, (35 + i * 10));
            
Point B = new Point(175, (35 + i * 10));
            
Grafico.DrawLine(new Pen(Brushes.Black, 3), A, B);
        
}

        context.Response.ContentType 
"Image/jpeg";
        
Imagen.Save(context.Response.OutputStream, ImageFormat.Jpeg);
    
}

    
#endregion
}

 

No hay mucho que explicar. En primer lugar generamos un texto aleatorio y lo guardamos en el campo RndTexto. Este campo publico podrá ser utilizado por otra clase cliente para comprobar, por ejemplo, que el texto introducido por el usuario coincide con el de la imagen. Después escribimos el texto aleatorio sobre una superficie de dibujo. También dibujamos dos lineas horizontales para dificultar un poco su lectura. 

Debemos registrar en el web.config este controlador HTTP para que ASP.NET sepa que debe hacer cuando entra una petición en el servidor.

      <system.web>
        
<httpHandlers>
            
<add verb="*" type="CaptchaHandler" path="*.captcha"/>
        </
httpHandlers>
      
</system.web>

Con esta configuración estamos indicando (por orden) que para cualquier tipo de petición GET o POST, debe ser manejada por el tipo CaptchaHandler. Por último, indicamos que cuando se solicite un archivo con extensión captcha nuestro controlador debe actuar sirviendo la respuesta implementada en el código.

Desde este momento, siempre que el código cliente solicite una imagen con la extensión indicada, se le servirá nuestro CAPTCHA personalizado. Es decir, al encontrarse con algo como lo siguiente:

        <img alt="" src="sitioWeb.captcha" />

Obtendremos una salida en el explorador cliente como esta.

 

 

Quizás el diseño no sea muy acertado, pero dejo para vosotros el mejorarlo. Wink


Tags:
Categories: Desarrollo Web
Posted by Oscar.SS on domingo, 14 de febrero de 2010 1:55
Permalink | Comentarios (0) | Post RSSRSS comment feed

Instalar la libería de AJAX Control Toolkit

La biblioteca de controles AJAX Control Toolkit es una poderosa característica que debemos tener siempre presente. Por ello vamos a ver como se instala en 7 sencillos pasos, para que quede integrada en nuestra herramienta de desarrollo Visual Studio 2008.

1. En primer lugar debemos dirigirnos al sitio oficial de descarga, que no es otro que CodePlex. En el siguiente enlace buscaremos la zona de descarga.

http://ajaxcontroltoolkit.codeplex.com/

 

2. Elegimos la versión que queremos descargar y descomprimimos el zip. En el momento de escribir este artículo esta disponible la versión que se muestra en la imagen.

 

3. Nos dirigimos a nuestro Visual Studio y abrimos un editor de páginas aspx. En el cuadro de herramientas, en la última ficha, "General", con el botón secundario seleccionamos la opción "Agregar Ficha". Le damos un nombre a la nueva ficha, por ejemplo, AJAX Control Toolkit.

 

4. Sobre la superficie de la nueva ficha repetimos la operación y esta vez seleccionamos "Elegir Elementos".

 

5. En la ventana que se abre, elementos del cuadro de herramientas, presionamos el botón "Examinar".

 

6. Buscamos la carpeta que acabamos de descargar y dentro de ella seleccionamos la siguiente dll. 

........./SampleWebSite/Bin/AjaxControlToolkit.dll

 

7. Presionamos el botón "Aceptar" y esperamos a que terminen de cargarse todos los binarios. Y ya tenemos todos los controles de AJAX en el cuadro de herramientas listos para ser utilizados.

¡A disfrutarlo! 


Posted by Oscar.SS on domingo, 07 de febrero de 2010 18:21
Permalink | Comentarios (0) | Post RSSRSS comment feed