Implementazione WPF MVVM INotifyPropertyChanged – Model o ViewModel

Ho letto una serie di dibattiti su dove implementare INotifyPropertyChanged qui su StackOverflow e altri blog, ma sembra che ci siano casi in cui è necessario implementarlo sul modello. Ecco il mio scenario: sto cercando feedback sulla mia conclusione o il mio approccio è sbagliato.

Sto usando questa implementazione di un ObservableDictionary ( ObservableDictionary ) perché ho bisogno di query performanti usando la chiave.

In questo dizionario metto la collezione di oggetti Model.

Nella mia VM, dichiaro un’istanza (libri) del dizionario e nel binding XAML ad essa.

      

Se implemento INotifyPropertyChanged nella VM di Books e cambio il valore di un nome di Book nel codice, l’interfaccia utente non viene aggiornata.

Se implemento INotifyPropertyChanged nella VM per Store e si modifica il valore di un nome Book in codice, l’interfaccia utente non viene aggiornata.

Se implemento INotifyProperyChanged sul modello e cambia il valore di un nome del libro nel codice, l’interfaccia utente viene aggiornata.

L’evento Changed non viene triggersto nel primo caso perché il setter del dizionario non viene chiamato, è Item (a Book).

Mi manca qualcosa perché se questa è l’interpretazione corretta, se voglio notifiche coerenti per i miei modelli indipendentemente dal fatto che siano vincolati direttamente da XAML o tramite una sorta di collezione, vorrei sempre che il modello implementasse INotifyProperyChanged.

A parte il riferimento alla DLL, personalmente non vedo INotifyPropertyChanged come una funzione dell’interfaccia utente – penso che dovrebbe essere definito in uno spazio dei nomi .net più generale – i miei 2 centesimi.

EDIT INIZIA QUI:

Avevamo un dibattito semantico così buono che mi mancava il nocciolo della mia domanda, quindi qui è postata di nuovo ma con un esempio MVVM molto semplice illustra la mia domanda.

I modelli:

 public class Book { public string Title { get; set; ) public List Authors { get; set; } } public class Author { public string Name { get; set; } } 

Il fornitore di dati per generare alcuni dati fittizi

 public class BookProvider { public ObservableCollection GetBooks() { ObservableCollection books = new ObservableCollection(); books.Add(new Book { Title = "Book1", Authors = new List { new Author { Name = "Joe" }, new Author { Name = "Phil" } } }); books.Add(new Book { Title = "Book2", Authors = new List { new Author { Name = "Jane" }, new Author { Name = "Bob" } } }); return books; } } 

Il ViewModel

  public class BookViewModel : INotifyPropertyChanged { private ObservableCollection books; public ObservableCollection Books { get { return books; } set { if (value != books) { books = value; NotifyPropertyChanged("Books"); } } } private BookProvider provider; public BookViewModel() { provider = new BookProvider(); Books = provider.GetBooks(); } // For testing the example public void MakeChange() { Books[0].Title = "Changed"; } public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } } 

XAML code behind Non sarebbe normalmente in questo modo – solo per un semplice esempio

 public partial class MainWindow : Window { private BookViewModel vm; public MainWindow() { InitializeComponent(); vm = new BookViewModel(); this.DataContext = vm; } private void Button_Click(object sender, RoutedEventArgs e) { vm.MakeChange(); } } 

XAML

                       

Come sopra codificato, quando clicco sul pulsante e cambi il valore nel primo Libro, l’interfaccia utente non cambia.

Tuttavia, quando sposto la proprietà INotifyPropertyChanged sul modello, funziona correttamente (aggiornamenti dell’interfaccia utente) poiché la modifica è nel settatore della proprietà del modello e non nella cartella della macchina virtuale:

 public class Book : INotifyPropertyChanged { private string title; public string Title { get { return title; } set { if (value != title) { title = value; NotifyPropertyChanged("Title"); } } } public List Authors { get; set; } public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } } 

Quindi, tornando alla mia domanda iniziale, come posso realizzare questo senza implementare INotifyPropertyChanged nel Modello?

Grazie.

Il fatto è che se si stesse seguendo MVVM, si avrebbe un BookViewModel per la class del modello Book . Quindi INotifyPropertyChanged un’implementazione INotifyPropertyChanged su quel modello di vista. Esattamente per questo scopo esiste MVVM (ma non solo).

Detto questo, l’ INotifyPropertyChanged deve essere implementato su classi di modelli di viste, non su modelli.

AGGIORNAMENTO : in risposta al tuo aggiornamento e alla nostra discussione nei commenti …

Per BookViewModel intendevo qualcos’altro. È necessario avvolgere in questo modello di vista non l’intera raccolta di oggetti del Book ma un singolo Book :

 public class BookViewModel : INotifyPropertyChanged { private Book book; public Book Book { get { return book; } } public string Title { get { return Book.Title; } set { Book.Title = value; NotifyPropertyChanged("Title"); } } public BookViewModel(Book book) { this.book = book; } public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } } 

E BookProvider restituirà ObservableCollection anziché ObservableCollection :

 public class BookProvider { public ObservableCollection GetBooks() { ObservableCollection books = new ObservableCollection(); books.Add(new BookViewModel(new Book { Title = "Book1", Authors = new List { new Author { Name = "Joe" }, new Author { Name = "Phil" } } })); books.Add(new BookViewModel(new Book { Title = "Book2", Authors = new List { new Author { Name = "Jane" }, new Author { Name = "Bob" } } })); return books; } } 

Come puoi vedere, quando aggiorni la proprietà Title del Book lo farai attraverso la proprietà Title del modello di visualizzazione corrispondente che genererà l’evento PropertyChanged , che attiverà l’aggiornamento dell’interfaccia utente.

Si prega di leggere questo articolo . Spiega come è ansible ridurre la duplicazione del codice implementando INotifyPropertyChanged nel modello.

Non confondere INotifyPropertyChanged con MVVM.

Considera cosa in realtà INotifyPropertyChanged è -> È un evento che spara per dire “Guarda, sono cambiato”. Se qualcuno importa, allora può fare qualcosa a riguardo, sia che si tratti di View, ViewModel, o qualsiasi altra cosa.

Quindi iniziamo con il tuo libro (modello). La proprietà Title può generare un evento modificato, perché non dovrebbe? Ha senso, il libro ha a che fare con le sue proprietà.

Ora per BookViewModel – fantastico, non abbiamo bisogno di duplicare il titolo e fare il pieno del nostro codice! Whoo!

Considera una vista in cui vogliamo vedere un elenco di libri o un libro con un elenco di autori. ViewModel può gestire proprietà aggiuntive specifiche per la vista, come IsSelected. Questo è un grande esempio: perché il Libro dovrebbe interessarsi se è stato selezionato o no? Questa è la responsabilità del ViewModel.


Ovviamente quanto sopra dipende dalla tua architettura, ma personalmente se sto creando una libreria di oggetti implementerò una class base con INotifyPropertyChanged e renderò le proprietà dell’object responsabili per l’triggerszione dell’evento.