illegal - windows forms c# thread




Automatizando el patrón de código InvokeRequired (6)

Aquí está el formulario que he estado usando en todo mi código.

private void DoGUISwitch()
{ 
    Invoke( ( MethodInvoker ) delegate {
        object1.Visible = true;
        object2.Visible = false;
    });
} 

He basado esto en la entrada del blog here . No me ha fallado este enfoque, así que no veo ninguna razón para complicar mi código con una comprobación de la propiedad InvokeRequired .

Espero que esto ayude.

Me he vuelto dolorosamente consciente de cuán a menudo uno necesita escribir el siguiente patrón de código en el código GUI controlado por eventos, donde

private void DoGUISwitch() {
    // cruisin for a bruisin' through exception city
    object1.Visible = true;
    object2.Visible = false;
}

se convierte en:

private void DoGUISwitch() {
    if (object1.InvokeRequired) {
        object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
    } else {
        object1.Visible = true;
        object2.Visible = false;
    }
}

Este es un patrón incómodo en C #, tanto para recordar como para escribir. ¿Alguien ha encontrado algún tipo de atajo o construcción que automatice esto hasta cierto punto? Sería genial si hubiera una manera de adjuntar una función a los objetos que hace esta comprobación sin tener que pasar por todo este trabajo adicional, como un object1.InvokeIfNecessary.visible = true atajo de tipo object1.InvokeIfNecessary.visible = true .

Las answers anteriores han discutido la impracticabilidad de llamar a Invoke () cada vez, e incluso entonces la sintaxis de Invoke () es ineficiente y aún incómoda de manejar.

Entonces, ¿alguien ha descubierto algún atajo?


Aquí hay una versión mejorada / combinada de las respuestas de Lee, Oliver y Stephan.

public delegate void InvokeIfRequiredDelegate<T>(T obj)
    where T : ISynchronizeInvoke;

public static void InvokeIfRequired<T>(this T obj, InvokeIfRequiredDelegate<T> action)
    where T : ISynchronizeInvoke
{
    if (obj.InvokeRequired)
    {
        obj.Invoke(action, new object[] { obj });
    }
    else
    {
        action(obj);
    }
} 

La plantilla permite un código flexible y sin conversión, que es mucho más legible, mientras que el delegado dedicado proporciona eficiencia.

progressBar1.InvokeIfRequired(o => 
{
    o.Style = ProgressBarStyle.Marquee;
    o.MarqueeAnimationSpeed = 40;
});

El enfoque de Lee se puede simplificar aún más

public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
    // See Update 2 for edits Mike de Klerk suggests to insert here.

    if (control.InvokeRequired) {
        control.Invoke(action);
    } else {
        action();
    }
}

Y se puede llamar así

richEditControl1.InvokeIfRequired(() =>
{
    // Do anything you want with the control here
    richEditControl1.RtfText = value;
    RtfHelpers.AddMissingStyles(richEditControl1);
});

No es necesario pasar el control como parámetro al delegado. C # crea automáticamente un closure .

ACTUALIZAR :

Según varios otros carteles, el Control se puede generalizar como ISynchronizeInvoke :

public static void InvokeIfRequired(this ISynchronizeInvoke obj,
                                         MethodInvoker action)
{
    if (obj.InvokeRequired) {
        var args = new object[0];
        obj.Invoke(action, args);
    } else {
        action();
    }
}

DonBoitnott señaló que a diferencia de Control la interfaz ISynchronizeInvoke requiere una matriz de objetos para el método Invoke como lista de parámetros para la action .

ACTUALIZACIÓN 2

Ediciones sugeridas por Mike de Klerk (vea el comentario en el 1er fragmento de código para insertar el punto):

// When the form, thus the control, isn't visible yet, InvokeRequired  returns false,
// resulting still in a cross-thread exception.
while (!control.Visible)
{
    System.Threading.Thread.Sleep(50);
}

Consulte el comentario de ToolmakerSteve a continuación para las inquietudes sobre esta sugerencia.


Me gusta hacer un poco diferente, me gusta llamarme "yo mismo" si es necesario con una Acción,

    private void AddRowToListView(ScannerRow row, bool suspend)
    {
        if (IsFormClosing)
            return;

        if (this.InvokeRequired)
        {
            var A = new Action(() => AddRowToListView(row, suspend));
            this.Invoke(A);
            return;
        }
         //as of here the Code is thread-safe

este es un patrón útil, el IsFormClosing es un campo que establezco en True cuando cierro el formulario, ya que puede haber algunos hilos de fondo que aún se estén ejecutando ...


Prefiero usar una instancia única de un método Delegado en lugar de crear una nueva instancia cada vez. En mi caso, solía mostrar el progreso y mensajes (información / error) de un Backroundworker copiando y lanzando datos grandes desde una instancia sql. Cada vez después de aproximadamente 70000 llamadas de mensajes y progreso, mi formulario dejó de funcionar y mostraba mensajes nuevos. Esto no ocurrió cuando comencé a usar un solo delegado de instancia global.

delegate void ShowMessageCallback(string message);

private void Form1_Load(object sender, EventArgs e)
{
    ShowMessageCallback showMessageDelegate = new ShowMessageCallback(ShowMessage);
}

private void ShowMessage(string message)
{
    if (this.InvokeRequired)
        this.Invoke(showMessageDelegate, message);
    else
        labelMessage.Text = message;           
}

void Message_OnMessage(object sender, Utilities.Message.MessageEventArgs e)
{
    ShowMessage(e.Message);
}

Puedes escribir un método de extensión:

public static void InvokeIfRequired(this Control c, Action<Control> action)
{
    if(c.InvokeRequired)
    {
        c.Invoke(new Action(() => action(c)));
    }
    else
    {
        action(c);
    }
}

Y úsalo así:

object1.InvokeIfRequired(c => { c.Visible = true; });

EDITAR: Como Simpzon señala en los comentarios, también puedes cambiar la firma a:

public static void InvokeIfRequired<T>(this T c, Action<T> action) 
    where T : Control




invokerequired