Я прочитал ряд дискуссий о том, где реализовать INotifyPropertyChanged, здесь, в StackOverflow и других блогах, но, похоже, бывают случаи, когда вам приходится реализовывать его в Модели. Вот мой сценарий - я ищу отзывы о своем заключении или мой подход неверен.
Я использую эту реализацию ObservableDictionary ( ObservableDictionary ), потому что мне нужны производительные запросы с использованием ключа.
В этом словаре я размещаю коллекцию объектов Model.
В моей виртуальной машине я объявляю экземпляр (Книги) словаря и в 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 на виртуальной машине для хранилища и изменю значение имени книги в коде, пользовательский интерфейс не будет обновлен.
Если я реализую 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;
}
}
ViewModel
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>
Как указано выше, когда я нажимаю кнопку и изменяю значение в первой книге, пользовательский интерфейс не изменяется.
Однако, когда я перемещаю 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 в модели?
Спасибо.