2010-03-12 10 views
5

La mia applicazione WPF è strutturata utilizzando il pattern MVVM. ViewModels comunicherà in modo asincrono con un server e quando i dati richiesti verranno restituiti, verrà richiamato un richiamo nel ViewModel e farà qualcosa con questi dati. Questo verrà eseguito su un thread che non è il thread dell'interfaccia utente. A volte questi callback implicano lavoro che deve essere fatto sul thread dell'interfaccia utente, quindi ho bisogno del Dispatcher. Questo potrebbe essere cose come:È considerato una cattiva pratica che gli oggetti ViewModel mantengano il Dispatcher?

  • Aggiunta di dati a un ObservableCollection
  • comandi trigger Prism che impostare qualcosa da visualizzare nella GUI
  • Creazione di oggetti WPF di qualche tipo.

Cerco di evitare quest'ultimo, ma i due primi punti qui ritengo siano cose ragionevoli da fare per ViewModels. Così; va bene avere ViewModels che mantiene il Dispatcher in grado di richiamare i comandi per il thread dell'interfaccia utente? O è considerata una cattiva pratica? E perché?

+0

Ho dovuto fare lo stesso nei controller: il controller si iscrive all'evento Load della vista che crea e in quel punto prende un riferimento al dispatcher della vista. Questo è particolarmente utile per l'esecuzione di delegati che sono stati passati in giro. – slugster

+0

Thx per la punta!Sto utilizzando un contenitore IoC e il contenitore IoC viene creato in App.xaml.cs. Presumo che venga eseguito nel thread dell'interfaccia utente, quindi il piano è quello di recuperare il dispatcher corrente nel momento in cui viene creato il contenitore IoC e aggiungerlo al contenitore. Resta da vedere se questo ha successo. – stiank81

+0

Funziona perfettamente. Oppure puoi semplicemente usare Dispatcher.CurrentDispatcher in qualsiasi parth che sai essere eseguito dal thread dell'interfaccia utente. Come per es. i costruttori ViewModel - se questi sono costruiti nel thread dell'interfaccia utente. – stiank81

risposta

3

Da ObservableCollection deve essere aggiornati sulla filettatura appartenenza (assumendo un'applicazione GUI), e ObservableCollections dovrebbe essere parte del ViewModel, allora c'è un caso chiaro per il ViewModel avere un Dispatcher.

Non riesco a vederlo essere parte del Modello.

+0

Non dovrebbe assolutamente far parte del modello. La soluzione alternativa che puoi fare è creare una nuova ObservableCollection e riempire il risultato con questo, ma suona davvero come un trucco per me .. – stiank81

+0

Ho una leggera variazione di MVVM: My Model è un codice non gestito in esecuzione su thread worker. Il modello richiama il mio codice .Net, che viene quindi inviato al thread della GUI. La funzione inviata in esecuzione nel thread della GUI aggiorna il ViewModel. In questo modo, la mia VM non ha bisogno di sapere sulla spedizione. – Surfbutler

1

Sono d'accordo con kyoryu e vorrei notare che crea solo una dipendenza dal lobrary di ServiceModel (che avete già) e non dalla Vista stessa, quindi c'è ben poco da obiettare contro questa costruzione.

Stavo provando alcune cose con WPF, una semplice VM e thread ieri e sono giunto alla conclusione che avevo assolutamente bisogno di passare il Dispatcher alla VM.

vedere anche Using WPF UI thread should always ensure STA apartment mode, right?

+0

Thx per la tua risposta! Sto iniziando a utilizzare il Dispatcher ora e semplifica davvero le cose. Prima di farlo, mi sono trovato a fare alcune soluzioni molto creative (hacky). Usando il Dispathcer penso che il codice sta migliorando molto. Ma immagino tu debba essere consapevole di quello che fai - non fare * qualsiasi cosa * solo perché puoi .. – stiank81

+0

Passa il Dispatcher alla VM? Probabilmente non è quello che farei. Il mio punto era che la VM potrebbe aver bisogno di un Dispatcher perché potrebbe possedere le cose che necessitano di ottenere Dipsatached. Tipicamente, mi piace fare il mio dispaccio all'ultimo momento. Se quella è la VM, bene, se è nella vista, va bene. Se il mio View fosse totalmente xaml, potrei essere tentato di avere la VM responsabile per il Dispatcher, solo per mantenere il View xaml puro. Ma probabilmente è troppo intelligente da parte mia. – kyoryu

+0

Ovviamente, questo potrebbe essere dovuto al fatto che nel mio lavoro di lavoro sto lavorando a frammenti di infrastrutture e stiamo cercando di possedere il contesto del thread, e sto rapidamente arrivando alla conclusione che ha più senso farlo così vicino ai bit che effettivamente si preoccupano il più possibile, invece di centralizzarlo ... – kyoryu

2

Idealmente, un ViewModel dovrebbe essere totalmente indipendente dalla tecnologia di interfaccia utente utilizzata. Dovremmo teoricamente essere in grado di riutilizzarlo per Windows Form (se magnaccia le Windows Form controlla un po 'per supportare meglio vincolante), per pagine Web (mi immagino qualche tipo di meccanismo di speciale qui che la compilazione il ViewModel anche in Javascript) e per eventuali tecnologie future. Non tutte queste tecnologie utilizzeranno il modello Dispatcher.

Detto questo, lo considero un compromesso pragmatico per includere lo Dispatcher nello ViewModel al giorno d'oggi. Nella mia classe di base ViewModel, io controllare la corrente Dispatcher:

protected override void OnPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (Deployment.Current.Dispatcher == null || Deployment.Current.Dispatcher.CheckAccess()) 
     { 
      base.OnPropertyChanged(sender, e); 
     } 
     else 
     { 
      Deployment.Current.Dispatcher.BeginInvoke(() => base.OnPropertyChanged(sender, e)); 
     } 
    } 

Ho ancora la dipendenza System.Windows naturalmente, ma vabbè. : ->

+0

Sembra buono poter riutilizzare ViewModels, ma non sono davvero d'accordo che questo sia l'obiettivo. Prendi in considerazione solo una cosa semplice come i binding dei comandi. Hai davvero bisogno di collegamenti ai comandi quando fai MVVM, ma Winforms ha ICommand? In realtà non ne sono sicuro, ma prima volevo realizzare il mio ViewModels su Mono, e almeno Mono non ha ICommand .. – stiank81

+0

Ancora - prezioso feedback! +1 – stiank81

+1

@heartmaster, se (davvero) è un obiettivo per riutilizzare la tua macchina virtuale, dovresti nascondere il dispatcher dietro un'interfaccia (che può essere anche reimplementata da InFormato InvokeRequired ecc.). –

0

Si consiglia di utilizzare invece AsyncOperation.

Problemi correlati