traves thread the than subprocesos other llamar from form example ejemplo created controles control como accessed c# winforms asynchronous udp

c# - thread - Acceder al control de UI desde el subproceso BackgroundWorker



invokerequired c# (6)

Tengo un botón en mi formulario de Windows que llama al método RunWorkerAsync (), esto a su vez realiza una acción que luego actualiza un ListBox en el mismo formulario.

Después de que el evento DoWork haya terminado, asigno el resultado para el evento (que es una lista), proceso el evento RunWorkerCompleted () y luego realizo el siguiente código para actualizar mi Listbox

que llama esto:

(Disculpas, el formato del código no funcionará)

Ahora cuando ejecuto la aplicación y presiono el botón de actualización aparece la siguiente excepción:

¿Cómo podría solucionar esto?

Editar:

La excepción se emite en la declaración siguiente, esto ocurre en el método DoWork donde borro los contenidos para mantener la lista actualizada;

listBoxServers.Items.Clear ();


Aquí hay un fragmento que me parece muy útil:

public static void ThreadSafe(Action action) { Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, new MethodInvoker(action)); }

Puede pasarle cualquier delegado de tipo Action o simplemente un lambda como este:

ThreadSafe(() => { [your code here] });

o

ThreadSafe(listBoxServers.Items.Clear);


Lo que he hecho es algo como esto cada vez que necesitas ejecutar algo a través de los hilos:

listBoxServers.BeginInvoke( (Action) (() => listBoxServers.Items.Clear()));


Los subprocesos de fondo no pueden actualizar la interfaz de usuario en las aplicaciones de Windows, por lo que debe revertir el control al subproceso de la interfaz de usuario para la actualización real.

Cree un método que invoque UpdateServerDetails en el hilo principal, como este:

private void DispatchServerDetails(List<ServerDetails> details) { Action<List<ServerDetails>> action = UpdateServerDetails; Dispatcher.Invoke(action) }

y luego llama a DispatchServerDetails lugar de UpdateServerDetails .

Algunas advertencias:
-Este funciona mejor en aplicaciones WPF, para WinForms, tendrás que saltar algunos aros, o puedes usar InvokeRequirired
-La actualización de la interfaz de usuario todavía es sincrónica, por lo que si UpdateServerDetails hace mucho trabajo, bloqueará el subproceso de la interfaz de usuario (no es tu caso, solo para estar seguro).


No puede llamar a Invoke en el cuadro de lista, sino en el formulario. Para aplicaciones WinForms utilizo algo como:

... this.Invoke((MethodInvoker)delegate() { // Do stuff on ANY control on the form. }); ...

Dependiendo de la versión de .NET, puede que tenga que declarar a un delegado para MethodInvoker usted mismo como

public delegate void MethodInvoker();

Sin embargo, también podría considerar usar la función ReportProgress del Background Worker. El controlador de evento respectivo debe invocarse en el contexto del hilo del formulario.


Acabo de descubrir una forma más simple sin usar Invoke:

int fakepercentage = -1; //some loop here......if no loop exists, just change the value to something else if (fakepercentage == -1) { fakepercentage = -2; } else { fakepercentage = -1; } backgroundworker1.ReportProgress(fakepercentage);

Luego, en backgroundworker1_ProgressChanged (remitente del objeto, ProgressChangedEventArgs e):

if (e.ProgressPercentage < 0) { //access your ui control safely here }


Usar Invoke en un proyecto de Windows Forms puede ser un poco complicado, existen algunas trampas que están documentadas pero son fáciles de perder. Recomiendo usar algo como lo que encontrarás en esta pregunta:

¿Es apropiado ampliar el control para proporcionar una funcionalidad Invoke / BeginInvoke constantemente segura?

Maneja casos en los que no se requiere la invocación, se llama desde diferentes subprocesos, el identificador es o no se crea, etcetcetc. Se puede modificar fácilmente para que sea SafeInvoke() y SafeBeginInvoke() si no eres fan del parámetro bool.

(Incluido aquí para su conveniencia:

/// Usage: this.lblTimeDisplay.SafeInvoke(() => this.lblTimeDisplay.Text = this.task.Duration.ToString(), false); // or string taskName = string.Empty; this.txtTaskName.SafeInvoke(() => taskName = this.txtTaskName.Text, true); /// <summary> /// Execute a method on the control''s owning thread. /// </summary> /// <param name="uiElement">The control that is being updated.</param> /// <param name="updater">The method that updates uiElement.</param> /// <param name="forceSynchronous">True to force synchronous execution of /// updater. False to allow asynchronous execution if the call is marshalled /// from a non-GUI thread. If the method is called on the GUI thread, /// execution is always synchronous.</param> public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous) { if (uiElement == null) { throw new ArgumentNullException("uiElement"); } if (uiElement.InvokeRequired) { if (forceSynchronous) { uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); }); } else { uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); }); } } else { if (!uiElement.IsHandleCreated) { // Do nothing if the handle isn''t created already. The user''s responsible // for ensuring that the handle they give us exists. return; } if (uiElement.IsDisposed) { throw new ObjectDisposedException("Control is already disposed."); } updater(); } }