2009-09-14 18 views
10

que tienen una funciónTema Control.Invoke

public void ShowAllFly() 
{ 
     cbFly.Items.Clear(); 
     cbFly.Items.Add("Uçuş Seçiniz..."); 

     dsFlyTableAdapters.tblFlyTableAdapter _t=new KTHY.dsFlyTableAdapters.tblFlyTableAdapter(); 
     dsFly _mds = new dsFly(); 
     _mds.EnforceConstraints = false; 
     dsFly.tblFlyDataTable _m = _mds.tblFly; 
     _t.Fill(_m); 
     foreach (DataRow _row in _m.Rows) 
     { 
      cbFly.Items.Add(_row["FlyID"].ToString()+"-"+_row["FlyName"].ToString() + "-" + _row["FlyDirection"].ToString() + "-" + _row["FlyDateTime"].ToString()); 
     } 
     _Thread.Abort(); 
     timer1.Enabled = false; 
     WaitPanel.Visible = false; 
} 

En Form_Load función como ésta;

{ 
    _Thread = new System.Threading.Thread(new System.Threading.ThreadStart(ShowAllFly)); 
    _Thread.Start(); 
    _Thread.Priority = System.Threading.ThreadPriority.Normal; 
} 

Pero cuando lo ejecuto;

en función ShowAllFly

cbFly.Items.Clear(); ---- HERE Gives ERROR LIKE Control.Invoke must be used to interact with controls created on a separate thread. 

¿Cuál es el problema?

Respuesta

48

Hay dos reglas de oro de rosca en Windows Forms:

  • Don' Toque cualquier propiedad o método de control (que no sean los que se enumeran explícitamente como correctos) de un hilo que no sea el que creó el "control" del control (generalmente solo hay un hilo de UI)
  • No bloquee el hilo de interfaz de usuario para cualquier período significativo de tiempo, o que va a hacer la aplicación que no responde

Con el fin de interactuar con la interfaz de usuario desde un subproceso diferente, es necesario "ordenar" la llamada al hilo de la interfaz de usuario, utilizando un delegado y llamando al Control.Invoke/BeginInvoke. Usted puede probar si necesita o no llamar al Invoke usando la propiedad InvokeRequired, pero en estos días yo personalmente tiendo a hacerlo de todos modos, no hay mucha penalización por invocar cuando no es necesario.

Las expresiones Lambda en C# 3 (o métodos anónimos en C# 2) hacen que esto sea mucho más agradable también.

Por ejemplo, se puede utilizar:

cbFly.Invoke((MethodInvoker)(() => cbFly.Items.Clear())); 

Todos los soportes en el camino un poco, por lo que es posible que desee añadir un método de extensión de este tipo, si está usando C# 3:

public static void Invoke(this Control control, MethodInvoker action) 
{ 
    control.Invoke(action); 
} 

Posteriormente, se podría hacer:

cbFly.Invoke(() => cbFly.Items.Clear()); 

que es un buen negocio más simple. Usualmente puede salirse con la suya usando un MethodInvoker al capturar cualquier variable a la que necesite acceder dentro del delegado.

Consulte my threading tutorial o Joe Albahari's para obtener más información.

Como una cuestión secundaria, veo que estás usando Thread.Abort - de hecho en tu propio hilo, a pesar de tener otras llamadas después de él. ¿Por qué? Anulando cualquier hilo otro que el suyo es una llamada de tipo "solo emergencias" (que generalmente debe ser seguida por la aplicación que se descarga de todos modos) y no veo ninguna razón para abortar el hilo actual cuando todavía hay trabajo por hacer después...

6

Interacción de los controles en otro hilo (ui) necesita ser invocada como tal:

public delegate void ProcessResultDelegate(string result); 
void ProcessResult(string result) 
{ 
    if (textBox1.InvokeRequired) 
    { 
     var d = new ProcessResultDelegate(ProcessResult); 
     d.Invoke(result); 
    } 
    else 
    { 
     textBox1.Text = result; 
    } 
} 
+3

Buena solución; byt Yo recomendaría usar 'Action ' desde el framework en lugar de rodar (y mantener) el suyo. –

+0

Sí ... esta es más o menos la forma estándar de hacer esto. La razón por la que esto es necesario es que textBox1.Text solo se puede cambiar en el subproceso en el que se creó el cuadro de texto. Invocar es el proceso utilizado para volver a ese subproceso. –

+0

@Fredrik: Tiene razón, pero este ejemplo se ve con más frecuencia, en gran parte porque ha durado más tiempo. Cualquiera de los métodos funcionará. –

3

Siempre he encontrado this article útil en este tema en particular.

En su ejemplo, intenta modificar varios controles de un hilo que no creó el control. Para solucionar este problema dada su ejemplo, en lugar de hacer esto (suponiendo que el método ShowAllFly() es un método en el formulario):

public void ShowAllFly() 
{ 
    Invoke((MethodsInvoker) delegate { 
     cbFly.Items.Clear(); 
     cbFly.Items.Add("Uçuş Seçiniz..."); 
     dsFlyTableAdapters.tblFlyTableAdapter _t = 
      new KTHY.dsFlyTableAdapters.tblFlyTableAdapter(); 
     dsFly _mds = new dsFly(); 
     _mds.EnforceConstraints = false; 
     dsFly.tblFlyDataTable _m = _mds.tblFly; 
     _t.Fill(_m); 
     foreach (DataRow _row in _m.Rows) 
     { 
      cbFly.Items.Add(_row["FlyID"].ToString() + "-" + 
          _row["FlyName"].ToString() + "-" + 
          _row["FlyDirection"].ToString() + "-" + 
          _row["FlyDateTime"].ToString()); 
     } 
     //_Thread.Abort(); // WHY ARE YOU TRYING TO DO THIS? 
     timer1.Enabled = false; 
     WaitPanel.Visible = false; 
    }); 
} 

Sólo para enfatizar el punto @ Jon Skeet hecho, he comentado a cabo la llamada para abortar el hilo. El hilo terminará por sí mismo. No hay razón para abortar de esta manera.

+1

Tu eres el mejor Matt. Gracias. Funciona muy bien. – atromgame

+0

Como él es el mejor, le di un voto positivo, que nadie antes que yo tenía (sugerencia, sugerencia). –

0

Debe ser invocado ... Pero invoque tiene que esperar el hilo principal quiero decir que no obtiene el error de esta manera, pero esto no funciona paralelamente si quiere ir a más de un proceso al mismo tiempo simplemente cree más que un hilo

Thread thread = new Thread(new delegate_method(method));//you must create delegate before 
thread.start(); 
Thread thread2 = new Thread(new delegate_method(method2));//you must create delegate before 
thread.start(); 

manejar dos proceso mismo tiempo

void method() 
{ 
//do something here -- working background Remember can not control any UI control from here 
finish_thread() 
} 

void method2() 
{ 
//do something here -- working background Remember can not control any UI control from here 
finish_thread() 
} 

void finish_thread() 
{ 
if(invoke.Required) 
{ 
//Here you have to call delegate method here with UI 
BeginInvoke(new delegate_method(finish_thread)); 
} 
else 
{ 
//Now you can control UI thread from here and also you finished background work 
//Do something working with UI thread 
textBox.Text = ""; 
} 
}