我已经在StackOverflow和其他博客上阅读了关于在何处实现INotifyPropertyChanged的一些争论,但似乎有些情况下你必须在Model上实现它 . 这是我的情景 - 我正在寻找关于我的结论的反馈或我的方法是错误的 .
我正在使用ObservableDictionary(ObservableDictionary)的这个实现,因为我需要使用密钥进行高性能查询 .
在这本词典中,我放置了Model对象的集合 .
在我的VM中,我声明了一个字典的实例(Books),并在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>
如果我在VM for Books上实现INotifyPropertyChanged并在代码中更改Book名称的值,则不会更新UI .
如果我在VM for Store上实现INotifyPropertyChanged并在代码中更改Book名称的值,则不会更新UI .
如果我在Model上实现INotifyProperyChanged并在代码中更改Book名称的值,则更新UI .
在第一种情况下不会触发Changed事件,因为未调用Dictionary setter,它的Item(a Book)是 .
我错过了什么,因为如果这是正确的解释,如果我想要我的模型的一致通知,无论它们是直接来自XAML还是通过某种集合,我总是希望模型实现INotifyProperyChanged .
顺便说一句,除了dll参考,我个人没有看到INotifyPropertyChanged作为UI函数 - 认为它应该在更通用的.net命名空间中定义 - 我的2美分 .
EDIT STARTS HERE:
我们有一个很好的语义辩论,我错过了我的问题的核心,所以这里再次发布,但有一个非常简单的MVVM示例说明我的问题 .
The Models:
public class Book
{
public string Title { get; set; )
public List<Author> Authors { get; set; }
}
public class Author
{
public string Name { get; set; }
}
The Data Provider to generate some dummy data
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;
}
}
The 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 code behind 通常不会这样 - 只是为了简单的例子
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();
}
}
The 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>
如上所述,当我单击按钮并更改第一本书中的值时,UI不会更改 .
但是,当我将INotifyPropertyChanged移动到模型时,它工作正常(UI更新),因为更改位于模型属性设置器中而不是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的情况下实现这一目标?
谢谢 .
3 回答
问题是,如果你关注MVVM,你的
Book
模型类会有一个BookViewModel
. 因此,您将在该视图模型上实现INotifyPropertyChanged
. 正是出于这个目的,MVVM存在(但不仅仅是) .话虽这么说,
INotifyPropertyChanged
必须在视图模型类上实现,而不是模型 .UPDATE :响应您的更新和我们在评论中的讨论......
BookViewModel
我的意思是别的 . 你需要在这个视图模型中包装Book
对象的整个集合,而不是个人Book
:你的
BookProvider
将返回ObservableCollection<BookViewModel>
而不是ObservableCollection<Book>
:如您所见,当您更新
Book
的Title
属性时,您将通过相应视图模型的Title
属性执行此操作,该属性将引发PropertyChanged
事件,这将触发UI更新 .请阅读this article . 它解释了如何通过在模型中实现
INotifyPropertyChanged
来减少代码重复 .不要将INotifyPropertyChanged与MVVM混淆 .
考虑一下INotifyPropertyChanged实际上是什么 - >这是一个激动的事件,说“嘿看,我已经改变了” . 如果有人关心,那么他们可以做些什么,无论他们是View,ViewModel还是其他什么 .
那么让我们从你的书(模型)开始吧 . Title属性可以触发已更改的事件,为什么不呢?这是有道理的,本书正在处理它自己的属性 .
现在对于BookViewModel - 太棒了,我们不需要复制 Headers 并增加我们的代码!喔!
考虑我们想要查看书籍列表的视图,或者带有作者列表的书籍 . 您的ViewModel可以处理特定于视图的其他属性,例如IsSelected . 这是一个很好的例子 - 为什么本书会关心它是否被选中?这是ViewModel的责任 .
显然上面取决于你的架构,但是如果我正在创建一个对象库,我将使用INotifyPropertyChanged实现一个基类,并使对象属性负责触发事件 .