如何提醒ViewModel模型中的变化...或者我应该如何?
我正在浏览一些MVVM文章,主要是这个和这个。
我的具体问题是: 如何将模型更改从模型传递给ViewModel?
在乔希的文章中,我没有看到他这样做。 ViewModel总是要求模型的属性。 在Rachel的例子中,她的模型实现了INotifyPropertyChanged
,并且引发了模型中的事件,但它们被视图本身所使用(请参阅她的文章/代码以获取更多关于她为什么这样做的更多细节)。
我看不到任何模型提示ViewModel对模型属性所做更改的示例。 这让我担心可能因为某种原因没有完成。 是否有模式提醒ViewModel模型中的变化? 这似乎是必要的,因为(1)对于每个模型可以有多于1个ViewModel,并且(2)即使只有一个ViewModel,对该模型的某些操作可能导致其他属性被改变。
我怀疑可能会有“你为什么要这么做?”的形式的答案/评论。 评论,所以这里是我的程序的描述。 我是MVVM的新手,所以也许我的整个设计都有问题。 我将简要描述一下。
我正在编写一些比“客户”或“产品”类更有趣的东西(至少对我来说!)。 我正在编程BlackJack。
我有一个没有任何代码的视图,只是依赖于绑定ViewModel中的属性和命令(参见Josh Smith的文章)。
无论是好还是坏,我都认为模型不应仅仅包含诸如PlayingCard
, Deck
之类的类,而且还包含保持整个游戏状态的BlackJackGame
类,并且知道玩家何时破产,庄家必须画牌,以及玩家和经销商当前得分是多少(小于21,21,胸围等)。
在BlackJackGame
我公开了像“DrawCard”这样的方法,并且在我看来,当绘制卡片时,应该更新CardScore
和IsBust
等属性,并将这些新值传递给ViewModel。 也许这是错误的想法?
有人可能会认为ViewModel称之为DrawCard()
方法,所以他应该知道要求得到更新的分数,并查明他是否破产。 意见?
在我的ViewModel中,我有逻辑来获取纸牌的实际图像(基于套装,等级)并将其用于视图。 模型不应该关注这个(也许其他ViewModel只会使用数字而不是纸牌图像)。 当然,也许有人会告诉我,模型应该甚至没有BlackJack游戏的概念,应该在ViewModel中处理?
如果您希望模型向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事件附加到它的情况,那么您可以使用Messaging系统,例如Prism的EventAggregator
或MVVM Light的Messenger
。
我在我的博客上简要概述了消息传递系统,但总而言之,任何对象都可以广播消息,任何对象都可以订阅以收听特定的消息。 因此,您可能会从一个对象广播PlayerScoreHasChangedMessage
,而另一个对象可以订阅以侦听这些类型的消息,并在它听到一个消息时更新它的PlayerScore
属性。
但我认为这不是您所描述的系统所需要的。
在理想的MVVM世界中,您的应用程序由您的ViewModel组成,而您的模型只是用于构建应用程序的模块。 它们通常只包含数据,所以不会有像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
,但为了简单起见,我将其忽略了)
简答:这取决于具体情况。
在你的例子中,模型正在“自己”更新,当然这些变化需要以某种方式传播给观点。 由于视图只能直接访问视图模型,这意味着模型必须将这些更改传递给相应的视图模型。 这样做的机制当然是INotifyPropertyChanged
,这意味着你会得到这样一个工作流程:
PropertyChanged
事件 DataContext
,属性被绑定等 PropertyChanged
并引发它自己的PropertyChanged
作为响应 另一方面,如果你的模型包含很少(或没有)业务逻辑,或者由于某种其他原因(例如获得事务性能力),你决定让每个视图模型“拥有”它的包装模型,然后所有对模型的修改都会通过视图模型如此的安排将不是必需的。
我在这里描述了另一个MVVM问题中的这种设计。
您的选择:
正如我所见, INotifyPropertyChanged
是.Net的基础部分。 即它在System.dll
。 在你的“模型”中实现它类似于实现事件结构。
如果你想要纯粹的POCO,那么你必须通过代理/服务来操作你的对象,然后通过监听代理来告诉ViewModel变化。
就个人而言,我只是松散地实施INotifyPropertyChanged,然后使用FODY为我做脏活。 它看起来和感觉POCO。
一个例子(使用FODY到IL编织PropertyChanged提高者):
public class NearlyPOCO: INotifyPropertyChanged
{
public string ValueA {get;set;}
public string ValueB {get;set;}
public event PropertyChangedEventHandler PropertyChanged;
}
那么你可以让你的ViewModel监听PropertyChanged的任何改变; 或财产特定的变化。
INotifyPropertyChanged路由的美妙之处在于,您将它与Extended ObservableCollection链接起来。 所以你把你附近的poco对象转储到一个集合中,然后听集合......如果有任何变化,在任何地方,你都会了解它。
老实说,这可能会加入“为什么不是由编译器自动处理的INotifyPropertyChanged”讨论,它可以解决以下问题:c#中的每个对象都应该有设施来通知它的任何部分是否已更改; 即默认实现INotifyPropertyChanged。 但是它并不是最好的路线,只需要最少的努力就是使用IL编织(特别是FODY)。
链接地址: http://www.djcxy.com/p/20279.html上一篇: How to alert ViewModel of changes in Model... or should I?