c# - visual - vb net form load




¿Cómo puedo hacer que las devoluciones de eventos en mis formularios de win sean seguras? (4)

Cuando te suscribes a un evento en un objeto desde un formulario, básicamente estás cediendo el control de tu método de devolución de llamada al origen del evento. No tiene idea si esa fuente de eventos elegirá activar el evento en un hilo diferente.

El problema es que cuando se invoca la devolución de llamada, no puede suponer que puede realizar controles de actualización en su formulario porque a veces esos controles arrojarán una expection si la devolución de llamada de evento fue invocada en un hilo diferente al hilo en el que se ejecutó el formulario.


Como el lazy programmer , tengo un método muy perezoso de hacer esto.

Lo que hago es simplemente esto.

private void DoInvoke(MethodInvoker del) {
    if (InvokeRequired) {
        Invoke(del);
    } else {
        del();
    }
}
//example of how to call it
private void tUpdateLabel(ToolStripStatusLabel lbl, String val) {
    DoInvoke(delegate { lbl.Text = val; });
}

Puede alinear el DoInvoke dentro de su función o esconderlo en una función separada para hacer el trabajo sucio por usted.

Solo tenga en cuenta que puede pasar funciones directamente al método DoInvoke.

private void directPass() {
    DoInvoke(this.directInvoke);
}
private void directInvoke() {
    textLabel.Text = "Directly passed.";
}

En muchos casos simples, puede usar el delegado de MethodInvoker y evitar la necesidad de crear su propio tipo de delegado.


Llego un poco tarde a este tema, pero es posible que desee echar un vistazo al Patrón asincrónico basado en eventos . Cuando se implementa correctamente, garantiza que los eventos siempre se generan desde el hilo de la interfaz de usuario.

Aquí hay un breve ejemplo que solo permite una invocación simultánea; El soporte de múltiples invocaciones / eventos requiere un poco más de plomería.

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public class MainForm : Form
    {
        private TypeWithAsync _type;

        [STAThread()]
        public static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new MainForm());
        }

        public MainForm()
        {
            _type = new TypeWithAsync();
            _type.DoSomethingCompleted += DoSomethingCompleted;

            var panel = new FlowLayoutPanel() { Dock = DockStyle.Fill };

            var btn = new Button() { Text = "Synchronous" };
            btn.Click += SyncClick;
            panel.Controls.Add(btn);

            btn = new Button { Text = "Asynchronous" };
            btn.Click += AsyncClick;
            panel.Controls.Add(btn);

            Controls.Add(panel);
        }

        private void SyncClick(object sender, EventArgs e)
        {
            int value = _type.DoSomething();
            MessageBox.Show(string.Format("DoSomething() returned {0}.", value));
        }

        private void AsyncClick(object sender, EventArgs e)
        {
            _type.DoSomethingAsync();
        }

        private void DoSomethingCompleted(object sender, DoSomethingCompletedEventArgs e)
        {
            MessageBox.Show(string.Format("DoSomethingAsync() returned {0}.", e.Value));
        }
    }

    class TypeWithAsync
    {
        private AsyncOperation _operation;

        // synchronous version of method
        public int DoSomething()
        {
            Thread.Sleep(5000);
            return 27;
        }

        // async version of method
        public void DoSomethingAsync()
        {
            if (_operation != null)
            {
                throw new InvalidOperationException("An async operation is already running.");
            }

            _operation = AsyncOperationManager.CreateOperation(null);
            ThreadPool.QueueUserWorkItem(DoSomethingAsyncCore);
        }

        // wrapper used by async method to call sync version of method, matches WaitCallback so it
        // can be queued by the thread pool
        private void DoSomethingAsyncCore(object state)
        {
            int returnValue = DoSomething();
            var e = new DoSomethingCompletedEventArgs(returnValue);
            _operation.PostOperationCompleted(RaiseDoSomethingCompleted, e);
        }

        // wrapper used so async method can raise the event; matches SendOrPostCallback
        private void RaiseDoSomethingCompleted(object args)
        {
            OnDoSomethingCompleted((DoSomethingCompletedEventArgs)args);
        }

        private void OnDoSomethingCompleted(DoSomethingCompletedEventArgs e)
        {
            var handler = DoSomethingCompleted;

            if (handler != null) { handler(this, e); }
        }

        public EventHandler<DoSomethingCompletedEventArgs> DoSomethingCompleted;
    }

    public class DoSomethingCompletedEventArgs : EventArgs
    {
        private int _value;

        public DoSomethingCompletedEventArgs(int value)
            : base()
        {
            _value = value;
        }

        public int Value
        {
            get { return _value; }
        }
    }
}

Para simplificar un poco el código de Simon, podría usar el delegado de acción genérico incorporado. Le ahorra a su código un montón de tipos de delegados que realmente no necesita. Además, en .NET 3.5 agregaron un parámetro params al método Invoke para que no tenga que definir una matriz temporal.

void SomethingHappened(object sender, EventArgs ea)
{
   if (InvokeRequired)
   {
      Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);
      return;
   }

   textBox1.Text = "Something happened";
}






events