2009-11-25 14 views
14

Ho un controllo utente che contiene diversi altri controlli utente. Sto usando MVVM. Ogni controllo utente ha una VM corrispondente. In che modo questi controlli utente inviano informazioni reciprocamente? Voglio evitare di scrivere qualsiasi codice nel codice xaml dietro. In particolare, sono interessato a come i controlli (all'interno del controllo utente principale) parleranno tra loro e come parleranno con il controllo utente del container.MVVM: qual è il modo ideale per usercontrols per parlare tra loro

EDIT: So che utilizzando eventi, i delegati mi aiuteranno a risolvere questo problema. Ma, voglio evitare di scrivere qualsiasi codice in xaml code-behind.

risposta

1

Penso che la soluzione migliore sarebbe utilizzare il modello di Publisher/Subscriber. Ogni controllo registra alcuni eventi e allega i deleghi agli eventi esposti da altri controlli.

Al fine di esporre eventi e allegarli sarà necessario utilizzare una sorta di servizio Mediatore/EventBroker. Ho trovato un buon esempio here

+0

Potete spiegarlo in dettaglio? Intendi usare i delegati e gli eventi CLR? – Sandbox

+0

Ho appena modificato la mia risposta aggiungendo il link alla presentazione con codice di esempio dal blog di Marlon Grech. I modelli di vista comunicano nel modello di editore/sottoscrittore (non è necessario alcun codice). Il servizio di mediazione utilizza i delegati CLR per la registrazione dei gestori di eventi. – PanJanek

1

Il modo migliore di farlo a mio avviso è tramite Comando (Comandi in routing/RelayCommand, ecc.).

Voglio evitare di scrivere qualsiasi codice nel codice xaml dietro.

Mentre questo è un obiettivo lodevole, è necessario applicare un po 'di praticità a questo, non dovrebbe essere applicato al 100% come un tipo di regola "non devi".

+0

http://codebetter.com/blogs/glenn.block/archive/2009/08/02/the-spirit-of-mvvm-viewmodel-it-s-not-a-code-counting-exercise.aspx –

+0

Grande post, dovrò leggere questo! –

0

È possibile comunicare tra elementi sull'interfaccia utente utilizzando il collegamento degli elementi, pertanto, supponendo che un controllo utente creato abbia esposto una proprietà, gli altri controlli utente potrebbero collegarsi ad esso. È possibile configurare l'associazione, utilizzare le proprietà di dipendenza invece delle proprietà di base/implementare INotifyPropertyChanged ma in teoria è possibile, ma richiede un po 'di accortezza per consentire la comunicazione in questo modo.

Probabilmente troverai molto più semplice utilizzare una combinazione di eventi, codice e proprietà piuttosto che provare un puro modo dichiarativo, ma in teoria possibile.

14

In genere, è meglio cercare di ridurre la quantità di comunicazione tra le parti, poiché ogni volta che due utenti "parlano" l'un l'altro, si introduce una dipendenza tra di loro.

Detto questo, ci sono un paio di cose da considerare:

  • controlli utente può sempre "parlare" con il loro controllo contenente via esponendo le proprietà e l'utilizzo di DataBinding. Questo è molto bello, dal momento che conserva lo stile MVVM in tutti gli aspetti.
  • Il controllo che contiene possibile utilizzare le proprietà di "link" due immobili in due Comandi utente insieme, ancora una volta, conservando i confini pulite

Se si ha bisogno di avere una comunicazione più esplicito, ci sono due approachs principali.

  1. Implementare un servizio comune a entrambi gli elementi e utilizzare Dependency Injection per fornire l'implementazione in fase di esecuzione. Ciò consente ai controlli di parlare al servizio, che a sua volta può mantenere i controlli sincronizzati, ma mantiene anche la dipendenza al minimo.
  2. Utilizzare una forma di messaggistica per passare i messaggi tra i controlli.Molti framework MVVM adottano questo approccio, poiché disaccoppia l'invio del messaggio dalla ricezione del messaggio, ancora una volta, mantenendo le dipendenze al minimo.
+3

Per il punto 2: MvvmFoundation di Josh Smith ha una classe 'Messenger' che utilizza un meccanismo Register/Notify per trasferire i messaggi tra le VM. Sono memorizzati come riferimenti deboli per evitare perdite di memoria e funzionano davvero bene! – kiwipom

+1

Sì, consiglierei anche la classe 'Messenger' in MVVM Foundation. Grandi cose. – Oskar

+1

Sì, questo è quello a cui stavo pensando quando ho menzionato il messaggio "Messaggi da passare". È una tecnica efficace, se non si desidera che DI inietti servizi. –

0

è possibile condividere alcuni oggetti vista del modello tra i controlli, così come i comandi ...

Per esempio, avete un po 'di controllo principale, che contiene altri due controlli. E hai alcune funzionalità di filtro nel controllo principale, ma vuoi consentire all'utente di impostare alcune parti del filtro nel primo sottocontrollo (come "Filtro completo") e alcune parti del filtro in un altro (come "Filtro rapido "). Inoltre, si desidera poter iniziare il filtraggio da qualsiasi sottocontrollo. Allora si potrebbe utilizzare il codice come questo:

public class MainControlViewModel : ObservableObject 
{ 
    public FirstControlViewModel firstControlViewModel; 
    public SecondControlViewModel firstControlViewModel; 

    public ICommand FilterCommand; 
    public FilterSettings FilterSettings; 

    public MainControlViewModel() 
    { 
     //... 

     this.firstControlViewModel = new FirstControlViewModel(this.FilterSettings, this.FilterCommand); 
     this.secondControlViewModel = new SecondControlViewModel(this.FilterSettings, this.FilterCommand); 
    } 
} 

public class FirstControlViewModel : ObservableObject 
{ 
    //... 
} 

public class SecondControlViewModel : ObservableObject 
{ 
    //... 
} 

In XAML di controllo principale si legano sotto-comandi DataContext alle appropriate visualizzare i modelli. Ogni volta che un sottocontrollo modifica l'impostazione del filtro o esegue un comando, verrà notificato un altro sottocontrollo.

3

Esistono molti meccanismi di differenziazione per questo, ma per prima cosa dovresti scoprire in quale livello della tua architettura appartiene questa comunicazione.

Uno degli scopi del framework MVVM è che è possibile creare viste diverse sullo stesso modello di visualizzazione. Questi usercontrols si parlerebbero solo nella vista che stai implementando attualmente, o dovrebbero parlare tra loro in altre possibili visualizzazioni? In quest'ultimo caso, si desidera implementarlo al di sotto del livello di visualizzazione, nel viewmodel o nel modello stesso.

Un esempio del primo caso può essere se l'applicazione è in esecuzione su una superficie di visualizzazione molto piccola. Forse i tuoi controlli utente devono competere per lo spazio visivo. Se l'utente fa clic su un controllo utente per massimizzare, gli altri devono ridurre al minimo. Questo non avrebbe nulla a che fare con il viewmodel, è solo un adattamento alla tecnologia.

O forse avete diversi modelli di visuale con diversi comandi, dove le cose possono accadere senza cambiare il modello. Un esempio di questo potrebbe essere la navigazione. Hai un elenco di qualcosa e un riquadro dei dettagli con campi e pulsanti di comando collegati all'elemento selezionato nell'elenco. Potresti voler testare la logica di quali pulsanti sono abilitati per quali elementi. Il modello non riguarda l'oggetto che stai guardando, solo quando vengono premuti i pulsanti, o i campi vengono modificati.

La necessità di questa comunicazione potrebbe anche essere nel modello stesso. Forse hai dati denormalizzati che vengono aggiornati perché altri dati vengono modificati. Quindi i vari modelli di visuale che sono in azione devono cambiare a causa di increspature delle modifiche nel modello.

Quindi, per riassumere: "Dipende ...."

0

Come altri hanno detto di avere un paio di opzioni.

Esponendo DepedencyProperties sui vostri controlli utente e vincolante per quelle proprietà fornisce una soluzione XAML puro nella maggior parte dei casi, ma può introdurre alcune dipendenze di interfaccia utente in modo che le associazioni di vedersi

L'altra opzione è un modello di messaggistica disaccoppiato per inviare messaggi tra ViewModels. Vorrei che i controlli utente si legassero alle proprietà sulle loro macchine virtuali e poi sulla modifica delle proprietà all'interno di quella VM si potesse "pubblicare" un messaggio che notifica agli altri "iscritti" che qualcosa è successo e che possono reagire a quel messaggio, ma vogliono .

Ho un post sul blog su questo argomento se aiuta: http://www.bradcunningham.net/2009/11/decoupled-viewmodel-messaging-part-1.html

0

se si sta utilizzando rigorosa MVVM, quindi l'utente-controllo è un Vista e dovrebbe solo "parlare", o meglio, legano, al suo ViewModel. Poiché i tuoi ViewModels probabilmente implementano già INotifyPropertyChanged, purché abbiano un riferimento reciproco, possono usare gli eventi PropertyChanged per ricevere una notifica quando cambiano le proprietà, oppure possono chiamare metodi (meglio se attraverso un'interfaccia) per comunicare con ogni altro.

+0

quindi vuoi dire se due controlli utente devono parlarsi, quindi dovrebbero farlo tramite le rispettive VM? e non credo di essere abbastanza d'accordo sul fatto che dovrebbero iscriversi l'un l'altro all'evento PropertyChanged. – Sandbox

+0

@Sandbox: nel caso in cui si utilizzi MVVM vero, la vista ne conosce il ViewModel, ma Views non dovrebbe conoscersi l'un l'altro, quindi no, non dovrebbero parlare direttamente tra loro. Sareste costretti a testare la comunicazione inter-vista se lo faceste in quel modo.E puoi usare qualunque metodo tu voglia per ViewModels per sapere o comunicare tra loro. .NET è pieno di opzioni. Usa quello che si adatta alle necessità. –

7

vostro problema concettuale è qui:

controllo Ogni utente ha un corrispondente VM.

Avere un ViewModel separato per ogni vista praticamente sconfigge il concetto di un ViewModel. ViewModels non dovrebbe essere uno a uno con le viste, altrimenti non sono altro che glorificato code-behind.

A ViewModel cattura il concetto di "stato dell'interfaccia utente corrente" - come ad esempio quale pagina ci si trova e se o non si sta modificando -. In contrapposizione ai valori dei dati correnti"

a raccogliere davvero i vantaggi di MV-VM, determinano il numero di classi ViewModel utilizzate in base a elementi distinti che necessitano di stato.Ad esempio, se si dispone di un elenco di elementi ciascuno dei quali può essere visualizzato in 3 stati, è necessaria una VM per articolo. , se si dispone di tre visualizzazioni che visualizzano i dati in 3 modi diversi a seconda di un'impostazione comune, l'impostazione comune deve essere acquisita in una singola VM.

Una volta che hai strutturato i tuoi ViewModels per riflettere il i requisiti del compito a portata di mano che generalmente non ci sono necessità o desiderio di comunicare lo stato tra le viste. Se c'è una tale necessità, la cosa migliore da fare è rivalutare il design ViewModel per vedere se un ViewModel condiviso potrebbe beneficiare di una piccola quantità di informazioni aggiuntive sullo stato.

Ci saranno momenti in cui la complessità dell'applicazione impone l'uso di diversi ViewModel per lo stesso oggetto modello. In questo caso, ViewModels può mantenere i riferimenti a un oggetto di stato comune.

Problemi correlati