Como hacer el Update de una Pagina aspx con un UpdatePanel, un Thread y un Timer
Generar un proceso largo que actualice el contenido de la página desde un Thread y a través de un UpdatePanel. Cuando queremos generar un proceso muy largo desde una aplicación web, y queremos que el progreso del proceso actualice información de la página web, podemos hacerlo con un UpdatePanel y un poco más. Cuando el proceso que queremos ejecutar es tan pesado que decidimos ponerlo dentro de un Thread, aún complicamos más el refresco de pantalla, por eso vamos a exponer los pasos y los conceptos.
Update Panel
El UpdatePanel utiliza tecnología Ajax para actualizar una parte de la página web, y no forzar el refresco de la página entera. Una técnica muy utilizada en aplicaciones asp.net. Imaginemos que tenemos el siguiente UpdatePanel.
<asp:UpdatePanel ID="updProgreso" runat="server"
UpdateMode="conditional">
<ContentTemplate>
<asp:Label ID="lblActualizar" runat="server" Text=""></asp:Label>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="BtnExecute1"/>
</Triggers>
</asp:UpdatePanel>
Cuando pulsamos sobre el botón “BtnExecute1″ podemos actualizar la label “lblActualizar” y refresacará sólo el UpdatePanel. Para ello podemos utilizar el método Update del UpdatePanel.
lblActualizar.Text = "Actualizamos la etiqueta";
updProgreso.Update();
Desde la parte del cliente viaja a la parte del Servidor, y cuando regresa es cuando actualiza el contenido de la etiqueta.
Cuando queremos actualizar el UpdatePanel desde un Thread
Si lo que queremos es realizar un proceso lento dentro del Botón “BtnExecute1″, por ejemplo que recorra un bucle y a cada salto queremos actualizar la label “lblActualizar”, tenemos un problema. El UpdatePanel sólo se actualizará una sóla vez. Cuando termine el proceso del “BtnExecute1″. Recordemos, desde la parte del cliente se envía una petición al Servidor, y cuando regresa es cuando actualiza el contenido de la etiqueta. Una primera solución podría ser crear un Thread. Una clase con un método “Procesar” que recorra un bucle y que mediante un Evento actualizar la etiqueta “lblActualizar”.
Thread oThread = new Thread(new ThreadStart(threadprocess.Procesar));
oThread.Start();
Excepción del UpdatePanel
Si queremos actualizar el UpdatePanel desde un Thread nos encontramos con el siguiente problema. Una Exception
The Update method can only be called on UpdatePanel with ID ‘updProgreso’ before Render.
El método de actualización sólo puede ser llamado UpdatePanel con ‘updProgreso’ identificación antes de Render.
Solución utilizar un Timer
El Timer hace de intermediario entre el Thread y el UpdatePanel. Con cada Tick del Timer si que podemos actualizar el UpdatePanel, pero para eso tenemos que agregarlo como Trigger (Desencadenador) del UpdatePanel, y entonces actualizar la página.
<asp:UpdatePanel ID="updProgreso" runat="server"
UpdateMode="conditional">
<ContentTemplate>
<asp:Timer ID="Timer1" runat="server" OnTick="timer_Ticked" Interval="1000"></asp:Timer>
<asp:Label ID="lblActualizar" runat="server" Text=""></asp:Label>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="BtnExecute1"/>
<asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
</Triggers>
</asp:UpdatePanel>
El Botón “btnExecute1″ inicia el Proceso y activa el Timer, y después con cada Tick del Timer se irá actualizando la información del proceso lento que queremos ejecutar.
La Clase que se ejecutará desde un Thread y los métodos de la página que recoge la información y refresca el UpdatePanel.
public delegate void OnRefresProgress(string str);
public delegate void OnRefresProgressEnd();
public delegate void OnException(string msg);
class ProcesoLento
{
public List<string> listavalores = null;
public event OnRefresProgress OnRefresProgress;
public event OnRefresProgressEnd OnRefresProgressEnd;
public event OnException OnException;
protected void ThrowRefresProgress(string cont, string numexp)
{
if (OnRefresProgress != null)
{
OnRefresProgress(cont, numexp);
}
}
protected void ThrowRefresProgressEnd()
{
if (OnRefresProgressEnd != null)
{
OnRefresProgressEnd();
}
}
protected void ThrowOnException(string msg)
{
if (OnException != null)
{
OnException(msg);
}
}
public void ProcessDelete()
{
try
{
System.Threading.Thread.Sleep(500);
int cont = 0;
foreach (string str in listavalores)
{
ThrowRefresProgress(string.Format("{0}.{1}",str,cont));
//Procesar(str)
System.Threading.Thread.Sleep(50);
cont++;
}
ThrowRefresProgress("Fin");
ThrowRefresProgressEnd();
}
catch (Exception ex)
{
ThrowOnException(ex.Message);
}
}
}
protected void RefreshProgress(string str)
{
//Variable Auxiliar es una variable que debe ser accesible desde el Tick del Timer y desde el Evento del Thread.
//Por ejemplo se puede guardar en la Session
VariableAuxiliar = str;
}
protected void timer_Ticked(object sender, EventArgs e)
{
lblActualizar.Text = "Actualizamos " + VariableAuxiliar;
updProgreso.Update();
}
protected void BtnExecute1_Click(object sender, EventArgs e)
{
ProcesoLento threadprocess = new ProcesoLento();
threadprocess.listavalores = ListaValoresReal; //Información para el Bucle
threadprocess.OnRefresProgress += RefreshProgress;
threadprocess.OnRefresProgressEnd += RefreshProgressEnd;
threadprocess.OnException += OnExceptionThread;
Thread oThread = new Thread(new ThreadStart(threadprocess.ProcessDelete));
oThread.Start();
//Activamos Timer que refrescará la pantalla con el progreso.
Timer1.Enabled = true;
}
Los métodos “RefreshProgress”, “RefreshProgressEnd”, “OnExceptionThread” son delegates o eventos, y podemos utilzarlos a nuestra conveniencia. Sobretodo para saber cuando finaliza el proceso del Thread o si ha tenido alguna excepción.
Conclusión
Espero haberme explicado bien.
Buen dia, revisando tu ejemplo y al tratar de replicarlo para ver el funcionamiento ya que tengo una situacion donde tengo que estar actualizando el update panel el cualcontrola el envio de varios hilos de ejecucion para el manejo de correos no me funciono del todo el ejemplo que pones, me gustaria me pudieras ayudar para poder ver bien como funciona tu ejemplo y yo poder ver la manera de implementarlo a la funcionalidad que quiero hacer, te agradeceria si me pudieras porporcionar el ejemplo completo que presentas en esta pagina referente a la actualizacion del upadepanel manejando hilos de ejecucion
@JoseMacias, lo siento pero no puedo pasarte debido a que no se donde esta el código que escribí en su día.
La clave esta en la configuración, en indicar los Triggers correctos. Yo también estuve un tiempo hasta que me funcionó.
La idea es, Crear un UpdatePanel (condicionalMode), poner un Timer y un Botón. Configurar que el Timer o el Botón pueden realizar el update (triggers). En el servidor el código que quieras. Trata de conseguir esto primero sin ningún Thread, luego ya podrás crear un Thread e ir complicándolo.
Saludos
La actualizacion del updatepanel utilizando un timer ya funciona, ya intente poner en el boton el thead y y con el debug entra a los metodos que controlan la actualizacion del updatepanel, pero la informacion no se ve reflejada en el updatepanel, me puedes orientar para ver que es lo que mes esta faltando
@JoseMacias. El proceso del Thread debe actualizar una propiedad o un objeto que se encuentre en la página, dentro del “ViewState”. (Sino recuerdo mal, la “VariableAuxiliar” creo que la tenía así). Luego es el Evento del Timer (Timer_Ticked) quien recoge la información de dicha propiedad y actualiza el updatePanel.
Es decir, el Thread actualiza un Objeto y el Timer recoge ese Objeto para mostrar la información en el Panel y después hace el updatePanel.update().
Quizás tengas que utilizar una sección crítica para actualizar y/o recoger la información del objeto. Prueba primero sin ella.
Comentame cualquier cosa.
Ya declare la variable como viewSate
public string VariableAuxiliar
{
get{ return ViewState["VariableAuxiliar"].ToString();}
set{ ViewState["VariableAuxiliar"]=value;}
}
pero la actualizacion del updatenel nunca se realiza el timer siempre se queda con el valor inicial de la variable que le asigno en el page load
if (!IsPostBack)
{
VariableAuxiliar = ” inicio “;
}
Sabes que es lo que me falta
¿Has probado de ponerlo en Session? Es que no recuerdo exactamente, pero la idea era esa.
ya lo hice, pero es como si manejara 2 sesiones diferentes, es como tener sesiones o variables para el hilo y otro para la pagina, cuando llega a la acualizacion del panel toma el primer valor guardado en la session o variable y es la que esta pintando siempre, alguna otra idea de que puede ser lo que me falta?
ya lo resolvi, gracias por tu apoyo me fue de gran ayuda
Buenas Jose, actualmente encuentro en uno de mis desarrollos la necesidad de implementar un metodo como el descrito, he tratado de ponerlo a funcionar pero me sucede lo que anteriormente describes: Variables que se quedan con el dato inicial de carga.