WPF MVVM cancel Window.Closing event
With MVVM Light you got EventToCommand
:
So you could in xaml wire up the closing event to the VM.
<Window ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<command:EventToCommand Command="{Binding ClosingCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
and in the VM:
public RelayCommand<CancelEventArgs> ClosingCommand { get; private set; }
ctor() {
ClosingCommand = new RelayCommand<CancelEventArgs>(args => args.Cancel = true);
}
If you do not want to pass CancelEventArgs
to the VM:
You could always take the similar approach with a Behavior
and just use a simple bool
from the VM(bind this bool to the Behavior) to indicate the closing event should be cancelled.
Update:
Download Link for following example
To do this with a Behavior
you could just have a Behavior
such as:
internal class CancelCloseWindowBehavior : Behavior<Window> {
public static readonly DependencyProperty CancelCloseProperty =
DependencyProperty.Register("CancelClose", typeof(bool),
typeof(CancelCloseWindowBehavior), new FrameworkPropertyMetadata(false));
public bool CancelClose {
get { return (bool) GetValue(CancelCloseProperty); }
set { SetValue(CancelCloseProperty, value); }
}
protected override void OnAttached() {
AssociatedObject.Closing += (sender, args) => args.Cancel = CancelClose;
}
}
Now in xaml:
<i:Interaction.Behaviors>
<local:CancelCloseWindowBehavior CancelClose="{Binding CancelClose}" />
</i:Interaction.Behaviors>
Where CancelClose
is a bool property from the VM which indicates if the Closing
event should be cancelled or not. In the attached example I have a Button
to toggle this bool from the VM that should let you test the Behavior
WPF & MVVM Light - Closing a child window with buttons via Messenger
public class WindowClass
{
public WindowClass()
{
var messenger = ServiceLocator.Current.GetInstance<IMessenger>();
messenger.Register<CloseWindowMessage>(this, _ => Close());
}
}
Where CloseWindowMessage
is
public class CloseWindowMessage : MessageBase {}
WPF & MVVM Light - Closing a specific child window via Messenger
Token approach:
You have a uniqueKey
you generate each of your AdventurerView
with. Just use that when sending the CloseWindowMessage
as the token.
Firstly in AdventurerViewModel
add a new property of type string say "WindowKey"
public string WindowKey { get; set; }
Next add a constructor to take the unique key in AdventureView
. In AdventurerView.xaml.cs
:
public AdventurerView()
:this(string.Empty) {}
public AdventurerView(string uniqueKey) {
InitializeComponent();
Messenger.Default.Register<CloseWindowMessage>(this, uniqueKey, s => Close());
}
Next in MainWindow.xaml.cs
switch
Messenger.Default.Register<OpenWindowMessage>(this, message =>
{
var uniqueKey = System.Guid.NewGuid().ToString();
...
adventurerWindow.Show();
});
to
Messenger.Default.Register<OpenWindowMessage>(this, message =>
{
var uniqueKey = System.Guid.NewGuid().ToString();
var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>(uniqueKey);
adventurerWindowVM.Adv = message.Argument;
adventurerWindowVM.WindowKey = uniqueKey;
var adventurerWindow = new AdventurerView(uniqueKey)
{
DataContext = adventurerWindowVM,
Owner = this
};
adventurerWindow.Closed += (sender, args) => SimpleIoc.Default.Unregister<AdventurerViewModel>(uniqueKey);
adventurerWindow.Show();
});
Finally in AdventurerViewModel.xaml.cs
:
switch
private void ExecuteAcceptCommand() {
Messenger.Default.Send(Adv.Name);
Messenger.Default.Send<CloseWindowMessage>(new CloseWindowMessage());
}
to
private void ExecuteAcceptCommand() {
Messenger.Default.Send(Adv.Name);
Messenger.Default.Send<CloseWindowMessage>(new CloseWindowMessage(), WindowKey);
}
Alternate:
Even-though the above would work perfectly fine, there is an alternate to this approach. Your "Message" is already a custom strong type(CloseWindowMessage
). Now you can just add WindowKey
as part of the message and have each Window
when it receives a new CloseWindowMessage
check the WindowKey
in the message against it's own key and Close()
accordingly.
Confirmation when closing window with 'X' button with MVVM light
You can use an EventToCommand
in an EventTrigger
to catch the closing event and set the Cancel
property of the passed CancelEventArgs
to true if you want to cancel the closing:
XAML:
<Window ...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF45"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<cmd:EventToCommand Command="{Binding OnClosingCommand}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
...
</Grid>
</Window>
ViewModel:
public class MainViewModel : ViewModelBase
{
public RelayCommand<CancelEventArgs> OnClosingCommand { get; set; }
public MainViewModel()
{
this.OnClosingCommand =
new RelayCommand<CancelEventArgs>(this.OnClosingCommandExecuted);
}
private void OnClosingCommandExecuted(CancelEventArgs cancelEventArgs)
{
...
if (mustCancelClosing)
{
cancelEventArgs.Cancel = true;
}
}
}
Related Topics
Why Does Timespan.Parseexact Not Work
Fake-Scrolling Containers with Very Many Controls
Possible Unintended Reference Comparison
C# Get a Control's Position on a Form
C# Generic "Where Constraint" with "Any Generic Type" Definition
Memory Efficiency and Performance of String.Replace .Net Framework
A Dependent Property in a Referentialconstraint Is Mapped to a Store-Generated Column
Unity Create UI Control from Script
How to Keep Console Window Open
How Math.Pow (And So On) Actually Works
Newtonsoft JSON.Net Deserialization Error Where Fields in JSON Change Order
Query String Not Working While Using Attribute Routing
String.Replace() VS. Stringbuilder.Replace()
Why Is It Impossible to Override a Getter-Only Property and Add a Setter