如何(正确)更新WPF应用程序的MVVM中的M?
已经通过了一系列爱德华Tanguay的问题,折射MVF应用程序的MVVM,可以在他的Fat Models的链接侧边栏,瘦瘦的ViewModel和哑视图中找到,这是最好的MVVM方法吗? ,他对Big smart ViewModels中的最终WPF应用程序,哑视图和任何模型,最好的MVVM方法感到有些困惑?
其M(模型)是客户类 :
//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 }; } }
返回当前用户。 那种,因为它返回了新创建的“当前”用户的重复…
但是,在需要的情况下,M的数据存储和更新在哪里?
假设,我想将模型的当前用户的FirstName
更改为“Gennady”?
我添加了一个按钮,用于使用此按钮单击事件处理程序更新模型:
private void button1_Click(object sender, RoutedEventArgs e) { }
旨在从中更改模型的数据,这将反映在GUI中。
如何通过单击此按钮来执行此操作…抱歉,将代码放入此按钮button1_Click()
?
或者我希望这样做有问题吗?
然后。 如何在MVVM中正确更新/更改M?
更新:
所有答案似乎都提到我不应该在M中进行更改,而是在VM上进行更改。
虽然我特别询问了引用的MV-VM实现 :
public CustomerViewModel() { _timer = new Timer(CheckForChangesInModel, null, 0, 1000); } private void CheckForChangesInModel(object state) { Customer currentCustomer = CustomerViewModel.GetCurrentCustomer(); MapFieldsFromModeltoViewModel(currentCustomer, this); } public static void MapFieldsFromModeltoViewModel (Customer model, CustomerViewModel viewModel) { viewModel.FirstName = model.FirstName; viewModel.LastName = model.LastName; viewModel.TimeOfMostRecentActivity = model.TimeOfMostRecentActivity; }
因此,例如,在实现Adolfo Perez的答案更改代码时, TextBox
的内容仅在“ _timer = new Timer(CheckForChangesInModel, null, 0, 1000);
设置的间隔时间内从“Jim”更改为“Gennady” _timer = new Timer(CheckForChangesInModel, null, 0, 1000);
。
我在WPF方法中引用MV-VM的所有逻辑都应该更新“M” ,以便VM捕获这些更改,而不是“VM”。
甚至更多,我无法理解,如果要在VM中进行更改,如果VM知道M,它们如何反映在M中 – 但反之亦然 – 模型不知道ViewModel)。
在MVVM中,您应该避免代码隐藏。 原因是您希望最终得到可测试的类,在这种情况下,您的VM完全独立于您的V. 您可以在VM上运行一组unit testing,而不涉及V. 您还可以在不影响业务逻辑的情况下挂钩不同类型的视图。
您的按钮将其Command属性绑定到VM中公开的ICommand
属性。 VM中的此命令将以您指定的方法处理您的单击事件。
在您的视图中:
在您的ViewModel中:
//Define your command public ICommand ChangeFirstNameCommand {get;set;} //Initialize your command in Constructor perhaps ChangeFirstNameCommand = new RelayCommand(OnChangeFirstName,CanChangeFirstName); private void OnChangeFirstName() { //Your FirstName TextBox in your V will be updated after you click the Button this.FirstName = "Gennady"; } private bool CanChangeFirstName() { //Add any validation to set whether your button is enabled or not. // WPF internals take care of this. return true; }
请记住,在这种模式中,您的V知道您的虚拟机,而您的虚拟机知道您的M但不是相反的方式,这一点非常重要。
在您的示例中,如果要更改Model FirstName属性,则必须执行以下操作:
- 创建一个实现
INotifyPropertyChanged
的VM - 在VM中公开您的M FirstName属性通知更改
-
在XAML视图中创建一个TextBox,并将其Text属性绑定到VM .FirstName设置Binding Mode = TwoWay。
当您在TextBox中键入时,您的FirstName将直接填充在VM-M中。 此外,由于双向绑定,如果您在VM中修改FirstName属性,该更改将自动反映在您的V中
- 将View.DataContext设置为VM 。 这是为所有数据绑定设置Context的原因,除非您指定了不同的绑定源。
- 如果要在数据库中保留更改,则在VM中注入一个服务类,用于处理CRUD操作
看看这个简单的例子:
http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute
您的模型是您的域(业务)对象。 你可以通过多种方式获得它们。 例如,您可能有一个存储库类,在您请求数据时为您提供数据,并在您希望存储时处理持久性。
您的视图模型是一个处理UI逻辑的类,如更新字段,对用户操作做出反应等。在您的情况下,您可以将CustomerRepository类的实例传递给视图模型。 然后在视图模型代码中,您从存储库中获取Customer实例,并填充您的UI元素绑定的视图模型属性。
您的视图只是您希望向用户显示信息的一组规则。 它必须尽可能具有声明性和逻辑性。
有这样的代码:
private void button1_Click(object sender, RoutedEventArgs e) { }
在你看来(或者更糟糕的是 – 在你的视图模型中)是一个巨大的错误,它打破了模式,可能(并且肯定会)导致麻烦。 您应该将ViewModel的ICommand字段绑定到按钮。 您不应该尝试以WinForm事件驱动的方式编写WPF应用程序。
这就是mvvm的一般工作方式,它的主要目的是在您的应用程序中支持多层体系结构 。
首先,您需要处理V
和VM
。
如果您对某个按钮使用Click
事件,那么您肯定不会遵循此体系结构。
您需要在视图中使用WPF
和XAML
绑定到ViewModel
, ViewModel
应该是特定或可能很多模型的子集,并向View
提供允许绑定的属性。
我也会考虑研究:
- 用于绑定按钮的
RelayCommand
和ICommand
。 - 用于交换模型和创建
CRUD
方式的Repository
模式
您所遵循的教程似乎并不是很好,因为这些概念没有真正正确地进行过,或者您还没有理解它们。
如果你有一个纯WPF应用程序,调查MVVM’反向’模式,ViewModel-First可能会很有趣。
对于webdevelopment,通常使用MVVM,因为网页是通过浏览器加载的,而View是通过创建ViewModel来构建的。
WPF用户不浏览页面(除非您使用页面导航),因此跟随VM-V-VM-M会更有趣:
interface IMyView Show(); //view implementations in different assemblies: //silverlight class SilverlightMyView:IMyView Show(); //wpf class WpfMyView:IMyView Show(); class MyViewModel IMyView _view; MyModel _model; //ex. view resolved by DI (Unity, ..) MyViewModel(IMyView view) _view = view Show(model as MyModel) _model = model; _view.DataContext = this; _view.Show();