【问题标题】:Open/Close View from ViewModel从 ViewModel 打开/关闭视图
【发布时间】:2013-08-28 09:56:30
【问题描述】:

我有一个被 2 个视图(AddClientView 和 SuggestedAddressesView)引用的 AddClientViewModel。 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();
        }
    }

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

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

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

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

【问题讨论】:

    标签: c# wpf mvvm view viewmodel


    【解决方案1】:

    一旦您从 VM 中引用 UI 元素(在本例中为视图),您就违反了建议的 MVVM 指南。仅此我们就可以知道在 VM 中创建 Window 对象是错误的。

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

    • 首先尝试在您的应用程序中保留 1 个视图 1 个 VM。它更简洁,允许您非常轻松地切换具有相同逻辑的 View 实现。将多个视图添加到同一个虚拟机即使不是“开创性的”,也只会让它变得笨拙。
    • 所以现在你得到了AddClientViewSuggestedAddressesView 和他们自己的VM。太好了!

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

    • 由于我们不能直接从我们的VM访问View(为了符合标准),我们可以使用Messenger(MVVM Light),EventAggregator(PRISM)等方法发送“消息" 当您需要打开/关闭视图并在视图中执行实际操作时,从 VM 到视图。
    • 这样,VM 只需启动消息,就可以对相同操作进行良好的单元测试,并且不引用任何 UI 元素。

    使用“Messenger”方式处理视图打开

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

    就是这样。现在你所拥有的是,当AddClientViewModel 想要显示SuggestedAddressesView 时,它会向它自己的视图发送一条消息,而视图又会创建并显示SuggestedAddressesView。这样,VM 就不会引用任何 View,我们会继续保持 MVVM 标准。

    使用“Messenger”方式处理视图close

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

    在此每个 VM 处理它自己的视图的关闭,并且您没有任何相互连接的依赖关系。

    您可以使用this 作为起点来指导您处理这种方法的“消息”。它有一个附件下载,您可以获取并查看Messenger 的工作原理。这适用于 MVVM Light,如果您不使用它或使用其他东西/您自己的 MVVM 实现,请使用它作为指南来帮助您获得所需。

    【讨论】:

    • 好吧,有道理!我遇到的问题是,我希望将从 SuggestedAddressesView 收集的信息传递给 AddClientViewModel(因此我使用 1 个 VM 来处理 2 个视图)。我这样做的原因是因为在 SuggestedAddressesView 中选择的地址被分配给在 AddClientViewModel 中定义的客户端。
    • @francisg3 查看我发布的示例链接。如果您在此处获得示例,则打开的第二个Window 模态/非模态将信息传递回主窗口。这也是您将用于满足您的要求的过程。您几乎可以使用 Messenger 发送消息(这里的消息将是您想要从 SuggestedAddressViewModel 发送到 AddClientViewModel 的数据)
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      从 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);
      

      【讨论】:

        猜你喜欢
        • 2010-11-27
        • 2010-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多