Cuando se está trabajando con Hilos (subprocesos), muchas veces necesitamos escribir información en un [TextBox] o bien cambiar la propiedad [Value] de un control [PregressBar].
Cuando se está trabajando con Hilos (subprocesos), muchas veces necesitamos escribir información en un TextBox (por ejemplo de un formulario), o como escribo en este Texto, cambiar el valor de la propieda [Value] de un control [ProgressBar] desde un hilo; Pero al intentarlo el programa muestra un error del tipo
y no permite que la información generada por un hilo, se muestre en un control del programa que lo ha lanzado.
Existen varias soluciones
Consiste en usar la propiedad Control.CheckForIllegalCrossThreadCalls.
Esta propiedad controla si se detectaran las llamadas de un hilo a él subproceso que lo ha creado o no. Si le damos el valor False tenemos el problema resuelto
'----------------------------------------------------- ' Evitar el error System.InvalidOperationException '----------------------------------------------------- ' http://msdn.microsoft.com/es-es/library/110t3299.aspx ' Obtiene o establece un valor que indica ' si se detectaran las llamadas de un hilo a el ' subproceso que lo ha creado o no Control.CheckForIllegalCrossThreadCalls = False ' /eof '-----------------------------------------------------
Consiste en usar delegados
La otra forma es más "Guay" pero reguiere mucho más trabajo, de hecho hay que escribir código para cada una de las funciones y/o propiedades del control que queramos usar desde los hilos, y además hay que contemplar
A continuación te pongo un código que modifica la propiedad [Value] de un Control ProgressBar, para que pueda ser llamada sin problemas desde los hilos.
Una Observacion:. Este control no es operativo, ademas hay que modificar de la misma forma las propiedades [Maximum],[Minimum], y [Refresh]
''' <summary> ''' Un control ProgressBar adaptado para ser usado por subprocesos ''' </summary> ''' <remarks></remarks> Public Class UCProgressBarMultihilos Inherits Windows.Forms.ProgressBar #Region " LLamadas desde Hilos. Control Progressbar. Property Value As Integer" ' ''' <summary> ''' <para> This delegate enables asynchronous calls for setting ''' the text property on a ProgressBar control. ''' </para> ''' <para> Este delegado permite que las llamadas asíncronas para el establecimiento de ''' La propiedad de valor en un control ProgressBar. ''' </para> ''' </summary> ''' <param name="posicionIntervalo"> ''' <para> Tipo <see cref="System.Integer">Integer</see></para> ''' <para> El valor que se pasa al control [ProgressBar], propiedad [Value] ''' para establecer la posición actual de la barra de progreso. ''' </para> ''' </param> ''' <remarks> ''' <para> ''' Si utiliza multithreading para mejorar el rendimiento de las ''' aplicaciones de Windows Forms, debe asegurarse de realizar ''' llamadas a los controles de forma segura. ''' </para> ''' <para> ''' El acceso a los controles de formularios Windows Forms no es ''' inherentemente seguro para los subprocesos. ''' Si dos o más subprocesos manipulan el estado de un control, ''' es posible obligar al control a pasar a un estado incoherente. ''' Se pueden dar otros errores relacionados con los subprocesos, ''' como condiciones de carrera e interbloqueos. ''' Es importante asegurarse de que el acceso a los controles ''' se realice de manera segura para los subprocesos. </para> ''' <code> ''' <para> Cómo: Realizar llamadas seguras para subprocesos en ''' controles de formularios Windows Forms</para> ''' <para> http://msdn.microsoft.com/es-es/library/ms171728.aspx</para> ''' </code> ''' <code> ''' <para> CA1034: Los tipos anidados no deben ser visibles</para> ''' <para> http://msdn.microsoft.com/es-es/library/ms182162.aspx </para> ''' </code> '''</remarks> Private Delegate Sub SetValorCallback(ByVal posicionIntervalo As Integer) ''' <summary> ''' Metodo que permite que los subprocesos llamen al ''' control [ProgressBar], propiedad [Value] ''' </summary> ''' <param name="posicionIntervalo"> ''' <para> Tipo <see cref="System.Integer">Integer</see></para> ''' <para> El valor que se pasa al control [ProgressBar], propiedad [Value] ''' para establecer la posición actual de la barra de progreso. ''' </para> ''' </param> ''' <remarks> ''' <para> ----------------------------------------------</para> ''' <para> This method demonstrates a pattern for making ''' thread-safe calls on a Windows Forms control. ''' </para> ''' <para> If the calling thread is different from the ''' thread that created the ProgressBar control, ''' this method creates a SetValorCallback and ''' calls itself asynchronously using the ''' Invoke method. ''' </para> ''' <para> If the calling thread is the same as the thread ''' that created the ProgressBar control, ''' the Text property is set directly. ''' </para> ''' <para> ----------------------------------------------</para> ''' <para> Este método demuestra un patrón para hacer seguras ''' llamadas a subprocesos. Hace una llamada a ''' un control de Windows Forms. ''' </para> ''' <para> Si el hilo que llama es diferente del que ha creado el ''' control ProgressBar, este método crea una [SetValorCallback] ''' y se hace llamar de forma asincrónicautilizando ''' el método MyBase.Invoke. ''' </para> ''' <para> Si el subproceso de llamada es el mismo que el subproceso ''' que creó el control ProgressBar, la propiedad [Value] ''' se establece directamente.</para> ''' <para> ----------------------------------------------</para> '''</remarks> Private Sub SetValor(ByVal posicionIntervalo As Integer) If posicionIntervalo < Minimum Then posicionIntervalo = Minimum If posicionIntervalo > Maximum Then posicionIntervalo = Maximum '-------------------------------------------------------- ' InvokeRequired required compares the thread ID of the ' calling thread to the thread ID of the creating thread. ' If these threads are different, it returns true. '-------------------------------------------------------- ' InvokeRequired compara el ID del hilo llamador ' con el ID del hilo llamado. ' Si son diferentes, devuelve true. '-------------------------------------------------------- If Me.InvokeRequired Then Dim d As New SetValorCallback(AddressOf SetValor) MyBase.Invoke(d, New Object() {posicionIntervalo}) Else MyBase.Value = posicionIntervalo End If End Sub ''' <summary> ''' Obtiene o establece la posición actual de la barra de progreso. ''' </summary> ''' <value> ''' <para> Posición del intervalo de la barra de progreso. </para> ''' <para> El valor predeterminado es 0. </para> ''' </value> Public Overloads Property Value As Integer Get Return MyBase.Value End Get Set(ByVal value As Integer) If MyBase.Value <> value Then ' Llamamos a la funcion que hemos escrito previamente ' para aceptar llamadas asincronas y no disparar un error SetValor(value) End If End Set End Property #End Region End Class
#Region "Refresh" Private Delegate Sub SetRefreshCallback() Private Sub SetRefresh() If Me.InvokeRequired Then Dim d As New SetRefreshCallback(AddressOf SetRefresh) MyBase.Invoke(d) Else MyBase.Refresh() End If End Sub Public Overloads Sub Refresh() SetRefresh() End Sub #End Region