【问题标题】:How to (correctly) update the M in MVVM of WPF application?如何(正确)更新 WPF 应用程序的 MVVM 中的 M?
【发布时间】:2013-04-19 09:20:56
【问题描述】:

通过了Edward Tanguay 的一系列问题,这些问题反映了 MVVM 用于 WPF 应用程序的使用,这些问题可以在他的Fat Models, skinny ViewModels and dumb Views, the best MVVM approach? 的链接侧边栏中找到,我对他有点困惑 Big smart ViewModels, dumb Views, and any model, the best MVVM approach? 中的最终 WPF 应用程序

它的M (Model) is Customer class

//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 上进行更改。
虽然我已经特别询问了referenced M-V-VM implementation

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;
}

因此,例如,在实现代码from Adolfo Perez's answer 更改时,TextBox 的内容仅在_timer = new Timer(CheckForChangesInModel, null, 0, 1000); 中设置的时间间隔内从“Jim”更改为“Gennady”。

referenced by me M-V-VM in WPF approach 的所有逻辑都应该更新 “M”,以便 VM 赶上这些变化,而不是“VM”。

我更不明白,如果要在 VM 中进行更改,如果 VM 知道 VM 怎么能反映在 M 中strong>M 但 - 反之亦然 - 模型不知道 ViewModel)。

【问题讨论】:

  • 自标题问题以来将问题标记为已回答:“如何(正确)更新 WPF 应用程序的 MVVM 中的 M?”尽管我应该以不同的方式提出问题,但得到了回答。比如,“如何在这种特定风格的 M-V-VM 中更新 M?”

标签: c# wpf mvvm conceptual


【解决方案1】:

在 MVVM 中,您应该避免代码隐藏。原因是您希望得到可测试的类,在这种情况下,您的 VM 完全独立于您的 V。您可以在您的 VM 上运行一组单元测试,而不涉及 V。您还可以在不影响业务逻辑的情况下挂钩不同类型的视图。

您的按钮会将其 Command 属性绑定到您的 VM 中公开的 ICommand 属性。 VM 中的此命令将以您指定的方法处理您的点击事件。

在你看来:

<Button Content="Change FirstName" 
        Command="{Binding Path=ChangeFirstNameCommand"}/>

在您的视图模型中:

//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 了解您的 VM,而您的 VM 了解您的 >M,但反之则不然。

在您的示例中,如果您想更改 Model FirstName 属性,您必须执行以下操作:

  • 创建一个实现INotifyPropertyChangedVM
  • 在您的 VM 中公开您的 M FirstName 属性以通知更改
  • 在您的 XAML 视图中创建一个 TextBox,并将其 Text 属性绑定到您的 VM。FirstName 设置 Binding Mode=TwoWay。

    <TextBox Text=
         "{Binding Path=FirstName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
    

当您在文本框中键入时,您的名字将直接填充到 VM-M 中。此外,由于双向绑定,如果您在 VM 中修改 FirstName 属性,该更改将自动反映在您的 V

  • 将您的 View.DataContext 设置为您的 VM。这是为所有数据绑定设置上下文的原因,除非您指定不同的绑定源。
  • 如果您想在数据库中持久化更改,请在您的 VM 中注入一个服务类,该类将负责 CRUD 操作

看看这个简单的例子:

http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute

【讨论】:

  • 谢谢。你能改变短语“在 MVVM 中没有代码隐藏”吗?我不确定我是否正确掌握它
  • 当然,只是改写了那部分。如果您还有其他问题,请告诉我
  • @ГеннадийВанинНовосибирск 澄清一下,当 Adolfo 在 MVVM 中说“没有代码隐藏”时,我认为他的真正意思是“代码背后没有应用程序/业务逻辑”。在代码隐藏中包含特定于 UI 的代码是非常好的,例如设置默认焦点或调整布局中元素的大小。
  • @AdolfoPerez 是的,我会考虑将 Grid 或 TreeView 修改为特定于 UI 的代码,这对我来说很好,因为您正在修改 View 层,而不是应用程序/数据层。也就是说,有时由于视图的某些限制,我不得不从视图的代码隐藏中修改我的数据层,但这确实不理想,您应该尽可能避免这种情况:)跨度>
  • 这些“复杂/密集”的计算对我来说听起来像是业务逻辑,属于业务逻辑层。可以将负责计算的 BLL 类或类注入到您的 VM 中,以操纵模型或用户输入。如果您举例说明您要达到的目标,我可能会为您提供更好的答案。
【解决方案2】:

您的模型是您的域(业务)对象。您可以通过多种方式获得它们。例如,您可能有一个 repository 类,它会在您请求数据时为您提供数据,并在您希望存储数据时处理持久性。

您的视图模型是一个处理 UI 逻辑的类,例如更新字段、对用户操作做出反应等。在您的情况下,您可以将 CustomerRepository 类的实例传递给您的视图模型。然后在视图模型代码中,您从存储库中获取 Customer 的实例,并在绑定 UI 元素时填充您的视图模型属性。

您的视图只是您希望如何向用户显示信息的一组规则。它必须尽可能具有声明性和逻辑性。

有这样的代码:

private void button1_Click(object sender, RoutedEventArgs e)
{


} 

在您看来(或者更糟糕的是 - 在您的视图模型中)是一个巨大的错误,它打破了模式并且可能(并且肯定会)导致麻烦。您应该将 ViewModel 的 ICommand 字段绑定到按钮。您不应尝试以 WinForm 事件驱动风格编写 WPF 应用程序。

这就是 mvvm 的一般工作方式,它的主要目的是在您的应用程序中支持 multi-tier architecture

【讨论】:

  • 谢谢,就 M-V-VM 而言,存储库类是什么?
  • 存储库是属于应用程序领域层的服务类。在 MVVM 术语中,它可以被视为模型的一部分。但不要误会:您的实际模型是一个 Customer 类(假设您有一个编辑表单)。您的视图模型根据用户的输入更新客户的字段。存储库仅处理模型对象的检索(例如从数据库中)并在用户完成输入时存储它。
【解决方案3】:

首先,您需要处理您的VVM

如果您对按钮使用Click 事件,那么您肯定没有遵循此架构。

您需要在您的视图中使用WPFXAML 来绑定到您的ViewModel,您的ViewModel 应该是特定或潜在的许多模型的子集,并将属性呈现给View允许绑定。

我也会考虑研究:

  • RelayCommandICommand 用于绑定您的按钮。
  • Repository 用于交换模型并创建CRUD 方式的模式

您所遵循的教程似乎不是很好,因为这些概念没有真正正确地表达或者您没有理解它们。

【讨论】:

  • 您能否推荐一个最简单的 C# 代码示例来说明 MVVM 的概念?
  • @ГеннадийВанинНовосибирск 有很多 MVVM 的代码示例,但是当涉及到架构/模式和实践时,最重要的不是代码,而是对概念的理解。从 MSDN 开始msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx
  • @LukeHennerley,谢谢,但您链接中的示例需要我安装 Windows7,然后安装 VS2012 + Prism 框架。我只是想要一些非常简单快速的东西来掌握概念。我在 Windows XP SP3、VS2010、.NET 4.0
  • @ГеннадийВанинНовосибирск 遵循细节和概念,而不是代码示例和解决方案。您可以将这些原则应用于任何格式,代码示例都在那里,因此只需将它们复制并粘贴到 VS2010 中即可。
  • @ГеннадийВанинНовосибирск 如果您正在寻找a simple MVVM example,我在我的博客上发布了一个不需要外部库的帖子。底部有一个下载示例项目的链接,我认为这将帮助您更好地了解不同层中的内容以及它们之间的交互方式。
【解决方案4】:

如果您有一个纯 WPF 应用程序,那么研究 MVVM“反向”模式 ViewModel-First 可能会很有趣。

对于 web 开发,通常使用 MVVM,因为网页是通过浏览器加载的,并且视图是通过创建 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();

【讨论】:

  • 您的意思是“VM-V-VM-M”而不是“MV-V-MV-M”吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-15
  • 1970-01-01
  • 2014-09-24
  • 1970-01-01
  • 1970-01-01
  • 2014-03-26
相关资源
最近更新 更多