Я прочитал ряд дебатов о том, где реализовать 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 в VM для Store и изменяю значение имени книги в коде, пользовательский интерфейс не обновляется.
Если я реализую INotifyProperyChanged на модели и изменяю значение имени книги в коде, пользовательский интерфейс обновляется.
Событие с измененным значением не запускается в первом случае, потому что средство для настройки словаря не вызывается, это элемент (книга).
Мне что-то не хватает, потому что, если это правильная интерпретация, если я хочу, чтобы последовательные уведомления для моих моделей были независимо от того, связаны ли они непосредственно с XAML или через какую-то коллекцию, я бы всегда хотел, чтобы модель реализовала INotifyProperyChanged.
Btw, помимо ссылки 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 в Model, он отлично работает (обновления пользовательского интерфейса), потому что изменение находится в настройке свойств модели не в книгах в VM:
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 в модели?
Спасибо.