2009-09-14 16 views
10

Ho una funzioneDiscussione 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; 
} 

In Form_Load funzione come questa;

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

Ma quando lo eseguo;

in funzione ShowAllFly

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

Qual è il problema?

risposta

48

Ci sono due regole d'oro di filettatura in Windows Form:

  • Don' t toccare qualsiasi proprietà o metodo di controllo (diversi da quelli esplicitamente elencati come validi) da qualsiasi thread diverso da quello che ha creato il "quadratino" del controllo (in genere c'è solo un thread dell'interfaccia utente)
  • Non bloccare il thread dell'interfaccia utente per un certo periodo di tempo significativo, o ti rendono l'applicazione che non risponde

Al fine di interagire con l'interfaccia utente da un thread diverso, è necessario "marshall" la chiamata al thread dell'interfaccia utente, utilizzando un delegato e chiamando Control.Invoke/BeginInvoke. È possibile verificare se è necessario chiamare Invoke utilizzando la proprietà InvokeRequired, ma in questi giorni personalmente tendo a farlo comunque - non c'è molta penalità per invocare quando non è necessario.

Le espressioni lambda in C# 3 (o metodi anonimi in C# 2) rendono questo molto più piacevole.

Per esempio, è possibile utilizzare:

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

Tutte le staffe ottenere nel modo un po ', così si potrebbe desiderare di aggiungere un metodo di estensione in questo modo, se si sta utilizzando C# 3:

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

Poi si potrebbe fare:

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

che è un buon affare più semplice. Di solito è possibile utilizzare MethodInvoker catturando tutte le variabili a cui è necessario accedere all'interno del delegato.

Vedere my threading tutorial o Joe Albahari's per ulteriori dettagli.

In secondo luogo, vedo che stai usando Thread.Abort - in effetti sul tuo thread, nonostante abbia altre chiamate dopo di esso. Perché? Interrompere qualsiasi thread altro del proprio è una chiamata di tipo "solo emergenze" (che di solito dovrebbe essere seguita dall'app che viene scaricata comunque) e non vedo alcun motivo per interrompere il thread corrente quando c'è ancora lavoro da fare dopo ...

6

interazione sui controlli in un altro thread (UI) ha bisogno di essere richiamato in questo modo:

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

buona soluzione; di solito raccomando di usare l'azione dal framework piuttosto che rotolare (e mantenere) il tuo. –

+0

Sì ... questo è praticamente il modo standard per farlo. Il motivo per cui questo è necessario è che textBox1.Text può essere modificato solo sul thread in cui è stata creata la textbox - il richiamo è il processo utilizzato per tornare a quel thread. –

+0

@Fredrik: hai ragione, ma questo esempio è visto più spesso, in gran parte perché è durato più a lungo. Entrambi i metodi funzioneranno. –

3

Ho sempre trovato utile this article su questo particolare problema.

Nel tuo esempio, stai cercando di modificare vari controlli da un thread che non ha creato il controllo. Per ovviare a questo problema dato il vostro esempio, fare questo, invece (supponendo che il metodo ShowAllFly() è un metodo sul modulo):

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; 
    }); 
} 

Proprio per sottolineare il punto @ Jon Skeet ha fatto, ho commentato fuori la chiamata per interrompere il thread. Il thread si concluderà da solo. Non c'è motivo di abortire in questo modo.

+1

Sei il migliore di Matt. Grazie. Funziona molto bene. – atromgame

+0

Dal momento che è il migliore, gli ho dato un upvote, che nessuno prima di me aveva (suggerimento, suggerimento). –

0

Deve essere invocato ... Ma invocare devi aspettare ancora thread principale voglio dire che non ottieni errore in questo modo ma questo non funziona esattamente parallelamente se vuoi passare più di un processo contemporaneamente crea solo più un filo

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(); 

maniglia due processo stesso tempo

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 = ""; 
} 
}