Cambie los controles de WPF desde un hilo no principal usando Dispatcher.Invoke

Resuelto D. Veloper asked hace 15 años • 4 respuestas

Recientemente comencé a programar en WPF y me encontré con el siguiente problema. No entiendo cómo utilizar el Dispatcher.Invoke()método. Tengo experiencia en subprocesos y he creado algunos programas simples de Windows Forms en los que solo usé el

Control.CheckForIllegalCrossThreadCalls = false;

Sí, sé que es bastante aburrido, pero se trataba de aplicaciones de seguimiento sencillas.

El hecho es que ahora estoy creando una aplicación WPF que recupera datos en segundo plano, comienzo un nuevo hilo para realizar la llamada para recuperar los datos (desde un servidor web), ahora quiero mostrarlos en mi formulario WPF. La cuestión es que no puedo configurar 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 de 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 coloque en un campo e inicie un nuevo hilo que espere hasta que se complete este campo y llame al despachador para mostrar los datos recuperados en los controles?

D. Veloper avatar Oct 29 '09 21:10 D. Veloper
Aceptado

Lo primero es entender que Dispatcher no está diseñado para ejecutar operaciones de bloqueo prolongadas (como recuperar datos de un servidor web...). Puede utilizar 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 utilizar el método ReportProgress para propagar los cambios en el hilo de la interfaz de usuario.

Si realmente necesita utilizar Dispatcher directamente, es bastante simple:

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => this.progressBar.Value = 50));
japf avatar Oct 29 '2009 14:10 japf

japf ha respondido correctamente. En caso de que 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 saber sobre el rendimiento:

Si su código NECESITA escribirse para un alto rendimiento, primero puede verificar si se requiere la invocación 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 (gastos generales en unos pocos nanosegundos). Sólo es mejor cuando desea ahorrar ese microsegundo necesario para realizar la 'invocación' a cualquier precio. Además, siempre existe la opción de crear dos métodos (uno con invocación y otro sin ella) cuando el método de llamada está seguro de si está en UI Thread o no. Es muy raro que se deba considerar este aspecto del despachador.

Yogee avatar Aug 30 '2015 16:08 Yogee

Cuando se está ejecutando un subproceso y desea ejecutar el subproceso principal de la interfaz de usuario que está bloqueado por el subproceso 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)));
Gopi Rao avatar Nov 15 '2018 01:11 Gopi Rao