Recientemente comencé a programar en WPF y me encontré con el siguiente problema. No entiendo cómo usar el Dispatcher.Invoke()
método. Tengo experiencia en subprocesos y he creado algunos programas sencillos de Windows Forms en los que acabo de usar el
Control.CheckForIllegalCrossThreadCalls = false;
Sí, sé que es bastante tonto, pero estas eran aplicaciones de monitoreo simples.
El hecho es que ahora estoy creando una aplicación WPF que recupera datos en segundo plano, comienzo un nuevo hilo para hacer la llamada para recuperar los datos (desde un servidor web), ahora quiero mostrarlo en mi formulario WPF. La cuestión es que no puedo establecer ningún control desde este hilo. Ni siquiera una etiqueta ni nada. ¿Como puede ésto ser resuelto?
Comentarios de respuesta:
@Jalfp:
¿Entonces uso este método Dispatcher en la 'nueva banda de rodadura' cuando obtengo los datos? ¿O debería hacer que un trabajador en segundo plano recupere los datos, los ponga en un campo e inicie un nuevo hilo que espera hasta que este campo se complete y llame al despachador para mostrar los datos recuperados en los controles?
fuente
Respuestas:
Lo primero es entender que el Dispatcher no está diseñado para ejecutar operaciones de bloqueo largas (como recuperar datos de un servidor web ...). Puede usar el Dispatcher cuando desee ejecutar una operación que se ejecutará en el subproceso de la interfaz de usuario (como actualizar el valor de una barra de progreso).
Lo que puede hacer es recuperar sus datos en un trabajador en segundo plano y usar el método ReportProgress para propagar cambios en el subproceso de la interfaz de usuario.
Si realmente necesita usar Dispatcher directamente, es bastante simple:
Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action(() => this.progressBar.Value = 50));
fuente
new Action(...)
.BeginInvoke
y, en su lugar, se produce el error del compilador CS1660.japf ha respondido correctamente. Por si acaso, si está buscando acciones de varias líneas, puede escribir lo siguiente.
Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action(() => { this.progressBar.Value = 50; }));
Información para otros usuarios que quieran conocer el rendimiento:
Si su código NECESITA ser escrito para un alto rendimiento, primero puede verificar si la invocación es requerida usando el indicador CheckAccess.
if(Application.Current.Dispatcher.CheckAccess()) { this.progressBar.Value = 50; } else { Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action(() => { this.progressBar.Value = 50; })); }
Tenga en cuenta que el método CheckAccess () está oculto en Visual Studio 2015, así que escríbalo sin esperar que intellisense lo muestre. Tenga en cuenta que CheckAccess tiene una sobrecarga de rendimiento (sobrecarga en unos pocos nanosegundos). Solo es mejor cuando desea ahorrar ese microsegundo requerido para realizar la 'invocación' a cualquier costo. Además, siempre hay una opción para crear dos métodos (activado con invocación y otro sin) cuando el método de llamada está seguro de si está en UI Thread o no. Es solo un caso muy raro en el que debería estar mirando este aspecto del despachador.
fuente
Cuando se está ejecutando un hilo y desea ejecutar el hilo principal de la IU que está bloqueado por el hilo actual, utilice lo siguiente:
hilo actual:
Dispatcher.CurrentDispatcher.Invoke(MethodName, new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.
Hilo principal de la interfaz de usuario:
Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action(() => MethodName(parameter)));
fuente
La respuesta de @japf anterior funciona bien y, en mi caso, quería cambiar el cursor del mouse de una Rueda giratoria a la Flecha normal una vez que el Navegador CEF terminó de cargar la página. En caso de que pueda ayudar a alguien, aquí está el código:
private void Browser_LoadingStateChanged(object sender, CefSharp.LoadingStateChangedEventArgs e) { if (!e.IsLoading) { // set the cursor back to arrow Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => Mouse.OverrideCursor = Cursors.Arrow)); } }
fuente