WPF MVVM INotifyPropertyChanged实现 - Model或ViewModel [英] WPF MVVM INotifyPropertyChanged Implementation - Model or ViewModel

查看:246
本文介绍了WPF MVVM INotifyPropertyChanged实现 - Model或ViewModel的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经阅读了关于在StackOverflow和其他博客上如何实现INotifyPropertyChanged的一些辩论,但是似乎有些情况下你必须在Model上实现它。这是我的场景 - 我正在寻找关于我的结论的反馈或者我的方法错误。

I have read a number of debates on where to implement INotifyPropertyChanged here on StackOverflow and other blogs but it seems that there are cases where you have to implement it on the Model. Here is my scenario - I am looking for feedback on my conclusion or is my approach wrong.

我正在使用ObservableDictionary的这个实现( ObservableDictionary ),因为我需要使用

I am using this implementation of an ObservableDictionary (ObservableDictionary) because I need performant queries using the key.

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

In this dictionary I place the collection of Model objects.

在我的虚拟机中,我声明一个

In my VM, I declare an instance (Books) of the dictionary and in the XAML bind to it.

    <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,并在代码中更改了Book名称的值, UI不会更新。

If I implement INotifyPropertyChanged on the VM for Books and change the value of a Book name in code, the UI is not updated.

如果在虚拟机上实现INotifyPropertyChanged存储,并在代码中更改Book名称的值,则UI不会更新。

If I implement INotifyPropertyChanged on the VM for Store and change the value of a Book name in code, the UI is not updated.

如果我在Model上实现INotifyProperyChanged,并在代码中更改一个Book名称的值,则UI将被更新。

If I implement INotifyProperyChanged on the Model and change the value a Book name in code, the UI is updated.

已更改事件没有在第一种情况下被触发,因为Dictionary setter没有被调用,它是Item(一本书)。

The Changed event is not fired in the first case because the Dictionary setter is not called, it's Item (a Book) is.

我错过了一些东西,因为如果这是正确的解释如果我想要对我的模型进行一致的通知,无论它们是直接从XAML绑定还是通过某种类型的集合,我总是希望模型实现INotifyProperyChanged。

Am I missing something because if this is the correct interpretation, if I want consistent notifications for my Models regardless of whether they are bound to directly from XAML or via some sort of collection, I would always want the Model to implement INotifyProperyChanged.

除了dll参考之外,Btw我个人没有看到INotifyPropertyChanged作为一个UI功能 - 认为它应该在一个更通用的.net命名空间中定义 - 我的2美分。

Btw, besides the dll reference, I personally do no see INotifyPropertyChanged as a UI function - think it should be defined in a more general .net namespace - my 2 cents.

编辑这里开始:

我们正在进行如此好的语义辩论我错过了我的问题的核心,所以这里再次发布,但是使用一个非常简单的MVVM示例来说明我的问题。

We were having such a good semantics debate that I missed the core of my question so here it is posted again but with a very simple MVVM example illustrate my question.

模型: / p>

The Models:

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 p>

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代码背后
通常不会这样 - 只是为了简单的例子

XAML code behind Would not normally to it this way - just for simple example

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 p>

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不会更改。

As coded above, when I click on the button and change the value in the first Book, the UI does not change.

但是,当我将INotifyPropertyChanged移动到模型时,它可以正常工作(UI更新)因为更改是在模型属性设置器不是虚拟机中的图书:

However, when I move the INotifyPropertyChanged to the Model it works fine (UI updates) because the change is in the Model property setter not Books in the 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如何实现?

So back to my original question, how do I accomplish this without implementing INotifyPropertyChanged in the Model?

谢谢。

推荐答案

事实是,如果你正在关注MVVM,你将有一个 BookViewModel 为你的模型类。因此,您将在该视图模型上使用 INotifyPropertyChanged 实现。正是为此目的MVVM存在(但不仅仅是)。

The thing is that if you were following MVVM, you would have a BookViewModel for your Book model class. So you would have a INotifyPropertyChanged implementation on that view model. Exactly for that purpose MVVM exists (but not only).

据说,必须实现 INotifyPropertyChanged 在视图模型类,而不是模型。

That being said, the INotifyPropertyChanged has to be implemented on view model classes, not models.

更新:为了回应您的更新和我们在评论中的讨论...

UPDATE: In response to your update and our discussion in comments...

BookViewModel 我的意思是其他的东西。您需要在此视图模型中包装不是 Book 对象的整个集合,而是单独的

By BookViewModel I meant something else. You need to wrap in this view model not the whole collection of Book objects but an individual Book:

public class BookViewModel : INotifyPropertyChanged
{
    private Book book;

    public Book Book {
        get { return book; }    
    }

    public string Title {
        get { return Book.Title; }
        set {
            Book.Title = value;
            NotifyPropertyChanged("Title");
        }         
    }

    public BookViewModel(Book book) {
        this.book = book;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(String info) {
        if (PropertyChanged != null) {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

而您的 BookProvider 将返回 ObservableCollection< BookViewModel> 而不是 ObservableCollection< Book> / p>

And your BookProvider will return ObservableCollection<BookViewModel> instead of ObservableCollection<Book>:

public class BookProvider
{
    public ObservableCollection<BookViewModel> GetBooks() {
        ObservableCollection<BookViewModel> books = new ObservableCollection<BookViewModel>();

        books.Add(new BookViewModel(new Book {
            Title = "Book1",
            Authors = new List<Author> { new Author { Name = "Joe" }, new Author { Name = "Phil" } }
        }));

        books.Add(new BookViewModel(new Book {
            Title = "Book2",
            Authors = new List<Author> { new Author { Name = "Jane" }, new Author { Name = "Bob" } }
        }));

        return books;
    }
}

如您所见,当您更新标题属性您将通过标题相应视图模型的属性将会引发 PropertyChanged 事件,这将触发UI更新。

As you can see, when you are updating the Title property of the Book you will be doing it through the Title property of the corresponding view model that will raise the PropertyChanged event, which will trigger the UI update.

这篇关于WPF MVVM INotifyPropertyChanged实现 - Model或ViewModel的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆