Como usar controles trabajando con subprocesos

Descripción general

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].

[TOC] Tabla de Contenidos


↑↑↑

Como usar Controles trabajando con hilos

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


↑↑↑

Solución Sencilla

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


↑↑↑

Ejemplo de código

'-----------------------------------------------------
      ' 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
      '-----------------------------------------------------

↑↑↑

Solución Compleja

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


↑↑↑

Ejemplo de código

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

↑↑↑

La propiedad Refress a modo de ejemplo

#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

↑↑↑

A.2.Enlaces

[Para saber mas]
[Grupo de documentos]
[Documento Index]
[Documento Start]
[Imprimir el Documento]