Handling the Window Closing Event with Wpf/Mvvm Light Toolkit

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



Leave a reply



Submit