Close Window from Viewmodel

Close Window from ViewModel

You can pass the window to your ViewModel using the CommandParameter. See my Example below.

I've implemented an CloseWindow Method which takes a Windows as parameter and closes it. The window is passed to the ViewModel via CommandParameter. Note that you need to define an x:Name for the window which should be close. In my XAML Window i call this method via Command and pass the window itself as a parameter to the ViewModel using CommandParameter.

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

ViewModel

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();
}
}

View

<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>

Note that i'm using the MVVM light framework, but the principal applies to every wpf application.

This solution violates of the MVVM pattern, because the view-model shouldn't know anything about the UI Implementation. If you want to strictly follow the MVVM programming paradigm you have to abstract the type of the view with an interface.

MVVM conform solution (Former EDIT2)

the user Crono mentions a valid point in the comment section:

Passing the Window object to the view model breaks the MVVM pattern
IMHO, because it forces your vm to know what it's being viewed in.

You can fix this by introducing an interface containing a close method.

Interface:

public interface ICloseable
{
void Close();
}

Your refactored ViewModel will look like this:

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();
}
}

You have to reference and implement the ICloseable interface in your view

View (Code behind)

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

Answer to the original question: (former EDIT1)

Your Login Button (Added CommandParameter):

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

Your code:

 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();
}
}

Implementing close window command with MVVM

You don't need to pass the View instance to your ViewModel layer. You can access the main window like this -

Application.Current.MainWindow.Close()

I see no issue in accessing your main window in ViewModel class as stated above. As per MVVM principle there should not be tight coupling between your View and ViewModel i.e. they should work be oblivious of others operation. Here, we are not passing anything to ViewModel from View. If you want to look for other options this might help you - Close window using MVVM

WPF (MVVM): Closing a view from Viewmodel?

Edit: See my blog post for a more detailed explanation.

When I need to achieve that, I use a IRequestCloseViewModel interface that I created.

This interface contains only one event: RequestClose. This event is raised by the ViewModel (which inherits from a ViewModelBase class AND implement IRequestCloseViewModel) when it wants to close its associated view.

In my app, all Window inherit from an abstract class ApplicationWindow. This abstract class is notified each time the DataContext changed and in the handler checks if the DataContext support the IRequestCloseViewModel. If this is the case, an event handler is set up to close the Window when the event is fired.

Alternatively, like Kent said, you can use screen controller that handle this mecanism in an external class.

WPF MVVM Close Window

I'd suggest you explicitly close the window you want to close rather than assuming it's the current main window.

With MVVM there are lots of different ways to do it, you can use an attached behavior or pass the window to the view model via the command parameter like this:

in the button xaml for the view:

CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"

in the command execute method in the view model:

if (parameter is System.Windows.Window)
{
WindowMainAdmin dashboard = new WindowMainAdmin();
dashboard.Show();
(parameter as System.Windows.Window).Close();
}

.

Alternatively, you could iterate all windows until you find the one you want.

foreach( Window window in Application.Current.Windows ) {
if(window is WindowOPHome)
{
window.Close();
break;
}
}

You might want to check on some other property other than just closing the first one of that type if you need to have more than one instance of a window open.

You could even adapt that to be a static close method within each window class.

MVVM - Close a window from view model

Closing the window is the view's responsibility, the view-model should know nothing about it. No wonder you're getting tangled up. If you don't need the window to stay on screen while the "button logic" is running, then just use a CompositeCommand from Prism (I don't like it much because it uses code-behind, but no matter) or equivalent to bind two commands to the button. On the other hand, if you need to keep the window on screen while the "button logic" is running, e.g. to display progress, and if your view model reflects this, then you can add a bool IsButtonLogicComplete property to your view model (don't forget INotifyPropertyChanged) and bind the window's 'closed' state to this property via an attached property/behavior like this one:

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}" ...

This will keep your view and your view model concerns nicely separated.

Closing window from ViewModel in another Class Library

One solution would be to define an interface in the class library where the view models are defined:

public interface IWindow
{
void Close();
}

Implement this interface in your window classes in the WPF application:

public partial class MainWindow : Window, IWindow { ... }

You can pass an IWindow reference to the view model instead of passing a System.Windows.Window reference. Then the view model knows only about an interface that can be easily mocked out in an unit test.

You may also want to consider using a window service to open and close windows. Please refer to my answer to the following question for an example:

MVVM show new window from VM when seperated projects



Related Topics



Leave a reply



Submit