一尘不染

WPF中的MVVM-如何向ViewModel发出有关模型更改的警报…还是应该?

c#

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

我的具体问题是: 如何将模型更改从模型传递到ViewModel?

在Josh的文章中,我看不到他这样做。ViewModel总是向模型询问属性。在Rachel的示例中,她确实具有模型实现INotifyPropertyChanged,并从模型中引发事件,但是这些事件仅供视图本身使用(有关执行此操作的详细信息,请参阅她的文章/代码)。

我在哪里都看不到模型向ViewModel发出模型属性更改警报的示例。这让我担心也许由于某些原因未完成。
有没有一种模式可以警告ViewModel模型中的更改?
似乎有必要,因为(1)可以想象每个模型有多个ViewModel,并且(2)即使只有一个ViewModel,对模型的某些操作也可能导致其他属性被更改。

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

我正在编写比“客户”或“产品”类更有趣的东西(至少对我而言!)。我正在编程BlackJack。

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

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

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

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

在我的ViewModel中,我有逻辑来获取一张扑克牌的真实图像(基于西装,等级)并使之可用于视图。该模型不必为此担心(也许其他ViewModel只会使用数字而不是纸牌图像)。当然,也许有人会告诉我,该模型甚至不应该具有BlackJack游戏的概念,而应该在ViewModel中进行处理?


阅读 259

收藏
2020-05-19

共1个答案

一尘不染

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

您的代码可能看起来像这样:

// 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");
}

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

如果您遇到的情况是实际上没有引用Model属性将PropertyChanged事件附加到该属性,则可以使用诸如Prism
EventAggregator或MVVM Light的消息系统Messenger

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

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

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

因此,您可能会拥有简单的Model数据对象,如下所示:

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

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

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

并且您将有一个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);
    }
}

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

2020-05-19