大智能ViewModels,哑视图和任何模型,最好的MVVM方法?

下面的代码是我之前的MVVM方法的重构( Fat Models,瘦的ViewModels和dumb Views,最好的MVVM方法? ),其中我将逻辑和INotifyPropertyChanged实现从模型转移回ViewModel 。 这更有意义,因为正如所指出的,您经常必须使用您无法更改或不想更改的模型,因此您的MVVM方法应该能够使用任何模型类,因为它碰巧存在。

此示例仍允许您在Visual Studio和Expression Blend中以设计模式查看模型中的实时数据 ,我认为这很重要,因为您可以拥有设计器连接的模拟数据存储,例如最小和最大的字符串。 UI可能会遇到,以便他可以根据这些极端情况调整设计。

问题:

  • 我有点惊讶我甚至不得不在我的ViewModel中“放一个计时器”,因为它似乎是INotifyPropertyChanged的一个function,它似乎是多余的,但这是我可以不断获得XAML UI的唯一方法(一次)每秒)反映我的模型的状态。 因此, 如果您遇到任何不利因素 ,例如使用线程或性能, 那么听到任何可能采用这种方法的人都会很有趣

如果您只是将XAML和代码复制到新的WPF项目中,则以下代码将起作用。

XAML:

               

代码背后:

 using System; using System.Windows; using System.ComponentModel; using System.Threading; namespace TestMvvm73892 { public partial class Window1 : Window { public Window1() { InitializeComponent(); } } //view model public class CustomerViewModel : INotifyPropertyChanged { private string _firstName; private string _lastName; private DateTime _timeOfMostRecentActivity; private Timer _timer; public string FirstName { get { return _firstName; } set { _firstName = value; this.RaisePropertyChanged("FirstName"); } } public string LastName { get { return _lastName; } set { _lastName = value; this.RaisePropertyChanged("LastName"); } } public DateTime TimeOfMostRecentActivity { get { return _timeOfMostRecentActivity; } set { _timeOfMostRecentActivity = value; this.RaisePropertyChanged("TimeOfMostRecentActivity"); } } public CustomerViewModel() { _timer = new Timer(CheckForChangesInModel, null, 0, 1000); } private void CheckForChangesInModel(object state) { Customer currentCustomer = CustomerViewModel.GetCurrentCustomer(); MapFieldsFromModeltoViewModel(currentCustomer, this); } public static CustomerViewModel GetCustomerViewModel() { CustomerViewModel customerViewModel = new CustomerViewModel(); Customer currentCustomer = CustomerViewModel.GetCurrentCustomer(); MapFieldsFromModeltoViewModel(currentCustomer, customerViewModel); return customerViewModel; } public static void MapFieldsFromModeltoViewModel (Customer model, CustomerViewModel viewModel) { viewModel.FirstName = model.FirstName; viewModel.LastName = model.LastName; viewModel.TimeOfMostRecentActivity = model.TimeOfMostRecentActivity; } public static Customer GetCurrentCustomer() { return Customer.GetCurrentCustomer(); } //INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } } //model public class Customer { public string FirstName { get; set; } public string LastName { get; set; } public DateTime TimeOfMostRecentActivity { get; set; } public static Customer GetCurrentCustomer() { return new Customer { FirstName = "Jim" , LastName = "Smith" , TimeOfMostRecentActivity = DateTime.Now }; } } } 

我喜欢上面的示例,我认为它实现了MVVM的精神。 但是,为了澄清,ViewModel代码和Model代码不应与实际Code Behind位于同一源文件中。 事实上,我认为他们不应该在同一个项目中。

根据我的理解,这是MVVM:

M – 模型是从业务层(BL)返回的数据。 这应该是轻量级的,包含只读数据。 Model类是哑的,不包含Update,Write或Delete逻辑,由BL作为请求,命令,操作等的结果生成.Model类不了解消费应用程序的表示需求,因此可以通过任何方式使用它们。 为了真正利用这种可重用性,我们希望Model类独立于UI项目。

VM – ViewModel包含通信层:它向BL发出请求并以适合呈现的方式处理结果。 与上面的示例一样,它也接收模型并根据特定的表示需求重新格式化。 把它想象成一个“绑定类”。 在上面的示例中,数据只是从一个对象移动到下一个对象,但ViewModel将负责诸如公开“FullName”类型属性或将前导零添加到ZipCode。 绑定类是实现INotifyPropertyChanged的绑定类是正确的。 而且,为了可重用性,我可能也会将此层提取到自己的项目中。 这将允许您尝试各种UI选项而不进行管道更改。

V – View绑定到VM中创建的Binding类对象。 View非常愚蠢:它对BL或VM一无所知。 数据可以在两个方向上绑定,但VM处理错误,validation等。任何数据同步操作都是通过将请求传回BL,并再次处理结果来处理的。

这将取决于应用程序的类型,但似乎很重要的是不断检查模型,看它是否已经改变。 假设您正在连接到从DAL构建业务对象(BO)的BL,该DAL连接到DB。 在这种情况下,你将不断重新创建BO,我相信这将是一个性能杀手。 您可以在BL上实现一个侦听通知的结账系统,或者有一个方法可以将上次已知的更改时间与实际进行比较,或者您可以将BL缓存在BL上。 只是一些想法。

另外,我在上面说过,模型应该是轻量级的。 有很多重量级选项,比如CSLA,但我不确定它们是否适合MVVM的想法。

我不是故意让自己成为一名专家,到目前为止,我在设计新软件架构时只研究过这些想法。 我想读一些关于这个主题的讨论。

我个人认为,虽然Model应该用于加载和存储数据, ViewModel的职责是知道何时需要这些数据,因此在ViewModel中使用计时器是有意义的。 通过这种方式,您可以将模型与不同的ViewModel一起使用(为此,只需检索一次数据,而不是每秒检索一次)。

几件事情要考虑:

  • 实现您的模型以支持异步数据检索(如果您想要定位Silverlight,这非常重要)
  • 从后台线程更新集合时要小心(在您的示例中不是问题,但是如果您需要使用ObservableCollection而不是记住它无法从非UI线程更新, 请在此处阅读更多内容 )