WPF MVVM设计问题

时间:2013-05-10 08:26:27

标签: c# design-patterns mvvm

我正在构建一个基于WPF的销售点系统来练习使用MVVM。 我没有使用任何MVVM框架,而是在Josh Smith的文章中使用了RelayCommandhttp://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030

我之前写过一个,没有应用MVVM模式(仍然使用WPF) 对于数据库访问,我使用了Entity Framework。它看起来像这样:

我的问题是,如何使用MVVM实现这一目标? 首先,我已经使用MVVM编写了Login窗口,我的直接问题是,

  1. 如何关闭Login窗口(处理它)并打开MainWindow
  2. 在非MVVM版本中,MainWindow仅包含屏幕截图的顶部,即按钮行。
  3. 我是否应该将“按钮栏”进一步拆分为自己的ViewViewModel,仅使用MainWindow作为将所有内容放在一起的地方?
  4. 如果是这样,我如何编码,当点击按钮时,“按钮栏”的ViewModel会告诉MainWindow的{​​{1}}加载相应的ViewModel s(我在非MVVM版本中使用View)进入System.Windows.Controls.Page?在非MVVM版本中,我使用的代码非常简单:

    Frame
  5. 如果没有,如何将其他private void btnCheckout_Click(object sender, RoutedEventArgs e) { mainFrame.Navigated += frame_Navigated; var pgCheckout = new pgCheckout(); mainFrame.Navigate(pgCheckout); //The frame } 加载到Page

2 个答案:

答案 0 :(得分:1)

解决这些问题的一种方法是创建一个负责加载和卸载视图的对象。

在应用程序开始时,将创建此对象的实例,对象将显示一个Window(将显示所有视图),创建ViewModel和View的实例,将View的DataContext设置为ViewModel并将视图传递给窗口以便可以显示(例如,设置为网格的子项)

关闭视图和/或打开另一个视图:当执行当前ViewModel的命令时,可以将消息发送到同一对象。该对象可以通知Window关闭当前视图,并可以打开第一个View的相同方式打开一个新视图。该命令甚至可以传递一个参数,该参数指示必须加载的ViewModel。

要发送消息,我建议a MessageBroker that uses weak events传递消息。如果您不使用弱事件,应用程序将面临占用内存的风险,因为事件处理程序会阻止垃圾回收清理。见MVVM Light for such a Messenger

关于你的问题:

  1. 按照上述步骤操作。

  2. 和3.是否拆分ViewModel和Views是一个重用和分解的问题,就像在任何其他应用程序中一样。

  3. 和5.不要对点击事件进行编码。执行命令传递所需的View,ViewModel以及可能承载View的容器(ContentControl?)。

答案 1 :(得分:1)

如果您不使用任何现有框架,则必须自己创建一个框架。 IMO你可以从中受益,因为你掌控着一切,但你也需要重新发明很多东西。 我可以描述一下我们是如何做到的,无论是对还是错,让这里的选票决定:)

首先,您不希望使用任何UI相关对象污染ViewModel代码。但是,ViewModel是关于视图逻辑的,你需要以某种方式控制演示(导航,用户消息......)。 因此,您可以在视图模型中引入某种视图服务来控制与视图相关的逻辑,但不依赖于UI框架中的任何内容。我们这样说:

public interface IViewService
{
   //show message dialog with message text
   void ShowMessageDialog(string message);
   //show Yes/No message dialog with message text. Retrun true if answer is Yes 
   bool AskQuestion(string message);
   //Navigate to some other viewmodel
   void NavigateTo(ViewModel someOtherViewModel);      
}

并且您必须使用WPF相关逻辑创建此类服务的具体实现。如何实现这些方法取决于您和您正在使用的UI框架(在本例中为WPF)。

在视图模型中,您需要以某种方式获取实现IViewService的对象。您可以使用依赖注入注入它,使用服务定位器获取实例,甚至可以使用一些硬编码的静态单例实例。 (IMO依赖注入是可行的方法,但也使事情变得复杂,你需要引入DI容器并使用DI容器创建所有viewmodel实例)。

在您的命令中,您可以调用服务的方法。

假设您的LoginViewModel的LoginCommand(登录按钮绑定的RelayCommand

private void ExecuteLoginCommand(object parameter)
{
      bool loginOk = Login(.....);
      if(loginOk)
         viewService.NavigateTo(new MainWindowViewModel);
      else
         viewService.ShowMessage("Login failed");
}

最重要的是,ViewModel控制了与表示相关的逻辑,但对后面的UI框架一无所知。所有“WPF”代码都在实现IViewService的类中。在您的视图模型中,您“编程到接口”,因此您没有与WPF逻辑紧密耦合,您的ViewModel是“可测试的”,您甚至可以在其他UI平台上重用相同的viewmodel代码。