domingo, 12 de junio de 2011

[ASP.NET][GridView] - Como seleccionar una fila

 

Introducción


He visto en reiteradas oportunidad que una operación simple como es el caso de operar con una fila de un GridView se puede transformarse en algo complejo, mas que nada motivado por la distintas formas que hay para realizar esta acción.

En este articulo veremos las algunas formas de lograrlo y como difieren las técnicas que se puede aplicarse.

Temas que se tratar:

1- Seleccionar una Row

a- Definiendo un CommandField

b- Usando un ImageButton y CommandName

c- Usando el evento RowCommand

2-Uso de DataKeyNames y DataKeys

   a- DataKeyNames con campos Múltiples

Para todos los casos planteados partiremos del mismo gridview, el cual se ira modificando para agregarle opciones y ver los distintos temas.

 

1- Seleccionar una Row


Existen varias formas de realizar una misma tarea, pero veremos aquí las dos mas simples y directas que se suelen encontrar cuando se necesita seleccionar un registro en el control gridview.

1a- Definiendo un CommandField

Iremos realizando los paso de forma visual así se comprende como proceder, remarcando luego como impacta esto en el html del grid

El primer paso será editar las columnas del GridView hasta visualizar el cuadro con las opciones de CommandField disponibles.

imagen1

imagen2

Para este caso en particular se agregara solo la opción de selección. Mediante las flechas laterales se puede posicionar el comando. También se dispone de distintos tipos de representación visual, como ser un Link, Button o Image.

imagen3

Para este caso usaremos un comando del tipo Image, por lo tanto se deberá definir la propiedad “SelectImageUrl”. Si se define del tipo Link y se quiere cambiar el texto, se usaría la propiedad “SelectText”.

El próximo paso será el de definir el evento de selección, para esto solo marcamos el gridview, y yendo a sus propiedades se podrá activar el evento SelectedIndexChanged

imagen4

El html resultante debería tener resaltadas las siguiente características

 imagen5

Con estos pasos ya estamos listos para capturar la acción de selección del gridview.

 

1b - Usando un ImageButton y CommandName

En esta alternativa se hará uso de un TemplateField, se procede de la misma forma del paso 1a, pero se agrega un item diferente

imagen6

Una vez que esta el témplate, se adapta modificando directamente en el html, incluyendo de esta forma el control ImageButton.

Es muy importante remarcar que el ImageButton deberá tener la propiedad CommandName=”Select” para que esta ejecute el evento SelectedIndexChanged

imagen7

A nivel de código de la pagina se encontrara la definición del evento

imagen8

Hay que aclarar que en este caso se uso un ImageButton para corresponder con la acción del punto 1a, en donde se define una imagen, pero si se requiere de un link solo será cuestión de usar un LinkButton, definiendo en este el CommandName=”Select”, es justamente el CommandName quien define que evento será lanzado al presionarse.

 

1c- Usando el evento RowCommand

Seguramente a estas alturas se preguntaran que cantidad de formas de hacer lo mismo, asi es, y para completarlo una opción extra.

Resulta que al definir un ImageButton (o LinkButton) en un TemplateItem y usar el CommandName=”Select” se habilita un evento adicionar para poder capturar esta acción, si es que el SelectedIndexChanged no nos convence.

Se trata del evento RowCommand.

Hay un pequeño detalle con este evento y se trata de la definición del CommandArgument para determinar que fila lanza la acción.

imagen9

[C#]

protected void gvPerson_RowCommand(object sender, GridViewCommandEventArgs e)
{
    if (e.CommandName == "Select")
    {
        //
        // Se obtiene indice de la row seleccionada
        //
        int index = Convert.ToInt32(e.CommandArgument);
        
        //
        // Obtengo el id de la entidad que se esta editando
        // en este caso de la entidad Person
        //
        int id = Convert.ToInt32(gvPerson.DataKeys[index].Value); 

    }

}

[VB.NET]

Protected Sub gvPerson_RowCommand(sender As Object, e As GridViewCommandEventArgs)
	
        If e.CommandName = "Select" Then
		'
		' Se obtiene indice de la row seleccionada
		'
		Dim index As Integer = Convert.ToInt32(e.CommandArgument)

		'
		' Obtengo el id de la entidad que se esta editando
		' en este caso de la entidad Person
		'

		Dim id As Integer = Convert.ToInt32(gvPerson.DataKeys(index).Value)
	End If

End Sub

En el ejemplo de la pagina WebForm4.aspx, se podrá probar como ambos eventos, tanto el RowCommand y el SelectedIndexChanged, pueden definirse, aunque lo normal es utilizar solo uno de estos.

 

2 - Uso de DataKeyNames y DataKeys


Una de las mejores técnicas usada para detectar que entidad se esta editando o seleccionado es por medio de id o código que esta tenga asignada, pero como logar hacerlo sin mostrar el identificador al usuario en una columna ?, es justamente el trabajo de estas dos propiedades que se consigue resolver el problema.

Si se presta atención al html este contaba con al definición de esta propiedad de nombre DataKeyNames

imagen10

Solo debe definirse que campo del origen de datos identifica a la entidad que se esta trabajando.

Cuando se lance el evento solo será cuestión de tomar la row que ejecuta la acción, y de esta, por el índice recuperar el valor del id de la entidad, en este caso el PersonID.

[C#]

protected void gvPerson_SelectedIndexChanged(object sender, EventArgs e)
{
    //
    // Se obtiene la fila seleccionada del gridview
    //
    GridViewRow row = gvPerson.SelectedRow;

    //
    // Obtengo el id de la entidad que se esta editando
    // en este caso de la entidad Person
    //
    int id = Convert.ToInt32(gvPerson.DataKeys[row.RowIndex].Value);


}

[VB.NET]

Protected Sub gvPerson_SelectedIndexChanged(sender As Object, e As EventArgs)
	'
	' Se obtiene la fila seleccionada del gridview
	'
	Dim row As GridViewRow = gvPerson.SelectedRow

	'
	' Obtengo el id de la entidad que se esta editando
	' en este caso de la entidad Person
	'
	Dim id As Integer = Convert.ToInt32(gvPerson.DataKeys(row.RowIndex).Value)


End Sub

2a- DataKeyNames con campos Múltiples

En este ejemplo solo se utilizo un valor simple para identificar a la entidad, pero el CommandName puede definir mas de un campo de información.

Por ejemplo que sucede si se quiere enviar el PersonId y el Nombre, esto es tan solo un ejemplo para demostrar las funcionalidad, no tiene una aplicación práctica en este caso, ya que con solo el PersonID seria mas que suficiente.

Para definir el DataKeyNames en el grid es tan simple como separar los campos por una coma.

imagen11

y en el código solo se toma el valor de la propiedad Values

[C#]

protected void gvPerson_SelectedIndexChanged(object sender, EventArgs e)
{
    //
    // Se obtiene la fila seleccionada del gridview
    //
    GridViewRow row = gvPerson.SelectedRow;

    //
    // Obtengo el id y el nombre  de la entidad que se esta editando
    // en este caso de la entidad Person
    //
    int id = Convert.ToInt32(gvPerson.DataKeys[row.RowIndex].Values["PersonID"]);

    string nombre = Convert.ToString(gvPerson.DataKeys[row.RowIndex].Values["FirstName"]);


}

[VB.NET]

Protected Sub gvPerson_SelectedIndexChanged(sender As Object, e As EventArgs)
	'
	' Se obtiene la fila seleccionada del gridview
	'
	Dim row As GridViewRow = gvPerson.SelectedRow

	'
	' Obtengo el id y el nombre  de la entidad que se esta editando
	' en este caso de la entidad Person
	'
	Dim id As Integer = Convert.ToInt32(gvPerson.DataKeys(row.RowIndex).Values("PersonID"))

	Dim nombre As String = Convert.ToString(gvPerson.DataKeys(row.RowIndex).Values("FirstName"))


End Sub

Ejemplo Código


Para el ejemplo se hizo uso de Visual Studio 2008 con SP1, el service pack es útil para poder hacer uso de Entity Framework y poder crear el ADO.NET Entity Data Model haciendo simple el acceso a la db.

La base de datos es Sql Server 2008 Express R2, y se encuentra el mdf dentro de la carpeta App_Data, pero se podría adjuntar al servicio de Sql Server o hacer uso del script que se encuentra en el proyecto de DataAccess.

 

 

domingo, 8 de mayo de 2011

[ASP.NET] GridView – Edición Empleados


Introducción


En esta ocasión se implementara al versión web de un articulo previo:
[WinForms] Edición Empleados – Grabar imagen en base de datos

Los puntos que se trataran con detalle serán:
- selección de un fila en el gridview
- eliminar un registro del grid
- visualizar una imagen que se encuentra dentro de la base de datos en un control de Image

 pantalla



1 – Selección  de una fila en el GridView


Existen varias formas de lograr este objetivo, en el ejemplo del este articulo aplique solo una de ellas haciendo uso del CommandField para definir las acciones sobre el grid, para ello hice uso de la opción visual:


 image


esto desplegara el dialogo:


 image


es aquí donde se definen las columnas entre ellas el CommandField, al seleccionarlo mostrara las propiedades de configuración, las propiedades de la sección “Behavior” es donde uno selecciona que botones quiere visualizar, es por eso que en este caso la propiedad “ShowSelecteButton” esta en True.

Además es importante la sección “Appearance” en donde se define “ButtonType” del tipo imagen y el “SelectImageUrl”, con la url del botones que se ve en el grid.
Concluida la definición de columnas y botones de acción, se debe especificar una propiedad muy importante en el grid, se trata del “DataKeyNames”, esta debería llevar el nombre la propiedad (o columna) del origen de datos que se usara como identificación de la entidad que se edita, en este caso como son empleado, será su id, definiéndose: DataKeyNames="IdEmpleado"

Este paso es importante ya que en los eventos se podrá recuperar sobre que entidad se debe aplicar la acción.
El botón de selección lanzara el evento “SelectedIndexChanging”, es por eso que será necesario definirlo en el grid: onselectedindexchanging="gvEmpleados_SelectedIndexChanging"
mientras que en el código de la pagina:

protected void gvEmpleados_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{
    int idempleado = Convert.ToInt32(gvEmpleados.DataKeys[e.NewSelectedIndex].Value);

    Response.Redirect(string.Format("EditarEmpleado.aspx?id={0}", idempleado));

}

Se hace uso del DataKeys para tomar el valor definido por el DataKeyNames, estas dos propiedades trabajan bien relacionadas entre ellas.
Lo último paso que queda es redireccionar a la pagina de edición.



2- Eliminar un registro


La operación de eliminar es muy parecida a la selección, solo cambia el comando usado para esto.
Al igual que la selección hay que configurar el CommandField habilitando la propiedad “ShowDeleteButton” en true.
Esta acción lanzara el evento “RowDeleting”, para lo cual especificamos su definición en el grid mediante: onrowdeleting="gvEmpleados_RowDeleting"
y luego en el código:

protected void gvEmpleados_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    int idempleado = Convert.ToInt32(gvEmpleados.DataKeys[e.RowIndex].Value);

    EmpleadosDAL.Eliminar(idempleado);

    CargarGrid(); //luego de eliminar se recarga el grid
}

Aquí también se hace uso del DataKeys para recuperar el id de la entidad que lanzo la acción.


3- Visualizar imagen


Cuando la imagen se encuentra dentro de la base de datos es necesario usar un intermediario para poder asignarla al control Image, y se haga visible al usuario.
Además todo esto sin generar archivos temporales que permitan el acceso a la imagen.
En este caso es el handler quien nos brinda esta ayuda, si se observa el código del mismo se ve muy simple, se recupera la entidad del empleado, y a continuación si contiene una imagen asociada se poniéndola en el Response, sino hay imagen se envía una por defecto, la cual indicara que no esta disponible (líneas: 21-23).

public class HttpImageHandler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        //
        // Se recupera la entidad empleado
        //
        int id = Convert.ToInt32(context.Request.Params["id"]);

        EmpleadoEntity empleado = EmpleadosDAL.ObtenerById(id);

        //
        // Arma el contexto que enviara la imagen en el response
        // se usa el nombre del empleado para el nombre del archivo que se envia
        //
        context.Response.Clear();
        context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}", empleado.Nombre)); 
        context.Response.ContentType = "image/jpg";

        byte[] imagenEmpleado = empleado.Imagen;
        if (empleado.Imagen == null)
            imagenEmpleado = File.ReadAllBytes(context.Server.MapPath("Imagenes/NoDisponible.jpg"));

        //
        // Se escribe en el response la imagen asociada al empleado
        //
        context.Response.BinaryWrite(imagenEmpleado);
        context.Response.End();
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

Esto es tanto usado por el grid para recuperar las imágenes que lista, como así también por cualquier otro control individual que necesites mostrar la imagen del empleado.
Es por eso que en el webform “EditarEmpleado.aspx”, cuando se carga el empleado todos sus datos se asignan directo a los controles, menos el control Image que recibe una url al handler para que tome de allí la imagen que debe mostrar (línea: 18)

private void CargarEmpleado(int id)
{
    EmpleadoEntity empleado = EmpleadosDAL.ObtenerById(id);

    if (empleado == null)
    {
        imgEmpleado.ImageUrl = "Imagenes/NoDisponible.jpg";
        return;
    }

    lblIdEmpleado.Text = Convert.ToString(empleado.IdEmpleado);
    txtNombre.Text = empleado.Nombre;
    txtApellido.Text = empleado.Apellido;
    txtFechaNacimiento.Text = empleado.FechaNacimiento.ToShortDateString();
    
    AsignarEstudios(empleado);

    imgEmpleado.ImageUrl = string.Format("imagen.ashx?id={0}", id);
}


Código del artículo



El ejemplo fue desarrollado con Visual Studio 2008, y base de datos Sql Server 2008 R2 Express.
Dentro de la carpeta “script” del proyecto “DataAccess” encontrara un archivo .sql que define la estructura de datos.

[C#]
[VB.NET]

[jQuery] RadioButton y CheckBox

 

Introducción

Después del artículo donde se analizo la forma de trabajar con controles de lista como ser los combos y listbox

[jQuery] Trabajo con ListBox y Combos

seguiremos con otro grupo de controles igual de importantes: Radio Buttons y Checkbox.

Listado de temas

  1. RadioButton
    1. Recuperar ítem Seleccionado
    2. Asignar Selección
    3. RadioButtonList
  2. CheckBox
    1. Recuperar ítem Seleccionado
    2. Asignar Selección
    3. CheckBoxList

 

1- RadioButton

1.1 – Recuperar ítem Seleccionado

En las siguientes líneas se observara algunas de las formas posibles en que se puede recuperar el ítem seleccionado en un radio button.

 

function btnEj1SeleccionUsandoClass_OnClick() {

    var valor = $('.Ej1radio:checked').val()

    $('#Ej1Resultado').html(valor);
}

function btnEj1SeleccionUsandoName_OnClick() {

    var valor = $("input[name='Ej1radio']:checked").val()

    $('#Ej1Resultado').html(valor);
}

function btnEj1SeleccionUsandoTabla_OnClick() {

    var valor = $("#Ej1Table :radio:checked").val()

    $('#Ej1Resultado').html(valor);
}

Básicamente en todas se hace uso del selector :checked para tomar los radio marcados por el usuario, al saber que solo uno puede seleccionarse, se recupera el valor mediante el método val()

1- se usa una clase asignada a los distintos radio button, por el uso del punto, este es el selector de clase de jquery

2- se selecciona por medio de atributo name, todos los radio deben tener el mismo

3- se selecciona la tabla que contiene los controles , luego los radio (mediante :radio), hay que mencionar que si en la misma tabla existe otro grupo de radio button este tipo de selección no podría ser aplicado, porque tomaría todos los radio sin discriminar cada agrupación

1.2 – Asignar selección

La asignación de un valor especifico a los radio se reduce a cambiar el atributo checked.

function btnEj2SeleccionarConTabla_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $("#Ej2Table :radio[value='" + valor + "']").attr('checked', true);

}


function btnEj2SeleccionarPorName_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;
        
    $(":radio[name='Ej2radio'][value='" + valor + "']").attr('checked', true);

}

En ambos caso se asigna el atributo “checked” a true para que este se desmarque, como los radio trabajaban en grupo no hace falta desmarcar al resto, ya que esto se hace automáticamente.

1- se selecciona la tabla que contiene el radio y se usa como filtro el valor ingresado en el textbox

2- se usa el nombre que define al grupo de radio buttons, luego sobre estos filtrar por el valor

También existen otras formas de seleccionar un ítem:

function btnEj2SeleccionarAtributoJavascript_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    var option = $(":radio[name='Ej2radio'][value='" + valor + "']");

    if (option.length > 0)
        option[0].checked = true;

}

function btnEj2SeleccionarConForEach_OnClick() {
    
    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $.each($("#Ej2Table :radio"), function() {

        if (this.value == valor) {
            this.checked = true;
        }

    });

}

1- se busca el option que coincide, pero la acción de checkear se logra por medio de la propiedad provista por javascript. El usar el ítem cero [0], no es porque la selección devuelve varios resultados en la búsqueda en un array, sino que se esta tomando el control javascript de la selección de jquery.

2- aquí la búsqueda se realiza mediante un loop por todos los radio, comparando su valor con el buscado, el que coincida será marcado

1.3 – RadioButtonList

En esta sección se hará uso de control propuesto por asp.net para representar un conjunto de radios.

Si se analiza con el “IE Developer Tools”, al cual se accede presionado F12, se podrá inspeccionar el html generado por el control de lista de radio buttons

Como se observa la opción esta compuesta por el input común de html, pero la descripción esta dentro de un label

function btnEj3SeleccionUsandoName_OnClick() {

    var option = $(":radio[name='rdlEj3']:checked");
    var texto = $('label', option.parent());

    var msg = 'texto: {0}, valor: {1}'.format(texto.html(), option.val());

    $('#Ej3Resultado').html(msg);
}

Es por eso que luego de tomar la option seleccionado se usa el parent() para subir un nivel, ahora estaremos posicionado en el tag <td>, y dentro de este se recupera el label, de esta forma se obtendrá la descripción del option marcado.

Algo interesante aquí es que en la línea

$('label', option.parent())

se esta usando un selector de un selector, o sea se buscara solo el label que este dentro de ese tag <td> y no en todo el sitio, es por eso que se separa por coma, a la izquierda iría la selección del ámbito donde aplica el selector de la derecha.

2- CheckBox

2.1 - Recuperar ítem Seleccionado

En la selección del checkbox se necesita algo mas de código, ya que pueden ser varios los ítems marcados.

En todos los ejemplos se recorren los checks elegidos y se vuelca a una array para mostrar el resultado en pantalla, aquí se aplica la misma técnica que en el artículo previo, cuando se analizo el ListBox.

function btnEj1SeleccionUsandoClass_OnClick() {

    var list = new Array();

    $.each($('.Ej1check:checked'), function() {

        var msg = 'valor: {0}'.format($(this).val());
        list.push(msg);

    });

    $('#Ej1Resultado').html(list.join('
'));
}

function btnEj1SeleccionUsandoName_OnClick() {
    
    var list = new Array();

    $.each($("input[name='Ej1check']:checked"), function() {

        var msg = 'valor: {0}'.format($(this).val());
        list.push(msg);

    });

    $('#Ej1Resultado').html(list.join('
'));
    
}

function btnEj1SeleccionUsandoTabla_OnClick() {

    var list = new Array();

    $.each($("#Ej1Table :checkbox:checked"), function() {

        var msg = 'valor: {0}'.format($(this).val());
        list.push(msg);

    });

    $('#Ej1Resultado').html(list.join('
'));
    
}

1- se hace uso de la clase asignada a los ckeck para determinar cuales fueron marcados, al igual que los radio se usa el punto de jquery como selector

2- el mismo nombre es usado en cada check que forma parte del grupo, este es usando este como selector, el name es un atributo por eso es que se define entre []

3- se selecciona la tabla que contiene los checks, recorriendo los seleccionados

2.2 - Asignar Selección

Aquí también hay varias formas de lograrlo al igual que los radio, pero a diferencia del control anterior si es necesario un paso previo que limpie la selección previa, es por eso que se hace uso del metodo attr() que asigna el atributo checked en false

$("#Ej2Table :checkbox").attr('checked', false);

puede variar la forma en que se aplica la selección de jquery, pero la idea es obtener todos los checks y de un golpe desmarcarlos.

En estos ejemplo se puede ingresar en el TextBox varios valores separados por coma, es por eso que se recorre cada numero ingresado para tomar el check que mapea con este valor y marcarlo. Se logra este objetivo por medio del $.each y el split() de la cadena de valores ingresados.

 

function btnEj2SeleccionarConTabla_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $("#Ej2Table :checkbox").attr('checked', false);
    
    $.each(valor.split(','), function() {
    
        $("#Ej2Table :checkbox[value='" + this + "']").attr('checked', true);

    });

}


function btnEj2SeleccionarPorName_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $(":checkbox[name='Ej2check']").attr('checked', false);

    $.each(valor.split(','), function() {

        $(":checkbox[name='Ej2check'][value='" + this + "']").attr('checked', true);

    });

}

1- se hace uso de la tabla para tomar los check que están contenidos

2- se recuperan los check por el atributo name asignado al grupo

 

function btnEj2SeleccionarAtributoJavascript_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $(":checkbox[name='Ej2check']").attr('checked', false);

    $.each(valor.split(','), function() {

        var option = $(":checkbox[name='Ej2check'][value='" + this + "']");
        
        if (option.length > 0)
            option[0].checked = true;
    });

}

3- se hace uso de la propiedad checked de javascript para marcar el control, aquí la selección recupera el check concreto, pero luego con el uso de [0] se toma el control puro en javasscript que contiene esa propiedad checked

Remarco que la propiedad checked no es parte de jquery, este usa el attr() para cambiar el valor, solo javascript posee esta propiedad, por eso el uso del [0] en el resultado del selector de jquery

 

function btnEj2SeleccionarConForEach_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;
    
    $(":checkbox[name='Ej2check']").attr('checked', false);
    
    
    $.each($("#Ej2Table :checkbox"), function() {

        var check = this;
        
        $.each(valor.split(','), function() {

            if (check.value == this)
                check.checked = true;

        });

    });

}

4- se aplica la misma técnica del punto 3, pero se realiza un doblo loop, primero por cada check en la tabla y luego por los valores ingresados. No digo que esta sea la forma mas optima de implementarlo, pero me pareció interesante mostrarlo para conocer como unir dos ciclos.

 

function btnEj2SeleccionarUniendoValores_OnClick() {

    var valor = $('#txtEj2').val();

    if (valor == null)
        return;

    $(":checkbox[name='Ej2check']").attr('checked', false);

    var valuesList = Array();

    $.each(valor.split(','), function() {

        var value = "[value='{0}']".format(this);
        valuesList.push(value);

    });

    $(":checkbox[name='Ej2check']").filter(valuesList.join(',')).attr('checked', true);

}

5- esta ultima técnica de selección es bastante particular ya que arma un array de filtros que es ejecutado mediante el método filter() de jquery sobre la selección de todos los checks. Es necesario marcar que cada ítem del filtro deberá ser separado por comas.

2.3- CheckBoxList

Al igual que con el RadioButtonList, es una buena idea inspeccionar el html resultante del render

Algo interesante que se puede observar es que el control rendiza una tabla de nombre “rdlEj3” que podría ser usada para seleccionar los check contenidos en esta.

function btnEj3SeleccionUsandoName_OnClick() {
   
    var options = $("#rdlEj3 :checkbox:checked");

    var list = new Array();

    $.each(options, function() {

        var option = $(this);
        var valor = $(this).parent().attr('hiddenValue');
        var texto = $('label', option.parent());

        var msg = 'texto: {0}, valor: {1}'.format(texto.html(), valor);
        list.push(msg);

    });

    $('#Ej3Resultado').html(list.join('
'));
}

En este caso se toma los check dentro de la tabla que asp.net crea para contener estos controles, tomando de esta solo los tildados.

Algo que debe marcarse es que el CheckBoxList no ingresa el valor de cada ítem, es por eso que en el código .net en el evento Page_Load cuando se asignan los ítems, se pusieron unas líneas que agregan el valor del ítem

foreach (ListItem item in rdlEj3.Items)
{
    item.Attributes["hiddenValue"] = item.Value; 
}

Esto se rendizan en el html dentro de un tag <span>, es por eso que en las líneas 9 al 11 se hace uso del parent() para subir un elemento al seleccionado y recuperar esta información.

El control base que provee asp.net solo crea el atributo value=”on” como puede verse en la imagen, el atributo “hiddenValue” es creado al en el loop de cada ítem luego de cargar la lista.

 

Ejemplo de código

 

viernes, 29 de abril de 2011

[jQuery] Trabajo con ListBox y Combos

 

Introducción

Este artículo se pretende continuar lo empezado en:

[jQuery] Por donde comenzar

En esta ocasión trataremos un conjunto diferente de controles, estos muy usados en un desarrollo web.

En este caso particular trataremos controles de lista: ListBox y Combos. Notando cuan similares son a la hora de trabajarlos.

Selección de Ítems

En los ejemplos indicados como 1 y 2, se recuperan los ítems marcados por parte del usuario, pudiendo manipularlos y trabajar con la información.

 

        function btnEj1Seleccion_OnClick() {

            var list = new Array();

            $.each($('#Ej1Select :selected'), function() {
               
                var msg = 'texto: {0}, valor: {1} 
'.format($(this).text(), $(this).val());
                list.push(msg);

            });

            $('#Ej1Resultado').html(list.join(''));
            
        }

        function btnEj2Seleccion_OnClick() {
           
            var optionSelected = $('#Ej2Select :selected');

            var msg = 'texto: {0}, valor: {1}'.format(optionSelected.text(), optionSelected.val());

            $('#Ej2Resultado').html(msg);

        }

Los selectores usados en ambos casos son idénticos, solo lo diferencia la selección múltiple que puede brindar un ListBox, es por eso que aplica un loop por cada ítem marcado por el usuario.

En este código hay que marcar algunos puntos de interés:

La función $.each() recorre la selección de una lista de ítems y aplica una función por cada uno de ellos.

En este caso en concreto se recorre cada ítem seleccionado de la lista, o sea se obtiene cada <option> tomando de este el valor y el texto. Es importante notar que esta función tiene acceso a cada ítem por medio del this, pero se trata de un objeto javascript y no jquery, es por eso que se aplica nuevamente un selector $(this), para tener acceso a los métodos que permiten acceder recuperar la información.

Con respecto al selector, este accede al control por medio del sus id, por eso el uso del #, pero de este se filtra además los seleccionados usando el :selected.

Por fuera de la función se crea un array de javascript para ubicar cada dato marcado, se inserta el texto resultante de cada item por medio de push(), y al final se lo une por medio del join(), para lograr un solo string.

Seguramente se habrá notado además el uso del una función format() para reemplazar en el texto ciertas posiciones con un dato en concreto, bien esta función en si no es estándar de javascript, sino que se encuentra definida en utils.js, y básicamente aplicaría como un método de extensión para los tipos de datos string.

El trabajo con combos es idéntico a la lista, pero solo se toma un ítem accediendo de forma directa, no requiere ser recorrido sus ítems, por eso es algo más simple aplicar los selectores.

 

Agregar / Remover Ítems

La sección marcada para el Ejemplo 3, representa la operación de agregar y remover elementos en una lista.

        function btnEj3Agregar_OnClick() {

            var ej3Texto = $('#txtEj3Texto');
            var ej3Valor = $('#txtEj3Valor');

            var newItem = $('<option/>').text(ej3Texto.val()).val(ej3Valor.val());
            $("#Ej3Select").append(newItem);

        }
        
        function btnEj3AgregarDespuesSeleccion_OnClick() {

            var itemsSelected = $('#Ej3Select :selected');

            if (itemsSelected.length == 0) {
                alert('Debe seleccionar un item');
                return;
            }
                
            var ej3Texto = $('#txtEj3Texto');
            var ej3Valor = $('#txtEj3Valor');

            var newItem = $('<option/>').text(ej3Texto.val()).val(ej3Valor.val());
            newItem.insertAfter(itemsSelected[0]);

        }
        

        function btnEj3Seleccion_OnClick() {
        
            var list = new Array();

            $.each($('#Ej3Select :selected'), function() {

                var msg = 'texto: {0}, valor: {1} 
'.format($(this).text(), $(this).val());
                list.push(msg);

            });

            $('#Ej3Resultado').html(list.join(''));
            
        }

        function btnEj3RemoverSeleccion_OnClick() {

            $.each($('#Ej3Select :selected'), function() {

                $(this).remove();

            });
        }

El agregar una nueva opción requiere que el html de <opción> sea creado, es por eso que se define este en el selector, para luego disponer de método de text() y val() para asignar la información proveniente del input del usuario.

Una vez creado el nuevo ítem solo se hace un append() a la lista existente, también se podrían usar métodos como ser insertBefore() o insertAfter() para controlar la ubicación del nuevo elemento, pero esto requiere que se indique que elemento será tomado como objetivo. En el ejemplo al hacer uso de estos métodos se debe asegurar que al menos un ítem esta marcado para que funcione correctamente, si mas de uno se ha seleccionado solo se toma el primero.

El remover un ítem es muy simple, solo se recorre la selección y se aplica el remove(), al igual que en la selección, el this representa cada ítem en el loop, aquí también es necesario aplicar nuevamente el selector para tener acceso a los método de jquery implementa.

Trabajar con combos es idéntico a las listas, solo que se opera con un solo ítem a la vez, el ejemplo 4 implementa justamente estas acciones en un combo:

function btnEj4Agregar_OnClick() {

    var ej4Texto = $('#txtEj4Texto');
    var ej4Valor = $('#txtEj4Valor');

    var newItem = $('<option/>').text(ej4Texto.val()).val(ej4Valor.val());
    $("#Ej4Select").append(newItem);

}

function btnEj4Seleccion_OnClick() {

    var optionSelected = $('#Ej4Select :selected');

    var msg = 'texto: {0}, valor: {1}'.format(optionSelected.text(), optionSelected.val());

    $('#Ej4Resultado').html(msg);

}

function btnEj4RemoverSeleccion_OnClick() {

    $('#Ej4Select :selected').remove()

}

Algo que quizás no he marcado en los otros ejemplos es que al utilizar algo como esto

var optionSelected = $('#Ej4Select :selected');

se debe comprender que se esta asignado a la variable la selección del objeto completo, es por eso que luego se puede acceder a la funcionalidad provista por jquery.

 

Asignar Selección

En algún momento seguramente se requiera marcar un valor de la lista o combo sin que el usuario realice la acción, sino que será por medio de código, por suerte jquery hace esto muy simple, implementándose de igual forma no importa de que lista se trate.

En los ejemplos 5 y 6, se puede apreciar la implementación:

function btnEj5SeleccionarPorValor_OnClick() {

    var valor = $('#txtEj5').val();
    
    $('#Ej5Select').val(valor);

}

function btnEj6SeleccionarPorValor_OnClick() {

    var valor = $('#txtEj6').val();

    $('#Ej6Select').val(valor);

}

Si se escribe un valor correcto en el TextBox, solo hará falta asignar el val() al selector del control y eso es todo.

Listas ASP.NET

Hasta ahora hemos estado trabajando en los ejemplos con controles html, pero se debió a que operar con estos o con el control de lista o combo del asp.net es idéntico.

En el ejemplo 7 se podrá apreciar justamente este punto:

 

        function btnEj7Agregar_OnClick() {

            var ej7Texto = $('#txtEj7Texto');
            var ej7Valor = $('#txtEj7Valor');

            var newItem = $('<option/>').text(ej7Texto.val()).val(ej7Valor.val());
            $('#<%=Ej7Select.ClientID%>').append(newItem);

        }

        function btnEj7Seleccion_OnClick() {

            var list = new Array();

            $.each($('#<%=Ej7Select.ClientID%> :selected'), function() {

                var msg = 'texto: {0}, valor: {1} 
'.format($(this).text(), $(this).val());
                list.push(msg);

            });

            $('#Ej7Resultado').html(list.join(''));

        }
        
        function btnEj7RemoverSeleccion_OnClick() {

            $.each($('#<%=Ej7Select.ClientID%> :selected'), function() {

                $(this).remove();

            });
        }

Aplicar selectores a un control de asp.net solo requiere el uso de la propiedad ClientID, si es que se usa el id del control para acceder al mismo, esto se aplica por si asp.net renombra esta propiedad al realizar el render del html que envía al cliente.

 

Código de Ejemplo

 

domingo, 17 de abril de 2011

[jQuery] Por donde comenzar

 

Introducción

jQuery es una de las librerías muy mencionada en los ultimo tiempo para el desarrollo de aplicaciones web.

Usar javascript de forma estándar puede ser algo bastante tedioso ya que no es una sintaxis del todo amigable para el trabajo diario, o al menos para quien esta acostumbrado a programa en un lenguaje amenos como ser C# o VB.NET.

Es por eso que surge jQuery, para solventar este problema, siendo el mas conocido entre los frameworks de javascript, (esta se gano su lugar en los témplate para la creación de proyectos como son los utilizados en aplicaciones con ASP.NET MVC), y el que dio impulso a la utilización de este tipo de frameworks,  vale aclarar además que también existen otro como ser: Prototype, MooTools, etc

Este articulo en particular se sumara a los miles que existen publicados para aportar un punto de inicio para quienes aun no conocen la magia de esta librería.

Aquí no se pretende explicar cada unos de los aspectos de jQuery, porque solo el tema se selectores es enorme y muy variado, sino que iremos a casos concretos, y analizaremos particularidades en el uso de la librería.

jQuery es muy amplio, con mucha flexibilidad, cuando la usen verán que no hay una sola forma de hacer las cosas, a veces se pueden aplicar un selector con distintas combinaciones y todas estarán correctas.

Es por eso que es necesario recurrir a la fuente y consultar la documentación provista por la propia librería en su sitio web: jQuery

Para empezar veremos dos puntos básicos que afectan a la interacciones de asp.net y javascript, luego continuaremos con el análisis de dos controles simples para entrar en tema.

 

1 – Acceder a los controles de asp.net

Algo que debe conocerse cuando se trabaja con controles desde javascript es como hacer referencia a estos.

Por ejemplo, si en el html de una pagina se define:

<input id="Text1" type="text" />

desde javascript podría seleccionar el control usando:

var text1 = document.getElementById('Text1');

de esta forma se seleccionado el control cuando se trata de uno del tipo html, pero que sucede si ahora se tiene un control de asp.net, aquí ya no somos quienes controlamos el render del control, por lo tanto el ID asignado al mismo puede variar.

Muy bien, ahora si realizamos unas pruebas y ponemos un asp:TextBox en una página

<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

al acceder desde el browser se vera como

<input name="TextBox1" type="text" id="TextBox1" />

ups, pero que sucedió aquí el id no se modifico, seguro pensaran que estoy mintiendo, pues bien, lo que sucede es que en algunas situaciones asp.net no cambie el id del control, porque en este caso no lo requiere, el verdadero problema se presente cuando este control esta contenido dentro de ciertos contenedores, como ser: un User Control, la Master Page, o controles que repiten un témplate, como ser el ListView, GridView, Repeater, etc

Entonces que sucede ahora si se crear un User Control (WebUserControl1.ascx) con el textbox dentro del mismo y se lo ubica en una pagina, al acceder al código del browser se obtendrá

<input name="WebUserControl11$TextBox1" type="text" id="WebUserControl11_TextBox1" />

Ahora si se comprueba como asp.net modifica el id asignado al control en tiempo de diseño, asp.net necesito ponerle un prefijo con el nombre del User Control para que este sea único, ya que en la pagina puede usarse mas de un mismo user control, sino redefine el id puede haber colisiones en los nombres.

Como se haría para seleccionar este control sino se conoce previamente el id que será generado, pues bien, se dispone en asp.net de la propiedad ClientID, siendo usada de la siguiente forma:

var userControlText1 = document.getElementById('<%=TextBox1.ClientID%>');

al inspeccionar el código resultante en el browser se obtendrá

var userControlText1 = document.getElementById('WebUserControl11_TextBox1');

el tag <%=  %> permite ubicar allí el valor de la propiedad ClientID.

Si bien esto que comento parece un dato menos, es bastante importante cuando se comienzan a dar los primeros paso en javascript mezclado con controles del asp.net

 

2 – Acceder a controles asp.net desde un archivo .js

Continuando con el tip del punto anterior, avanzaremos en un tema que por ahí pueda generar dificultades a la hora de ordenar el código.

En algunas oportunidades uno requiera hacer uso de archivos .js para llevar allí el código javascript y trabajarlo de forma separada al html que esta en el aspx, pero es aquí donde empiezan los problemas.

Resulta que si se hace uso del tag <% %>, este solo puede ser aplicado dentro de la página donde se hace el render por parte de asp.net, o sea solo funciona dentro del aspx

El archivo .js es referenciado por el aspx, pero este no se incluye en el proceso de render, por lo tanto el ClientID nunca será reemplazado.

Pero hay una solución a este punto, la cual consiste en dejar la definición del tag <% %> dentro del aspx, pero que la misma solo asigne a una variable javascript el nombre del control, luego sera usada en el .js para así poder tomar el control y trabajarlo.

En la carpeta “Ejemplo2” esta la implementación del código de ejemplo, se observara la declaración de las líneas

<script type="text/javascript">

    var TextBox1 = '<%=TextBox1.ClientID%>';

</script>

en donde se asigna en la pagina aspx el nombre como string, para luego dentro del .js usar

function ObtenerValor() {

    var text1 = document.getElementById(TextBox1);
    alert(text1.value);

}

Es necesario apreciar como cambio la selección al usar el getElementById(), usando ahora el nombre de la variable.

El único problema que se presenta es que por cada control que se quiera usar dentro de .js será necesario crear una variable en la pagina que incluya el ID que asp.net asigna.

 

3 – Obtener y asignar el valor de un Texbox

Seguramente se preguntaran “cuando comenzamos”, es cierto hasta aquí se presentaron temas mas relacionados con javascript, que al propio jquery, pero eran necesario tenerlos presente ya que estos también afectan al desarrollo.

Bien, comencemos con algo simple.

Seleccionemos el valor de un TextBox, copiando el contenido a otro:

[HTML]

<form id="form1" runat="server">
    <table>
        <tr>
            <td>
                <input id="txtInfo" type="text" />
            </td>
            <td> 
                <input id="btnMostrar" type="button" value="button" onclick="Mostrar_OnClick();"/>
            </td>
            
        </tr>
        <tr>
            <td colspan="2"> 
                <input id="txtInfoCopia" type="text" />
            </td>
        </tr>
    </table>
</form>

[JavaScript]

function Mostrar_OnClick() {

    var info = $('#txtInfo').val();

    $('#txtInfoCopia').val(info);
    
}
 

como se observa jquery lo hizo bien simple.

Es preciso conocer que cuando uno selecciona un control y lo hace por el atributo “id” debe anteponer el “#”, este es uno de los tanto selectores tiene jquery que iremos viendo.

Para que esto funcione es necesario contar con la librería, en este caso se encuentra en la línea

<script src="../script/jquery-1.5.2.min.js" type="text/javascript"></script>

por lo general se sitúa en la propia pagina, pero también es común verlo en la Master Page para que todas incluyan la librería, y no se tenga que declarar en cada una particularmente.

Y que sucede si se tratan de controles de asp.net, bien es igual de simple, pero conociendo lo que vimos en el punto: “1 – Acceder a los controles de asp.net”, se obtendría la misma funcionalidad usando:

[JavaScript]

function MostrarASPNET_OnClick() {

    var info = $('#<%=txtInfoASPNET.ClientID%>').val();

    $('#<%=txtInfoCopiaASPNET.ClientID%>').val(info);

}

Avancemos un poco inicializando los textbox, para hacerlo individualmente se podría aplicar algo como esto:

[JavaScript]

function LimpiarASPNET_OnClick() {

    $('#<%=txtInfoASPNET.ClientID%>').val('');

    $('#<%=txtInfoCopiaASPNET.ClientID%>').val('');

}

es idéntico a lo que ya se viene viendo solo que no se le pasa valor

Y si se quiere limpiar el contenido de todos los textbox, se deberá asignar uno a uno?, no es necesario, hay técnicas mucho mas simples:

[JavaScript]

function LimpiarTodos_OnClick() {

    $(':text').val('');

}

aquí se puede ver otro selector distinto proporcionado por jQuery, en este caso se trata de un selector por tipo text el cual aplicara el valor a todos los controles que encuentre del tipo textbox.

Ahora bien, lo lindo que tiene esta librería es que no hay una sola forma de hacer esto, si se revisa la documentación: :text Selector, menciona que usar $('[type=text]') o $('*:text') y también $('input:text') son equivalentes, pero por simplicidad en la escritura se aplica :text

Nota: los ejemplos de esta sección se encuentra en el “Ejemplo 3” del código publicado.

4 – Trabajar con controles Label

Asignar un valor a un label difiere un poco de como se haría a un TextBox.

En el ejemplo de la carpeta “Ejemplo 4” se encontrara una muestra de como trabajar con este otro control.

en la pagina se agrego un label html

<label id="lblInfoCopia" />

y uno de asp.net

<asp:Label ID="lblInfoCopiaASPNET" runat="server"></asp:Label>

accederlo de forma individual no representa mayor problema, salvo por el hecho de cambiar el val() por el html() para tomar o asignar un valor.

Pero el seleccionar todos los labels de la pagina no es tan directo, ya que se renderiza diferentes en html estos controles, si se analiza el código generado en el browser se notara que el label de html sigue siendo un label, pero el de asp.net no lo es, ya que es transformado a un tag <span> cuando se convierte

Es por esto que debe seleccionarse distinto cada control.

[JavaScript]

function Mostrar_OnClick() {

    var info = $('#txtInfo').val();

    $('#lblInfoCopia').html(info);

}

function MostrarASPNET_OnClick() {

    var info = $('#<%=txtInfoASPNET.ClientID%>').val();

    $('#<%=lblInfoCopiaASPNET.ClientID%>').html(info);

}

function LimpiarASPNET_OnClick() {

    $('#<%=txtInfoASPNET.ClientID%>').val('');

    $('#<%=lblInfoCopiaASPNET.ClientID%>').html('');

}

function LimpiarTodos_OnClick() {

    $(':text').val('');
    $('label').html('');
    $('span').html('');
}

 

 

Conclusión

Hasta aquí hemos tenido una introducción general a las particularidades del uso de javascript cuando se tratan controles asp.net.

También hemos visto como tratar dos controles muy usados en las paginas como son los textbox y labels.

En próximos artículos veremos mas controles ya sean html o asp.net, como adjuntar evento y recorrer el resultado de una selección para trabajar con cada uno de sus ítems

 

Código Ejemplo


 

domingo, 13 de marzo de 2011

[ASP.NET] Grabar Archivo en base de datos

 

Introducción


Este articulo podría considerarse la continuación de:

[ASP.NET] - Guardar Imagen base de datos

básicamente se extenderá el articulo anterior aportando los temas:

  • subir un archivo a una carpeta dentro del sitio
    • validación de las extensiones que puede subirse
    • listar los archivos de la carpeta
    • distintas formas de representar el link de descarga
      • link directo al archivo
      • link a una pagina que administrara la descarga
    • borrado del archivo de la carpeta del sitio
  • subir el archivos a una base de datos
    • listar los registro de la tabla
    • distintas formas de implementar la descarga
      • usando un handler
      • usando una página que administre la descarga
    • eliminar registro de la base de datos, por ende el archivo

 

Subir un archivo a una carpeta del sitio


Para esta primera sección se trabajara con el contenido desarrollado en la carpeta “GuardarEnCarpeta” del proyecto que se puede descargar en el articulo, la carpeta contiene la página “ListarArchivos.aspx”, cuya funcionalidad será la de subir (FileUpload) y además listas los archivos (GridView), de una carpeta determinada dentro de la estructura del sitio web.

    <form id="form1" runat="server">
    <div>
        <asp:FileUpload ID="FileUpload1" runat="server" />
        <br />
        <br />
        <asp:Button ID="btnSubirArchivo" runat="server" Text="Subir Archivo" 
            onclick="btnSubirArchivo_Click"  />
        <br />
        <asp:RegularExpressionValidator 
             id="RegularExpressionValidator1" runat="server" 
             ErrorMessage="Solo pdf, doc o xls son permitidos." 
             ValidationExpression="^(([a-zA-Z]:)|(\\{2}\w+)\$?)(\\(\w[\w].*))+(.pdf|.doc|xls)$" 
             ControlToValidate="FileUpload1">
            </asp:RegularExpressionValidator>
        <br />
        <br />
        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" CellPadding="4"
            ForeColor="#333333" GridLines="None" onrowdeleting="GridView1_RowDeleting" DataKeyNames="Name">
            <RowStyle BackColor="#EFF3FB" />
            <Columns>
               <asp:TemplateField HeaderText="Eliminar" ItemStyle-HorizontalAlign="Center">
                    <ItemTemplate>
                        <asp:ImageButton ID="imgEliminar" runat="server" CommandName="Delete"  ImageUrl="~/imagenes/delete.png" Width="24px" Height="24px"/>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Nombre Archivo">
                    <ItemTemplate>
                        <asp:HyperLink ID="nombre" runat="server" NavigateUrl='<%# Eval("Name", "~/files/{0}") %>'
                            Text='<%# Eval("Name") %>'>
                        </asp:HyperLink>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:BoundField DataField="Length" HeaderText="Tamaño" />
                <asp:TemplateField HeaderText="Descargar" ItemStyle-HorizontalAlign="Center">
                    <ItemTemplate>
                        <asp:HyperLink ID="descarga" runat="server" NavigateUrl='<%# Eval("Name", "~/GuardarEnCarpeta/Download.aspx?filename={0}") %>'>
                               <img src="../imagenes/download.gif" alt="" width="30px" height="30px" style="border-width:0px;" />
                        </asp:HyperLink>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
            <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
            <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
            <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <EditRowStyle BackColor="#2461BF" />
            <AlternatingRowStyle BackColor="White" />
        </asp:GridView>
    </div>
    </form>

 

Es importante resaltar algunos puntos de este código.

 

Validación en las extensión que pueden ser subidas al sitio

Se hace uso del control RegularExpressionValidator, para proporcionar una validación del lado del cliente, que impida subir archivos en una extensión invalida, o no deseada.

<asp:RegularExpressionValidator 
             id="RegularExpressionValidator1" runat="server" 
             ErrorMessage="Solo pdf, doc o xls son permitidos." 
             ValidationExpression="^(([a-zA-Z]:)|(\\{2}\w+)\$?)(\\(\w[\w].*))+(.pdf|.doc|xls)$" 
             ControlToValidate="FileUpload1">
</asp:RegularExpressionValidator>

Dos formas diferentes de exponer el link de descarga

El gridview que lista los archivos de la carpeta expone dos link de descarga del archivo listado, uno de ellos al presionar el nombre:

<asp:TemplateField HeaderText="Nombre Archivo">
    <ItemTemplate>
        <asp:HyperLink ID="nombre" runat="server" NavigateUrl='<%# Eval("Name", "~/files/{0}") %>'
            Text='<%# Eval("Name") %>'>
        </asp:HyperLink>
    </ItemTemplate>
</asp:TemplateField>

Prestar atención a la definición del NavidateUrl, en este la url especifica concretamente al archivo, por medio del path relativo usando el ~/, esto se puede lograr porque al archivo esta físicamente alojado en una carpeta en al estructura del sitio publicado.

La otra alternativa se presenta al presionar sobre la imagen de descarga:

<asp:TemplateField HeaderText="Descargar" ItemStyle-HorizontalAlign="Center">
    <ItemTemplate>
        <asp:HyperLink ID="descarga" runat="server" NavigateUrl='<%# Eval("Name", "~/GuardarEnCarpeta/Download.aspx?filename={0}") %>'>
               <img src="../imagenes/download.gif" alt="" width="30px" height="30px" style="border-width:0px;" />
        </asp:HyperLink>
    </ItemTemplate>
</asp:TemplateField>

En este otro caso se define una pagina web “Download.aspx” con un valor que pasara por querystring, esta pagina será la responsable de tomar el archivo desde su lugar físico y lo enviara en el Response.

Es importante en este punto analizar que acciones toma esta pagina.

public partial class Download : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string filename = Request.QueryString["filename"].ToString(); 

        Response.Clear();

        Response.AddHeader("content-disposition", string.Format("attachment;filename={0}", filename));
        Response.ContentType = "application/octet-stream";

        Response.WriteFile(Server.MapPath(Path.Combine("~/files", filename)));

        Response.End();

    }
}

El código que implementa es muy simple, solo toma el archivo que recibe por la url, define información para la respuesta y escribe en el Response el archivo para ser enviado.

Un punto no menor es la definición del ContentType del tipo “application/octet-stream”, este forzara la descarga mostrando el cuadro de download al usuario para que seleccione donde quiere descargarlo. Si en lugar de usar este se hubiera definido el tipo concreto para carda archivo, al realizarse la descarga se mostraría embebido el documento en el browser, por supuesto esto sucederá si el usuario posee un editor para este documento.

Eliminación del archivo subido el sitio

La eliminación del archivo esta implementada en los botones de gridview, pero para esto funcione se definen varios puntos, el primero tiene que ver con la columna en el grid

<asp:TemplateField HeaderText="Eliminar" ItemStyle-HorizontalAlign="Center">
     <ItemTemplate>
         <asp:ImageButton ID="imgEliminar" runat="server" CommandName="Delete"  ImageUrl="~/imagenes/delete.png" Width="24px" Height="24px"/>
     </ItemTemplate>
 </asp:TemplateField>

la cual es un ImagenButton con el CommandName = “Delete”, que lleve el nombre “Delete” no es arbitrario, sino que con este nombre lanzara el evento  RowDeleting del gridview, es por eso que se asocia el evento onrowdeleting="GridView1_RowDeleting" además de las propiedad DataKeyNames="Name", para poder tomar el nombre del archivo que se quiere eliminar.

Si ahora se analiza el evento:

protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    //se obtiene el nombre de campo definido en el DataKeyNames del gridview
    string fileName = Convert.ToString(GridView1.DataKeys[e.RowIndex].Value);
    //se define el path fisico del archivo
    string fullPath = Path.Combine(Server.MapPath("~/files"), fileName);

    File.Delete(fullPath);

    Response.Redirect("ListarArchivos.aspx");

}

en este se hace uso del DataKeys, para obtener el nombre del archivo, esto se logra gracias al DataKeyNames definido con el campo del nombre del archivo, ahora solo queda obtener la ruta física y proceder a eliminar.

 

Subir un archivo a la base de datos


En esta otra sección cambiaremos de carpeta del trabajo dentro del sitio, ahora sera “GuardarEnDb”, aquí también se cuenta con un GridView para listar los archivos registrados en la db, y el FileUpload para subirlos al sitio.

    <form id="form1" runat="server">
    <div>
        <asp:FileUpload ID="FileUpload1" runat="server" />
        <br />
        <br />
        <asp:Button ID="btnGuardar" runat="server" Text="Guardar Archivo" OnClick="btnGuardar_Click" />
        <br />
        <asp:RegularExpressionValidator 
             id="RegularExpressionValidator1" runat="server" 
             ErrorMessage="Solo pdf, doc o xls son permitidos." 
             ValidationExpression="^(([a-zA-Z]:)|(\\{2}\w+)\$?)(\\(\w[\w].*))+(.pdf|.doc|xls)$" 
             ControlToValidate="FileUpload1">
        </asp:RegularExpressionValidator>
        <br />
        <br />
        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" CellPadding="4"
            ForeColor="#333333" GridLines="None" DataKeyNames="Id" 
            onrowdeleting="GridView1_RowDeleting">
            <RowStyle BackColor="#EFF3FB" />
            <Columns>
                <asp:TemplateField HeaderText="Eliminar" ItemStyle-HorizontalAlign="Center">
                    <ItemTemplate>
                        <asp:ImageButton ID="imgEliminar" runat="server" CommandName="Delete"  ImageUrl="~/imagenes/delete.png" Width="24px" Height="24px"/>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Nombre Archivo">
                    <ItemTemplate>
                        <asp:HyperLink ID="nombre" runat="server" NavigateUrl='<%# Eval("Id", "~/DescargaArchivo.ashx?id={0}") %>'
                            Text='<%# Eval("Nombre") %>'>
                        </asp:HyperLink>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:BoundField DataField="Length" HeaderText="Tamaño" />
                <asp:TemplateField HeaderText="Descargar">
                    <ItemTemplate>
                        <asp:HyperLink ID="descarga" runat="server" NavigateUrl='<%# Eval("Id", "~/GuardarEnDb/Download.aspx?id={0}") %>'>
                               <img src="../imagenes/download.gif" alt="" width="30px" height="30px" style="border-width:0px;" />
                        </asp:HyperLink>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
            <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
            <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
            <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <EditRowStyle BackColor="#2461BF" />
            <AlternatingRowStyle BackColor="White" />
        </asp:GridView>
    </div>
    </form>

A simple vista pareciera idéntico al ejemplo tratado en el punto 2 del articulo, pero hay detalles que deben analizarse, porque hay unas cuantas diferencias.

Dos formas diferentes de exponer el link de descarga

Aquí también nos encontramos con distintas formas de descarga, pero en este caso al tratarse de una base de datos ya no se puede acceder directo al archivo por su url, será necesario contar con intermediarios que obtengan el archivo desde la db y lo envíen como respuesta.

Una de esta formas se define por medio de un handler, en donde la columna en el grid poseerá una url al mismo.

<asp:TemplateField HeaderText="Nombre Archivo">
    <ItemTemplate>
        <asp:HyperLink ID="nombre" runat="server" NavigateUrl='<%# Eval("Id", "~/DescargaArchivo.ashx?id={0}") %>'
            Text='<%# Eval("Nombre") %>'>
        </asp:HyperLink>
    </ItemTemplate>
</asp:TemplateField>

Nótese que lo importante aquí no es el nombre que se use para referirse al handler, ya que este puede ser cualquiera, lo importante es la extensión usada (en este caso ashx), ya que la misma será definida en el web.config, en la línea:

<httpHandlers>
	.
	.
	<add verb="*" path="*.ashx" type="GuardarArchivoBaseDatos.GuardarArchivo.HttpImageHandler"/>

</httpHandlers>

La implementación del handler se encuentra en la clase HttpImageHandler.cs, dentro del código del mismo se simula el comportamiento que tendría la definición del link si se lo define directo al archivo, es por eso que se define un ContentType concreto para cada extensión especifica de los archivos soportados.

public class HttpImageHandler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        int id = Convert.ToInt32(context.Request.Params["id"]);

        Archivo archivo = ArchivosDAL.GetById(id);

        context.Response.Clear();
        context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}", archivo.Nombre));

        switch (Path.GetExtension(archivo.Nombre).ToLower())
        {
            case ".pdf":
                context.Response.ContentType = "application/pdf";
                break;
            case ".doc":
                context.Response.ContentType = "application/msword";
                break;
            case ".xls":
                context.Response.ContentType = "application/vnd.ms-excel";
                break;
        }

        context.Response.BinaryWrite(archivo.ContenidoArchivo);
        context.Response.End();
    }

    public bool IsReusable
    {
        get { return false; }
    }

}

Nota: podrías haberse definido directamente el “application/octet-stream” en el hadler, pero se realizo de esta forma para mostrar distintas variantes que pueden aplicarse.

Es en la otra alternativa se define la página “Download.aspx”, pero esta tiene algunas diferencias con respeto a su semejante cuando se trabaja con archivos.

<asp:TemplateField HeaderText="Descargar">
    <ItemTemplate>
        <asp:HyperLink ID="descarga" runat="server" NavigateUrl='<%# Eval("Id", "~/GuardarEnDb/Download.aspx?id={0}") %>'>
               <img src="../imagenes/download.gif" alt="" width="30px" height="30px" style="border-width:0px;" />
        </asp:HyperLink>
    </ItemTemplate>
</asp:TemplateField>

Si bien pareciera engañar la igualdad con el ejemplo del punto 2.2, este tiene sus diferencias, una muy clara es el uso del campo “Id” como parámetro de la url del link, al usar una base de datos estamos trabajando con registro y los mismo se identifican por su clave.

public partial class Download : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        int id = Convert.ToInt32(Request.QueryString["id"]);

        Archivo archivo = ArchivosDAL.GetById(id);

        Response.Clear();

        Response.AddHeader("content-disposition", string.Format("attachment;filename={0}", archivo.Nombre));
        Response.ContentType = "application/octet-stream";

        Response.BinaryWrite(archivo.ContenidoArchivo);
        Response.End();

    }
}

Se toma el id enviado en el querystring, se recupera la información del archivo por medio de una consulta a la DB y luego de definir el ContentType, se envía el array de byte por el Response, nótese que aquí no se escribe un archivo porque no se tiene uno, sino que se opera directamente con los byte.

Eliminación del archivo subido el sitio

Las diferencia en este punto con lo visto en el 2.3, es que aquí se debe hacer uso del id de la entidad que se quiere eliminar, y no el nombre del archivo, es por eso que se define el DataKeyNames, con el “Id”, y luego en el código:

protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    //se obtiene el nombre de campo definido en el DataKeyNames del gridview
    int id = Convert.ToInt32(GridView1.DataKeys[e.RowIndex].Value);

    ArchivosDAL.DeleteById(id);

    CargarListadImagenes();
}

se toma este para ejecuta la consulta de DELETE del registro en la db

 

Código de ejemplo


El desarrollo del ejemplo fue realizado con Visual Studio 2008, y la db empleada es Sql Server 2008 Express, el mdf de la misma se encuentra en la carpeta App_Data, y la cadena de conexión se define en el Web.config

 

[C#] 
[VB.NET] 
 

martes, 8 de marzo de 2011

[Análisis] CampusMVP - ADO.NET Entity Framework 4.0 - Aplicaciones y servicios centrados en datos

 

Diariamente durante la participación en los foros noto que tema recurrente esta relacionado con el accesos a datos, existen muchas dudas de las técnicas que se deben aplicar para trabajar la información de un repositorio.

En el blog trato de ejemplificar de forma practica las situaciones mas comunes que he visto planteadas en el foro, pero debo reconocer que son muchas y muy variadas como para tratarlas todas.

Mas que nada estas se generan por un nivel inicial de quién consulta, sumado a la no dedicación del tiempo suficiente para madurar la tecnología, quizás realizando practicas que vayan subiendo en complejidad de de forma progresiva muchas de las dudas nunca se hubieran planteado. Por lo general si se busca como guía un libro sobre el tema, esta podría ser una alternativa, pero seguramente esté en ingles, y muchas veces el no tener un gran dominio del idioma puede terminar restando, haciendo muy lento el aprendizaje.

Lo anteriormente dicho genera un problema : "falta de capacitación". La profesión de sistemas requiere de constante capacitación que permita obtener nuevas habilidades sobre un tema; sino se madura la tecnología es más que seguro que se encontrarán miles de trabas.

Es aquí donde CampusMVP puede aportar un gran beneficio, tuve la oportunidad gracias al contacto de Fred Lores de analizar unos de los cursos online que brindan
y la verdad me quedé maravillado, me refiero a este en particular:

Preparación de la certificación 70-516 TS: Accessing Data with Microsoft® .NET Framework 4

Como comenté más arriba el acceso a datos y manipulación de la información desde mi punto de vista es un aspecto importantísimo en el desarrollo de sistemas, ojo con esto no estoy minimizando que la presentación (WinForms, WFP, asp.net) y comunicación (WCF) sean aspectos menos importantes, pero sí noto que el trabajar con datos es uno de los aspecto que más dudas y consultas generan todos los días.

Esta inquietud se la plateé a Fred y es por eso que de todos los cursos expuestos en CampusMVP el de acceso a datos fue el que destaco.

Después de recorrerlo un buen rato destaco las características:

  • el curso está totalmente en español, lo cual no hay que menospreciar teniendo en cuenta que en sistemas no abunda documentación en nuestro idioma, el material comúnmente encontrado esta en inglés, lo cual implica ir algo más lento si es un idioma que no se domina del todo.
  • claramente explicado y con una lectura muy simple, algo que me llamó la atención es lo natural que se da la lectura de los temas, por supuesto esto también está relacionado a la experiencia previa que uno tenga. Si ya se han realizado algunas incursiones es lógico que resulte más simple la lectura, pero así y todo resultan muy claros los temas tratados. 
  • la complejidad de cada tema avanza de forma progresiva, a medida recorren los temas estos van aumentando de complejidad, el curso está estructurados de forma escalonada para que resulte práctico.
    Este punto, unido a los ejemplo de código que se brindan durante la explicación, aporta una clara comprensión de cada tema tratado.
  • tiene la cantidad justa de imágenes que explican paso a paso como realizar cada tarea, pareciera un dato menor pero es importante que las explicaciones escritas se complementen con imágenes y que ésto se haga de forma adecuada no es simple (como se dice: una imagen vale más de mil palabras, pero tampoco es cuestión de desbordar todos los temas con miles de print screen de pantallas).
    Además se cuenta con videos que complementan aquellos temas en donde se requiere mostrar un proceso completo.

En el link del curso se puede observar el temario tratado, pero este no solo incluye ADO.NET para el acceso a datos, sino que cubre en totalidad la certificación 70-516 (para aquel que quiera rendirla, lo cual es recomendable si se realiza el curso)
Es por eso que se incluyen puntos relacionado con ORM, más que útil si se quiere aprender las últimas novedades que agilicen el desarrollo:

  • Entity Framework
  • Linq to Sql

Es más, este va mas allá incluyendo tecnologías actuales como ser:

  • Sync Framework
  • WCF Data Service

Al acceder al curso se dispone de autoevaluaciones para afianzar lo aprendido durante la unidad.

Para coronar el postre con una frutilla, el acceso al curso permite la descarga de 3 libros en formato pdf, entre ellos el más destacado.

ADO.NET Entity Framework 4.0 - Aplicaciones y servicios centrados en datos

Conclusión, si se busca aprender o profundizar los conocimiento en el acceso a datos, en español, de simple lectura y comprensión, sumado a una buena estructura en el temario, este curso es el indicado.