从ViewModel打开/关闭视图

6
我是一名有用的助手,可以翻译文本。

我有一个AddClientViewModel,被两个视图引用(AddClientView和SuggestedAddressesView)。AddClientView是一个表单,其中有一个地址字段。该表单有一个验证按钮,使用地理编码验证输入的地址。如果返回多个地址,则会打开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();
        }
    }

这里是SuggestedAddressesView构造函数,其中AddClientViewModel继承自ViewModelBase。
    public SuggestedAddressesView(ViewModelBase viewModel)
    {
        InitializeComponent();
        viewModel.ClosingRequest += (sender, e) => this.Close();
    }

我遇到的另一个问题是,当我从AddClientViewModel调用OnClosingRequest()时... AddClientView和SuggestedAddressesView都将关闭。 我知道这是因为两个视图引用了同一个ViewModel。 这不是我想要的行为。 我希望能够独立关闭任何一个窗口。
在ViewModel中打开View是否符合正确的MVVM结构?如何使窗口能够独立关闭?
3个回答

5
一旦你从 VM 引用 UI 元素(在本例中是 View),就违反了建议的 MVVM 准则。光凭这一点,我们就知道在 VM 中创建 Window 对象是错误的。
现在需要改正:
首先,尽量在应用程序中保持一个 View <-> 1 VM 的关系。这样更清晰,使您能够轻松地切换视图实现而不改变逻辑。即使没有"颠覆性"的影响,将多个 View 添加到同一个 VM 中也会使它变得笨拙。
从 VM 实现打开/关闭 View:
由于我们不能直接从 VM 访问 View(以符合标准),因此我们可以使用诸如使用 Messenger(MVVM Light)、EventAggregator(PRISM)等方法来在需要打开/关闭 View 时从 VM 发送“消息”到 View 并在 View 中执行实际操作。
这样,VM 只初始化消息,并且可以进行单元测试,而不引用任何 UI 元素。
使用“Messenger”方法处理 View 的打开:
按照您的逻辑,应该由 AddClientViewModel 请求打开 SuggestedAddressesView。
因此,当检测到 SuggestedAddresses.Count > 0 时,应向 AddClientView 发送消息,要求其打开 SuggestedAddressesView。
在 AddClientView.xaml.cs 中收到此消息时,您将执行 VM 中正在执行的操作。创建 SuggestedAddressesView 对象并调用 .Show()。
以上步骤的一个额外步骤是将 SuggestedAddressesView 的 DataContext 分配为 SuggestedAddressesViewModel。
就这样。现在你拥有的是,当 AddClientViewModel 想要显示 SuggestedAddressesView 时,它向自己的 View 发送一个消息,而 View 反过来创建并显示 SuggestedAddressesView。这样,VM 就不引用任何 View,我们遵循 MVVM 标准。
使用“Messenger”方法处理 View 的关闭:
  • 关闭一个View很简单。当你需要从VM中关闭视图时,你向它自己的View发送一条消息要求关闭。
  • 收到此消息后,View通过.Hide()/.Close()或其他方式关闭自身。

在这种方法中,每个VM处理自己的View的关闭,而不会有任何相互依赖的关系。

您可以使用此链接作为起点,指导您处理此方法的“消息”。它附带了一个可下载的文件,您可以获取并查看Messenger如何工作。如果您不使用MVVM Light或使用其他/自己的MVVM实现,请将其用作指导,以帮助您达到所需的效果。


好的,这很有道理!我遇到的问题是,我想将从SuggestedAddressesView收集的信息传递给AddClientViewModel(这就是为什么我在使用一个VM处理两个视图)。我这样做的原因是,SuggestedAddressesView中选择的地址被分配给了AddClientViewModel中定义的客户端。 - Duvarian
1
@francisg3看看我帖出的示例链接。如果你在那个例子中理解了,第二个打开的“窗口”(Modal/Non-Modal)将信息传回MainWindow。这就是您的需求所使用的过程。基本上,您会使用Messenger发送一条消息(这里的消息将是您要从SuggestedAddressViewModel发送到AddClientViewModel的数据)。 - Viv

0

从ViewModel打开窗口:

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

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

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

然后。 在MainWindowViewModel文件中创建NavigationService.cs类的实例。 然后

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

0

你可以使用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();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接