2009-03-17 3 views
9

Ho un'applicazione MVVM. In uno dei ViewModels è il 'FindFilesCommand' che popola una ObservableCollection. Quindi implemento un 'RemoveFilesCommand' nello stesso ViewModel. Questo comando fa apparire una finestra per ottenere un po 'più di input da parte dell'utente.Il posto migliore per aprire una nuova finestra in Vista modello ViewModel

Dove/qual è il modo migliore per farlo mantenendo il paradigma MVVM? In qualche modo facendo:

new WhateverWindow().Show()

nel ViewModel sembra sbagliato.

Cheers,

Steve

+0

Ho risposto a una domanda molto simile in [questo post] (http://stackoverflow.com/a/15512972/385995). –

risposta

2

Personalmente, considero questo scenario come uno in cui il modello di visualizzazione finestra principale desidera eseguire il prospetto di un compito per l'utente finale.

Dovrebbe essere responsabile della creazione dell'attività e dell'inizializzazione. La vista dovrebbe essere responsabile della creazione e della visualizzazione della finestra secondaria e dell'utilizzo dell'attività come modello di visualizzazione della finestra appena istanziata.

L'attività può essere annullata o confermata. Alza una notifica quando è completata.

La finestra utilizza la notifica per chiudersi. Il modello di visualizzazione genitore utilizza la notifica per eseguire un lavoro aggiuntivo una volta che l'attività ha eseguito il commit se è in corso un'operazione di follow-up.

Credo che questo sia il più vicino alla cosa naturale/intuitiva che le persone fanno con il loro approccio code-behind, ma reinterpretato per dividere le preoccupazioni indipendenti dall'interfaccia utente in un modello di vista, senza introdurre un ulteriore sovraccarico concettuale come servizi ecc.

Ho un'implementazione di questo per Silverlight. Vedi http://www.nikhilk.net/ViewModel-Dialogs-Task-Pattern.aspx per maggiori dettagli ... Mi piacerebbe sentire commenti/ulteriori suggerimenti su questo.

-1

per i dialoghi di questo tipo. Lo definisco come una classe nidificata di FindFilesCommand. Se la finestra di dialogo di base viene usata tra molti comandi, la definisco in un modulo accessibile a quei comandi e ha il comando di configurare la finestra di dialogo di conseguenza.

Gli oggetti comando sono sufficienti per mostrare il modo in cui il dialogo interagisce con il resto del software. Nel mio software gli oggetti Command risiedono nelle loro librerie, quindi le finestre di dialogo sono nascoste dal resto del sistema.

Per fare qualsiasi cosa, l'entusiasmo è eccessivo secondo me. Inoltre, cerca di mantenerlo ai massimi livelli, spesso implicando la creazione di molte interfacce e metodi di registrazione aggiuntivi. È un sacco di codice per poco guadagno.

Come ogni struttura, la devozione servile ti condurrà in qualche strano vicolo. Hai bisogno di usare il giudizio per vedere se ci sono altre tecniche da usare quando ottieni un cattivo odore di codice. Anche a mio parere, le finestre di dialogo dovrebbero essere strettamente vincolate e definite accanto al comando che le usa. In questo modo, cinque anni dopo, posso tornare a quella sezione del codice e vedere tutto ciò che il comando ha a che fare.

Sempre nelle poche istanze in cui una finestra di dialogo è utile per più comandi, la definisco in un modulo comune a tutti. Tuttavia nel mio software forse 1 su 20 dialoghi è come questo. L'eccezione principale è la finestra di dialogo di apertura/salvataggio del file. Se una finestra di dialogo viene utilizzata da dozzine di comandi, passerò all'intero percorso di definizione di un'interfaccia, creando un modulo per implementare quell'interfaccia e registrare tale modulo.

Se la localizzazione per l'uso internazionale è importante per la tua applicazione, dovrai assicurarti di avere un account con questo schema poiché tutti i moduli non sono in un modulo.

+0

Ciao. Penso che avere finestre di dialogo concrete nel ViewModel possa rompere la testabilità. –

0

Ho avuto questo problema anche con MVVM. Il mio primo pensiero è cercare di trovare un modo per non usare il dialogo. Usando WPF è molto più facile trovare un modo più semplice di fare le cose che con una finestra di dialogo.

Quando ciò non è possibile, l'opzione migliore sembra essere che il ViewModel chiama una classe condivisa per ottenere le informazioni dall'utente. ViewModel dovrebbe essere completamente inconsapevole della visualizzazione di una finestra di dialogo.

Quindi, come un semplice esempio, se si desiderava che l'utente confermasse l'eliminazione, ViewModel poteva chiamare DialogHelper.ConfirmDeletion(), che restituirebbe un valore booleano se l'utente ha risposto sì o no. La visualizzazione effettiva della finestra di dialogo verrebbe eseguita nella classe Helper.

Per finestre di dialogo più avanzate, restituendo molti dati, il metodo di supporto deve restituire un oggetto con tutte le informazioni dalla finestra di dialogo in esso.

Sono d'accordo che non è la soluzione più fluida con il resto di MVVM, ma non ho ancora trovato esempi migliori.

+0

chiamare "DialogHelper.ConfirmDeletion()" di chiamare effettivamente la finestra di dialogo non rende alcuna imho di defference. Stai ancora mescolando l'interfaccia utente pura nel codice. (Dopotutto, DialogHelper suggerisce già che è un aiutante di finestre ...) –

+0

Sono d'accordo, ma non vedo alcun modo migliore per farlo. La finestra di dialogo deve essere visualizzata da ViewModel per funzionare. Penso che avere il ViewModel almeno non mostrare esplicitamente la finestra di dialogo sia un buon passo, comunque. In questo modo le finestre di dialogo possono essere riutilizzate e sostituite in modo completo con il minimo sforzo. – timothymcgrath

+0

Sto pensando che forse gli eventi potrebbero essere la strada da percorrere? Non sono ancora sicuro dei dettagli e come si integri con il puro MVVM. –

0

Devo dire che i servizi sono la strada da percorrere qui.

L'interfaccia di servizio fornisce un modo per restituire i dati. Quindi l'effettiva implementazione di quel servizio può mostrare una finestra di dialogo o qualsiasi altra cosa per ottenere le informazioni necessarie nell'interfaccia.

In questo modo è possibile testare l'interfaccia di servizio nei test e il ViewModel non è il più saggio. Per quanto riguarda ViewModel, ha richiesto un servizio per alcune informazioni e ha ricevuto ciò di cui aveva bisogno.

+0

Ciao. Chi sarebbe il proprietario del servizio? La vista o il modello? Non sembra abbastanza giusto in nessuno di quei posti. –

+0

Cosa intendi per chi è il proprietario del servizio? È un singleton. Inoltre, se si pensa a ciò che fornisce il servizio, è la VM che la chiama perché è ciò che deve confermare l'eliminazione, ecc. –

+0

Spiacente, intendevo "in quale spazio dei nomi opera l'interfaccia di servizio". –

1

In realtà esempio Southridge di Jaime Rodriguez e Karl Shifflet, che stanno creando la finestra nel ViewModel, più precisamente nella parte eseguire un comando associato:

protected void OnShowDetails (object param) 
    { 
     // DetailsWindow window = new DetailsWindow(); 
     ListingDetailsWindow window = new ListingDetailsWindow(); 
     window.DataContext = new ListingDetailsViewModel (param as Listing, this.CurrentProfile) ; 
     ViewManager.Current.ShowWindow(window, true); 
    } 

Ecco il link: http://blogs.msdn.com/jaimer/archive/2009/02/10/m-v-vm-training-day-sample-application-and-decks.aspx

Immagino che questo non sia un grosso problema. Dopo tutto, il Viewmodel funge da "colla" tra la vista e il livello aziendale/livello dati, quindi è normale essere accoppiati alla vista (UI) ...

+0

Grazie per la risposta. Questo non significa che ViewModel abbia un riferimento alla vista per ListingDetailsWindow (e anche il ViewManager?). –

+1

No, un ViewModel dovrebbe idealmente essere indipendente dalla tecnologia dell'interfaccia utente. Certo è colla, ma dovrebbe evitare qualsiasi dipendenza da una vista concreta e, se possibile, qualsiasi dipendenza da materiale/meccanica dell'interfaccia utente concreta. – Falcon

1

Onyx (http://www.codeplex.com/wpfonyx) fornirà un equo bella soluzione per questo. Per fare un esempio, guardate il servizio ICommonDialogProvider, che può essere utilizzato da un ViewModel come questo:

ICommonFileDialogProvider provider = this.View.GetService<ICommonDialogProvider>(); 
IOpenFileDialog openDialog = provider.CreateOpenFileDialog(); 
// configure the IOpenFileDialog here... removed for brevity 
openDialog.ShowDialog(); 

Questo è molto simile ad usare l'OpenFileDialog cemento, ma è pienamente verificabile. La quantità di disaccoppiamento di cui hai veramente bisogno sarebbe un dettaglio di implementazione per te. Ad esempio, nel tuo caso potresti volere un servizio che nasconda del tutto il fatto che stai usando una finestra di dialogo. Qualcosa sulla falsariga di:

public interface IRemoveFiles 
{ 
    string[] GetFilesToRemove(); 
} 

IRemoveFiles removeFiles = this.View.GetService<IRemoveFiles>(); 
string[] files = removeFiles.GetFilesToRemove(); 

È quindi necessario garantire la vista ha un'implementazione per il servizio IRemoveFiles, per i quali vi sono diversi opzioni a vostra disposizione.

Onyx non è ancora pronto per il rilascio, ma il codice è completamente funzionante e utilizzabile come punto di riferimento. Spero di rilasciare stabilizzare l'interfaccia V1 molto presto, e verrà rilasciato non appena avremo documentazione e campioni decenti.

+0

Ciao. Il 'this.View.GetService()': Che cosa restituisce this.View e sarebbe necessario un riferimento specifico a qualsiasi tipo nel mio spazio dei nomi 'vista'? Per me, sembrerebbe che inizialmente si rompa MVVM - badate bene, potrei sbagliarmi - Sono molto nuovo ad esso –

+0

Restituisce un'istanza View, parte del framework Onyx. View è un IServiceProvider e fornisce anche l'accesso a ViewElement (l'elemento in cui è associata la vista), come DependencyObject. Se non usi mai ViewElement, non stai violando i principi MVVM. Guarda l'esempio semplice. – wekempf

0

Quello che stiamo facendo è somethng così, ciò che è descritto qui: http://www.codeproject.com/KB/WPF/DialogBehavior.aspx?msg=3439968#xx3439968xx

Il ViewModel ha una proprietà che si chiama ConfirmDeletionViewModel. Non appena imposto la proprietà, il comportamento apre la finestra di dialogo (modale o meno) e utilizza ConfirmDeletionViewModel. Inoltre sto passando un delegato che viene eseguito quando l'utente vuole chiudere la finestra di dialogo. Questo è fondamentalmente un delegato che imposta la proprietà ConfirmDeletionViewModel su null.

Problemi correlati