从ViewModel关闭窗口


95

我使用创建了一个Login,window control以允许用户登录到WPF我正在创建的应用程序。

到目前为止,我已经创建了一个检查用户是否已经在为正确的凭据进入了一个方法username,并passwordtextbox登录屏幕,上binding2 properties

我是通过创建一个bool像这样的方法来实现的。

public bool CheckLogin()
{
    var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();

    if (user == null)
    {
        MessageBox.Show("Unable to Login, incorrect credentials.");
        return false;
    }
    else if (this.Username == user.Username || this.Password.ToString() == user.Password)
    {
        MessageBox.Show("Welcome " + user.Username + ", you have successfully logged in.");

        return true;
    }
    else
    {
        MessageBox.Show("Unable to Login, incorrect credentials.");
        return false;
    }
}

public ICommand ShowLoginCommand
{
    get
    {
        if (this.showLoginCommand == null)
        {
            this.showLoginCommand = new RelayCommand(this.LoginExecute, null);
        }
        return this.showLoginCommand;
    }
}

private void LoginExecute()
{
    this.CheckLogin();
} 

我也有一个commandbind对我的按钮之类的xaml东西;

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />

当我输入用户名和密码时,它会执行适当的代码,无论是对还是错。但是,当用户名和密码均正确时,如何从ViewModel中关闭此窗口?

我以前尝试过使用a,dialog modal但效果不佳。此外,在我的app.xaml中,我做了如下操作,首先加载登录页面,然后为true,则加载实际的应用程序。

private void ApplicationStart(object sender, StartupEventArgs e)
{
    Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;

    var dialog = new UserView();

    if (dialog.ShowDialog() == true)
    {
        var mainWindow = new MainWindow();
        Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
        Current.MainWindow = mainWindow;
        mainWindow.Show();
    }
    else
    {
        MessageBox.Show("Unable to load application.", "Error", MessageBoxButton.OK);
        Current.Shutdown(-1);
    }
}

问题:如何Window control从ViewModel 关闭登录名?

提前致谢。


Answers:


149

您可以使用将该窗口传递给ViewModel CommandParameter。请参阅下面的示例。

我已经实现了一个CloseWindow方法,该方法将Windows作为参数并将其关闭。窗口通过传递给ViewModel CommandParameter。请注意,您需要为x:Name应该关闭的窗口定义一个。在我的XAML窗口中,我通过调用此方法,Command并使用将窗口本身作为参数传递给ViewModel CommandParameter

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

视图模型

public RelayCommand<Window> CloseWindowCommand { get; private set; }

public MainViewModel()
{
    this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow);
}

private void CloseWindow(Window window)
{
    if (window != null)
    {
       window.Close();
    }
}

视图

<Window x:Class="ClientLibTestTool.ErrorView"
        x:Name="TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:localization="clr-namespace:ClientLibTestTool.ViewLanguages"
        DataContext="{Binding Main, Source={StaticResource Locator}}"
        Title="{x:Static localization:localization.HeaderErrorView}"
        Height="600" Width="800"
        ResizeMode="NoResize"
        WindowStartupLocation="CenterScreen">
    <Grid> 
        <Button Content="{x:Static localization:localization.ButtonClose}" 
                Height="30" 
                Width="100" 
                Margin="0,0,10,10" 
                IsCancel="True" 
                VerticalAlignment="Bottom" 
                HorizontalAlignment="Right" 
                Command="{Binding CloseWindowCommand, Mode=OneWay}" 
                CommandParameter="{Binding ElementName=TestWindow}"/>
    </Grid>
</Window>

请注意,我使用的是MVVM light框架,但是该原理适用于每个wpf应用程序。

该解决方案违反了MVVM模式,因为视图模型不应该了解有关UI实现的任何信息。如果要严格遵循MVVM编程范例,则必须使用接口抽象视图的类型。

MVVM符合解决方案(以前的EDIT2)

用户Crono在评论部分提到了一个有效的观点:

将Window对象传递给视图模型会破坏MVVM模式恕我直言,因为它迫使您的vm知道正在查看的内容。

您可以通过引入包含close方法的接口来解决此问题。

接口:

public interface ICloseable
{
    void Close();
}

重构的ViewModel将如下所示:

视图模型

public RelayCommand<ICloseable> CloseWindowCommand { get; private set; }

public MainViewModel()
{
    this.CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow);
}

private void CloseWindow(ICloseable window)
{
    if (window != null)
    {
        window.Close();
    }
}

您必须引用并实现ICloseable视图中的接口

查看(后面的代码)

public partial class MainWindow : Window, ICloseable
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

回答原始问题:以前是EDIT1)

您的登录按钮(添加了CommandParameter):

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>

您的代码:

 public RelayCommand<Window> CloseWindowCommand { get; private set; } // the <Window> is important for your solution!

 public MainViewModel() 
 {
     //initialize the CloseWindowCommand. Again, mind the <Window>
     //you don't have to do this in your constructor but it is good practice, thought
     this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow);
 }

 public bool CheckLogin(Window loginWindow) //Added loginWindow Parameter
 {
    var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();

    if (user == null)
    {
        MessageBox.Show("Unable to Login, incorrect credentials.");
        return false;
    }
    else if (this.Username == user.Username || this.Password.ToString() == user.Password)
    {
        MessageBox.Show("Welcome "+ user.Username + ", you have successfully logged in.");
        this.CloseWindow(loginWindow); //Added call to CloseWindow Method
        return true;
    }
    else
    {
        MessageBox.Show("Unable to Login, incorrect credentials.");
        return false;
    }
 }

 //Added CloseWindow Method
 private void CloseWindow(Window window)
 {
     if (window != null)
     {
         window.Close();
     }
 }

1
感谢更新@Joel。最后一个问题,由于该方法接受Window的参数,并且当我在命令中调用该方法时,它期望有一个参数,我是否会创建一个为该方法调用的本地Window参数,例如;private void LoginExecute(){this.CheckLogin();}<-CheckLogin需要接受一个参数。
WPFNoob

抱歉,我不明白,您能否澄清您的问题?
乔尔

14
如果您不喜欢为窗口命名,则也可以这样绑定参数:CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Jacco Dieleman 2014年

33
Window对象传递给视图模型会破坏MVVM模式IMHO,因为它会迫使您的vm知道正在查看的对象。如果视图是MDI界面中的停靠选项卡,那该怎么办?进行此IMHO的正确方法是传递某种IUIHost接口,该接口实现Close方法,并具有想要显示vm实现该视图的任何视图。
克罗诺2015年

2
可以,因为该接口隐藏了ViewModel的具体实现。ViewModel除了实现Close()方法外,对视图一无所知。因此,视图可以是任何东西:WPF窗口,WinForms窗体,UWP应用程序甚至WPF网格。它将视图与视图模型分离。
乔尔(Joel)

34

保持MVVM,我认为使用Blend SDK的Behaviors(System.Windows.Interactivity)或Prism的自定义交互请求都可以很好地解决这种情况。

如果采用“行为”路线,则应遵循以下一般思路:

public class CloseWindowBehavior : Behavior<Window>
{
    public bool CloseTrigger
    {
        get { return (bool)GetValue(CloseTriggerProperty); }
        set { SetValue(CloseTriggerProperty, value); }
    }

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

    private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behavior = d as CloseWindowBehavior;

        if (behavior != null)
        {
            behavior.OnCloseTriggerChanged();
        }
    }

    private void OnCloseTriggerChanged()
    {
        // when closetrigger is true, close the window
        if (this.CloseTrigger)
        {
            this.AssociatedObject.Close();
        }
    }
}

然后在窗口中,您只需将CloseTrigger绑定到一个布尔值,该值将在您希望窗口关闭时设置。

<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:local="clr-namespace:TestApp"
        Title="MainWindow" Height="350" Width="525">
    <i:Interaction.Behaviors>
        <local:CloseWindowBehavior CloseTrigger="{Binding CloseTrigger}" />
    </i:Interaction.Behaviors>

    <Grid>

    </Grid>
</Window>

最后,您的DataContext / ViewModel将具有一个您希望在窗口关闭时设置的属性,如下所示:

public class MainWindowViewModel : INotifyPropertyChanged
{
    private bool closeTrigger;

    /// <summary>
    /// Gets or Sets if the main window should be closed
    /// </summary>
    public bool CloseTrigger
    {
        get { return this.closeTrigger; }
        set
        {
            this.closeTrigger = value;
            RaisePropertyChanged(nameof(CloseTrigger));
        }
    }

    public MainWindowViewModel()
    {
        // just setting for example, close the window
        CloseTrigger = true;
    }

    protected void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

(设置您的Window.DataContext = new MainWindowViewModel())


感谢@Steve的答复,您提到了将CloseTrigger绑定到boolean值。当您这么说时,您是说我DataTrigger要实现这一目标吗?
WPFNoob

抱歉,我应该更加明确-我的视图模型上有一个属性(在上面的示例中,该属性称为CloseTrigger)将被设置为true,最终将触发该行为。我更新了答案
Steve Van Treeck

这可行,但是我必须更改应用程序的加载方式。因为我在主要应用程序中使用的是Window,所以它也会杀死所有子窗口。谢谢。
WPFNoob

将属性设置为true以执行操作很麻烦IMO。
Josh Noe

33

通常,我需要在视图模型上放置一个事件,然后将其Window.Close()绑定到将视图模型绑定到窗口上时

public class LoginViewModel
{
    public event EventHandler OnRequestClose;

    private void Login()
    {
        // Login logic here
        OnRequestClose(this, new EventArgs());
    }
}

并且在创建登录窗口时

var vm = new LoginViewModel();
var loginWindow = new LoginWindow
{
    DataContext = vm
};
vm.OnRequestClose += (s, e) => loginWindow.Close();

loginWindow.ShowDialog(); 

11
匿名代表会很快被编写,但值得注意的是,该事件无法取消注册(可能是问题也可能不是问题)。通常,最好使用成熟的事件处理程序。
Mathieu Guindon 2013年

我最喜欢这个。无论如何,在显示窗口时(例如LoadedContentRendered用于主窗口,对话框服务等)都很难避免进行特殊处理,通过ViewModel事件向其中添加一点对我来说是很干净的。3行代码实际上并不需要任何可重用性解决方案。PS:纯粹的MVVM还是适合书呆子。
Sinatr 2015年

男孩,这帮助了我。
Dimitri

这比公认的答案要好得多,因为它不会破坏MVVM模式。
惊吓

22

可能已经晚了,但这是我的答案

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

1
为什么这不是实际答案?
user2529011

1
@ user2529011至少有些人会抱怨说,viewmodel应该对Application.Current.Windows
一无所知

-1。视图模型应该对视图一无所知。您也可以为此在后面的代码中编写它。
亚历杭德罗

13

好吧,这是我在多个项目中使用的东西。它可能看起来像hack,但效果很好。

public class AttachedProperties : DependencyObject //adds a bindable DialogResult to window
{
    public static readonly DependencyProperty DialogResultProperty = 
        DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(AttachedProperties), 
        new PropertyMetaData(default(bool?), OnDialogResultChanged));

    public bool? DialogResult
    {
        get { return (bool?)GetValue(DialogResultProperty); }
        set { SetValue(DialogResultProperty, value); }
    }

    private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var window = d as Window;
        if (window == null)
            return;

        window.DialogResult = (bool?)e.NewValue;
    }
}

现在,您可以绑定DialogResult到VM并设置其属性值。在Window将关闭,当值设置。

<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult -->
<Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />

这是生产环境中正在运行的内容的摘要

<Window x:Class="AC.Frontend.Controls.DialogControl.Dialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:DialogControl="clr-namespace:AC.Frontend.Controls.DialogControl" 
        xmlns:hlp="clr-namespace:AC.Frontend.Helper"
        MinHeight="150" MinWidth="300" ResizeMode="NoResize" SizeToContent="WidthAndHeight"
        WindowStartupLocation="CenterScreen" Title="{Binding Title}"
        hlp:AttachedProperties.DialogResult="{Binding DialogResult}" WindowStyle="ToolWindow" ShowInTaskbar="True"
        Language="{Binding UiCulture, Source={StaticResource Strings}}">
        <!-- A lot more stuff here -->
</Window>

如您所见,我xmlns:hlp="clr-namespace:AC.Frontend.Helper"先声明命名空间,然后再声明绑定hlp:AttachedProperties.DialogResult="{Binding DialogResult}"

AttachedProperty看起来像这样。这与我昨天发布的内容不同,但是恕我直言,它应该没有任何作用。

public class AttachedProperties
{
    #region DialogResult

    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged));

    private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var wnd = d as Window;
        if (wnd == null)
            return;

        wnd.DialogResult = (bool?) e.NewValue;
    }

    public static bool? GetDialogResult(DependencyObject dp)
    {
        if (dp == null) throw new ArgumentNullException("dp");

        return (bool?)dp.GetValue(DialogResultProperty);
    }

    public static void SetDialogResult(DependencyObject dp, object value)
    {
        if (dp == null) throw new ArgumentNullException("dp");

        dp.SetValue(DialogResultProperty, value);
    }

    #endregion
}

不,这不是一个愚蠢的问题。就像<Window />我在片段中说明的那样,只需将绑定的声明放入元素中即可。我懒得写其余的(命名空间声明等),通常也在那里声明。
DHN 2013年

1
请参考我的编辑。我发布了生产代码,因此我确定它可以正常工作。看起来有些不同,但是我昨天发布的代码也应该起作用。
DHN

感谢您清理。原来,我在调用错误的名称空间:S。我是否只需要创建一个datatrigger并将其分配给按钮即可使其正常工作?再次对nooby问题感到抱歉。
WPFNoob

谢谢-好吧,我只是意识到自己问了太多问题,这些问题看似愚蠢而愚蠢,浪费了人们的时间!但是回到我的问题。在您提到的所有内容之后,如何关闭窗口?使用DataTrigger¬ and setting value 真实的吗?
WPFNoob

1
好吧,这就是我要离开的部分。; o)考虑DataContextDialog。我希望VM设置为DataContext提供一个命令,该命令设置属性DialogResult或您绑定到true或的任何内容false,以便Dialog关闭。
DHN

13

简单的方法

public interface IRequireViewIdentification
{
    Guid ViewID { get; }
}

实施到ViewModel

public class MyViewVM : IRequireViewIdentification
{
    private Guid _viewId;

    public Guid ViewID
    {
        get { return _viewId; }
    }

    public MyViewVM()
    {
        _viewId = Guid.NewGuid();
    }
}

添加常规的窗口管理器帮助器

public static class WindowManager
{
    public static void CloseWindow(Guid id)
    {
        foreach (Window window in Application.Current.Windows)
        {
            var w_id = window.DataContext as IRequireViewIdentification;
            if (w_id != null && w_id.ViewID.Equals(id))
            {
                window.Close();
            }
        }
    }
}

然后像这样在viewmodel中关闭它

WindowManager.CloseWindow(ViewID);

一个非常好的解决方案。
DonBoitnott

关闭窗口时,我更改了WindowManager的设置以设置dialogresult结果。if(w_id!= null && w_id.ViewID.Equals(id)){window.DialogResult = dialogResult; window.Close(); 像这样调用它:WindowManager.CloseWindow(_viewId,true);
lebhero

不错的解决方案,尽管可以使viewmodel和紧密耦合WindowManager,而后者又与View(就而言PresentationFramework)紧密耦合。如果将WindowManager服务通过接口传递给viewmodel 会更好。然后,您将能够(说)轻松地将解决方案迁移到其他平台。
惊吓

4

这是一个使用MVVM Light Messenger代替事件的简单示例。单击按钮时,视图模型会发送关闭消息:

    public MainViewModel()
    {
        QuitCommand = new RelayCommand(ExecuteQuitCommand);
    }

    public RelayCommand QuitCommand { get; private set; }

    private void ExecuteQuitCommand() 
    {
        Messenger.Default.Send<CloseMessage>(new CloseMessage());
    }

然后在窗口后面的代码中接收它。

    public Main()
    {   
        InitializeComponent();
        Messenger.Default.Register<CloseMessage>(this, HandleCloseMessage);
    }

    private void HandleCloseMessage(CloseMessage closeMessage)
    {
        Close();
    }

能否请教我在哪里可以找到CloseMessage的实现?
罗曼O

CloseMessage只是一个空类,用于标识要发送的消息的类型。(它也可能包含复杂的消息信息,在这里不需要。)
IngoB,

4

怎么样这个

ViewModel:

class ViewModel
{
    public Action CloseAction { get; set; }
    private void Stuff()
    {
       // Do Stuff
       CloseAction(); // closes the window
    }
}

在您的ViewModel中,使用CloseAction()关闭窗口,就像上面的示例一样。

视图:

public View()
{
    InitializeComponent();
    ViewModel vm = new ViewModel (); // this creates an instance of the ViewModel
    this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View
    if (vm.CloseAction == null)
        vm.CloseAction = new Action(() => this.Close());
}

3

我知道这是一篇旧文章,可能没有人会滚动到这么远,我知道我没有。因此,经过数小时的尝试,我找到了这个博客,并把它杀死了。最简单的方法,尝试了一下,它就像一种魅力。

博客

在ViewModel中:

...

public bool CanClose { get; set; }

private RelayCommand closeCommand;
public ICommand CloseCommand
{
    get
    {
        if(closeCommand == null)
        (
            closeCommand = new RelayCommand(param => Close(), param => CanClose);
        )
    }
}

public void Close()
{
    this.Close();
}

...

向ViewModel中添加一个Action属性,但可以从View的代码隐藏文件中对其进行定义。这将使我们在ViewModel上动态定义一个指向View的引用。

在ViewModel上,我们只需添加:

public Action CloseAction { get; set; }

在View上,我们将其定义如下:

public View()
{
    InitializeComponent() // this draws the View
    ViewModel vm = new ViewModel(); // this creates an instance of the ViewModel
    this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View
    if ( vm.CloseAction == null )
        vm.CloseAction = new Action(() => this.Close());
}

链接已断开:/
忠实支持Monica19 '10

@gusmally你确定吗?我正常打开了它,然后
Serlok

2

您可以像这样在ViewModel中创建新的事件处理程序。

public event EventHandler RequestClose;

    protected void OnRequestClose()
    {
        if (RequestClose != null)
            RequestClose(this, EventArgs.Empty);
    }

然后为ExitCommand定义RelayCommand。

private RelayCommand _CloseCommand;
    public ICommand CloseCommand
    {
        get
        {
            if(this._CloseCommand==null)
                this._CloseCommand=new RelayCommand(CloseClick);
            return this._CloseCommand;
        }
    }

    private void CloseClick(object obj)
    {
        OnRequestClose();
    }

然后在XAML文件中设置

<Button Command="{Binding CloseCommand}" />

在xaml.cs文件中设置DataContext并订阅我们创建的事件。

public partial class MainWindow : Window
{
    private ViewModel mainViewModel = null;
    public MainWindow()
    {
        InitializeComponent();
        mainViewModel = new ViewModel();
        this.DataContext = mainViewModel;
        mainViewModel.RequestClose += delegate(object sender, EventArgs args) { this.Close(); };
    }
}

我使用了MVVM Light Messenger代替了事件。
Hamish Gunn

1

我提供的方法是在ViewModel中声明事件,并使用如下所示的InvokeMethodAction。

样本ViewModel

public class MainWindowViewModel : BindableBase, ICloseable
{
    public DelegateCommand SomeCommand { get; private set; }
    #region ICloseable Implementation
    public event EventHandler CloseRequested;        

    public void RaiseCloseNotification()
    {
        var handler = CloseRequested;
        if (handler != null)
        {
            handler.Invoke(this, EventArgs.Empty);
        }
    }
    #endregion

    public MainWindowViewModel()
    {
        SomeCommand = new DelegateCommand(() =>
        {
            //when you decide to close window
            RaiseCloseNotification();
        });
    }
}

I Closeable界面如下所示,但不需要执行此操作。ICloseable将有助于创建通用视图服务,因此,如果通过依赖项注入来构造视图和ViewModel,那么您可以做的是

internal interface ICloseable
{
    event EventHandler CloseRequested;
}

使用ICloseable

var viewModel = new MainWindowViewModel();
        // As service is generic and don't know whether it can request close event
        var window = new Window() { Content = new MainView() };
        var closeable = viewModel as ICloseable;
        if (closeable != null)
        {
            closeable.CloseRequested += (s, e) => window.Close();
        }

下面是Xaml,即使您不实现接口,也可以使用此xaml,它只需要您的视图模型即可引发CloseRquested。

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFRx"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
xmlns:ViewModels="clr-namespace:WPFRx.ViewModels" x:Name="window" x:Class="WPFRx.MainWindow"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525" 
d:DataContext="{d:DesignInstance {x:Type ViewModels:MainWindowViewModel}}">

<i:Interaction.Triggers>
    <i:EventTrigger SourceObject="{Binding Mode=OneWay}" EventName="CloseRequested" >
        <ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

<Grid>
    <Button Content="Some Content" Command="{Binding SomeCommand}" Width="100" Height="25"/>
</Grid>


1

您可以Messenger从MVVMLight工具箱中使用。在您ViewModel发送这样的消息:
Messenger.Default.Send(new NotificationMessage("Close"));
然后在您的Windows代码后面,之后InitializeComponent,注册该消息,如下所示:

Messenger.Default.Register<NotificationMessage>(this, m=>{
    if(m.Notification == "Close") 
    {
        this.Close();
    }
   });

您可以在此处找到有关MVVMLight工具箱的更多信息: Codeplex上的MVVMLight工具箱

请注意,MVVM中没有“根本没有代码隐藏规则”,您可以在代码隐藏视图中注册消息。


0

这很简单。您可以为Login-LoginViewModel创建自己的ViewModel类。您可以创建视图var dialog = new UserView(); 在您的LoginViewModel中。您可以将Command LoginCommand设置为按钮。

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />

<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />

ViewModel类:

public class LoginViewModel
{
    Window dialog;
    public bool ShowLogin()
    {
       dialog = new UserView();
       dialog.DataContext = this; // set up ViewModel into View
       if (dialog.ShowDialog() == true)
       {
         return true;
       }

       return false;
    }

    ICommand _loginCommand
    public ICommand LoginCommand
    {
        get
        {
            if (_loginCommand == null)
                _loginCommand = new RelayCommand(param => this.Login());

            return _loginCommand;
        }
    }

    public void CloseLoginView()
    {
            if (dialog != null)
          dialog.Close();
    }   

    public void Login()
    {
        if(CheckLogin()==true)
        {
            CloseLoginView();         
        }
        else
        {
          // write error message
        }
    }

    public bool CheckLogin()
    {
      // ... check login code
      return true;
    }
}

3
是的,这也是有效的解决方案。但是,如果您要坚持使用MVVM以及将虚拟机和视图分离,则将打破这种模式。
DHN 2013年

嗨,@ misak-尝试实现您的解决方案(与其他答案一样),它Object reference not set to an instance of an object.为CloseLoginView方法抛出。有什么建议如何解决这个问题?
WPFNoob

@WPFNoob-我再次托盘解决方案。示例正常工作。您想通过电子邮件发送完整的Visual Studio解决方案吗?
misak 2013年

@WPFNoob-我看到了问题。您正在将实例创建为var dialog = new UserView();。清除关键字var(本地实例)将覆盖LoginViewModel中的全局实例
misak,

0

这是我很简单的一种方法:

YourWindow.xaml.cs

//In your constructor
public YourWindow()
{
    InitializeComponent();
    DataContext = new YourWindowViewModel(this);
}

YourWindowViewModel.cs

private YourWindow window;//so we can kill the window

//In your constructor
public YourWindowViewModel(YourWindow window)
{
    this.window = window;
}

//to close the window
public void CloseWindow()
{
    window.Close();
}

我选择的答案没有任何问题,我只是认为这可能是更简单的方法!


8
这要求您的ViewModel知道并引用您的View。
AndrewS 2015年

@AndrewS为什么这么糟?
thestephenstanton

9
要遵循MVVM模式,ViewModel不应了解View。
MetalMikester,2015年

1
为了对此进行扩展,MVVM的重点是使您的大多数GUI代码单元都可测试。视图具有大量的依赖关系,这使得它们无法进行单元测试。ViewModels应该是可单元测试的,但是如果您给它们直接依赖于视图,则不会。
ILMTitan

为了进一步扩展此功能,正确编写的MVVM可让您轻松地将解决方案迁移到其他平台。特别是,您应该能够重用视图模型,而无需进行任何更改。在这种情况下,如果您将解决方案移至Android,则将无法使用,因为Android没有Window的概念。-1用于MVVM中断解决方案。
惊吓

0

您可以将window视为服务(例如UI服务),并通过接口将自身传递给viewmodel ,例如:

public interface IMainWindowAccess
{
    void Close(bool result);
}

public class MainWindow : IMainWindowAccess
{
    // (...)
    public void Close(bool result)
    {
        DialogResult = result;
        Close();
    }
}

public class MainWindowViewModel
{
    private IMainWindowAccess access;

    public MainWindowViewModel(IMainWindowAccess access)
    {
        this.access = access;
    }

    public void DoClose()
    {
        access.Close(true);
    }
}

该解决方案具有将视图本身传递给viewmodel的最大优势,而不会破坏MVVM,因为尽管物理视图传递给viewmodel,但后者仍然不了解前者,只能看到一些IMainWindowAccess。因此,例如,如果我们想将此解决方案迁移到其他平台,则仅是IMainWindowAccess为例如Activity

我在此处发布解决方案,以提出一种与事件不同的方法(尽管实际上非常相似),因为它似乎比实现事件(附加/分离等)要简单一些,但仍然与MVVM模式保持一致。



-7

System.Environment.Exit(0); 在视图模型将工作。


6
不,不会。它将退出应用程序,并且不关闭当前窗口。
Tilak 2014年

这解决了我的问题,因为关闭mainWindow(对我来说)==退出了应用程序。从不同线程调用时,除此方法外,所有建议的方法都有一些棘手的地方。但是这种方法并不真正在乎谁是调用者线程:)那就是我所需要的!
哈默德(Hamed)2015年

BuAHahahAHahahAha抱歉无法抗拒
L.Trabacchin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.