Domanda

Sto scrivendo un'app WinForms che ha due modalità: console o GUI. Tre progetti all'interno della stessa soluzione, uno per l'app console, uno per i moduli dell'interfaccia utente e il terzo per mantenere la logica che collegheranno anche le due interfacce. L'app della console funziona perfettamente.

Un modello che contiene le selezioni dell'utente, ha un IList<T> dove T è un oggetto locale, Step, che implementa INotifyPropertyChanged, quindi nell'interfaccia utente questo è montato su un DataGridView. Tutto va bene in fase di esecuzione, lo stato iniziale degli oggetti si riflette sullo schermo.

Ciascuno degli StepResult oggetti è un'attività che viene eseguita a sua volta; alcune proprietà cambieranno, tornando all'IList e passandole a DataGridView.

Questa azione nelle versioni dell'interfaccia utente viene eseguita creando un BackgroundWorker che riporta gli eventi all'interfaccia utente. r fa ciò e genera un oggetto <=> che è un tipo elencato che indica un risultato (ad esempio, Running, NotRun, OK, NotOK, Caveat) e una stringa per indicare un messaggio (perché il passaggio è stato eseguito ma non abbastanza previsto, ovvero con un avvertimento). Normalmente le azioni implicheranno un'interazione con il database, ma in modalità debug genererò casualmente un risultato.

Se il messaggio è null, non c'è mai un problema, ma se generi una risposta come questa:

StepResult returnvalue = new StepResult(stat, "completed with caveat")

Viene visualizzato un errore che indica che si accede a DataGridView da un thread diverso da quello su cui è stato creato. (Lo sto passando attraverso un gestore personalizzato che dovrebbe gestire l'invocazione quando richiesto - forse no?)

Quindi, se creo una risposta unica, ad es. utilizzando un numero casuale <=>:

StepResult returnvalue = new StepResult(stat, r.ToString());

le azioni hanno esito positivo senza problemi, i numeri sono scritti in modo pulito su DataGridView.

Sono sconcertato. Suppongo che sia in qualche modo un problema letterale di stringa, ma qualcuno può fornire una spiegazione più chiara?

È stato utile?

Soluzione

Dato che stai eseguendo il binding dell'interfaccia utente tramite abbonamento a un evento, potresti trovare utile ; è un esempio che ho scritto qualche tempo fa che mostra come sottoclassare BindingList<T> in modo che le notifiche vengano automaticamente raggruppate nel thread dell'interfaccia utente.

Se non esiste un contesto di sincronizzazione (ovvero la modalità console), viene ripristinato il semplice richiamo diretto, quindi non vi è alcun sovraccarico. Durante l'esecuzione nel thread dell'interfaccia utente, si noti che questo utilizza essenzialmente Control.Invoke, che a sua volta esegue direttamente il delegato se si trova sul thread dell'interfaccia utente. Quindi c'è solo un interruttore se i dati vengono modificati da un thread non UI - juts quello che vogliamo ;-p

Altri suggerimenti

Hai risposto alla tua domanda: -

  

Viene visualizzato un errore che indica che si accede a DataGridView da un thread diverso da quello su cui è stato creato.

WinForms insiste sul fatto che tutte le azioni eseguite su moduli e controlli vengono eseguite nel contesto del thread in cui è stato creato il modulo. Il motivo è complesso, ma ha molto a che fare con l'API Win32 sottostante. Per i dettagli, consultare le varie voci sul The Old New Thing .

Quello che devi fare è usare i metodi InvokeRequired e Invoke per assicurarti che i controlli siano sempre accessibili dallo stesso thread (pseudocodice):

object Form.SomeFunction (args)
{
  if (InvokeRequired)
  {
    return Invoke (new delegate (Form.Somefunction), args);
  }
  else
  {
    return result_of_some_action;
  }
}

Ho avuto lo stesso problema prima. Forse questo articolo che ho pubblicato al riguardo può essere d'aiuto.

http: //cyberkruz.vox .com / library / post / net-problema-asincroni-e-finestre-Forms.html

Ho trovato questo articolo - " Aggiornamento IBindingList da thread diverso " - che puntava il dito sulla colpa alla BindingList -

  

Poiché BindingList non è configurato per operazioni asincrone, è necessario aggiornare BindingList dallo stesso thread su cui era controllato.

Il passaggio esplicito del modulo padre come ISynchronizeInvoke oggetto e la creazione di un wrapper per BindingList<T> ha funzionato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top