Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
WPF MVVM INotifyPropertyChanged实现-模型或视图模型_Wpf_Data Binding_Mvvm_Observablecollection_Inotifypropertychanged - Fatal编程技术网

WPF MVVM INotifyPropertyChanged实现-模型或视图模型

WPF MVVM INotifyPropertyChanged实现-模型或视图模型,wpf,data-binding,mvvm,observablecollection,inotifypropertychanged,Wpf,Data Binding,Mvvm,Observablecollection,Inotifypropertychanged,我在StackOverflow和其他博客上读到了一些关于在何处实现INotifyPropertyChanged的辩论,但似乎有些情况下您必须在模型上实现它。这是我的情况-我正在寻找关于我的结论的反馈,或者我的方法是错误的 我正在使用observedictionary()的这个实现,因为我需要使用键执行查询 在本词典中,我放置了模型对象的集合 在我的VM中,我声明字典的一个实例(Books)并在XAML中绑定到它 <tk:DataGrid AutoGenerateColumns="F

我在StackOverflow和其他博客上读到了一些关于在何处实现INotifyPropertyChanged的辩论,但似乎有些情况下您必须在模型上实现它。这是我的情况-我正在寻找关于我的结论的反馈,或者我的方法是错误的

我正在使用observedictionary()的这个实现,因为我需要使用键执行查询

在本词典中,我放置了模型对象的集合

在我的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>  
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
{
私有字符串标题;
公共字符串标题{
获取{返回标题;}
设置{
如果(值!=标题){
标题=价值;
通知财产变更(“所有权”);
}
}
}
公共列表作者{get;set;}
公共事件属性更改事件处理程序属性更改;
受保护的void NotifyPropertyChanged(字符串信息){
if(PropertyChanged!=null){
PropertyChanged(此,新PropertyChangedEventArgs(信息));
}
}
}
那么回到我最初的问题,如何在不在模型中实现INotifyPropertyChanged的情况下实现这一点呢


谢谢。

问题是,如果您在学习MVVM,您的
图书
模型类将有一个
图书视图模型
。因此,您将在该视图模型上有一个
INotifyPropertyChanged
实现。正是出于这个目的,MVVM的存在(但不仅限于此)

也就是说,
INotifyPropertyChanged
必须在视图模型类而不是模型上实现

更新:针对您的更新和我们在评论中的讨论

我说的
BookViewModel
是指别的东西。您需要在此视图模型中包装的不是整个
Book
对象集合,而是单个
Book

公共类BookViewModel:INotifyPropertyChanged
{
私人书籍;
公共图书{
获取{returnbook;}
}
公共字符串标题{
获取{return Book.Title;}
设置{
书名=价值;
通知财产变更(“所有权”);
}         
}
公共图书视图模型(图书){
这本书;
}
公共事件属性更改事件处理程序属性更改;
受保护的void NotifyPropertyChanged(字符串信息){
if(PropertyChanged!=null){
PropertyChanged(此,新PropertyChangedEventArgs(信息));
}
}
}
而您的
图书提供者
将返回
可观察到的收藏
,而不是
可观察到的收藏

公共类图书提供者
{
公共可观测集合GetBooks(){
ObservableCollection books=新的ObservableCollection();
books.Add(新建BookViewModel)(新建书本{
Title=“Book1”,
作者=新列表{新作者{Name=“Joe”},新作者{Name=“Phil”}
}));
books.Add(新建BookViewModel)(新建书本{
Title=“Book2”,
Authors=new List{new Author{Name=“Jane”},new Author{Name=“Bob”}
}));
还书;
}
}

如您所见,当您更新
书籍
标题
属性时,您将通过相应视图模型的
标题
属性进行更新,该属性将引发
属性更改
事件,该事件将触发UI更新。

请阅读。它解释了如何通过在模型中实现
INotifyPropertyChanged
来减少代码重复。

不要将INotifyPropertyChanged与MVVM混淆

考虑一下INotifyPropertyChanged实际上是什么->这是一个会触发说“嘿,看,我变了”的事件。如果有人关心,那么他们可以做些什么,不管是视图、视图模型还是其他什么

让我们从你的书(模型)开始。Title属性可以触发已更改的事件,为什么不呢?这是有道理的,这本书是在处理它自己的属性

现在对于BookViewModel-太好了,我们不需要复制标题和放大代码!哇

考虑一个视图,其中我们希望看到一个书籍列表,或者一个包含作者列表的书籍。ViewModel可以处理视图特定的其他属性,例如IsSelected。这是一个很好的例子——为什么这本书会在意它是否被选中?这是ViewModel的责任



显然,上述内容取决于您的体系结构,但就我个人而言,如果我正在创建一个对象库,我将使用INotifyPropertyChanged实现一个基类,并使对象属性负责触发事件。

hmmm,我使用的是MVVM。我使用的是每个视图的ViewModel,而不是每个模型。ViewModel不仅包含视图绑定到的模型属性(有几个),还包含视图的命令实现。然后作为属性添加到视图的ViewModel以进行绑定的仅限于模型的BookViewModel可能只会执行INotifyPropertyChanged。为什么不在模型中这样做呢。@IUnknown-如果在模型中这样做,您将添加UI
    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));
        }
    }
}
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();
    }
}
<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>
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));
        }
    }
}