martes, 7 de diciembre de 2010

AsyncFileUpload + GridView. El infierno de un postback.

EDITADO: Me dá un problema, para mí, insalvable que es que me hace un doble postback. Cuidadin con esto.
En este día de puente, en donde la inmensa mayoría se están tomando unas merecidas vacaciones, heme aquí disfrutando como un enano con la suma del control AsyncFileUpload + un GridView.
Lo primero, aviso que la estructura de utilizar un datatable, seguro que se puede mejorar. De echo seguro que refactorizaré para mejorar. Pero el error que me ha consumido casi cinco horas de trabajo ha sido el siguiente: AsyncFileUpload NO hace postback en el evento de servidor AsyncFileUpload_UploadedComplete…(demonios!!).
El escenario es que voy a leer un fichero de texto del cliente, y lo voy a introducir directamente en un GridView. Para ello lo primero es saber que el AsyncFileUpload tiene tres eventos de cliente y uno de servidor. Por lo cual conformaremos nuestro control así:
<asp:AsyncFileUpload ID="AsyncFileUpload" Width="865px" 
OnClientUploadError="uploadError"
OnClientUploadStarted="StartUpload" 
OnClientUploadComplete="UploadComplete"
OnUploadedComplete="AsyncFileUpload_UploadedComplete"
CompleteBackColor="Lime"
UploaderStyle="Modern" ErrorBackColor="Red" 
UploadingBackColor="#66CCFF" runat="server"  />

Los Javascript que comprende la actuación de los tres primeros eventos en cliente son (*No consigo utilizar el Control.IdClient en un js)

// Funciones de AsyncFileUpload
function uploadError(sender, args) {
document.getElementById('ctl00_chPrincipal_lblStatus').innerText = args.get_fileName(),
"<span style='color:red;'>" + args.get_errorMessage() + "</span>";
}

function StartUpload(sender, args) {
document.getElementById('ctl00_chPrincipal_lblStatus').innerText = 'Uploading Started.';
}

function UploadComplete(sender, args) {
document.getElementById('ctl00_chPrincipal_lblStatus').innerText = 'Fichero subido corréctamente';
 __doPostBack('tctl00$chPrincipal$UpdatePanel', '');
}

Y el método que captura el evento de servidor es el siguiente:

protected void AsyncFileUpload_UploadedComplete(object sender, AjaxControlToolkit.AsyncFileUploadEventArgs e)
{
DataTable listaMatriculas = new DataTable();
listaMatriculas.Columns.Add("Matricula", typeof(string));
StreamReader lectorDelFichero = new StreamReader(AsyncFileUpload.FileContent);

string linea;
do
{
linea = lectorDelFichero.ReadLine();
DataRow fila = listaMatriculas.NewRow();
fila["Matricula"] = linea;
listaMatriculas.Rows.Add(fila);
}
while (linea != null);

GridMatriculas.DataSource = listaMatriculas;
GridMatriculas.DataBind();

}

El problema es que por mucho que le dijera que hiciera el DataBind del Grid, este no se actualizaba. Lo peor es que como no se actualizaba pensé que era un problema de la fuente de datos y salte de ArrayList, a List y luego a var de Linq… y nada, no se veía.


Desesperado, le puse un botón para recargar y, asombroso, eso sí que funciona. Lo cual me llevó a la idea de poner un UpdatePanel.Update()… lo cual tampoco sirvió. Entonces, me dije, esto es un problema de postback y me puse a mirar por San Google hasta llegar al excelente blog Schelfaut.NET (Sven Schelfaut's technical blog) en donde está la solución que he puesto en negrita. En el evento de cliente  UploadComplete, se fuera un postback sobre el UpdatePanel que contiene al GridView. Y con esto todo va como la seda.


La siguiente refactorización urgente es poder invocar directamente al IdClient  de un control desde un javascript que viene de una librería js. Y en vez de tener que construir una DataTable, poder utilizar una lista. Ya que StreamReader no soporta Linq.


Espero que os sea útil.

1 comentario:

Unknown dijo...

Pero tctl00$chPrincipal$UpdatePanel es el nombre del update panel que te da el asp? que ?