Salve sono nuovo del forum, spero che sia qualcuno che mi possa aiutare.Sono da poco alle prese con wpf e il pattern mvvm, che trovo interessante per certi versi ma molto complesso e oneroso per altri forse perché sono all'inizio. Tra i vari problemi uno mi sta assillando in particolare: come gestire diverse finestre all'interno della stessa applicazione. Mi spiego meglio. In rete ho fatto molte ricerche, ma ho trovato sempre esempi banali con applicazioni che usano soltanto una finesta. Nell'esempio allegato al WPF MVVM Toolkit su codeplex c'è un esempio molto interessante (il messenger.demo) che apre le viste relative alle conversazioni monitorando la ObservableCollection conversations, e, quando ne viene aggiunta una, crea la vista relativa. In un altro esempio invece la vista viene creata nel codebehiond della vista stessa, oserei dire in modo classico In altro ancora invece la vista è creata in un Command nel ViewModel. Qualcuno mi può chiarire quale è l'implementazione più coerente con il pattern MVVM?
Grazie
Ti rimando ad un interessante articolo presente su MSDN.
Dimmi se riesce a risolvere i tuoi dubbi.
Capisc e dotnet tu? No! E allor che parl a fà!
Nel pattern Model-View-ViewModel è l'application controller a gestire il ciclo di vita delle finestre quindi la cosa migliore sarebbe sempre inoltrare l'evento di richiesta di apertura di una finestra all'application controller, così come quello di chiusura. Come puoi immaginare la cosa è un po' scocciante, specialmente con le finestre modali, quindi personalmente nelle mie implementazioni distinguo sempre tra finestre che ho battezzato 'di popup' che semplicemente mi modificano il dettaglio di un elemento o mostrano delle informazioni (insoma quella che hanno solamente i classisi Ok e Annulla) che gestisco dalla finestra stessa, e quelle che invece sono finestre a se stanti, per le quali richiedo l'apertura con un evento che sarà gestito dall'application controller.
Grazie Antonio, l'articolo era uno di quelli che avevo già visto, è molto interessante, ma non chiarisce il mio problema.
Michele, da quanto hai detto ho capito che l'application controller è un oggetto che ha responsabilità di aprire e chiudere le finestre dell'applicazione.
Provo a fare un ragionamento con un esempio pratico:
la vista CustomersView contiene il CustomersViewModel che espone una una ObservableCollection di oggetti customer e un SelectedCustomer per il cliente corrente.
Se volessi modificare il customer correntemente selezionato potrei:
In ogni caso il metodo dell'application controller dovrà ricevere il parametro Customer e creare gli oggetti EditCustomerView ed EditCustomerViewModel.
La prima soluzione mi consentirebbe di utilizzare il command ma potrebbe creare problemi di Testing oltre al fatto che il ViewModel non dovrebbe conoscere la View.
La seconda soluzione invece richiede la scrittura di codebehind con la conseguemza che il meccanismo dei Command non può essere utilizzato.
Sto sbagliando qualcosa?
Quale è la soluzione più corretta secondo voi?
Avete qualche link da suggerirmi?
grazie per la disponibilità
Scusami ma forse avevo dato per scontato che tu conoscessi il concetto di Application Controller, ti rimando al webcast di Corrado Cavalli che riesce sicuramente ad esprimerlo meglio di me:
http://www.microsoft.com/italy/beit/Generic.aspx?video=24878a7f-235d-43ae-8aec-f09c2224fe49
Riguardo al tuo esempio la procedura corretta di fare la cosa secondo il pattern è:
1. Realizzare le Window CustomersView e CustomerView e fare in modo che entrambe estendino un'interfaccia IView (questo per la testabilita)
2. Realizzare i ViewModel CustomersViewModel e CustomerViewModel, meglio ancora che estendino una classe ViewModel base in cui ti liberi dell'onere di implementare INotifyPropertyChanged (e magari, come ho fatto io ti standardizzi un po' di command, i classici conferma, annulla, nuovo, modifica, ecc..)
3. Ti definisci un ButtonCommand generico:
public class ButtonCommand : ICommand
{
readonly Action<Object> _execute;
readonly Predicate<Object> _canExecute;
public ButtonCommand(Action<Object> execute, Predicate<Object> canExecute)
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#region Membri di ICommand
public bool CanExecute(object parameter)
return _canExecute == null ? true : _canExecute(parameter);
public event EventHandler CanExecuteChanged
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
public void Execute(object parameter)
_execute(parameter);
#endregion
4. In CustomersViewModel crei ed esponi un ButtonCommand per il quale definisci l'action e il predicato per l'abilitazione; nell'action generi un evento (su cui l'application controller deve essere in ascolto) che ti aprirà la finestra (banalmente potresti crearti un EventArg nel quale passi il nome della view che vuoi aprire), nel predicato magari ritorni true se è selezionato un elemento nella lista (il tuo Selectedcustomer).
5. Chiaramente hai il problema di passare al ViewModel della finestra da aprire l'elemento corrente: basta che Customer implementi un'interfaccia o implementi una classe base astratta da usare sempre nell'eventargs personalizzato per il passaggio del parametro.
Come ti dicevo nel post precedente, siccome è una cosa abbastanza comune, io mi sono creato una IView e una IPopupView per distinguere le view che contengono gli insiemi di elementi da quelli che contengono il singolo elemento da editare, a quel punto, con un po' di polimorfismo tutto diventa semplice. Se ti può interessare per il testing puoi usare Unity (vedi le enterprise library) e decidere chi far istanziare in corrispondenza di una certa interfaccia in modalità di testing o im modalità di esecuzione.
Sei stato chiarissimo oltre che molto utile.
Figurati, per qualsiasi cosa posta pure quando vuoi!
Associazione Culturale DotNetCampania - C.F.: 95127870632