WPF MVVM: How to close a window - c#

I have a Button that closes my window when it's clicked:
<Button x:Name="buttonOk" IsCancel="True">Ok</Button>
That's fine until I add a Command to the Button i.e.
<Button x:Name="buttonOk"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
Now it doesn't close presumably because I am handling the Command. I can fix this by putting an EventHandler in and calling this.Close() i.e.
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}"
IsCancel="True">Ok</Button>
but now I have code in my code behind i.e. the method SaveCommand. I am using the MVVM pattern and SaveCommand is the only code in my code behind.
How can I do this differently so as not to use code behind?

I just completed a blog post on this very topic. In a nutshell, add an Action property to your ViewModel with get and set accessors. Then define the Action from your View constructor. Finally, invoke your action in the bound command that should close the window.
In the ViewModel:
public Action CloseAction { get; set;}
and in the View constructor:
private View()
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
if ( vm.CloseAction == null )
vm.CloseAction = new Action(this.Close);
}
Finally, in whatever bound command that should close the window, we can simply invoke
CloseAction(); // Calls Close() method of the View
This worked for me, seemed like a fairly elegant solution, and saved me a bunch of coding.

Very clean and MVVM way is to use InteractionTrigger and CallMethodAction defined in Microsoft.Interactivity.Core
You will need to add a new namespace as below
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
You will need the Microsoft.Xmal.Behaviours.Wpf assembly and then the below xaml code will work.
<Button Content="Save" Command="{Binding SaveCommand}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction MethodName="Close"
TargetObject="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType=Window}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
You don't need any code behind or anything else and can also call any other method of Window.

As someone commented, the code I have posted is not MVVM friendly, how about the second solution?
1st, not MVVM solution (I will not delete this as a reference)
XAML:
<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
ViewModel:
public ICommand OkCommand
{
get
{
if (_okCommand == null)
{
_okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
}
return _okCommand ;
}
}
void DoOk(Window win)
{
// Your Code
win.DialogResult = true;
win.Close();
}
bool CanDoOk(Window win) { return true; }
2nd, probably better solution:
Using attached behaviours
XAML
<Button Content="Ok and Close" Command="{Binding OkCommand}" b:CloseOnClickBehaviour.IsEnabled="True" />
View Model
public ICommand OkCommand
{
get { return _okCommand; }
}
Behaviour Class
Something similar to this:
public static class CloseOnClickBehaviour
{
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(CloseOnClickBehaviour),
new PropertyMetadata(false, OnIsEnabledPropertyChanged)
);
public static bool GetIsEnabled(DependencyObject obj)
{
var val = obj.GetValue(IsEnabledProperty);
return (bool)val;
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
static void OnIsEnabledPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
{
var button = dpo as Button;
if (button == null)
return;
var oldValue = (bool)args.OldValue;
var newValue = (bool)args.NewValue;
if (!oldValue && newValue)
{
button.Click += OnClick;
}
else if (oldValue && !newValue)
{
button.PreviewMouseLeftButtonDown -= OnClick;
}
}
static void OnClick(object sender, RoutedEventArgs e)
{
var button = sender as Button;
if (button == null)
return;
var win = Window.GetWindow(button);
if (win == null)
return;
win.Close();
}
}

I'd personally use a behaviour to do this sort of thing:
public class WindowCloseBehaviour : Behavior<Window>
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(WindowCloseBehaviour));
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(WindowCloseBehaviour));
public static readonly DependencyProperty CloseButtonProperty =
DependencyProperty.Register(
"CloseButton",
typeof(Button),
typeof(WindowCloseBehaviour),
new FrameworkPropertyMetadata(null, OnButtonChanged));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public Button CloseButton
{
get { return (Button)GetValue(CloseButtonProperty); }
set { SetValue(CloseButtonProperty, value); }
}
private static void OnButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = (Window)((WindowCloseBehaviour)d).AssociatedObject;
((Button) e.NewValue).Click +=
(s, e1) =>
{
var command = ((WindowCloseBehaviour)d).Command;
var commandParameter = ((WindowCloseBehaviour)d).CommandParameter;
if (command != null)
{
command.Execute(commandParameter);
}
window.Close();
};
}
}
You can then attach this to your Window and Button to do the work:
<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplication6"
Title="Window1" Height="300" Width="300">
<i:Interaction.Behaviors>
<local:WindowCloseBehaviour CloseButton="{Binding ElementName=closeButton}"/>
</i:Interaction.Behaviors>
<Grid>
<Button Name="closeButton">Close</Button>
</Grid>
</Window>
I've added Command and CommandParameter here so you can run a command before the Window closes.

For small apps, I use my own Application Controller for showing, closing and disposing windows and DataContexts. It's a central point in UI of an application.
It's something like this:
//It is singleton, I will just post 2 methods and their invocations
public void ShowNewWindow(Window window, object dataContext = null, bool dialog = true)
{
window.DataContext = dataContext;
addToWindowRegistry(dataContext, window);
if (dialog)
window.ShowDialog();
else
window.Show();
}
public void CloseWindow(object dataContextSender)
{
var correspondingWindows = windowRegistry.Where(c => c.DataContext.Equals(dataContextSender)).ToList();
foreach (var pair in correspondingWindows)
{
pair.Window.Close();
}
}
and their invocations from ViewModels:
// Show new Window with DataContext
ApplicationController.Instance.ShowNewWindow(
new ClientCardsWindow(),
new ClientCardsVM(),
false);
// Close Current Window from viewModel
ApplicationController.Instance.CloseWindow(this);
Of course you can find some restrictions in my solution. Again: I use it for small projects, and it's enough. If you're interested, I can post full code here or somewhere else/

I've tried to resolve this issue in some generic, MVVM way, but I always find that I end up unnecessary complex logic. To achieve close behavior I have made an exception from the rule of no code behind and resorted to simply using good ol' events in code behind:
XAML:
<Button Content="Close" Click="OnCloseClicked" />
Code behind:
private void OnCloseClicked(object sender, EventArgs e)
{
Visibility = Visibility.Collapsed;
}
Although I wish this would be better supported using commands/MVVM, I simply think that there is no simpler and more clear solution than using events.

I use the Publish Subscribe pattern for complicated class-dependencies:
ViewModel:
public class ViewModel : ViewModelBase
{
public ViewModel()
{
CloseComand = new DelegateCommand((obj) =>
{
MessageBus.Instance.Publish(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, null);
});
}
}
Window:
public partial class SomeWindow : Window
{
Subscription _subscription = new Subscription();
public SomeWindow()
{
InitializeComponent();
_subscription.Subscribe(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, obj =>
{
this.Close();
});
}
}
You can leverage Bizmonger.Patterns to get the MessageBus.
MessageBus
public class MessageBus
{
#region Singleton
static MessageBus _messageBus = null;
private MessageBus() { }
public static MessageBus Instance
{
get
{
if (_messageBus == null)
{
_messageBus = new MessageBus();
}
return _messageBus;
}
}
#endregion
#region Members
List<Observer> _observers = new List<Observer>();
List<Observer> _oneTimeObservers = new List<Observer>();
List<Observer> _waitingSubscribers = new List<Observer>();
List<Observer> _waitingUnsubscribers = new List<Observer>();
int _publishingCount = 0;
#endregion
public void Subscribe(string message, Action<object> response)
{
Subscribe(message, response, _observers);
}
public void SubscribeFirstPublication(string message, Action<object> response)
{
Subscribe(message, response, _oneTimeObservers);
}
public int Unsubscribe(string message, Action<object> response)
{
var observers = new List<Observer>(_observers.Where(o => o.Respond == response).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Respond == response));
observers.AddRange(_oneTimeObservers.Where(o => o.Respond == response));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public int Unsubscribe(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription).ToList());
observers.AddRange(_waitingSubscribers.Where(o => o.Subscription == subscription));
observers.AddRange(_oneTimeObservers.Where(o => o.Subscription == subscription));
if (_publishingCount == 0)
{
observers.ForEach(o => _observers.Remove(o));
}
else
{
_waitingUnsubscribers.AddRange(observers);
}
return observers.Count;
}
public void Publish(string message, object payload)
{
_publishingCount++;
Publish(_observers, message, payload);
Publish(_oneTimeObservers, message, payload);
Publish(_waitingSubscribers, message, payload);
_oneTimeObservers.RemoveAll(o => o.Subscription == message);
_waitingUnsubscribers.Clear();
_publishingCount--;
}
private void Publish(List<Observer> observers, string message, object payload)
{
Debug.Assert(_publishingCount >= 0);
var subscribers = observers.Where(o => o.Subscription.ToLower() == message.ToLower());
foreach (var subscriber in subscribers)
{
subscriber.Respond(payload);
}
}
public IEnumerable<Observer> GetObservers(string subscription)
{
var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription));
return observers;
}
public void Clear()
{
_observers.Clear();
_oneTimeObservers.Clear();
}
#region Helpers
private void Subscribe(string message, Action<object> response, List<Observer> observers)
{
Debug.Assert(_publishingCount >= 0);
var observer = new Observer() { Subscription = message, Respond = response };
if (_publishingCount == 0)
{
observers.Add(observer);
}
else
{
_waitingSubscribers.Add(observer);
}
}
#endregion
}
}
Subscription
public class Subscription
{
#region Members
List<Observer> _observerList = new List<Observer>();
#endregion
public void Unsubscribe(string subscription)
{
var observers = _observerList.Where(o => o.Subscription == subscription);
foreach (var observer in observers)
{
MessageBus.Instance.Unsubscribe(observer.Subscription, observer.Respond);
}
_observerList.Where(o => o.Subscription == subscription).ToList().ForEach(o => _observerList.Remove(o));
}
public void Subscribe(string subscription, Action<object> response)
{
MessageBus.Instance.Subscribe(subscription, response);
_observerList.Add(new Observer() { Subscription = subscription, Respond = response });
}
public void SubscribeFirstPublication(string subscription, Action<object> response)
{
MessageBus.Instance.SubscribeFirstPublication(subscription, response);
}
}

There is a useful behavior for this task which doesn't break MVVM, a Behavior, introduced with Expression Blend 3, to allow the View to hook into commands defined completely within the ViewModel.
This behavior demonstrates a simple technique for allowing the
ViewModel to manage the closing events of the View in a
Model-View-ViewModel application.
This allows you to hook up a behavior in your View (UserControl) which
will provide control over the control's Window, allowing the ViewModel
to control whether the window can be closed via standard ICommands.
Using Behaviors to Allow the ViewModel to Manage View Lifetime in M-V-VM
http://gallery.expression.microsoft.com/WindowCloseBehavior/
Above link has been archived to http://code.msdn.microsoft.com/Window-Close-Attached-fef26a66#content

I struggled with this topic for some time, and eventually went with the simplest approach that is still consistent with MVVM: Have the button execute the Command that does all the heavy lifting and have the button's Click handler close the window.
XAML
<Button x:Name="buttonOk"
Click="closeWindow"
Command="{Binding SaveCommand}" />
XAML.cs
public void closeWindow()
{
this.DialogResult = true;
}
SaveCommand.cs
// I'm in my own file, not the code-behind!
True, there is still code-behind, but there isn't anything inherently bad about that. And it makes the most sense to me, from an OO perspective, to just tell the window to close itself.

We have the name property in the .xaml definition:
x:Name="WindowsForm"
Then we have the button:
<Button Command="{Binding CloseCommand}"
CommandParameter="{Binding ElementName=WindowsForm}" />
Then in the ViewModel:
public DelegateCommand <Object> CloseCommand { get; private set; }
Constructor for that view model:
this.CloseCommand = new DelegateCommand<object>(this.CloseAction);
Then at last, the action method:
private void CloseAction (object obj)
{
Window Win = obj as Window;
Win.Close();
}
I used this code to close a pop-up window from an application..

I found myself having to do this on a WPF application based on .Net Core 3.0, where unfortunately behaviour support was not yet officially available in the Microsoft.Xaml.Behaviors.Wpf NuGet package.
Instead, I went with a solution that made use of the Façade design pattern.
Interface:
public interface IWindowFacade
{
void Close();
}
Window:
public partial class MainWindow : Window, IWindowFacade
…
Standard command property on the view model:
public ICommand ExitCommand
…
Control binding:
<MenuItem Header="E_xit" Command="{Binding ExitCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
Command:
public class ExitCommand : ICommand
{
…
public void Execute(object parameter)
{
var windowFacade = parameter as IWindowFacade;
windowFacade?.Close();
}
…
}
Because the Close() method is already implemented by the Window class, applying the façade interface to the window is the only required code behind in the UI layer (for this simple example). The command in the presentation layer avoids any dependencies on the view/UI layer as it has no idea what it is talking to when it calls the Close method on the façade.

In your current window xaml.cs file, call the below code:
var curWnd = Window.GetWindow(this); // passing current window context
curWnd?.Close();
This should do the thing.
It worked for me, hope will do the same for you )

I have following solution in Silverlight. Would also be in WPF.
ChildWindowExt.cs:
namespace System.Windows.Controls
{
public class ChildWindowExt : ChildWindow
{
public static readonly DependencyProperty IsOpenedProperty =
DependencyProperty.Register(
"IsOpened",
typeof(bool),
typeof(ChildWindowExt),
new PropertyMetadata(false, IsOpenedChanged));
private static void IsOpenedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == false)
{
ChildWindowExt window = d as ChildWindowExt;
window.Close();
}
else if ((bool)e.NewValue == true)
{
ChildWindowExt window = d as ChildWindowExt;
window.Show();
}
}
public bool IsOpened
{
get { return (bool)GetValue(IsOpenedProperty); }
set { SetValue(IsOpenedProperty, value); }
}
protected override void OnClosing(ComponentModel.CancelEventArgs e)
{
this.IsOpened = false;
base.OnClosing(e);
}
protected override void OnOpened()
{
this.IsOpened = true;
base.OnOpened();
}
}
}
ItemWindow.xaml:
<extControls:ChildWindowExt
x:Class="MyProject.ItemWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:extControls="clr-namespace:System.Windows.Controls"
Title="{Binding Title}" IsOpened="{Binding IsOpened, Mode=TwoWay}" Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<Button Command="{Binding UpdateCommand}" Content="OK" Width="70" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</extControls:ChildWindowExt>
ItemViewModel.cs:
private bool _IsOpened;
public bool IsOpened
{
get
{
return _IsOpened;
}
set
{
if (!Equals(_IsOpened, value))
{
_IsOpened = value;
RaisePropertyChanged("IsOpened");
}
}
}
private RelayCommand _UpdateCommand;
/// <summary>
/// Insert / Update data entity
/// </summary>
public RelayCommand UpdateCommand
{
get
{
if (_UpdateCommand == null)
{
_UpdateCommand = new RelayCommand(
() =>
{
// Insert / Update data entity
...
IsOpened = false;
},
() =>
{
return true;
});
}
return _UpdateCommand;
}
}
ItemsViewModel.cs:
private RelayCommand _InsertItemCommand;
/// <summary>
///
/// </summary>
public RelayCommand InsertItemCommand
{
get
{
if (_InsertItemCommand == null)
{
_InsertItemCommand = new RelayCommand(
() =>
{
ItemWindow itemWin = new ItemWindow();
itemWin.DataContext = new ItemViewModel();
itemWin.Show();
// OR
// ItemWindow itemWin = new ItemWindow();
// ItemViewModel newItem = new ItemViewModel();
// itemWin.DataContext = newItem;
// newItem.IsOpened = true;
},
() =>
{
return true;
});
}
return _InsertItemCommand;
}
}
MainPage.xaml:
<Grid x:Name="LayoutRoot">
<Button Command="{Binding InsertItemCommand}" Content="Add New" Width="70" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Grid>
I wish you all good ideas and projects ;-)

This might helps you, closing a wpf window using mvvm with minimal code behind: http://jkshay.com/closing-a-wpf-window-using-mvvm-and-minimal-code-behind/

I think the most simple way has not been included already (almost). Instead of using Behaviours which adds new dependencies just use attached properties:
using System;
using System.Windows;
using System.Windows.Controls;
public class DialogButtonManager
{
public static readonly DependencyProperty IsAcceptButtonProperty = DependencyProperty.RegisterAttached("IsAcceptButton", typeof(bool), typeof(DialogButtonManager), new FrameworkPropertyMetadata(OnIsAcceptButtonPropertyChanged));
public static readonly DependencyProperty IsCancelButtonProperty = DependencyProperty.RegisterAttached("IsCancelButton", typeof(bool), typeof(DialogButtonManager), new FrameworkPropertyMetadata(OnIsCancelButtonPropertyChanged));
public static void SetIsAcceptButton(UIElement element, bool value)
{
element.SetValue(IsAcceptButtonProperty, value);
}
public static bool GetIsAcceptButton(UIElement element)
{
return (bool)element.GetValue(IsAcceptButtonProperty);
}
public static void SetIsCancelButton(UIElement element, bool value)
{
element.SetValue(IsCancelButtonProperty, value);
}
public static bool GetIsCancelButton(UIElement element)
{
return (bool)element.GetValue(IsCancelButtonProperty);
}
private static void OnIsAcceptButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Button button = sender as Button;
if (button != null)
{
if ((bool)e.NewValue)
{
SetAcceptButton(button);
}
else
{
ResetAcceptButton(button);
}
}
}
private static void OnIsCancelButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Button button = sender as Button;
if (button != null)
{
if ((bool)e.NewValue)
{
SetCancelButton(button);
}
else
{
ResetCancelButton(button);
}
}
}
private static void SetAcceptButton(Button button)
{
Window window = Window.GetWindow(button);
button.Command = new RelayCommand(new Action<object>(ExecuteAccept));
button.CommandParameter = window;
}
private static void ResetAcceptButton(Button button)
{
button.Command = null;
button.CommandParameter = null;
}
private static void ExecuteAccept(object buttonWindow)
{
Window window = (Window)buttonWindow;
window.DialogResult = true;
}
private static void SetCancelButton(Button button)
{
Window window = Window.GetWindow(button);
button.Command = new RelayCommand(new Action<object>(ExecuteCancel));
button.CommandParameter = window;
}
private static void ResetCancelButton(Button button)
{
button.Command = null;
button.CommandParameter = null;
}
private static void ExecuteCancel(object buttonWindow)
{
Window window = (Window)buttonWindow;
window.DialogResult = false;
}
}
Then just set it on your dialog buttons:
<UniformGrid Grid.Row="2" Grid.Column="1" Rows="1" Columns="2" Margin="3" >
<Button Content="Accept" IsDefault="True" Padding="3" Margin="3,0,3,0" DialogButtonManager.IsAcceptButton="True" />
<Button Content="Cancel" IsCancel="True" Padding="3" Margin="3,0,3,0" DialogButtonManager.IsCancelButton="True" />
</UniformGrid>

I also had to deal with this problem, so here my solution. It works great for me.
1. Create class DelegateCommand
public class DelegateCommand<T> : ICommand
{
private Predicate<T> _canExecuteMethod;
private readonly Action<T> _executeMethod;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<T> executeMethod) : this(executeMethod, null)
{
}
public DelegateCommand(Action<T> executeMethod, Predicate<T> canExecuteMethod)
{
this._canExecuteMethod = canExecuteMethod;
this._executeMethod = executeMethod ?? throw new ArgumentNullException(nameof(executeMethod), "Command is not specified.");
}
public void RaiseCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
CanExecuteChanged(this, null);
}
public bool CanExecute(object parameter)
{
return _canExecuteMethod == null || _canExecuteMethod((T)parameter) == true;
}
public void Execute(object parameter)
{
_executeMethod((T)parameter);
}
}
2. Define your command
public DelegateCommand<Window> CloseWindowCommand { get; private set; }
public MyViewModel()//ctor of your viewmodel
{
//do something
CloseWindowCommand = new DelegateCommand<Window>(CloseWindow);
}
public void CloseWindow(Window win) // this method is also in your viewmodel
{
//do something
win?.Close();
}
3. Bind your command in the view
public MyView(Window win) //ctor of your view, window as parameter
{
InitializeComponent();
MyButton.CommandParameter = win;
MyButton.Command = ((MyViewModel)this.DataContext).CloseWindowCommand;
}
4. And now the window
Window win = new Window()
{
Title = "My Window",
Height = 800,
Width = 800,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
};
win.Content = new MyView(win);
win.ShowDialog();
so thats it, you can also bind the command in the xaml file and find the window with FindAncestor and bind it to the command parameter.

I've been searching for a solution to the same problem and found that doing following works fine. The solution is similar to what OP has mentioned in his question with some differences:
No need of IsCancel property.
Code behind should not close window. Just set DialogResult
In my case it first executes code behind and then view model command bound to the button.
XAML
<Button x:Name="buttonOk" Click="Save_Click" Command="{Binding SaveCommand}">OK</Button>
Code Behind
private void Apply_OnClick(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
View Model
private void Save()
{
// Save data.
}
Hope this helps.

You could rephrase the question, and by doing so - coming up with another solution.
How can I enable communication between views, viewmodels and whatnot in an MVVM environment?
You could use the Mediator pattern. It's basically a notification system. For the actual Mediator implementation, google for it or ask me and I can email it.
Make a Command whose purpose is to close the view.
public void Execute( object parameter )
{
this.viewModel.DisposeMyStuff();
Mediator.NotifyColleagues(Mediator.Token.ConfigWindowShouldClose);
}
The Mediator will raise a notification (a token)
Listen to this notification (token) like this in the View codebehind constructor:
public ClientConfigView()
{
InitializeComponent();
Mediator.ListenOn(Mediator.Token.ConfigWindowShouldClose, callback => this.Close() );
}

The solution to close a window in wpf that that worked for me is not answered here so i thought i would add my solution too.
private static Window GetWindow(DependencyObject sender)
{
Window window = null;
if (sender is Window)
window = (Window)sender;
if (window == null)
window = Window.GetWindow(sender);
return window;
}
private void CloseWindow(object sender, RoutedEventArgs e)
{
var button = (Button)sender as DependencyObject;
Window window = GetWindow(button);
if (window != null)
window.Close();
// window.Visibility = Visibility.Hidden;
// choose between window.close or set window.visibility to close or hide the window.
// }
}
Add CloseWindow event to the button in you window as following.
<Button Content="Cancel" Click="CloseWindow" >

Simple approach is close window on saveComand Implementation.
Use below code to close window.
Application.Current.Windows[1].Close();
It will close the child window.

Without any dependencies.
<Window ...>
...
<Button Command="{x:Static SystemCommands.CloseWindowCommand}" />
</Window>

You can do it without code behind. Create command, in Execute method call "Save" method on viewmodel and after that call close method on edit window, which you can pass to the command by parameter:
public void Execute(object parameter)
{
_mainViewModel.SaveSomething();
var editWindow = parameter as MyEditWindow;
editWindow?.Close();
}
Save&Close button XAML:
<Button Content"Save&Close" Command="{Binding SaveCmd}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" IsDefault="True" />

Related

WPF Notify content change when it's property changed

I've set label Content to some custom class:
<Label>
<local:SomeContent x:Name="SomeContent" some="abc" />
</Label>
This properly displays "abc" in a view. However I can't figure out how do I notify the Label that the content property have changed i.e. this:
SomeContent.some = "xyz";
Will not cause the label to update it's view.
I know I can set binding to label's Content property. I have already like 7 different, working methods to achieve automatic update. However I'm interested in this particular behavior because it will save me a ton of work in some scenarios i.e the requirements are:
Label content is always the same SomeContent instance, only it's properties are changed.
No label content binding. The label should take a content object and refresh whenever the content is modified.
Initial value of some property can be set in XAML
some property can be changed in code, causing label refresh.
Am I missing something, or it's not possible?
This is my current implementation of SomeContent:
public class SomeContent : DependencyObject, INotifyPropertyChanged {
public static readonly DependencyProperty someProperty =
DependencyProperty.Register(nameof(some), typeof(string),
typeof(SomeContent),
new PropertyMetadata("", onDPChange)
);
private static void onDPChange(DependencyObject d, DependencyPropertyChangedEventArgs e) {
//throw new NotImplementedException();
(d as SomeContent).some = e.NewValue as String;
}
public event PropertyChangedEventHandler PropertyChanged;
public string some {
get => (string)GetValue(someProperty);
set {
SetValue(someProperty, value);
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(some))
);
}
}
public override string ToString() => some;
}
I turns out it's not possible to do it without third side code. So I wrote a helper class to do it easy now.
Dynamic object
public class SomeContent : IChangeNotifer {
public event Action<object> MODIFIED;
private string _some;
public string some {
get => _some;
set {
_some = value;
MODIFIED?.Invoke(this);
}
}
public override string ToString() => some;
}
You can add it to xaml file and it will be updated automatically. Single additional step is to add UIReseter somewhere bellow the elements that suppose to be auto-updated but that is needed only one for multiple contents in a tree.
Usage
<Window x:Class="DependencyContentTest.MainWindow"
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:DependencyContentTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<local:UIReseter />
<Label>
<local:SomeContent x:Name="SomeContent" some="abcd" />
</Label>
<Grid>
<Label>
<local:SomeContent x:Name="nested" some="nyest"/>
</Label>
</Grid>
</StackPanel>
</Window>
MainWindow code
public partial class MainWindow : Window {
private Timer t;
public MainWindow() {
InitializeComponent();
t = new Timer(onTimer, null, 5000, Timeout.Infinite);
MouseDown += (s,e) => { SomeContent.some = "iii"; };
}
private void onTimer(object state) {
Dispatcher.Invoke(() => {
SomeContent.some = "aaaa";
nested.some = "xxx";
});
}
}
And this is the helper class that handles the update
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using H = System.Windows.LogicalTreeHelper;
using FE = System.Windows.FrameworkElement;
using DO = System.Windows.DependencyObject;
using System.Reflection;
using System.Windows.Markup;
namespace DependencyContentTest
{
public interface IChangeNotifer {
/// <summary>Dispatched when this object was modified.</summary>
event Action<object> MODIFIED;
}
/// <summary>This element tracks nested <see cref="IChangeNotifer"/> descendant objects (in logical tree) of this object's parent element and resets a child in it's panel property.
/// Only static (XAML) objects are supported i.e. object added to the tree dynamically at runtime will not be tracked.</summary>
public class UIReseter : UIElement {
public int searchDepth { get; set; } = int.MaxValue;
protected override void OnVisualParentChanged(DO oldParent){
if (VisualParent is FE p) p.Loaded += (s, e) => bind(p);
}
private void bind(FE parent, int dl = 0) {
if (parent == null || dl > searchDepth) return;
var chs = H.GetChildren(parent);
foreach (object ch in chs) {
if (ch is UIReseter r && r != this) throw new Exception($#"There's overlapping ""{nameof(UIReseter)}"" instance in the tree. Use single global instance of check ""{nameof(UIReseter.searchDepth)}"" levels.");
if (ch is IChangeNotifer sc) trackObject(sc, parent);
else bind(ch as FE, ++dl);
}
}
private Dictionary<IChangeNotifer, Reseter> tracked = new Dictionary<IChangeNotifer, Reseter>();
private void trackObject(IChangeNotifer sc, FE parent) {
var cp = getContentProperty(parent);
if (cp == null) return;
var r = tracked.nev(sc, () => new Reseter {
child = sc,
parent = parent,
content = cp,
});
r.track();
}
private PropertyInfo getContentProperty(FE parent) {
var pt = parent.GetType();
var cp = parent.GetType().GetProperties(
BindingFlags.Public |
BindingFlags.Instance
).FirstOrDefault(i => Attribute.IsDefined(i,
typeof(ContentPropertyAttribute)));
return cp ?? pt.GetProperty("Content");
}
private class Reseter {
public DO parent;
public IChangeNotifer child;
public PropertyInfo content;
private bool isTracking = false;
/// <summary>Function called by <see cref="IChangeNotifer"/> on <see cref="IChangeNotifer.MODIFIED"/> event.</summary>
/// <param name="ch"></param>
public void reset(object ch) {
if(! isChildOf(child, parent)) return;
//TODO: Handle multi-child parents
content.SetValue(parent, null);
content.SetValue(parent, child);
}
public void track() {
if (isTracking) return;
child.MODIFIED += reset;
}
private bool isChildOf(IChangeNotifer ch, DO p) {
if(ch is DO dch) {
if (H.GetParent(dch) == p) return true;
child.MODIFIED -= reset; isTracking = false;
return false;
}
var chs = H.GetChildren(p);
foreach (var c in chs) if (c == ch) return true;
child.MODIFIED -= reset; isTracking = false;
return false;
}
}
}
public static class DictionaryExtension {
public static V nev<K,V>(this Dictionary<K,V> d, K k, Func<V> c) {
if (d.ContainsKey(k)) return d[k];
var v = c(); d.Add(k, v); return v;
}
}
}
It could be improved and it not fully tested but it works for current purposes.
Additional problem is that some elements like TextBox cry about not suppopring SomeContent, like it is so hard to use ToString()... but that is another story, and is not related to my question.
Updated answer:
I'd throw away implementing SomeContent as a Dependency property and use a UserControl instead:
<UserControl x:Class="WpfApp1.SomeContent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock Text="{Binding some, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SomeContent}}}"/>
</Grid>
</UserControl>
Then in code behind:
/// <summary>
/// Interaction logic for SomeContent.xaml
/// </summary>
public partial class SomeContent : UserControl
{
public static readonly DependencyProperty someProperty =
DependencyProperty.Register(nameof(some), typeof(string),
typeof(SomeContent),
new PropertyMetadata("")
);
public string some
{
get => (string)GetValue(someProperty);
set => SetValue(someProperty, value);
}
public SomeContent()
{
InitializeComponent();
}
}
Next, implement a view model that implements INotifyPropertyChanged:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _somePropertyOnMyViewModel;
public string SomePropertyOnMyViewModel
{
get => _somePropertyOnMyViewModel;
set { _somePropertyOnMyViewModel = value; OnPropertyChanged(); }
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then create an instance of MyViewModel in your view and assign it to your view's DataContext:
public class MyView : Window
{
public MyView()
{
InitializeComponent();
DataContext = new MyViewModel();
}
}
Then, finally, in MyView use the markup I provided in my original answer:
<Label>
<local:SomeContent x:Name="SomeContent" some="{Binding
SomePropertyOnMyViewModel" />
</Label>

Bind command in view model to keyboard shortcut

I'm using C#, WPF, ReactiveUI and Prism to create an application with many different views (user controls). On some views there are buttons/menu items that bind to a command in the view model. I would like these buttons to also activate using a key combination such as ctrl+s, etc....
What I've tried
InputBindings but that only works when the view that defines these input bindings has focus.
ApplicationCommands the predefined commands like ApplicationCommands.Close seem useful. I can reference them both in the view and the view model, but I don't know how subscribe to them in my view model. It also seems that I have to 'activate' the command first, or at least change CanExecute since any button bound to such command stays disabled.
What I wish for
Let's say I have a view that represents the top menu bar MenuView with a button myButton and a corresponding view model MenuViewModel with a command myCommand. I would like to bind myButton to myCommand and the keyboard shortcut ctrl+u to myCommand without MenuView knowing about the implementation of its view model. The keyboard shortcut should work as long as the window that contains MenuView has focus.
I don't really care if the keyboard short-cut is either in the view or view model.
You could create an attached Blend behaviour that handles the PreviewKeyDown event of the parent window:
public class KeyboardShortcutBehavior : Behavior<FrameworkElement>
{
private Window _parentWindow;
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(nameof(Command), typeof(ICommand),
typeof(KeyboardShortcutBehavior), new FrameworkPropertyMetadata(null));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty ModifierKeyProperty =
DependencyProperty.Register(nameof(ModifierKey), typeof(ModifierKeys),
typeof(KeyboardShortcutBehavior), new FrameworkPropertyMetadata(ModifierKeys.None));
public ModifierKeys ModifierKey
{
get { return (ModifierKeys)GetValue(ModifierKeyProperty); }
set { SetValue(ModifierKeyProperty, value); }
}
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register(nameof(Key), typeof(Key),
typeof(KeyboardShortcutBehavior), new FrameworkPropertyMetadata(Key.None));
public Key Key
{
get { return (Key)GetValue(KeyProperty); }
set { SetValue(KeyProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
AssociatedObject.Unloaded += AssociatedObject_Unloaded;
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
_parentWindow = Window.GetWindow(AssociatedObject);
if(_parentWindow != null)
{
_parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
}
}
private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(Command != null && ModifierKey != ModifierKeys.None && Key != Key.None && Keyboard.Modifiers == ModifierKey && e.Key == Key)
Command.Execute(null);
}
private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
{
if(_parentWindow != null)
{
_parentWindow.PreviewKeyDown -= ParentWindow_PreviewKeyDown;
}
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= AssociatedObject_Loaded;
AssociatedObject.Unloaded -= AssociatedObject_Loaded;
}
}
Sample usage:
<TextBox xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Behaviors>
<local:KeyboardShortcutBehavior ModifierKey="Ctrl" Key="U" Command="{Binding myCommand}" />
</i:Interaction.Behaviors>
</TextBox>
In code behind easy. Create some utility function that eventually lead to an observable of the parent window key events. Note that you will need the ReactiveUI.Events library.
Some utils for handling load and unload of controls.
public static void LoadUnloadHandler
( this FrameworkElement control
, Func<IDisposable> action
)
{
var state = false;
var cleanup = new SerialDisposable();
Observable.Merge
(Observable.Return(control.IsLoaded)
, control.Events().Loaded.Select(x => true)
, control.Events().Unloaded.Select(x => false)
)
.Subscribe(isLoadEvent =>
{
if (!state)
{
// unloaded state
if (isLoadEvent)
{
state = true;
cleanup.Disposable = new CompositeDisposable(action());
}
}
else
{
// loaded state
if (!isLoadEvent)
{
state = false;
cleanup.Disposable = Disposable.Empty;
}
}
});
}
public static IObservable<T> LoadUnloadHandler<T>(this FrameworkElement control, Func<IObservable<T>> generator)
{
Subject<T> subject = new Subject<T>();
control.LoadUnloadHandler(() => generator().Subscribe(v => subject.OnNext(v)));
return subject;
}
and one specifically for handling the window of a loaded control
public static IObservable<T> LoadUnloadHandler<T>
(this FrameworkElement control, Func<Window, IObservable<T>> generator)
{
Subject<T> subject = new Subject<T>();
control.LoadUnloadHandler(() => generator(Window.GetWindow(control)).Subscribe(v => subject.OnNext(v)));
return subject;
}
and finally a key handler for the parent window of any control
public static IObservable<KeyEventArgs> ParentWindowKeyEventObservable(this FrameworkElement control)
=> control.LoadUnloadHandler((Window window) => window.Events().PreviewKeyDown);
now you can do
Button b;
b.ParentWindowKeyEventObservable()
.Subscribe( kEvent => {
myCommand.Execute();
}
It might seem a bit complex but I use the LoadUnloadHandler on most user controls to aquire and dispose resources as the UI lifecycle progresses.
You want to use KeyBindings for this. This allows you to bind keyboard key combos to a command. Read the docs here: https://msdn.microsoft.com/en-us/library/system.windows.input.keybinding(v=vs.110).aspx

Is send command from ViewModel to View violating MVVM?

I want my DataGrid scroll to bottom as new item added to underlying ObservableCollection. To achieve this, I make an interface similar to ICommand but in "reverse" way.
public interface IViewModelCommand
{
void Execute(object parameter);
}
Implementation
public class ViewModelRelayCommand : IViewModelCommand
{
private readonly Action<object> _action;
public ViewModelRelayCommand(Action<object> action)
{
if(action == null)
throw new ArgumentNullException("action");
_action = action;
}
public void Execute(object parameter)
{
_action(parameter);
}
}
My ViewModel
private IViewModelCommand _scrollAction;
public IViewModelCommand ScrollAction
{
get { return _scrollAction; }
set
{
if (_scrollAction == value)
return;
_scrollAction = value;OnPropertyChanged();
}
}
Then I create behavior for my DataGrid.
(Scroll to end code taken from here)
public sealed class DataGridBehavior : Behavior<DataGrid>
{
public static readonly DependencyProperty ScrollToEndProperty =
DependencyProperty.Register (
"ScrollToEnd",
typeof(IViewModelCommand),
typeof(DataGridBehavior),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None)
);
public IViewModelCommand ScrollToEnd
{
get { return (IViewModelCommand)GetValue(ScrollToEndProperty); }
set { SetValue(ScrollToEndProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
ScrollToEnd = new ViewModelRelayCommand(Scroll);
}
protected override void OnDetaching()
{
base.OnDetaching();
ScrollToEnd = null;
}
private void Scroll(object parameter)
{
var mainDataGrid = AssociatedObject;
if (mainDataGrid.Items.Count > 0)
{
var border = VisualTreeHelper.GetChild(mainDataGrid, 0) as Decorator;
if (border != null)
{
var scroll = border.Child as ScrollViewer;
if (scroll != null) scroll.ScrollToEnd();
}
}
}
}
And attach it to my DataGrid
<i:Interaction.Behaviors>
<local:DataGridBehavior ScrollToEnd="{Binding ScrollAction, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
Then from my ViewModel I just call if (_scrollAction != null) _scrollAction.Execute(null); to scroll my grid and it works very well.
My question, is this violating MVVM?
just a little bit...
In my experience MVVM is most healthfully practiced as a rough guideline. There's no use in finding solutions to keep MVVM straight when your actual programming task doesn't require it, especially if you got working solutions up and running.
But have you thought of an event instead of the command approach?
In that case this may be useful to you: How can I Have a WPF EventTrigger on a View trigger when the underlying Viewmodel dictates it should?

TextboxHelper.ButtonCommand Binding using Caliburn

I am having a problem binding the TextboxHelper.ButtonCommand (from mahapps.metro) to my view model using caliburn.
At the moment I have this working using a delegate command.
View:
<TextBox Name="TextBoxContent"
mui:TextboxHelper.ButtonContent="s"
mui:TextboxHelper.ButtonCommand="{Binding DelCommand, Mode=OneWay}"
Style="{DynamicResource ButtonCommandMuiTextBox}" />
ViewMode:
ICommand DelCommand
{
get {return new Command();}
}
void Command() { //Handle press here }
However I really would like to use caliburn not the delegate command to acheive this. I have tried using the event triggers on the textbox to no avail, like so...
<TextBox Name="TextBoxContent" mui:TextboxHelper.ButtonContent="s"
Style="{DynamicResource ButtonCommandMuiTextBox}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="mui:TextboxHelper.ButtonCommand">
<i:EventTrigger.Actions>
<cal:ActionMessage MethodName="Command"/>
</i:EventTrigger.Actions>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Is there a reason why this can't be done?
Thanks
I have created an attached property attaching the Caliburn Message to the MahApps TextBox button.
public static class MahappsHelper
{
/// <summary>
/// Attach Caliburn cal:Message.Attach for the Mahapps TextBoxHelper.Button
/// </summary>
public static readonly DependencyProperty ButtonMessageProperty =
DependencyProperty.RegisterAttached("ButtonMessage", typeof(string), typeof(MahappsHelper), new PropertyMetadata(null, ButtonMessageChanged));
public static string GetButtonMessage(DependencyObject obj)
{
return (string)obj.GetValue(ButtonMessageProperty);
}
public static void SetButtonMessage(DependencyObject obj, string value)
{
obj.SetValue(ButtonMessageProperty, value);
}
private static void ButtonMessageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement textBox = d as TextBox;
if (d == null)
textBox = d as PasswordBox;
if (d == null)
textBox = d as ComboBox;
if (textBox == null)
throw new ArgumentException("ButtonMessage does not work with control " + d.GetType());
textBox.Loaded -= ButtonMessageTextBox_Loaded;
Button button = GetTextBoxButton(textBox);
if (button != null)
{
SetButtonMessage(textBox, button);
return;
}
// cannot get button, try it in the Loaded event
textBox.Loaded += ButtonMessageTextBox_Loaded;
}
private static Button GetTextBoxButton(FrameworkElement textBox)
{
return TreeHelper.FindChild<Button>(textBox, "PART_ClearText");
}
private static void ButtonMessageTextBox_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement textBox = (FrameworkElement)sender;
textBox.Loaded -= ButtonMessageTextBox_Loaded;
Button button = GetTextBoxButton(textBox);
SetButtonMessage(textBox, button);
}
/// <summary>
/// Set Caliburn Message to the TextBox button.
/// </summary>
private static void SetButtonMessage(FrameworkElement textBox, Button button)
{
if (button == null)
return;
string message = GetButtonMessage(textBox);
Caliburn.Micro.Message.SetAttach(button, message);
}
}
And usage:
<TextBox x:Name="SearchBox" mahapps:TextBoxHelper.Watermark="Search ..."
cal:Message.Attach="[KeyDown] = [Search(SearchBox.Text, $eventArgs)]"
my:MahappsHelper.ButtonMessage="Search(SearchBox.Text)" />
This is because it's not an event, you could solve this by turning the call to the command into an attached event then have caliburn watch that event instead.
I'll omit the attached event code, because it's lengthy but can be found here: Custom attached events in WPF
Something like
public class MyControlExtension
{
public static readonly DependencyProperty SendMahAppsCommandAsEventProperty = DependencyProperty.RegisterAttached("SendMahAppsCommandAsEvent", typeof(bool), ... etc ... );
public static SetSendMahAppsCommandAsEvent(DependencyObject element, bool value)
{
if (value)
TextboxHelper.SetButtonCommand(element, CreateCommand(element));
else
TextboxHelper.SetButtonCommand(null);
}
public static bool GetHeaderSizingGroup(DependencyObject element)
{
return (bool) element.GetValue(SendMahAppsCommandAsEventProperty);
}
private static ICommand CreateCommand(DependencyObject element)
{
return new MyCommandThatRaisesAttachedEvent(element);
}
}

Close Window from ViewModel [duplicate]

This question already has answers here:
How should the ViewModel close the form?
(25 answers)
Closed 1 year ago.
Im creating a Login using a window control to allow a user to login into a WPF application that I am creating.
So far, I have created a method that checks whether the user has entered in the correct credentials for the username and password in a textbox on the login screen, binding two properties.
I have achieved this by creating a bool method, like so;
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();
}
I also have a command that I bind to my button within the xaml like so;
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />
When I enter in the username and password it executes the appropriated code, whether it being right, or wrong. But how can I close this window from the ViewModel when both username and password are correct?
I have previously tried using a dialog modal but it didn't quite work out. Furthermore, within my app.xaml, I have done something like the following, which loads the login page first, then once true, loads the actual application.
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);
}
}
Question: How can I close the Login Window control from the ViewModel?
Thanks in advance.
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();
}
}
I usually put an event on the view model when I need to do this and then hook it up to the Window.Close() when binding the view model to the window
public class LoginViewModel
{
public event EventHandler OnRequestClose;
private void Login()
{
// Login logic here
OnRequestClose(this, new EventArgs());
}
}
And when creating the login window
var vm = new LoginViewModel();
var loginWindow = new LoginWindow
{
DataContext = vm
};
vm.OnRequestClose += (s, e) => loginWindow.Close();
loginWindow.ShowDialog();
Staying MVVM, I think using either Behaviors from the Blend SDK (System.Windows.Interactivity) or a custom interaction request from Prism could work really well for this sort of situation.
If going the Behavior route, here's the general idea:
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();
}
}
}
Then in your window, you would just bind the CloseTrigger to a boolean value that would be set when you wanted the window to close.
<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>
Finally, your DataContext/ViewModel would have a property that you'd set when you wanted the window to close like this:
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;
}
(set your Window.DataContext = new MainWindowViewModel())
it may be late, but here is my answer
foreach (Window item in Application.Current.Windows)
{
if (item.DataContext == this) item.Close();
}
Well here is something I used in several projects. It may look like a hack, but it works fine.
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;
}
}
Now you can bind DialogResult to a VM and set its value of a property. The Window will close, when the value is set.
<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult -->
<Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />
This is an abstract of what's running in our production environment
<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>
As you can see, I'm declaring the namespace xmlns:hlp="clr-namespace:AC.Frontend.Helper" first and afterwards the binding hlp:AttachedProperties.DialogResult="{Binding DialogResult}".
The AttachedProperty looks like this. It's not the same I posted yesterday, but IMHO it shouldn't have any effect.
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
}
Easy way
public interface IRequireViewIdentification
{
Guid ViewID { get; }
}
Implement to ViewModel
public class MyViewVM : IRequireViewIdentification
{
private Guid _viewId;
public Guid ViewID
{
get { return _viewId; }
}
public MyViewVM()
{
_viewId = Guid.NewGuid();
}
}
Add general window manager helper
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();
}
}
}
}
And close it like this in viewmodel
WindowManager.CloseWindow(ViewID);
How about this ?
ViewModel:
class ViewModel
{
public Action CloseAction { get; set; }
private void Stuff()
{
// Do Stuff
CloseAction(); // closes the window
}
}
In your ViewModel use CloseAction() to close the window just like in the example above.
View:
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());
}
I know this is an old post, probably no one would scroll this far, I know I didn't. So, after hours of trying different stuff, I found this blog and dude killed it. Simplest way to do this, tried it and it works like a charm.
Blog
In the 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();
}
...
add an Action property to the ViewModel, but define it from the View’s code-behind file. This will let us dynamically define a reference on the ViewModel that points to the View.
On the ViewModel, we’ll simply add:
public Action CloseAction { get; set; }
And on the View, we’ll define it as such:
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());
}
Here is a simple example using the MVVM Light Messenger instead of an event. The view model sends a close message when a button is clicked:
public MainViewModel()
{
QuitCommand = new RelayCommand(ExecuteQuitCommand);
}
public RelayCommand QuitCommand { get; private set; }
private void ExecuteQuitCommand()
{
Messenger.Default.Send<CloseMessage>(new CloseMessage());
}
Then it is received in the code behind of the window.
public Main()
{
InitializeComponent();
Messenger.Default.Register<CloseMessage>(this, HandleCloseMessage);
}
private void HandleCloseMessage(CloseMessage closeMessage)
{
Close();
}
You can create new Event handler in the ViewModel like this.
public event EventHandler RequestClose;
protected void OnRequestClose()
{
if (RequestClose != null)
RequestClose(this, EventArgs.Empty);
}
Then Define RelayCommand for ExitCommand.
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();
}
Then In XAML file set
<Button Command="{Binding CloseCommand}" />
Set the DataContext in the xaml.cs File and Subscribe to the event we created.
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(); };
}
}
My proffered way is Declare event in ViewModel and use blend InvokeMethodAction as below.
Sample 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 interface is as below but don't require to perform this action. ICloseable will help in creating generic view service, so if you construct view and ViewModel by dependency injection then what you can do is
internal interface ICloseable
{
event EventHandler CloseRequested;
}
Use of 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();
}
And Below is Xaml, You can use this xaml even if you don't implement interface, it will only need your view model to raise 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>
You can use Messenger from MVVMLight toolkit. in your ViewModel send a message like this:
Messenger.Default.Send(new NotificationMessage("Close"));
then in your windows code behind, after InitializeComponent, register for that message like this:
Messenger.Default.Register<NotificationMessage>(this, m=>{
if(m.Notification == "Close")
{
this.Close();
}
});
you can find more about MVVMLight toolkit here:
MVVMLight toolkit on Codeplex
Notice that there is not a "no code-behind at all rule" in MVVM and you can do registering for messages in a view code-behind.
You may treat window as a service (eg. UI service) and pass itself to viewmodel via an interface, as such:
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);
}
}
This solution have most upsides of passing the view itself to viewmodel without having downside of breaking MVVM, because though physically view is passed to viewmodel, the latter still don't know about the former, it sees only some IMainWindowAccess. So for instance if we wanted to migrate this solution to other platform, it would be only a matter of implementing IMainWindowAccess properly for, say, an Activity.
I'm posting the solution here to propose a different approach than events (though it's actually very similar), because it seems a little bit simpler than events to implement (attaching/detaching etc.), but still aligns nicely with MVVM pattern.
It's simple.
You can create your own ViewModel class for Login - LoginViewModel.
You can create view var dialog = new UserView(); inside your LoginViewModel.
And you can set-up Command LoginCommand into button.
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />
and
<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />
ViewModel class:
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;
}
}
This is a way I did it pretty simply:
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();
}
I don't see anything wrong with the answer you chose, I just thought this might be a more simple way to do it!
In MVVM WPF I usually design my View as a UserControl. And It is just a matter of how you want to display It. If you want It to be in a Window, then you could do a WindowService class:
public class WindowService
{
//...
public void Show_window(object viewModel, int height, int width, string title)
{
var window = new Window
{
Content = viewModel,
Title = title,
Height = height,
Width = width,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = Application.Current.MainWindow,
Style = (Style)Application.Current.FindResource("Window_style") //even style can be added
};
//If you own custom window style, then you can bind close/minimize/maxmize/restore buttons like this
window.CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, OnCloseWindow));
window.CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, OnMaximizeWindow, OnCanResizeWindow));
window.CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, OnMinimizeWindow, OnCanMinimizeWindow));
window.CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, OnRestoreWindow, OnCanResizeWindow));
window.ShowDialog();
}
public void Close_window(object viewmodel)
{
//Close window
foreach (Window item in Application.Current.Windows)
{
if (item.Content == viewmodel) item.Close();
}
}
}
Using my approach is simple. Usually you want to close Window when something happens in It. So, when It does, just call Close_window method from corresponding ViewModel - the one which is DataContext of UserControl which is displayed in a Window. Look bottom example:
1.) We open Window from some Viewmodel:
public class MyViewModel // ViewModel where you open window
{
private readonly WindowService _windowservice // or inject/inherit from Base
public MyViewModel()
{
_windowservice = new WindowService();
}
private void Example_method()
{
//...Open window
_windowservice.Show_window(new WindowViewModel(),100,100,"Test window");
}
}
2.) Our Window is allready opened, now we want to close It :
public class WindowViewModel // ViewModel which is your Window content!
{
private readonly WindowService _windowservice // or inject/inherit from Base
public MyViewModel()
{
_windowservice = new WindowService();
}
private void Example_method()
{
//Close window
_windowservice.Close(this); //Pass a reference of viewmodel to method
}
}
This solution is far less elegant that other accepted answers, but for me It works. I use It widely in projects, so far no problems with It. But I'm sure that someone will come and say "That is a violation of MVVM principle".
You can close the current window just by using the following code:
Application.Current.Windows[0].Close();
System.Environment.Exit(0); in view model would work.

Categories