ViewModel应如何关闭表单?

时间:2009-02-02 00:17:14

标签: c# wpf mvvm

我正在尝试学习WPF和MVVM问题,但是遇到了麻烦。 这个问题类似但不完全相同as this one (handling-dialogs-in-wpf-with-mvvm) ......

我有一个使用MVVM模式编写的“登录”表单。

此表单包含一个ViewModel,其中包含用户名和密码,这些用户名和密码使用普通数据绑定绑定到XAML中的视图。 它还有一个“登录”命令,该命令绑定到表单上的“登录”按钮,使用常规数据绑定。

当“Login”命令触发时,它会调用ViewModel中的一个函数,该函数将关闭并通过网络发送数据以进行登录。当此函数完成时,有2个操作:

  1. 登录无效 - 我们只显示一个MessageBox,一切正常

  2. 登录有效,我们需要关闭“登录”表单并将其返回为DialogResult ...

  3. 问题是,ViewModel对实际视图一无所知,那么如何关闭视图并告诉它返回特定的DialogResult?我可以在CodeBehind中粘贴一些代码,和/或将View传递给ViewModel,但这似乎会完全击败MVVM的整个点......


    更新

    最后,我刚刚违反了MVVM模式的“纯度”,并让View发布了Closed事件,并公开了Close方法。然后,ViewModel将调用view.Close。该视图仅通过接口已知并通过IOC容器连接,因此不会丢失可测试性或可维护性。

    接受的答案是-5票,这似乎很愚蠢!虽然我很清楚通过在“纯粹”时解决问题所获得的良好感受,当然我不是唯一一个认为200行事件,命令和行为只是为了避免单线方法的人“模式”和“纯度”的名称有点荒谬......

26 个答案:

答案 0 :(得分:311)

答案 1 :(得分:64)

从我的角度来看,问题非常好,因为同样的方法不仅用于“登录”窗口,而且用于任何类型的窗口。我已经回顾了很多建议,没有一个对我来说没问题。请查看我从MVVM design pattern article

中提取的建议

每个ViewModel类都应继承WorkspaceViewModel具有RequestClose类型的CloseCommand事件和ICommand属性的CloseCommandRequestClose属性的默认实现将引发OnLoaded事件。

为了关闭窗口,应该覆盖窗口的void CustomerWindow_Loaded(object sender, RoutedEventArgs e) { CustomerViewModel customer = CustomerViewModel.GetYourCustomer(); DataContext = customer; customer.RequestClose += () => { Close(); }; } 方法:

OnStartup
您应用的

protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); MainWindow window = new MainWindow(); var viewModel = new MainWindowViewModel(); viewModel.RequestClose += window.Close; window.DataContext = viewModel; window.Show(); } 方法:

RequestClose

我想CloseCommand中的WorkspaceViewModel事件和public abstract class WorkspaceViewModel : ViewModelBase // There's nothing interesting in ViewModelBase as it only implements the INotifyPropertyChanged interface { RelayCommand _closeCommand; public ICommand CloseCommand { get { if (_closeCommand == null) { _closeCommand = new RelayCommand( param => Close(), param => CanClose() ); } return _closeCommand; } } public event Action RequestClose; public virtual void Close() { if ( RequestClose != null ) { RequestClose(); } } public virtual bool CanClose() { return true; } } 属性实现非常清楚,但我会证明它们是一致的:

RelayCommand

public class RelayCommand : ICommand { #region Constructors public RelayCommand(Action<object> execute, Predicate<object> canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } #endregion // Constructors #region ICommand Members [DebuggerStepThrough] public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute(parameter); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameter) { _execute(parameter); } #endregion // ICommand Members #region Fields readonly Action<object> _execute; readonly Predicate<object> _canExecute; #endregion // Fields } 的源代码:

{{1}}

P.S。不要因为这些来源对我不好!如果我昨天有他们,那将节省我几个小时......

P.P.S。欢迎任何意见或建议。

答案 2 :(得分:18)

我使用附加行为来关闭窗口。 将ViewModel上的“signal”属性绑定到附加行为(我实际上使用了一个触发器) 当它设置为true时,该行为将关闭窗口。

http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/

答案 3 :(得分:15)

有很多评论在这里争论MVVM的优点和缺点。对我来说,我同意Nir;这是一个适当使用模式的问题,MVVM并不总是适合。人们似乎已经愿意牺牲所有最重要的软件设计原则,以使其适合MVVM。

那就是说..我认为你的情况可能适合一点点重构。

在大多数情况下,我遇到过,WPF使您无需多个Window。也许您可以尝试使用FramePage代替Windows DialogResult s。

在您的情况下,我的建议是LoginFormViewModel处理LoginCommand,如果登录无效,请将LoginFormViewModel上的属性设置为适当的值(false或一些枚举值,如UserAuthenticationStates.FailedAuthentication)。为成功登录(true或其他枚举值),您也会这样做。然后,您可以使用DataTrigger响应各种用户身份验证状态,并使用简单的Setter来更改Source的{​​{1}}属性。

让您的登录窗口返回Frame我认为是您感到困惑的地方; DialogResult实际上是ViewModel的属性。在我看来,WPF的经验有限,当一些事情感觉不对时,通常是因为我在考虑如何在WinForms中做同样的事情。

希望有所帮助。

答案 4 :(得分:9)

假设您的登录对话框是第一个创建的窗口,请在LoginViewModel类中尝试:

    void OnLoginResponse(bool loginSucceded)
    {
        if (loginSucceded)
        {
            Window1 window = new Window1() { DataContext = new MainWindowViewModel() };
            window.Show();

            App.Current.MainWindow.Close();
            App.Current.MainWindow = window;
        }
        else
        {
            LoginError = true;
        }
    }

答案 5 :(得分:6)

我处理它的方法是在我的ViewModel中添加一个事件处理程序。当用户成功登录后,我将触发该事件。在我的视图中,我会附加到此事件,当它被解雇时,我会关闭窗口。

答案 6 :(得分:5)

这是一个简单而干净的解决方案 - 您可以向ViewModel添加一个事件,并指示Window在触发该事件时自行关闭。

有关详细信息,请参阅我的博客帖子Close window from ViewModel

答案 7 :(得分:4)

这是我最初做的,哪些确实有效,但它似乎相当啰嗦和丑陋(全局静态任何东西永远都不好)

1:App.xaml.cs

public partial class App : Application
{
    // create a new global custom WPF Command
    public static readonly RoutedUICommand LoggedIn = new RoutedUICommand();
}

2:LoginForm.xaml

// bind the global command to a local eventhandler
<CommandBinding Command="client:App.LoggedIn" Executed="OnLoggedIn" />

3:LoginForm.xaml.cs

// implement the local eventhandler in codebehind
private void OnLoggedIn( object sender, ExecutedRoutedEventArgs e )
{
    DialogResult = true;
    Close();
}

4:LoginFormViewModel.cs

// fire the global command from the viewmodel
private void OnRemoteServerReturnedSuccess()
{
    App.LoggedIn.Execute(this, null);
}

我后来删除了所有这些代码,只是让LoginFormViewModel在它的视图上调用了Close方法。它最终变得更好,更容易遵循。恕我直言,模式的重点是让人们更容易理解你的应用正在做什么,在这种情况下,MVVM让它更难理解,比如果我没有使用它,现在是 -pattern。

答案 8 :(得分:3)

仅供参考,我遇到了同样的问题,我想我想出了一个不需要全局或静态的解决方案,尽管它可能不是最好的答案。我让你们自己决定。

在我的例子中,实例化要显示的Window的ViewModel(让我们称之为ViewModelMain)也知道LoginFormViewModel(以上面的情况为例)。

所以我做的是在LoginFormViewModel上创建一个类型为ICommand的属性(让我们称之为CloseWindowCommand)。然后,在我在Window上调用.ShowDialog()之前,我将LoginFormViewModel上的CloseWindowCommand属性设置为我实例化的Window.Close()方法。然后在LoginFormViewModel里面我只需要调用CloseWindowCommand.Execute()来关闭窗口。

我认为这是一种解决方法/黑客攻击,但它在没有真正打破MVVM模式的情况下运行良好。

随意批评这个过程,我可以接受它! :)

答案 9 :(得分:3)

这可能已经很晚了,但我遇到了同样的问题,我发现了一个适合我的解决方案。

我无法弄清楚如何在没有对话框的情况下创建应用程序(可能只是一个心灵障碍)。所以我与MVVM陷入僵局并展示了一个对话框。所以我遇到了这篇CodeProject文章:

http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

这是一个UserControl,它基本上允许窗口在另一个窗口的可视树中(xaml中不允许)。它还公开了一个名为IsShowing的布尔值DependencyPropert。

你可以设置一个样式,通常在resourcedictionary中,当控件的Content属性通过触发器时,基本上显示对话框!= null:

<Style TargetType="{x:Type d:Dialog}">
    <Style.Triggers>
        <Trigger Property="HasContent"  Value="True">
            <Setter Property="Showing" Value="True" />
        </Trigger>
    </Style.Triggers>
</Style>

在要显示对话框的视图中,只需输入:

<d:Dialog Content="{Binding Path=DialogViewModel}"/>

在你的ViewModel中你所要做的就是将属性设置为一个值(注意:ViewModel类必须支持INotifyPropertyChanged才能让视图知道发生的事情。)

像这样:

DialogViewModel = new DisplayViewModel();

要将ViewModel与View匹配,您应该在resourcedictionary中使用这样的内容:

<DataTemplate DataType="{x:Type vm:DisplayViewModel}">
    <vw:DisplayView/>
</DataTemplate>

通过所有这些,您可以获得一个单行代码来显示对话框。你得到的问题是你不能只用上面的代码关闭对话框。这就是为什么你必须在一个ViewModel基类中放入一个事件,DisplayViewModel继承自上面的代码,而不是上面的代码,写这个

        var vm = new DisplayViewModel();
        vm.RequestClose += new RequestCloseHandler(DisplayViewModel_RequestClose);
        DialogViewModel = vm;

然后您可以通过回调来处理对话框的结果。

这可能看起来有点复杂,但一旦奠定了基础,它就非常简单。这又是我的实现,我相信还有其他的:)

希望这有帮助,它救了我。

答案 10 :(得分:3)

public partial class MyWindow: Window
{
    public ApplicationSelection()
    {
      InitializeComponent();

      MyViewModel viewModel = new MyViewModel();

      DataContext = viewModel;

      viewModel.RequestClose += () => { Close(); };

    }
}

public class MyViewModel
{

  //...Your code...

  public event Action RequestClose;

  public virtual void Close()
  {
    if (RequestClose != null)
    {
      RequestClose();
    }
  }

  public void SomeFunction()
  {
     //...Do something...
     Close();
  }
}

答案 11 :(得分:3)

好的,所以这个问题将近6岁,我仍然无法在这里找到我认为这是正确的答案,所以请允许我分享我的“2美分”......

我实际上有两种方法可以做到,第一种方法是简单的方式......第二种是右方方式,所以如果你正在寻找合适的方式,只需跳过#1并跳转到#2

1。快速简便(但不完整)

如果我只有一个小项目,我有时只在ViewModel中创建一个 CloseWindowAction

        public Action CloseWindow { get; set; } // In MyViewModel.cs

无论谁创建了View,或者在View后面的代码中,我只需设置Action将调用的方法:

(记住MVVM是关于View和ViewModel的分离...... View的代码behins仍然是View,只要有正确的分离,你就不会违反模式)

如果某些ViewModel创建了一个新窗口:

private void CreateNewView()
{
    MyView window = new MyView();
    window.DataContext = new MyViewModel
                             {
                                 CloseWindow = window.Close,
                             }; 
    window.ShowDialog();
}

或者如果你想在主窗口中使用它,只需将它放在View的构造函数下:

public MyView()
{
    InitializeComponent();           
    this.DataContext = new MainViewModel
                           {
                                CloseWindow = this.Close
                           };
}

如果要关闭窗口,只需在ViewModel上调用Action。

2。正确的方式

现在正确的做法是使用 Prism (恕我直言),所有相关内容都可以found here

您可以制作交互请求,使用新窗口中需要的任何数据填充它,午餐,关闭它甚至接收数据。所有这些封装和MVVM批准。您甚至获取窗口关闭状态的状态,例如用户CanceledAccepted(确定按钮)窗口,如果您需要,则返回数据即可。它有点复杂,答案#1,但它更完整,是微软推荐的模式。

我给出的链接包含所有代码片段和示例,所以我不打算在这里放置任何代码,只需阅读Prism Quick Start的下载文章并运行它,只需要一点点就可以了解它更加冗长以使其发挥作用,但其好处远远大于关闭窗口。

答案 12 :(得分:2)

您可以让ViewModel公开View注册的事件。然后,当ViewModel决定关闭视图的时间时,它会触发导致视图关闭的事件。如果您希望传回特定的结果值,那么您将在ViewModel中拥有一个属性。

答案 13 :(得分:0)

行为是这里最方便的方式。

  • 一方面,它可以绑定到给定的viewmodel(可以 信号“关闭表格!”)

  • 另一方面,它可以访问表单本身,因此可以订阅必要的表单特定事件,或显示确认对话框或其他任何内容。

第一次看到写作必要的行为很无聊。但是,从现在开始,您可以通过精确的单线程XAML片段在所需的每个表单上重复使用它。如有必要,您可以将其作为单独的程序集提取,以便它可以包含在您想要的任何下一个项目中。

答案 14 :(得分:0)

Dependency Property /任意View(或您要关闭的UserControl)中创建 Window 。如下所示:

 public bool CloseTrigger
        {
            get { return (bool)GetValue(CloseTriggerProperty); }
            set { SetValue(CloseTriggerProperty, value); }
        }

        public static readonly DependencyProperty CloseTriggerProperty =
            DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(ControlEventBase), new PropertyMetadata(new PropertyChangedCallback(OnCloseTriggerChanged)));

        private static void OnCloseTriggerChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
        {
            //write Window Exit Code
        }

并从 ViewModel的属性中绑定

<Window x:Class="WpfStackOverflowTempProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"  Width="525"
        CloseTrigger="{Binding Path=CloseWindow,Mode=TwoWay}"

属性 VeiwModel

private bool closeWindow;

    public bool CloseWindow
    {
        get { return closeWindow; }
        set 
        { 
            closeWindow = value;
            RaiseChane("CloseWindow");
        }
    }

现在,通过更改ViewModel中的 CloseWindow 值来触发关闭操作。 :)

答案 15 :(得分:0)

这是一个简单的无错解决方案(带源代码),它对我有用。

  1. INotifyPropertyChanged

  2. 派生您的ViewModel
  3. 在ViewModel中创建一个可观察的属性 CloseDialog

    public void Execute()
    {
        // Do your task here
    
        // if task successful, assign true to CloseDialog
        CloseDialog = true;
    }
    
    private bool _closeDialog;
    public bool CloseDialog
    {
        get { return _closeDialog; }
        set { _closeDialog = value; OnPropertyChanged(); }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    private void OnPropertyChanged([CallerMemberName]string property = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
    

    }

  4. 在视图中附加处理程序以进行此属性更改

        _loginDialogViewModel = new LoginDialogViewModel();
        loginPanel.DataContext = _loginDialogViewModel;
        _loginDialogViewModel.PropertyChanged += OnPropertyChanged;
    
  5. 现在你差不多完成了。在事件处理程序中生成DialogResult = true

    protected void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        if (args.PropertyName == "CloseDialog")
        {
            DialogResult = true;
        }
    }
    

答案 16 :(得分:0)

虽然这没有回答如何通过viewmodel执行此操作的问题,但这确实显示了如何仅使用XAML +混合SDK来执行此操作。

我选择下载并使用Blend SDK中的两个文件,您可以通过NuGet将这两个文件作为Microsoft的软件包。文件是:

System.Windows.Interactivity.dll和Microsoft.Expression.Interactions.dll

Microsoft.Expression.Interactions.dll为您提供了很好的功能,例如能够在您的viewmodel或其他目标上设置属性或调用方法,并且还有其他小部件。

一些XAML:

<Window x:Class="Blah.Blah.MyWindow"
    ...
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
  ...>
 <StackPanel>
    <Button x:Name="OKButton" Content="OK">
       <i:Interaction.Triggers>
          <i:EventTrigger EventName="Click">
             <ei:ChangePropertyAction
                      TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                      PropertyName="DialogResult"
                      Value="True"
                      IsEnabled="{Binding SomeBoolOnTheVM}" />                                
          </i:EventTrigger>
    </Button>
    <Button x:Name="CancelButton" Content="Cancel">
       <i:Interaction.Triggers>
          <i:EventTrigger EventName="Click">
             <ei:ChangePropertyAction
                      TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                      PropertyName="DialogResult"
                      Value="False" />                                
          </i:EventTrigger>
    </Button>

    <Button x:Name="CloseButton" Content="Close">
       <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <!-- method being invoked should be void w/ no args -->
                    <ei:CallMethodAction
                        TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                        MethodName="Close" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
    </Button>
 <StackPanel>
</Window>

请注意,如果您只是简单的确定/取消行为,只要窗口显示为Window.ShowDialog(),您就可以使用IsDefault和IsCancel属性。 我个人遇到问题,有一个IsDefault属性设置为true的按钮,但是在加载页面时它被隐藏了。它显示后似乎不想很好地播放,所以我只是设置了如上所示的Window.DialogResult属性,它适用于我。

答案 17 :(得分:0)

我已经阅读了所有的答案,但我必须说,其中大多数都不够好甚至更糟。

您可以使用 DialogService 类来处理此问题,该类负责显示对话框窗口并返回对话框结果。我创建了sample project来证明它的实现和使用。

这里是最重要的部分:

//we will call this interface in our viewmodels
public interface IDialogService
{
    bool? ShowDialog(object dialogViewModel, string caption);
}

//we need to display logindialog from mainwindow
public class MainWindowViewModel : ViewModelBase
{
    public string Message {get; set;}
    public void ShowLoginCommandExecute()
    {
        var loginViewModel = new LoginViewModel();
        var dialogResult = this.DialogService.ShowDialog(loginViewModel, "Please, log in");

        //after dialog is closed, do someting
        if (dialogResult == true && loginViewModel.IsLoginSuccessful)
        {
            this.Message = string.Format("Hello, {0}!", loginViewModel.Username);
        }
    }
}


public class DialogService : IDialogService
{
    public bool? ShowDialog(object dialogViewModel, string caption)
    {
        var contentView = ViewLocator.GetView(dialogViewModel);
        var dlg = new DialogWindow
        {
            Title = caption
        };
        dlg.PART_ContentControl.Content = contentView;

        return dlg.ShowDialog();
    }
}

这不是更简单吗?比EventAggregator或其他类似解决方案更简洁,更易读,最后但更不容易调试?

正如您所看到的,在我的视图模型中,我已经使用了我在帖子中描述的ViewModel第一种方法:Best practice for calling View from ViewModel in WPF

当然,在现实世界中,DialogService.ShowDialog必须有更多选项来配置对话框,例如他们应该执行的按钮和命令。有不同的方式这样做,但它超出了范围:)

答案 18 :(得分:0)

我会这样:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;    
using GalaSoft.MvvmLight.Messaging; 

// View

public partial class TestCloseWindow : Window
{
    public TestCloseWindow() {
        InitializeComponent();
        Messenger.Default.Register<CloseWindowMsg>(this, (msg) => Close());
    }
}

// View Model

public class MainViewModel: ViewModelBase
{
    ICommand _closeChildWindowCommand;

    public ICommand CloseChildWindowCommand {
        get {
            return _closeChildWindowCommand?? (_closeChildWindowCommand = new RelayCommand(() => {
                Messenger.Default.Send(new CloseWindowMsg());
        }));
        }
    }
}

public class CloseWindowMsg
{
}

答案 19 :(得分:0)

我最终混合了Joe White's answerAdam Mills's answer中的一些代码,因为我需要在编程创建的窗口中显示用户控件。所以DialogCloser不需要在窗口上,它可以在用户控件本身上

<UserControl ...
    xmlns:xw="clr-namespace:Wpf"
    xw:DialogCloser.DialogResult="{Binding DialogResult}">

如果用户控件没有附加到窗口本身,DialogCloser会找到它的窗口。

namespace Wpf
{
  public static class DialogCloser
  {
    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached(
            "DialogResult",
            typeof(bool?),
            typeof(DialogCloser),
            new PropertyMetadata(DialogResultChanged));

    private static void DialogResultChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
      var window = d.GetWindow();
      if (window != null)
        window.DialogResult = e.NewValue as bool?;
    }

    public static void SetDialogResult(DependencyObject target, bool? value)
    {
      target.SetValue(DialogResultProperty, value);
    }
  }

  public static class Extensions
  {
    public static Window GetWindow(this DependencyObject sender_)
    {
      Window window = sender_ as Window;        
      return window ?? Window.GetWindow( sender_ );
    }
  }
}

答案 20 :(得分:0)

我实施了Joe White的解决方案,但偶尔遇到问题&#34; 只有在创建Window并显示为对话框&#34;后才能设置DialogResult。错误。

我在View关闭后保持ViewModel,偶尔我后来使用相同的VM打开了一个新的View。看来在旧View被垃圾收集之前关闭新视图会导致 DialogResultChanged 尝试在关闭的窗口上设置 DialogResult 属性,从而引发错误。

我的解决方法是更改​​ DialogResultChanged 以检查窗口的 IsLoaded 属性:

private static void DialogResultChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
{
    var window = d as Window;
    if (window != null && window.IsLoaded)
        window.DialogResult = e.NewValue as bool?;
}

进行此更改后,将忽略对已关闭对话框的任何附件。

答案 21 :(得分:0)

另一个解决方案是在View Model中使用INotifyPropertyChanged创建属性,如DialogResult,然后在Code Behind中写下这个:

public class SomeWindow: ChildWindow
{
    private SomeViewModel _someViewModel;

    public SomeWindow()
    {
        InitializeComponent();

        this.Loaded += SomeWindow_Loaded;
        this.Closed += SomeWindow_Closed;
    }

    void SomeWindow_Loaded(object sender, RoutedEventArgs e)
    {
        _someViewModel = this.DataContext as SomeViewModel;
        _someViewModel.PropertyChanged += _someViewModel_PropertyChanged;
    }

    void SomeWindow_Closed(object sender, System.EventArgs e)
    {
        _someViewModel.PropertyChanged -= _someViewModel_PropertyChanged;
        this.Loaded -= SomeWindow_Loaded;
        this.Closed -= SomeWindow_Closed;
    }

    void _someViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == SomeViewModel.DialogResultPropertyName)
        {
            this.DialogResult = _someViewModel.DialogResult;
        }
    }
}

最重要的片段是_someViewModel_PropertyChangedDialogResultPropertyName可以是SomeViewModel中的一些公共const字符串。

如果在ViewModel中很难做到这一点,我会使用这种技巧在View Controls中进行一些更改。在ViewModel中的OnPropertyChanged,您可以在View中执行任何操作。 ViewModel仍然是“单元可测试的”,代码中的一些小代码行没有区别。

答案 22 :(得分:0)

为了增加大量的答案,我想添加以下内容。假设您的ViewModel上有一个ICommand,并且您希望该命令关闭其窗口(或任何其他操作),您可以使用以下内容。

var windows = Application.Current.Windows;
for (var i=0;i< windows.Count;i++ )
    if (windows[i].DataContext == this)
        windows[i].Close();

它并不完美,可能很难测试(因为很难模拟/存根静态)但它比其他解决方案更清晰(恕我直言)。

埃里克

答案 23 :(得分:0)

为什么不将窗口作为命令参数传递?

C#:

 private void Cancel( Window window )
  {
     window.Close();
  }

  private ICommand _cancelCommand;
  public ICommand CancelCommand
  {
     get
     {
        return _cancelCommand ?? ( _cancelCommand = new Command.RelayCommand<Window>(
                                                      ( window ) => Cancel( window ),
                                                      ( window ) => ( true ) ) );
     }
  }

XAML:

<Window x:Class="WPFRunApp.MainWindow"
        x:Name="_runWindow"
...
   <Button Content="Cancel"
           Command="{Binding Path=CancelCommand}"
           CommandParameter="{Binding ElementName=_runWindow}" />

答案 24 :(得分:-1)

您需要关闭窗口,只需将其放在viewmodel中:

当当

  foreach (Window window in Application.Current.Windows)
        {
            if (window.DataContext == this)
            {
                window.Close();
                return;
            }
        }

答案 25 :(得分:-10)

Application.Current.MainWindow.Close() 

太多了!