c# - understanding - Actualización de la interfaz de usuario de eventos usando asyc a la espera
task void c# (4)
// Solo declara un delegado asi
delegate void Add(string msg);
// Luego declara el método de delegado así:
var add = new Add((msg) => {
_listBox_Output.Items.Add(msg);
});
// Ahora solo llama al delegado:
void pwe_StatusUpdate(string updateMsg)
{
_listBox_Output.Invoke(add,updateMsg);
}
Estoy tratando de entender cómo actualizar una IU de un evento mientras uso el patrón async / await. A continuación se muestra el código de prueba que estoy usando en una aplicación WinForm. Ni siquiera estoy seguro de que esta sea la manera correcta de hacerlo. ¿Qué es necesario para permitir que el método pwe_StatusUpdate actualice la interfaz de usuario? El error de operación de subproceso se produce allí.
Gracias por leer.
// calling code
ProcessWithEvents pwe = new ProcessWithEvents();
pwe.StatusUpdate += pwe_StatusUpdate;
await pwe.Run();
void pwe_StatusUpdate(string updateMsg)
{
// Error Here: Cross-thread operation not valid: Control ''_listBox_Output'' accessed from a thread other than the thread it was created on.
_listBox_Output.Items.Add(updateMsg);
}
-
// Class with long running process and event
public delegate void StatusUpdateHandler(string updateMsg);
public class ProcessWithEvents
{
public event StatusUpdateHandler StatusUpdate;
public async Task Run()
{
await Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
RaiseUpdateEvent(String.Format("Update {0}", i));
Thread.Sleep(500);
}
});
}
private void RaiseUpdateEvent(string msg)
{
if (StatusUpdate != null)
StatusUpdate(msg);
}
}
-
Aquí hay otro ejemplo.
async void DoExport()
{
var rMsg = "";
var t = await Task<bool>.Factory.StartNew(() => ExportAsMonthReport(LastMonth.Name, LastYear.Name, out rMsg));
if (t)
{
BeginInvoke((Action)(() =>
{
spinnerMain.Visible = false;
menuItemMonth.Enabled = true;
MetroMessageBox.Show(this, rMsg, "Export", MessageBoxButtons.OK, MessageBoxIcon.Information, 200);
}));
}
else
{
BeginInvoke((Action)(() =>
{
spinnerMain.Visible = false;
menuItemMonth.Enabled = true;
MetroMessageBox.Show(this, rMsg, "Export", MessageBoxButtons.OK, MessageBoxIcon.Error, 200);
}));
}
}
Debe utilizar el método de Control
de Control
. Ejecuta algún código en el hilo de Control. También puede verificar la propiedad InvokeRequired
para verificar si necesita llamar al método Invoke
(comprueba si la persona que llama está en un hilo diferente al que se creó el control).
Ejemplo simple:
void SomeAsyncMethod()
{
// Do some work
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)(() =>
{
DoUpdateUI();
}
));
}
else
{
DoUpdateUI();
}
}
void DoUpdateUI()
{
// Your UI update code here
}
En algunos casos, debe verificar la propiedad IsHandleCreated
de Control
antes de llamar al método Invoke
. Si IsHandleCreated
devuelve false, IsHandleCreated
esperar mientras se creará el identificador de Control
El patrón async
tiene soporte para actualizaciones de progreso .
En resumen, su método async
puede tomar un IProgress<T>
, y su código de llamada pasa en una implementación de esa interfaz (generalmente Progress<T>
).
public class ProcessWithUpdates
{
public async Task Run(IProgress<string> progress)
{
await Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
if (progress != null)
progress.Report(String.Format("Update {0}", i));
Thread.Sleep(500);
}
});
}
}
// calling code
ProcessWithUpdates pwp = new ProcessWithUpdates();
await pwp.Run(new Progress<string>(pwp_StatusUpdate));