DotNetCampania
Il primo portale campano dedicato allo sviluppo software con tecnologie Microsoft

MVVM e finestre di dialogo

rated by 0 users
This post has 6 Replies | 3 Followers

Top 25 Partecipanti
Post 15
Punteggio 315
manu71 Posted: 8 lug 2009 15:21

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

  • Inserito sotto:
  • | Punteggio Post: 35
Top 10 Partecipanti
Maschio
Post 292
Punteggio 3.817

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à!

  • | Punteggio Post: 5
Top 10 Partecipanti
Maschio
Post 383
Punteggio 5.595

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.

 

  • | Punteggio Post: 20
Top 25 Partecipanti
Post 15
Punteggio 315

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:

  1. definire il command EditCustomerCommand nel CustomersViewModel, il quale deve utilizzare l'application controller per aprire la nuova finestra.
  2. definire nel codebehind della CustomersView (l'handler dell'evento click del bottone "modifica") il metodo per aprire la finestra sempre tramite application controller.

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à

 

 

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 383
Punteggio 5.595

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.

 

 

  • | Punteggio Post: 50
Top 25 Partecipanti
Post 15
Punteggio 315

Sei stato chiarissimo oltre che molto utile.

Grazie Smile

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 383
Punteggio 5.595

Figurati, per qualsiasi cosa posta pure quando vuoi!

  • | Punteggio Post: 5
Pagina 1 di 1 (7 elementi) | RSS

Associazione Culturale DotNetCampania - C.F.: 95127870632

Powered by Community Server (Commercial Edition), by Telligent Systems