【问题标题】:MVVM - Close a window from view modelMVVM - 从视图模型中关闭一个窗口
【发布时间】:2018-06-12 04:12:32
【问题描述】:

在我的应用程序中,我使用 prism 并尝试实现以下概念:

有一个通信窗口可以有两个可能的用户控件。我有窗口和用户控件的 ViewModel。 在每个用户控件中,我都有一些按钮。对于某些按钮,我需要在 ViewModel 中执行一些逻辑,当逻辑完成后,关闭父窗口。 我尝试将 parant 窗口作为命令参数发送,如下所示:

CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"

并在 viewModel 中使用以下代码关闭窗口:

// Create the command
OpenChannelCommand = new RelayCommand(OpenChannel, IsValidFields);
...
private void OpenChannel()
{
  // do some logic...
  CloseWindow();
}
private GalaSoft.MvvmLight.Command.RelayCommand<object> _closeCommand;
private GalaSoft.MvvmLight.Command.RelayCommand<object> CloseWindow()
{
    _closeCommand = new GalaSoft.MvvmLight.Command.RelayCommand<object>((o) => ((Window)o).Close(), (o) => true);
    return _closeCommand;
}

但窗口仍然没有关闭。

编辑:

用户控件 XAML 代码为:

<Button Content="Open Channel" Command="{Binding OpenChannelCommand}" 
                CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>

用户控件ViewModel代码为:

public RelayCommand OpenChannelCommand { get; set; } 
ctor() 
{
   OpenChannelCommand = new RelayCommand(OpenChannel, IsValidFields);
}     
private void OpenChannel() 
{    
   // logic 
   CloseWindow(); 
}
private GalaSoft.MvvmLight.Command.RelayCommand<object> CloseWindow()
{
    _closeCommand = new GalaSoft.MvvmLight.Command.RelayCommand<object>((o) => ((Window)o).Close(), (o) => true);
    return _closeCommand;
 }

这是我目前尝试的完整实施。 当为 CloseWindow 方法设置断点时,它会在视图模式初始化时命中,并且在按钮单击命令中再次调用它之后不会做任何事情。

【问题讨论】:

  • 您是否有理由不能使用事件处理程序来启动 VM 活动,然后在完成后关闭窗口?
  • 首先,感谢您的快速回复。我没看懂你的回答。我应该在哪里创建事件处理程序?在弹出窗口中?它不会破坏 MVVM 模式?
  • 如果你在((Window)o).Close()添加一个断点,它会在那里中断吗?
  • 通常对于这样的事情,我会在 XAML 中的按钮对象上创建一个事件处理程序,然后让它调用你想要的 VM 上的方法,然后关闭窗口。现在,这是否会破坏 MVVM 模式还有待商榷,但我个人认为,让 View 了解 View Model 并不是一种“罪过”(这就是你想要做的)在这里)。
  • 如果您将OpenChannelCommand 链接到按钮,则必须将参数(参考窗口)发送到此命令。您不需要 CloseWindow 成为命令。只是一个以窗口为参数的 ptr 函数。

标签: c# wpf mvvm binding prism


【解决方案1】:

关闭窗口是视图的责任,视图模型应该对此一无所知。难怪你会纠结。如果您在“按钮逻辑”运行时不需要窗口留在屏幕上,那么只需使用 Prism 的CompositeCommand(我不太喜欢它,因为它使用代码隐藏,但没关系)或相当于将两个命令绑定到按钮。另一方面,如果您需要在“按钮逻辑”运行时将窗口保持在屏幕上,例如显示进度,如果您的视图模型反映了这一点,那么您可以将bool IsButtonLogicComplete 属性添加到您的视图模型(不要忘记INotifyPropertyChanged)并通过附加属性将窗口的“关闭”状态绑定到此属性/像这样的行为:

public static class AttachedProperties
{
    // in Visual Studio, the `propa` snippet inserts the boilerplate
    public static DependencyProperty ForceCloseProperty =
        DependencyProperty.RegisterAttached ("ForceClose",
        typeof (bool), typeof (AttachedProperties), new UIPropertyMetadata (false, (d, e) =>
        {
            var w  = d as Window ;
            if (w != null && (bool) e.NewValue)
            {
                w.DialogResult = true ;
                w.Close () ;
            }
        })) ;

    public static bool GetForceClose (DependencyObject obj)
    {
        return (bool) obj.GetValue (ForceCloseProperty) ;
    }

    public static void SetForceClose (DependencyObject obj, bool value)
    {
        obj.SetValue (ForceCloseProperty, value) ;
    }
}

<!-- in .xaml -->
<Window
  xmlns:local="clr-namespace:YourNamespace"
  local:AttachedProperties.ForceClose="{Binding IsButtonLogicComplete}" ...

这将使您的视图和您的视图模型关注点很好地分开。

【讨论】:

  • 嗨 Anton,我正在尝试实现您使用 DependencyProperty 的方法,但我不明白在哪里实现它。我应该在 Window ViewModel 还是在 UserControl ViewModel 中设置此方法?关于 XAML,谁是 ForceClose? Window 应该如何知道它是谁?
  • 将像这样的附加属性放入带有视图(不是视图模型!)代码的某个地方的公共静态类中很方便。 This looks like a good intro 附加属性。
  • 有多种方法可以解决这个问题。除了提出的建议之外,还可以使用 MVVM Dialogs 之类的框架或使用 MVVM Light 中的中介来解决它。关于关闭窗口是否是视图模型的责任,当然可以! MVVM 中没有任何东西可以阻止您打开或关闭对话框,只要您以一种仍可测试的间接方式进行操作。
  • 当然。但是,使用框架,您无法了解 WPF 的基础知识。
  • 重新关闭。在我看来,窗口属于视图,关闭或保持打开完全是视图的业务。不同的(例如移动)视图可能会启动转换而不是关闭窗口,但使用相同的视图模型。我对将视图机制与视图模型分开是相当教条的,这种方法对我有用; YMMV。至于从视图模型打开对话框,我可以看到,对于必须向用户询问紧急需要的信息(例如凭据)的模态对话框,但正如您所说,这些服务通常是在没有直接引用视图内容的情况下注入的。
猜你喜欢
  • 1970-01-01
  • 2015-05-13
  • 2012-11-17
  • 2013-10-09
  • 1970-01-01
  • 2023-03-30
  • 2020-09-04
  • 2018-08-02
  • 2012-06-18
相关资源
最近更新 更多