从ViewModel打开/关闭视图

本文关键字:视图 ViewModel 打开 | 更新日期: 2023-09-27 18:28:55

我有一个AddClientViewModel,它被两个视图(AddClientView和SuggestedAddressesView)引用。AddClientView是一个具有地址字段的表单。该表单有一个验证按钮,用于使用Geocoding验证输入的地址。如果返回多个地址,则会打开SuggestedAddressesView。

以下是我目前的做法:

AddClientViewModel:

    private void ValidateExecute(object obj)
    {
        SuggestedAddresses = new ObservableCollection<DBHelper.GeocodeService.GeocodeResult>(GeoCodeTest.SuggestedAddress(FormattedAddress));
        ....
        if (SuggestedAddresses.Count > 0)
        {
            var window = new SuggestedAddressesView(this);
            window.DataContext = this;
            window.Show();
        }
    }

这是AddClientViewModel从ViewModelBase 继承的SuggestedAddressesView构造函数

    public SuggestedAddressesView(ViewModelBase viewModel)
    {
        InitializeComponent();
        viewModel.ClosingRequest += (sender, e) => this.Close();
    }

我遇到的另一个问题是,当我从AddClientViewModel调用OnClosingRequest()时。。。AddClientView和SuggestedAddressesView都关闭。我知道发生这种情况是因为两个视图都引用了相同的ViewModel。这不是我想要的行为。我希望能够独立关闭任一窗口。

从ViewModel打开视图是否是正确的MVVM结构?如何独立关闭窗口

从ViewModel打开/关闭视图

一旦您从VM中引用UI元素(在本例中为View),您就违反了建议的MVVM准则。这样我们就可以知道在虚拟机中创建Window对象是错误的。

所以现在开始纠正这个问题:

  • 首先尝试保持1视图<->应用程序中有1个虚拟机。它更干净,并且允许您非常容易地切换具有相同逻辑的View实现。将多个视图添加到同一个虚拟机中,即使不是"突破性的",也会使其变得笨拙
  • 现在您得到了AddClientViewSuggestedAddressesView,它们有自己的VM。太棒了

从VM实现视图打开/关闭:

  • 由于我们无法直接从虚拟机访问视图(以符合标准),当您需要打开/关闭视图并在视图中进行实际操作时,我们可以使用Messenger(MVVM Light)、EventAggregator(PRISM)等方法从虚拟机向视图发送"消息"
  • 通过这种方式,VM只启动消息,并且可以对相同的操作进行良好的单元测试,并且不引用任何UI元素

使用"信使"方法处理Viewopen:

  • 根据您的逻辑,AddClientViewModel必须要求打开SuggestedAddressesView
  • 因此,当您检测到SuggestedAddresses.Count > 0时,您会向AddClientView发送一条消息,要求其打开SuggestedAddressesView
  • AddClientView.xaml.cs中,收到此消息后,您将执行当前在VM中执行的操作。创建一个SuggestedAddressesView的对象,并对其调用.Show()
  • 在上述步骤的过程中,您要添加的一个额外步骤是将SuggestedAddressesViewDataContext分配为SuggestedAddressesViewModel

就是这样。现在,当AddClientViewModel想要显示SuggestedAddressesView时,它会向自己的View发送一条消息,View会创建并显示SuggestedAddressesView。通过这种方式,虚拟机不引用任何视图,并且我们保持MVVM标准。

使用"信使"方法处理视图关闭

  • 关闭View非常简单。同样,当您需要从VM关闭视图时,您会向它自己的视图发送一条消息,要求关闭它
  • 收到此消息后,View几乎会通过.Hide()/.Close()关闭自己,或者以其他方式关闭它

在这个过程中,每个虚拟机都处理自己的视图关闭,并且没有任何相互连接的依赖关系。

您可以使用this作为起点,指导您处理此方法的"消息"。它附带了一个下载,您可以查看Messenger的工作原理。这是MVVM Light,如果您不使用它或使用其他东西/您自己的MVVM实现,请将它作为指南,帮助您获得所需内容。

您可以使用RelayCommand,以便按如下方式发送参数:

Command="{Binding CloseWindowCommand, Mode=OneWay}" 
CommandParameter="{Binding ElementName=TestWindow}"

使用此选项可以关闭各个视图。

示例:

public ICommand CloseCommand
    {
        get
        {
            return new RelayCommand(OnClose, IsEnable);
        }
    }
public void OnClose(object param)
    {
       AddClientView/SuggestedAddressesView Obj = param as AddClientView/SuggestedAddressesView;
       obj.Close();
    }

从ViewModel:打开窗口

创建NavigationService.cs类以打开窗口:让NavigationService.cs

现在将以下代码放入该类文件中。

   public void ShowWindow1Screen(Window1ViewModel window1ViewModel)
       {
           Window1= new Window1();
           Window1.DataContext = window1ViewModel;
           Window1.Owner = Window1View;
           Window1.ShowDialog();
       }

那么。创建NavigationService.cs类MainWindowViewModel文件的实例。然后

Window1ViewModel window1ViewModel = new Vindow1ViewModel();
window1ViewModel.Name = MainWindowTextValue;
NavigationService navigationService = new NavigationService();
navigationService.ShowWindow1Screen(window1ViewModel);