Я прочитал ряд дебатов о том, где реализовать INotifyPropertyChanged здесь, в StackOverflow и других блогах, но, похоже, есть случаи, когда вам нужно реализовать его в модели. Вот мой сценарий - я ищу отзывы о моем заключении или мой подход неверен.
Я использую эту реализацию ObservableDictionary (ObservableDictionary), потому что мне нужны эффективные запросы с использованием ключа.
В этот словарь я помещаю коллекцию объектов модели.
В моей виртуальной машине я объявляю экземпляр (Книги) словаря и привязываюсь к нему в XAML.
<tk:DataGrid AutoGenerateColumns="False" Grid.Row="1" ItemsSource="{Binding Mode=TwoWay, Path=Books.Store}" Grid.ColumnSpan="2" Margin="3">
<tk:DataGrid.Columns>
<tk:DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=Value.Name}" MinWidth="100" Header="Name" />
<tk:DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=Value.Details}" MinWidth="300" Header="Details" />
</tk:DataGrid.Columns>
</tk:DataGrid>
Если я реализую INotifyPropertyChanged на виртуальной машине для книг и изменю значение имени книги в коде, пользовательский интерфейс не обновится.
Если я реализую INotifyPropertyChanged на виртуальной машине для Store и изменю значение имени книги в коде, пользовательский интерфейс не обновится.
Если я реализую INotifyProperyChanged в модели и изменю значение имени книги в коде, пользовательский интерфейс будет обновлен.
Событие Changed не запускается в первом случае, потому что не вызывается установщик словаря, это Item (книга).
Мне что-то не хватает, потому что, если это правильная интерпретация, если мне нужны согласованные уведомления для моих моделей, независимо от того, привязаны ли они непосредственно к XAML или через какую-то коллекцию, я всегда хотел бы, чтобы модель реализовывала INotifyProperyChanged.
Кстати, помимо ссылки на dll, я лично не вижу INotifyPropertyChanged как функцию пользовательского интерфейса - думаю, ее следует определять в более общем пространстве имен .net - мои 2 цента.
ИЗМЕНИТЬ НАЧАЛО ЗДЕСЬ:
У нас были такие хорошие дебаты по семантике, что я пропустил суть своего вопроса, поэтому здесь он публикуется снова, но с очень простым примером MVVM, иллюстрирующим мой вопрос.
Модели:
public class Book
{
public string Title { get; set; )
public List<Author> Authors { get; set; }
}
public class Author
{
public string Name { get; set; }
}
Поставщик данных для создания фиктивных данных
public class BookProvider
{
public ObservableCollection<Book> GetBooks() {
ObservableCollection<Book> books = new ObservableCollection<Book>();
books.Add(new Book {
Title = "Book1",
Authors = new List<Author> { new Author { Name = "Joe" }, new Author { Name = "Phil" } }
});
books.Add(new Book {
Title = "Book2",
Authors = new List<Author> { new Author { Name = "Jane" }, new Author { Name = "Bob" } }
});
return books;
}
}
Модель просмотра
public class BookViewModel : INotifyPropertyChanged
{
private ObservableCollection<Book> books;
public ObservableCollection<Book> 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 позади. Обычно это не так - просто для простого примера.
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
<Window x:Class="BookTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="242*" />
<RowDefinition Height="69*" />
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Books}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}" />
<ListBox ItemsSource="{Binding Authors}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontStyle="Italic" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" Content="Change" Click="Button_Click" />
</Grid>
As coded above, when I click on the button and change the value in the first Book, the UI does not change.
Однако, когда я перемещаю INotifyPropertyChanged в модель, он отлично работает (обновления пользовательского интерфейса), потому что изменение находится в установщике свойств модели, а не в книгах в виртуальной машине:
public class Book : INotifyPropertyChanged
{
private string title;
public string Title {
get { return title; }
set {
if (value != title) {
title = value;
NotifyPropertyChanged("Title");
}
}
}
public List<Author> Authors { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Итак, возвращаясь к моему первоначальному вопросу, как мне добиться этого без реализации INotifyPropertyChanged в модели?
Спасибо.