jueves, 12 de febrero de 2009

GridView Dinámico con DataTable y ViewState

Saludos, creo que espere bastante tiempo antes de publicar una entrada en este blog pero el trabajo ha sido abrumador, jejeje.
Bueno para empezar tuve un problema con crear controles en tiempo de ejecución, así que esta solucion tiene una pequeña historia con algo de teoría:
Cuando programamos en Asp net es común que se nos presenten complicaciones con los controles creados en tiempo de ejecución ya que al crearlos se muestran perfectamente al usuario, pero solo la primera vez, ya que si hay un postback de la pagina, los controles se mueren y hay que redibujarlos para que el usuario pueda volver a verlos, lo que significa, oh desilucion la pérdida de los datos que se nos haya ocurrido meterle a nuestros amados controles.
Para persistir los datos entre las idas y venidas de la pagina entre el cliente y el servidor existe en .Net una maravillosa herramienta llamada View State, la cual de forma natural realiza esta tarea en todos los controles de .Net para evitar la pérdida de los datos, pero como nuestros controles son creados en tiempo de ejecución tendriamos que incluir a mano dicho uso del View State.
Hasta aqui todo suena de lujo, es sencillo incluir valores en el view state y utilizarlos en la pagina para llevar y traer valores es relativamente sencillo, pero el detalle es que para poder incluir algo en el view state, éste debe ser serializable.
al iniciar este experimento me lleve el chubasco de que cuando guardaba mi caja de texto en vel view state no habia problema, el problema era al intentar traer de regreso el valor, ya que me marcaba un pequeño error:

El tipo 'System.Web.UI.WebControls.TextBox' del ensamblado 'System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' no está marcado como serializable.


bastante tiempo le di vueltas al por que era esto hasta que le pregunte a un amigo (Edwin el crédito es tuyo) si sabia algo al respecto, entonces me preguntó el por que deseaba hacerlo de esta manera con los textboxes, y me dijo :¿por que no lo haces con un gridview y lo rellenas con una tabla? las tablas si son serializables, así que despues de meditarlo me dije, ¿por que no lo hago asi? y bueno lo que salio fue algo mas o menos así:
en primer lugar el html, ¿que necesitaba? un grid view, así que a escribir un poco de código:
El html:

como no se puede poner escrito lo veremos en imagen

En éste código pusimos un grid view un poco estilizado con un item template llamado txtName y un require field validator para forzar al usuario a escribir algo en la caja, el valor a recibor se declara en el Eval y es "nombreAlumno" asi que con esto esta terminada la parte del usuario

y en el behind :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
//la libreia para usar la clase DataTable
using System.Data;

public partial class _Default : System.Web.UI.Page
{
//las variables que trabajarán con el view state
private DataTable __tabla;
protected DataTable tabla// nuestra variable de view state es ViewState["tabla"]
{
get
{
if (ViewState["tabla"] != null)
return (DataTable)ViewState["tabla"];
else
{
__tabla = new DataTable();
ViewState["tabla"] = (DataTable)__tabla.Clone();
return this.__tabla;
}
}
set
{
ViewState["tabla"] = value;
this.__tabla = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)//en caso que no sea un post back o sea la primera carga de la página
{
//si no tenemos columnas declaradas en la tabla guardada en el view state
if (tabla.Columns.Count == 0)
{
cargaColumnas();//este metodo se encargará de insertarle columnas a la tabla
}
}

//este mètodo leera la grilla cuando se haga un postback para traer los datos que hayamos
//modificado en la parte del usuario y con esto cargar nuestra tabla guardada en el view state
//
leeGrilla();

}
protected void Page_PreRender(object sender, EventArgs e)
{
//en este evento cargaremos el data source del grid asi como el bindeo de los datos
usaEnlace();
}

protected void cargaColumnas()
{
//ingresamos la columna a nuestra tabla
tabla.Columns.Add("nombre", typeof(string));
}
protected void btnAgrega_Click(object sender, EventArgs e)
{
DataRow dr = tabla.NewRow();//instanciamos una nueva fila de nuestra tabla       
tabla.Rows.Add(dr);//insertamos la copia de fila y le creamos una nueva fila a nuestra tabla
}
protected void leeGrilla()
{
if (grvAlumnos.Rows.Count > 0)//en caso que el grid tenga filas realizamos las operaciones de lectura
{
tabla.Rows.Clear();//eliminamos las filas de la tabla guardada en el grid
foreach (GridViewRow gr in grvAlumnos.Rows)//recorremos la grilla de datos
{
DataRow dr = tabla.NewRow();
//el truco para traer los datos de el template de la grilla es buscar los controles gracias de nuevo Edwin jeje
// al hacer el retrieve de el itemtemplate lo casteamos a tipo TextBox para poder usar el text (esto puede ser para cualquier control)
dr["nombre"] = ((TextBox)gr.Cells[0].Controls[1]).Text;
tabla.Rows.Add(dr);//agregamos la fila en la tabla
}              
}
}

protected void usaEnlace()
{
grvAlumnos.DataSource = tabla;//asignamos como datasource al grid la tabla que guardamos en el view state
grvAlumnos.DataBind();//bindeamos los datos de la grilla
}

}

Explicamos un poco el código:
bueno para empezar las librerias, sin la System.Data no hay DataTable asi que a agregarla, después tenemos la variable que movera los datos a traves del view state, una vez con esto trabajamos con la tabla, hay que ponerles columnas asi que estas dependeran de los datos que querramos guardar, para el ejemplo los datos en la grilla estan en un item template con un textbox dentro pero puede ser cualquier control, la carga de las columnas se hara la primera vez que cargue la pagina y en caso de no tener columnas en la tabla, posteriormente si se quieren agregar columnas a la grilla se deberan de agregar en la tabla y volver a poner el data source de la grilla y el bindeo de datos, los enlaces de datos los realizamos en el Page_PreRender ya que es el ultimo evento antes de mostrar la pagina y si lo hacemos en el load puede provocar un conflicto , eso es otra hisoria.
Ahora, cada vez que se haga un postback hay que revisa los datos de la grilla para ahora llenar con estos la tabla y rebindear los datos, esto es relativamente sencillo pero no es tan obvio hasta que no lo vemos en vivo, a mi me costo mas de un dia el encontrar la solución, y esto con ayuda de el buen Edwin, saludos, espero les sirva para alguna cosilla que requieran hacer, dudas comentarios, sugerencias y correcciones con gusto las atendere, este es mi primer post con una solucion así que estoy muy contento, aunque no le pude poner con colores el codigo para diferenciar, pero espero corregir rsto pronto, saludos y estamos en contacto.

Como es mejor con ejemplos aqui les dejo el ejemplo para descargar, esta hecho con el web developer 2008 express, saludos.
gridview dinamico....

8 comentarios:

Claudia dijo...

hola me sirvio tu blog pude tomar algunos tips muchas gracias a gente como tu y el amigo del que hablas ok

Osman Bismarck dijo...

Me equivoque de blog..sorry...esta muy bueno...gracias

Osman Bismarck dijo...

Hola. esta muy bueno.. y me ayudo pero ahora si quiero eliminar un row de este datable como lo hago???

Alonso dijo...

Esta muy bueno pero tengo una consulta!!
Por ejemplo quiero poner una imagen ("etiquetas img ") en una columna de mi datatable, para posteriormente ponerlo en mi grilla pero no sale.
Incluso he probado cambiando el datatype del colum a Byte pero no me resulta. (Originalmente estaba como String)
ejm:
column = New DataColumn
column.DataType = System.Type.GetType("System.Byte")
column.ColumnName = "Imagen"
TableImagenes.Columns.Add(column)

/******/

row("Imagen") = vimagen

Te agradeceria que me orientes o que me brindaras una solucion. GRacias!!1

Mi mail es a.sagashaka@gmail.com

Saludos

Carlos Abraham Reséndiz Martínez dijo...

Saludos Alonso perdona por la tardansa pero he estado un poco saturado, brindarte una solucion por el momento no puedo, jeje bendito trabajo, pero tengo una orentación que puede servirte, en alguna ocacion en la escuela utilizé imagenes en el gridview con un picturebox y la ruta, ahora el picture box tambien puede usarse con una imagen cargada directamente desde la base de datos, puedes comenzar con estas bases colocando el picturebox en un item template de la grilla y te agradeceria postearas aqui si tienes solucion, de cualquier forma en el siguiente poste que espero sea pronto hare algo como lo que quieres, espero te sea de ayuda o tal vez ya sepas lo que te digo pero de cualquier forma estamos en contacto, si tienes msn podriamos platicar al respecto, mi mail es f_raptor@hotmail.com , saludos

Piero Guerrero dijo...

hola muchas gracias por tu post, de verdad me sirvio lo hice tal y como lo propusiste y funcionó
gracias

pablovilan dijo...

Q lastima que no esta en Vb, es buenisima la solucion! =( FELICITACIONES!

Carlos Abraham Reséndiz Martínez dijo...

pasarlo a vb no es tan dificil, ademas hay herramientas que pueden facilitarte la vida,ya tengo algo abandonado este blog, proximamente una nueva entrada :D