【发布时间】:2011-07-14 05:03:51
【问题描述】:
我在 StackOverflow 和其他博客上阅读了很多关于在哪里实现 INotifyPropertyChanged 的辩论,但似乎有些情况下您必须在模型上实现它。这是我的场景 - 我正在寻找关于我的结论的反馈,或者我的方法是否错误。
我正在使用 ObservableDictionary (ObservableDictionary) 的这种实现,因为我需要使用密钥进行高性能查询。
在这本字典中,我放置了模型对象的集合。
在我的 VM 中,我声明了字典的一个实例(书籍)并在 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 上为 Books 实现 INotifyPropertyChanged 并在代码中更改 Book 名称的值,则 UI 不会更新。
如果我在 VM 上为 Store 实现 INotifyPropertyChanged 并在代码中更改书名的值,则 UI 不会更新。
如果我在模型上实现 INotifyProperyChanged 并在代码中将值更改为书名,则 UI 会更新。
在第一种情况下不会触发 Changed 事件,因为没有调用 Dictionary setter,而是调用了 Item(a Book)。
我是否遗漏了什么,因为如果这是正确的解释,如果我希望我的模型得到一致的通知,无论它们是直接来自 XAML 还是通过某种集合绑定,我总是希望模型实现 INotifyProperyChanged。
顺便说一句,除了 dll 参考,我个人不认为 INotifyPropertyChanged 是一个 UI 函数 - 认为它应该在更通用的 .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>
如上代码,当我单击按钮并更改第一本书中的值时,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,我该如何做到这一点?
谢谢。
【问题讨论】:
标签: wpf data-binding mvvm observablecollection inotifypropertychanged