2010-06-15 17 views
9

Ho riscontrato un problema con un'applicazione Windows Form.Cosa c'è di sbagliato nella mia chiamata cross-thread in Windows Form?

Un modulo deve essere visualizzato da un altro thread. Quindi, nella classe del form, ho il seguente codice:

private delegate void DisplayDialogCallback(); 

public void DisplayDialog() 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new DisplayDialogCallback(DisplayDialog)); 
    } 
    else 
    { 
     this.ShowDialog(); 
    } 
} 

Ora, ogni volta che ho eseguito questo, un InvalidOperationException è gettato sulla linea this.ShowDialog();:

"operazione cross-thread non valida: Controllo 'SampleForm' accessibile da un thread diverso dal thread su cui è stato creato. "

Cosa c'è di sbagliato in questo pezzo di codice? Non è un modo valido per effettuare chiamate cross-thread? C'è qualcosa di speciale con ShowDialog()?

+3

Per curiosità, che cosa IsHandleCreated show? –

+0

@Marc Gravell: IsHandleCreated è falso. Quindi, naturalmente, come hanno detto diverse persone, il codice è in esecuzione prima che il modulo venga mostrato. –

risposta

4

provare questo:

private delegate void DisplayDialogCallback(); 

public void DisplayDialog() 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new DisplayDialogCallback(DisplayDialog)); 
    } 
    else 
    { 
     if (this.Handle != (IntPtr)0) // you can also use: this.IsHandleCreated 
     { 
      this.ShowDialog(); 

      if (this.CanFocus) 
      { 
       this.Focus(); 
      } 
     } 
     else 
     { 
      // Handle the error 
     } 
    } 
} 

prega di notare che InvokeRequired rendimenti

vero se Handle del controllo è stato creato su un thread diverso rispetto al thread di chiamata (indi che si deve effettuare chiamate al controllo tramite un metodo di richiamo); altrimenti, falso.

e quindi, se il controllo non è stato creato, il valore restituito sarà false!

8

Probabilmente stai eseguendo questo codice prima che il modulo sia stato mostrato.
Pertanto, InvokeRequired restituisce false.

5

Credo che quello che sta succedendo qui sia che questo codice viene eseguito prima che venga mostrato mai il Form.

Quando uno Form viene creato in .Net, non acquisisce immediatamente affinità per un determinato thread. Solo quando alcune operazioni vengono eseguite come mostrarlo o afferrare il manico, guadagna affinità. Prima che ciò accada, è difficile per InvokeRequired funzionare correttamente.

In questo caso particolare non viene stabilita alcuna affinità e non esiste controllo genitore in modo che InvokeRequired restituisca false poiché non è in grado di determinare il thread originale.

Il modo per risolvere questo problema è stabilire affinità per il controllo quando viene creato sul thread dell'interfaccia utente. Il modo migliore per farlo è chiedere il controllo della proprietà handle.

var notUsed = control.Handle; 
+0

Strano, ma ottenere 'control.Handle' non cambia nulla: l'eccezione è ancora generata. Invece, il suggerimento di Lorenzo funziona bene. IMHO, questo è causato dal fatto che il compilatore rimuove solo 'var notUsed = control.Handle;' line, mentre la variabile 'notUsed' è ... non utilizzata. –

0

Si può sempre provare a provare con un controllo diverso.

Ad esempio, è possibile accedere Application.Forms collezioni

public Control GetControlToInvokeAgainst() 
{ 
    if(Application.Forms.Count > 0) 
    { 
     return Application.Forms[0]; 
    } 
    return null; 
} 

Poi nel metodo DisplayDialog(), chiamare il GetControlToInvokeAgainst() e di test per nulla prima di tentare di effettuare una chiamata InvokeRequired.

0

Molto probabilmente l'handle del controllo non è stato ancora creato, nel qual caso Control.InvokeRequired restituisce false.

Controllare la proprietà Control.IsHandleCreated per vedere se questo è il caso.

0

Penso anche che SLaks sia corretto. Da msdn (http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx):

Se non è possibile trovare alcuna maniglia appropriata, il metodo InvokeRequired restituisce false.

Se è possibile nel tuo caso, proverei a combinare la creazione e la visualizzazione del controllo in un unico metodo, ad es.:

public DisplayDialog static Show() 
{ 
    var result = new DisplayDialog; //possibly cache instance of the dialog if needed, but this could be tricky 
    result.ShowDialog(); 
    return result; 
} 

è possibile chiamare Visualizza da un thread diverso

1

Probabilmente si arriva a questo codice prima che il modulo sia stato mostrato e quindi l'handle della finestra non sia stato creato.

è possibile aggiungere questo codice prima il codice e tutto dovrebbe essere buono:

if (! this.IsHandleCreated) 
    this.CreateHandle(); 

Edit: C'è un altro problema con il codice. Una volta visualizzato il modulo, non è possibile chiamare nuovamente ShowDialog(). Otterrai un'eccezione di operazione non valida. Potresti voler modificare questo metodo come altri hanno proposto.

potrebbe essere meglio serviti chiamando lo ShowDialog() direttamente dalla classe di chiamata e hanno un altro metodo per BringToFront() o qualcosa di simile ...