Chi deve controllare la navigazione in un'applicazione MVVM?

31

Esempio 1: ho una vista visualizzata nella mia applicazione MVVM (usiamo Silverlight ai fini della discussione) e clicco su un pulsante che dovrebbe portarmi a una nuova pagina.

Esempio n. 2: quella stessa vista ha un altro pulsante che, se cliccato, dovrebbe aprire una vista dettagli in una finestra figlia (finestra di dialogo).

Sappiamo che ci saranno oggetti Command esposti dal nostro ViewModel legati ai pulsanti con metodi che rispondono al clic dell'utente. Ma che cosa allora? Come completiamo l'azione? Anche se usiamo un cosiddetto NavigationService, cosa stiamo dicendo?

Per essere più specifici, in un modello View-first tradizionale (come gli schemi di navigazione basati su URL come sul web o il framework di navigazione integrato SL) gli oggetti Command dovrebbero sapere quale Vista visualizzare in seguito. Ciò sembra superare la linea quando si tratta della separazione delle preoccupazioni promosse dal modello.

D'altra parte, se il pulsante non era collegato a un oggetto Command e si comportava come un collegamento ipertestuale, le regole di navigazione potevano essere definite nel markup. Ma vogliamo che le Views controllino il flusso delle applicazioni e non sia la navigazione solo un altro tipo di business logic? (Posso dire di sì in alcuni casi e no in altri.)

Per me, l'implementazione utopica del pattern MVVM (e ho sentito altri che professano questo) sarebbe di avere il ViewModel cablato in modo tale che l'applicazione possa girare senza headless (cioè senza Views). Ciò fornisce la maggior parte della superficie per i test basati su codice e rende le viste un vero skin sull'applicazione. E il mio ViewModel non dovrebbe preoccuparsi se viene visualizzato nella finestra principale, in un pannello mobile o in una finestra secondaria, dovrebbe?

In base a questo approccio, a runtime è necessario un altro meccanismo per "legare" quale vista dovrebbe essere visualizzata per ciascun ViewModel. Ma cosa succede se vogliamo condividere una vista con più ViewModels o viceversa?

Quindi, data la necessità di gestire la relazione View-ViewModel in modo da sapere cosa visualizzare quando insieme alla necessità di navigare tra le visualizzazioni, inclusa la visualizzazione di finestre figlio / finestre di dialogo, come possiamo davvero realizzarlo nel pattern MVVM?

    
posta SonOfPirate 16.11.2011 - 17:18
fonte

3 risposte

6

Per motivi di chiusura, ho pensato di pubblicare la direzione che alla fine ho scelto per risolvere questo problema.

La prima decisione è stata quella di sfruttare il framework Silverlight Page Navigation fornito immediatamente. Questa decisione si basava su diversi fattori, tra cui la consapevolezza che questo tipo di navigazione viene portato avanti da Microsoft nelle app Windows 8 Metro ed è coerente con la navigazione nelle app Phone 7.

Per farlo funzionare, ho poi esaminato il lavoro che ASP.NET MVC ha svolto con la navigazione basata sulla convenzione. Il controllo Frame utilizza gli URI per individuare la 'pagina' da visualizzare. La somiglianza ha offerto l'opportunità di utilizzare un approccio simile basato sulla convenzione nell'app Silverlight. Il trucco stava nel far funzionare tutto insieme in un modo MVVM.

La soluzione è il NavigationService. Questo servizio espone diversi metodi, come NavigateTo e Back, che ViewModels può utilizzare per avviare una modifica della pagina. Quando viene richiesta una nuova pagina, NavigationService invia un CurrentPageChangedMessage utilizzando la funzione MVVMLight Messenger.

La vista che contiene il controllo Frame ha il proprio ViewModel impostato come DataContext che sta ascoltando questo messaggio. Alla ricezione, il nome della nuova vista viene sottoposto a una funzione di mappatura che applica le regole della convenzione e imposta la proprietà CurrentPage. La proprietà Source del controllo Frame è associata alla proprietà CurrentPage. Di conseguenza, l'impostazione della proprietà aggiorna l'origine e attiva la navigazione.

Ritornare al NavigationService. Il metodo NavigateTo accetta il nome della pagina di destinazione. Per assicurarsi che ViewModels non abbia problemi di interfaccia utente, il nome utilizzato è il nome del ViewModel da visualizzare. Ho effettivamente creato un'enumerazione che ha un campo per ogni ViewModel navigabile come aiuto e per eliminare le stringhe magiche in tutta l'app. La funzione di mappatura sopra menzionata eliminerà il suffisso "ViewModel" dal nome, aggiungerà "Pagina" al nome e imposterà il nome completo in "Views {Name} Page.xaml".

Quindi, per esempio, per andare alla vista dei dettagli del cliente, posso chiamare:

NavigationService.NavigateTo(ViewModels.CustomerDetails);

Il valore di CustomerDetails è "CustomerDetailsViewModel" che è mappato a "Views \ CustomerDetailsPage.xaml".

La bellezza di questo approccio è che l'interfaccia utente è completamente disaccoppiata dal ViewModels ma abbiamo il pieno supporto per la navigazione. Ora posso reskin la mia applicazione e comunque quando lo ritengo opportuno senza modifiche al codice.

Spero che la spiegazione aiuti.

    
risposta data 03.02.2012 - 03:53
fonte
21

La navigazione dovrebbe essere sempre gestita nel ViewModel.

Sei sulla strada giusta pensando che la perfetta implementazione del modello di progettazione MVVM significherebbe che potresti eseguire l'applicazione interamente senza Views e non puoi farlo se le tue Views controllano la tua navigazione.

Di solito ho un ApplicationViewModel o ShellViewModel , che gestisce lo stato generale della mia applicazione. Questo include CurrentPage (che è un ViewModel) e il codice per la gestione di ChangePageEvents . (Spesso viene utilizzato anche per altri oggetti a livello di applicazione come CurrentUser o ErrorMessages)

Quindi se qualsiasi ViewModel, ovunque, trasmette un ChangePageEvent(new SomePageViewModel) , ShellViewModel prenderà in considerazione quel messaggio e cambierà CurrentPage in qualunque pagina specificata nel messaggio.

In realtà ho scritto un post sul blog Navigazione con MVVM se sei interessato

    
risposta data 16.11.2011 - 18:33
fonte
2

Simile a quello che ha detto Rachel, la mia applicazione per lo più MVVM ha un Presenter per gestire gli scambi tra finestre o pagine. Rachel chiama questo ApplicationViewModel , ma nella mia esperienza, in genere deve fare molto di più che essere un target vincolante (come ricevere messaggi, creare Windows, ecc.) Quindi è tecnicamente più simile a un Presenter o% co_de tradizionale %.

Nella mia domanda, il mio Controller inizia con Presenter . Il CurrentViewModel intercetta tutte le comunicazioni tra Presenter e View . Una delle cose che ViewModel può fare durante un'interazione è restituire un nuovo ViewModel , il che significa che deve essere visualizzata una nuova pagina o una nuova ViewModel . Window si occupa di creare o sovrascrivere Presenter per il nuovo View e impostare ViewModel .

Il risultato di un'azione può anche essere che un DataContext è "completo", nel qual caso il ViewModel lo rileva e chiude la finestra, o apre questo Presenter dallo stack VM e torna alla visualizzazione la pagina precedente.

    
risposta data 17.11.2011 - 03:11
fonte

Leggi altre domande sui tag