C# WPF 中的 MVVM - 如何提醒 ViewModel 模型中的更改......还是我应该?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15439841/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 16:50:04  来源:igfitidea点击:

MVVM in WPF - How to alert ViewModel of changes in Model... or should I?

c#.netwpfmvvm

提问by Dave

I am going through some MVVM articles, primarily thisand this.

我正在阅读一些 MVVM 文章,主要是这个这个

My specific question is: How do I communicate Model changes from the Model to the ViewModel?

我的具体问题是:如何将 Model 更改从 Model 传达到 ViewModel?

In Josh's article, I don't see that he does this. The ViewModel always asks the Model for properties. In Rachel's example, she does have the model implement INotifyPropertyChanged, and raises events from the model, but they are for consumption by the view itself (see her article/code for more detail on why she does this).

在 Josh 的文章中,我没有看到他这样做。ViewModel 总是向 Model 询问属性。在 Rachel 的示例中,她确实有模型实施INotifyPropertyChanged,并从模型中引发事件,但它们供视图本身使用(有关她为什么这样做的更多详细信息,请参阅她的文章/代码)。

Nowhere do I see examples where the model alerts the ViewModel of changes to model properties. This has me worried that perhaps it's not done for some reason. Is there a pattern for alerting the ViewModel of changes in the Model?It would seem to be necessary as (1) conceivably there are more than 1 ViewModel for each model, and (2) even if there is just one ViewModel, some action on the model might result in other properties being changed.

我在任何地方都没有看到模型提醒 ViewModel 模型属性更改的示例。这让我担心可能由于某种原因没有完成。 是否有一种模式可以提醒 ViewModel 模型中的更改?这似乎是必要的,因为 (1) 可以想象每个模型有 1 个以上的 ViewModel,并且 (2) 即使只有一个 ViewModel,对模型的某些操作也可能导致其他属性发生更改。

I suspect that there might be answers/comments of the form "Why would you want to do that?" comments, so here's a description of my program. I'm new to MVVM so perhaps my whole design is faulty. I'll briefly describe it.

我怀疑可能会有“您为什么要这样做?”形式的答案/评论。评论,所以这里是我的程序的描述。我是 MVVM 的新手,所以也许我的整个设计有问题。我将简要描述它。

I am programming up something that is more interesting (at least, to me!) than "Customer" or "Product" classes. I am programming BlackHyman.

我正在编写比“客户”或“产品”类更有趣的东西(至少对我来说!)。我正在编程二十一点。

I have a View that doesn't have any code behind and just relies on binding to properties and commands in the ViewModel (see Josh Smith's article).

我有一个没有任何代码的视图,它只依赖于绑定到 ViewModel 中的属性和命令(请参阅 Josh Smith 的文章)。

For better or worse, I took the attitude that the Model should contain not just classes such as PlayingCard, Deck, but also the BlackHymanGameclass that keeps state of the whole game, and knows when the player has gone bust, the dealer has to draw cards, and what the player and dealer current score is (less than 21, 21, bust, etc.).

是好还是坏,我把该模型应该不仅包含类,如态度PlayingCardDeck但也BlackHymanGame认为保持整场比赛的状态类的,知道什么时候该玩家已经破产,经销商有画卡,玩家和庄家当前的分数是多少(小于 21、21、半身像等)。

From BlackHymanGameI expose methods like "DrawCard" and it occurred to me that when a card is drawn, properties such as CardScore, and IsBustshould be updated and these new values communicated to the ViewModel. Perhaps that's faulty thinking?

BlackHymanGame我公开诸如“DrawCard”之类的方法开始,我突然想到,在绘制卡片时CardScoreIsBust应该更新诸如和 之类的属性,并将这些新值传达给 ViewModel。也许这是错误的想法?

One could take the attitude that the ViewModel called the DrawCard()method so he should know to ask for an updated score and find out if he is bust or not. Opinions?

人们可以采取 ViewModel 调用该DrawCard()方法的态度,因此他应该知道要求更新分数并找出他是否破产。意见?

In my ViewModel, I have the logic to grab an actual image of a playing card (based on suit,rank) and make it available for the view. The model should not be concerned with this (perhaps other ViewModel would just use numbers instead of playing card images). Of course, perhaps some will tell me that the Model should not even have the concept of a BlackHyman game and that should be handled in the ViewModel?

在我的 ViewModel 中,我拥有获取一张扑克牌的实际图像(基于花色、等级)并使其可用于视图的逻辑。模型不应该关心这个(也许其他 ViewModel 只会使用数字而不是扑克牌图像)。当然,也许有人会告诉我,Model 甚至不应该有 BlackHyman 游戏的概念,而应该在 ViewModel 中处理?

采纳答案by Rachel

If you want your Models to alert the ViewModels of changes, they should implement INotifyPropertyChanged, and the ViewModels should subscribe to receive PropertyChange notifications.

如果您希望您的模型向 ViewModels 发出更改警报,则它们应实现INotifyPropertyChanged,并且 ViewModels 应订阅以接收 PropertyChange 通知。

Your code might look something like this:

您的代码可能如下所示:

// Attach EventHandler
PlayerModel.PropertyChanged += PlayerModel_PropertyChanged;

...

// When property gets changed in the Model, raise the PropertyChanged 
// event of the ViewModel copy of the property
PlayerModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SomeProperty")
        RaisePropertyChanged("ViewModelCopyOfSomeProperty");
}

But typically this is only needed if more than one object will be making changes to the Model's data, which is not usually the case.

但通常只有在多个对象将对模型的数据进行更改时才需要这样做,但通常情况并非如此。

If you ever have a case where you don't actually have a reference to your Model property to attach the PropertyChanged event to it, then you can use a Messaging system such as Prism's EventAggregatoror MVVM Light's Messenger.

如果您遇到过实际上没有引用 Model 属性来将 PropertyChanged 事件附加到它的情况,那么您可以使用消息系统,例如 PrismEventAggregator或 MVVM Light 的Messenger.

I have a brief overview of messaging systemson my blog, however to summarize it, any object can broadcast a message, and any object can subscribe to listen for specific messages. So you might broadcast a PlayerScoreHasChangedMessagefrom one object, and another object can subscribe to listen for those types of messages and update it's PlayerScoreproperty when it hears one.

我在我的博客上简要概述了消息传递系统,但总而言之,任何对象都可以广播消息,任何对象都可以订阅以侦听特定消息。因此,您可能会PlayerScoreHasChangedMessage从一个对象广播 a ,而另一个对象可以订阅以侦听这些类型的消息,并PlayerScore在听到消息时更新其属性。

But I don't think this is needed for the system you have described.

但是我认为您描述的系统不需要这样做。

In an ideal MVVM world, your application is comprised of your ViewModels, and your Models are the just the blocks used to build your application. They typically only contain data, so would not have methods such as DrawCard()(that would be in a ViewModel)

在理想的 MVVM 世界中,您的应用程序由您的 ViewModel 组成,而您的模型只是用于构建您的应用程序的块。它们通常只包含数据,因此不会有诸如DrawCard()(在 ViewModel 中)之类的方法

So you would probably have plain Model data objects like these:

所以你可能会有像这样的普通模型数据对象:

class CardModel
{
    int Score;
    SuitEnum Suit;
    CardEnum CardValue;
}

class PlayerModel 
{
    ObservableCollection<Card> FaceUpCards;
    ObservableCollection<Card> FaceDownCards;
    int CurrentScore;

    bool IsBust
    {
        get
        {
            return Score > 21;
        }
    }
}

and you'd have a ViewModel object like

你会有一个 ViewModel 对象,比如

public class GameViewModel
{
    ObservableCollection<CardModel> Deck;
    PlayerModel Dealer;
    PlayerModel Player;

    ICommand DrawCardCommand;

    void DrawCard(Player currentPlayer)
    {
        var nextCard = Deck.First();
        currentPlayer.FaceUpCards.Add(nextCard);

        if (currentPlayer.IsBust)
            // Process next player turn

        Deck.Remove(nextCard);
    }
}

(Above objects should all implement INotifyPropertyChanged, but I left it out for simplicity)

(以上对象都应该实现INotifyPropertyChanged,但为了简单起见我把它省略了)

回答by Mash

You can raise events from the model, which the viewmodel would need to subscribe to.

您可以从模型中引发事件,视图模型需要订阅这些事件。

For example, I recently worked on a project for which I had to generate a treeview (naturally, the model had a hierarchical nature to it). In the model I had an observablecollection called ChildElements.

例如,我最近从事一个项目,我必须为其生成树视图(自然,该模型具有层次结构)。在模型中,我有一个名为ChildElements.

In the viewmodel, I had stored a reference to the object in the model, and subscribed to the CollectionChangedevent of the observablecollection, like so: ModelObject.ChildElements.CollectionChanged += new CollectionChangedEventHandler(insert function reference here)...

在视图模型中,我已经在模型中存储了对对象的引用,并订阅CollectionChanged了 observablecollection的事件,如下所示:ModelObject.ChildElements.CollectionChanged += new CollectionChangedEventHandler(insert function reference here)...

Then your viewmodel gets automatically notified once a change happens in the model. You can follow the same concept using PropertyChanged, but you will need to explicitly raise property change events from your model for that to work.

然后,一旦模型发生更改,您的视图模型就会自动收到通知。您可以使用 遵循相同的概念PropertyChanged,但您需要从模型中显式引发属性更改事件才能使其工作。

回答by Jon

Short answer: it depends on the specifics.

简短的回答:这取决于具体情况。

In your example the models are being updated "on their own" and these changes of course need to somehow propagate to the views. Since the views can only directly access the viewmodels, it means the model must communicate these changes to the corresponding viewmodel. The established mechanism for doing so is of course INotifyPropertyChanged, which means that you 'll get a workflow like this:

在您的示例中,模型正在“自行”更新,这些更改当然需要以某种方式传播到视图。由于视图只能直接访问视图模型,这意味着模型必须将这些更改传达给相应的视图模型。这样做的既定机制当然是INotifyPropertyChanged,这意味着您将获得这样的工作流程:

  1. Viewmodel is created and wraps model
  2. Viewmodel subscribes to model's PropertyChangedevent
  3. Viewmodel is set as view's DataContext, properties are bound etc
  4. View triggers action on viewmodel
  5. Viewmodel calls method on model
  6. Model updates itself
  7. Viewmodel handles model's PropertyChangedand raises its own PropertyChangedin response
  8. View reflects the changes in its bindings, closing the feedback loop
  1. Viewmodel 被创建并包装模型
  2. Viewmodel 订阅模型的PropertyChanged事件
  3. Viewmodel 设置为 view's DataContext,属性被绑定等
  4. 视图触发视图模型上的操作
  5. Viewmodel 在模型上调用方法
  6. 模型自行更新
  7. Viewmodel 处理模型PropertyChanged并提升自己PropertyChanged的响应
  8. 视图反映其绑定的变化,关闭反馈循环

On the other hand if your models contained little (or no) business logic, or if for some other reason (such as gaining transactional capability) you decided to let each viewmodel "own" its wrapped model then all modifications to the model would pass through the viewmodel so such an arrangement would not be necessary.

另一方面,如果您的模型包含很少(或没有)业务逻辑,或者如果出于某种其他原因(例如获得事务能力),您决定让每个视图模型“拥有”它的包装模型,那么对模型的所有修改都将通过视图模型,因此不需要这样的安排。

I describe such a design in another MVVM question here.

我在此处的另一个 MVVM 问题中描述了这样的设计。

回答by HappyNomad

I have been advocating the directional Model -> View Model -> View flow of changes for a long time now, as you can see in the Flow of Changessection of my MVVM articlefrom 2008. This requires implementing INotifyPropertyChangedon the model. As far as I can tell, it's since become common practice.

长期以来,我一直在提倡定向模型 -> 视图模型 -> 视图变更流,正如您在2008 年MVVM 文章的变更流部分中看到的那样。这需要在模型上实现。据我所知,它已经成为普遍做法。INotifyPropertyChanged

Because you mentioned Josh Smith, take a look at his PropertyChanged class. It's a helper class for subscribing to the model's INotifyPropertyChanged.PropertyChangedevent.

因为你提到了 Josh Smith,所以看看他的 PropertyChanged 类。它是一个用于订阅模型INotifyPropertyChanged.PropertyChanged事件的辅助类。

You can actually take this approach much further, as I have recenty by creating my PropertiesUpdater class. Properties on the view-model are computed as complex expressions that include one or more properties on the model.

您实际上可以更进一步地采用这种方法,因为我最近通过创建我的 PropertiesUpdater 类。视图模型上的属性被计算为包含模型上一个或多个属性的复杂表达式。

回答by Meirion Hughes

Your choices:

您的选择:

  • Implement INotifyPropertyChanged
  • Events
  • POCO with Proxy manipulator
  • 实现 INotifyPropertyChanged
  • 活动
  • 带有代理操纵器的 POCO

As I see it, INotifyPropertyChangedis a fundamental part of .Net. i.e. its in System.dll. Implementing it in your "Model" is akin to implementing an event structure.

在我看来,它INotifyPropertyChanged是 .Net 的基本组成部分。即它在System.dll。在您的“模型”中实现它类似于实现一个事件结构。

If you want pure POCO, then you effectively have to manipulate your objects via proxies/services and then your ViewModel is notified of changes by listening to the proxy.

如果您想要纯 POCO,那么您必须通过代理/服务有效地操作您的对象,然后您的 ViewModel 通过侦听代理收到更改通知。

Personally I just loosely implement INotifyPropertyChanged and then use FODYto do the dirty work for me. It looks and feels POCO.

就我个人而言,我只是松散地实现了 INotifyPropertyChanged,然后使用FODY为我做脏活。它的外观和感觉都是 POCO。

An example (using FODY to IL Weave the PropertyChanged raisers):

一个例子(使用 FODY IL Weave the PropertyChanged raisers):

public class NearlyPOCO: INotifyPropertyChanged
{
     public string ValueA {get;set;}
     public string ValueB {get;set;}

     public event PropertyChangedEventHandler PropertyChanged;
}

then you can have your ViewModel listen to PropertyChanged for any changes; or property specific changes.

然后你可以让你的 ViewModel 监听 PropertyChanged 的​​任何变化;或财产特定的变化。

The beauty of the INotifyPropertyChanged route, is you chain it up with an Extended ObservableCollection. So you dump your near poco objects into a collection, and listen to the collection... if anything changes, anywhere, you learn about it.

INotifyPropertyChanged 路由的美妙之处在于您将它与Extended ObservableCollection链接起来。因此,您将附近的 poco 对象转储到一个集合中,然后收听该集合……如果有任何变化,无论何时何地,您都会了解它。

I'll be honest, this could join the "Why wasn't INotifyPropertyChanged autmatically handled by the compiler" discussion, which devolves to: Every object in c# should have the facility to notify if any part of it was changed; i.e. implement INotifyPropertyChanged by default. But it doesn't and the best route, that requires the least amount of effort, is to use IL Weaving (specifically FODY).

老实说,这可以加入“为什么 INotifyPropertyChanged 不是由编译器自动处理”的讨论,该讨论将演变为:c# 中的每个对象都应该能够通知它的任何部分是否发生更改;即默认实现 INotifyPropertyChanged。但它不会,而需要最少努力的最佳途径是使用 IL Weaving(特别是FODY)。

回答by Ibrahim Najjar

The guys did an amazing job answering this but in situations like this i really feel that the MVVM pattern is a pain so i would go and use a Supervising Controller or a Passive View approach and let go of the binding system at least for model objects that are generate changes on their own.

这些家伙在回答这个问题时做得非常出色,但在这种情况下,我真的觉得 MVVM 模式很痛苦,所以我会去使用监督控制器或被动视图方法,并至少为模型对象放开绑定系统会自行产生变化。

回答by Vladimir Dorokhov

Notification based on INotifyPropertyChangedand INotifyCollectionChangedis exactly what you need. To simplify your life with subscription to property changes, compile-time validation of property name, avoiding memory leaks, I would advice you to use PropertyObserverfrom Josh Smith's MVVM Foundation. As this project is open source, you can add just that class to your project from sources.

基于INotifyPropertyChangedINotifyCollectionChanged 的通知正是您所需要的。为了通过订阅属性更改、编译时验证属性名称、避免内存泄漏来简化您的生活,我建议您使用来自Josh Smith 的 MVVM Foundation 的PropertyObserver。由于该项目是开源的,您可以从源代码中将该类添加到您的项目中。

To understand, how to use PropertyObserver read this article.

要了解如何使用 PropertyObserver,请阅读这篇文章

Also, have a look deeper at Reactive Extensions (Rx). You can expose IObserver<T>from your model and subscribe to it in view model.

另外,更深入地了解Reactive Extensions (Rx)。您可以从模型中公开IObserver<T>并在视图模型中订阅它。

回答by VoteCoffee

I found this article helpful: http://social.msdn.microsoft.com/Forums/vstudio/en-US/3eb70678-c216-414f-a4a5-e1e3e557bb95/mvvm-businesslogic-is-part-of-the-?forum=wpf

我发现这篇文章很有帮助:http: //social.msdn.microsoft.com/Forums/vstudio/en-US/3eb70678-c216-414f-a4a5-e1e3e557bb95/mvvm-businesslogic-is-part-of-the-?forum =wpf

My summary:

我的总结:

The idea behind MVVM organization is to allow easier reuse of views and models and also to allow decoupled testing. Your view-model is a model that represent the view entities, your model represents the business entities.

MVVM 组织背后的想法是允许更轻松地重用视图和模型,并允许解耦测试。您的视图模型是代表视图实体的模型,您的模型代表业务实体。

What if you wanted to make a poker game later? Much of the UI should be reusable. If your game logic is bound up in your view-model, it would be very hard to reuse those elements without having to reprogram the view-model. What if you want to change your user interface? If your game logic is coupled to your view-model logic, you would need to recheck that your game still works. What if you want to create a desktop and a web app? If your view-model contains the game logic, it would become complicated trying to maintain these two applications side-by-side as the application logic would inevitably be bound up with the business logic in the view-model.

如果您以后想制作扑克游戏怎么办?大部分 UI 应该是可重用的。如果您的游戏逻辑绑定在您的视图模型中,那么在不必重新编程视图模型的情况下重用这些元素将是非常困难的。如果您想更改用户界面怎么办?如果您的游戏逻辑与视图模型逻辑耦合,您需要重新检查您的游戏是否仍然有效。如果您想创建桌面和 Web 应用程序怎么办?如果您的视图模型包含游戏逻辑,尝试并排维护这两个应用程序会变得很复杂,因为应用程序逻辑将不可避免地与视图模型中的业务逻辑绑定在一起。

Data change notifications and data validation happen in every layer (the view, the view-model, and the model).

数据更改通知和数据验证发生在每一层(视图、视图模型和模型)中。

The model contains your data representations (entities) and business logic specific to those entities. A deck of cards is a logical 'thing' with inherent properties. A good deck can't have duplicate cards put into it. It needs to expose a way to get the top card(s). It needs to know not to give out more cards than it has left. Such deck behaviors are part of the model because they inherent to a deck of cards. There will also be dealer models, player models, hand models, etc. These models can and will interact.

该模型包含您的数据表示(实体)和特定于这些实体的业务逻辑。一副纸牌是具有固有属性的合乎逻辑的“事物”。一个好的套牌不能有重复的牌。它需要公开一种获得顶牌的方法。它需要知道不要发出比剩下的更多的牌。这种牌组行为是模型的一部分,因为它们是一副牌所固有的。还会有庄家模型、玩家模型、手牌模型等。这些模型可以并且将会交互。

The view-model would consist of the presentation and application logic. All the work associated with displaying the game is separate from the logic of the game. This could include displaying hands as images, requests for cards to the dealer model, user display settings, etc.

视图模型将由表示和应用程序逻辑组成。与显示游戏相关的所有工作都与游戏逻辑分开。这可能包括将手牌显示为图像、向经销商模型请求卡片、用户显示设置等。

The guts of the article:

文章的核心:

Basically, the way that I like to explain this is that your business logic and entities comprise the model. This is what your specific application is using, but could be shared across many applications.

The View is the presentation layer - anything relating to actually directly interfacing with the user.

The ViewModel is basically the "glue" that's specific to your application that links the two together.

I have a nice diagram here that shows how they interface:

http://reedcopsey.com/2010/01/06/better-user-and-developer-experiences-from-windows-forms-to-wpf-with-mvvm-part-7-mvvm/

In your case - lets tackle some of the specifics...

Validation: This typically comes in 2 forms. The validation related to user input would happen in the ViewModel (primarily) and the View (ie: "Numeric" TextBox preventing text from being entered is handled for you in the view, etc). As such, the validation of the input from the user is typically a VM concern. That being said, there's often a second "layer" of validation - this is the validation that the data being used matches the business rules. This often is part of the model itself - when you push data to your Model, it may cause validation errors. The VM will then have to remap this information back to the View.

Operations "behind the scenes with no view, like writing to DB, sending email, etc": This is really part of the "Domain Specific Operations" in my diagram, and is really purely part of the Model. This is what you're trying to expose via the application. The ViewModel acts as a bridge to expose this information, but the operations are pure-Model.

Operations for the ViewModel: The ViewModel needs more than just INPC - it also needs any operation that are specific to your application (not your business logic), such as saving preferences and user state, etc. This is going to vary app. by app., even when interfacing the same "model".

A good way to think about it - Say you want to make 2 versions of your ordering system. The first is in WPF, and the second is a web interface.

The shared logic that deals with the orders themselves (sending emails, entering into DB, etc) is the Model. Your application is exposing these operations and data to the user, but doing it in 2 ways.

In the WPF application, the user interface (what the viewer interacts with) is the "view" - in the web application, this is basically the code that (at least eventually) is turned into javascript + html + css on the client.

The ViewModel is the rest of the "glue" that is required to adapt your model (these operations related to ordering) in order to make it work with the specific view technology/layer you're using.

基本上,我喜欢解释的方式是您的业务逻辑和实体构成模型。这是您的特定应用程序正在使用的,但可以在许多应用程序之间共享。

视图是表示层 - 与实际直接与用户交互相关的任何内容。

ViewModel 基本上是特定于您的应用程序的“胶水”,它将两者链接在一起。

我在这里有一个很好的图表,显示了它们的交互方式:

http://reedcopsey.com/2010/01/06/better-user-and-developer-experiences-from-windows-forms-to-wpf-with-mvvm-part-7-mvvm/

在你的情况下 - 让我们解决一些细节......

验证:这通常有两种形式。与用户输入相关的验证将发生在 ViewModel(主要)和 View 中(即:在视图中为您处理防止输入文本的“数字”文本框等)。因此,用户输入的验证通常是 VM 关注的问题。话虽如此,通常还有第二个“验证层”——这是所使用的数据与业务规则匹配的验证。这通常是模型本身的一部分 - 当您将数据推送到模型时,可能会导致验证错误。然后,VM 必须将此信息重新映射回视图。

“没有视图的幕后操作,例如写入数据库、发送电子邮件等”:这实际上是我图中“特定于域的操作”的一部分,并且纯粹是模型的一部分。这就是您试图通过应用程序公开的内容。ViewModel 充当公开这些信息的桥梁,但操作是纯模型的。

ViewModel 的操作:ViewModel 需要的不仅仅是 INPC - 它还需要任何特定于您的应用程序(而不是您的业务逻辑)的操作,例如保存首选项和用户状态等。这将因应用程序而异。通过应用程序,即使在连接相同的“模型”时也是如此。

一个很好的思考方式 - 假设您想要制作 2 个版本的订购系统。第一个是 WPF,第二个是 Web 界面。

处理订单本身(发送电子邮件、进入数据库等)的共享逻辑是模型。您的应用程序将这些操作和数据公开给用户,但有两种方式。

在 WPF 应用程序中,用户界面(查看器与之交互的)是“视图”——在 Web 应用程序中,这基本上是(至少最终)在客户端转换为 javascript + html + css 的代码。

ViewModel 是调整您的模型(这些与排序相关的操作)以使其与您正在使用的特定视图技术/层一起工作所需的其余“胶水”。

回答by Roemer

Fairly old thread but after a lot of searching I came up with my own solution: A PropertyChangedProxy

相当古老的线程,但经过大量搜索后,我想出了自己的解决方案:A PropertyChangedProxy

With this class, you can easily register to someone else's NotifyPropertyChanged and take appropriate actions if it is fired for the registered property.

使用这个类,您可以轻松地注册到其他人的 NotifyPropertyChanged 并在为注册的属性触发时采取适当的操作。

Here's a sample of how this could look like when you have a model property "Status" which can change on it's own and then should automatically notify the ViewModel to fire it's own PropertyChanged on it's "Status" property so that the view is also notified :)

这是一个示例,说明当您拥有一个模型属性“Status”时,该属性可以自行更改,然后应自动通知 ViewModel 在其“Status”属性上触发它自己的 PropertyChanged,以便也通知视图: )

public class MyModel : INotifyPropertyChanged
{
    private string _status;
    public string Status
    {
        get { return _status; }
        set { _status = value; OnPropertyChanged(); }
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MyViewModel : INotifyPropertyChanged
{
    public string Status
    {
        get { return _model.Status; }
    }

    private PropertyChangedProxy<MyModel, string> _statusPropertyChangedProxy;
    private MyModel _model;
    public MyViewModel(MyModel model)
    {
        _model = model;
        _statusPropertyChangedProxy = new PropertyChangedProxy<MyModel, string>(
            _model, myModel => myModel.Status, s => OnPropertyChanged("Status")
        );
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

and here's the class itself:

这是课程本身:

/// <summary>
/// Proxy class to easily take actions when a specific property in the "source" changed
/// </summary>
/// Last updated: 20.01.2015
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TPropType">Type of the property</typeparam>
public class PropertyChangedProxy<TSource, TPropType> where TSource : INotifyPropertyChanged
{
    private readonly Func<TSource, TPropType> _getValueFunc;
    private readonly TSource _source;
    private readonly Action<TPropType> _onPropertyChanged;
    private readonly string _modelPropertyname;

    /// <summary>
    /// Constructor for a property changed proxy
    /// </summary>
    /// <param name="source">The source object to listen for property changes</param>
    /// <param name="selectorExpression">Expression to the property of the source</param>
    /// <param name="onPropertyChanged">Action to take when a property changed was fired</param>
    public PropertyChangedProxy(TSource source, Expression<Func<TSource, TPropType>> selectorExpression, Action<TPropType> onPropertyChanged)
    {
        _source = source;
        _onPropertyChanged = onPropertyChanged;
        // Property "getter" to get the value
        _getValueFunc = selectorExpression.Compile();
        // Name of the property
        var body = (MemberExpression)selectorExpression.Body;
        _modelPropertyname = body.Member.Name;
        // Changed event
        _source.PropertyChanged += SourcePropertyChanged;
    }

    private void SourcePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == _modelPropertyname)
        {
            _onPropertyChanged(_getValueFunc(_source));
        }
    }
}

回答by Art

This seems to me like a really important question - even when there is no pressure to do it. I am working on a test project, which involves a TreeView. There are menu items and such that are mapped to commands, for example Delete. Presently, I am updating both the model and the view model from within the view model.

在我看来,这似乎是一个非常重要的问题——即使在没有压力的情况下也是如此。我正在处理一个涉及 TreeView 的测试项目。有菜单项等映射到命令,例如删除。目前,我正在从视图模型中更新模型和视图模型。

For example,

例如,

public void DeleteItemExecute ()
{
    DesignObjectViewModel node = this.SelectedNode;    // Action is on selected item
    DocStructureManagement.DeleteNode(node.DesignObject); // Remove from application
    node.Remove();                                // Remove from view model
    Controller.UpdateDocument();                  // Signal document has changed
}

This is simple, but seems to have a very basic flaw. A typical unit test would execute the command, then check the result in the view model. But this does not test that the model update was correct, since the two are updated simultaneously.

这很简单,但似乎有一个非常基本的缺陷。典型的单元测试会执行命令,然后在视图模型中检查结果。但这并不能测试模型更新是否正确,因为两者是同时更新的。

So perhaps it is better to use techniques like PropertyObserver to let the model update trigger a view model update. The same unit test would now only work if both actions were successful.

所以也许最好使用像 PropertyObserver 这样的技术让模型更新触发视图模型更新。相同的单元测试现在只有在两个操作都成功的情况下才能工作。

This is not a potential answer, I realize, but it seems worth putting out there.

我意识到,这不是一个潜在的答案,但似乎值得放在那里。