canton7 / Stylet

A very lightweight but powerful ViewModel-First MVVM framework for WPF for .NET Framework and .NET Core, inspired by Caliburn.Micro.
MIT License
988 stars 143 forks source link

Properly listen to application closing event from a view model. #136

Closed daawaan4U closed 4 years ago

daawaan4U commented 4 years ago

I haven't found a way to listen to the closing of an application from a view model so that I can notify the user before the app will finally close (e.g. asking the user to save the active file before closing). I've been thinking of using the IEventAggregator to publish an application-closing event from the OnClose event of the bootstrapper to broadcast that the application will close and I will just subsribe the view model.

Hopefully, are there any existing methods in Stylet that I might have missed that can notify viewmodels about the lifetime of the application?

Framework: .NET Core 3.1 Visual Studio version: 16.6.0 Stylet version: 1.3.3

canton7 commented 4 years ago

Override Screen.CanCloseAsync?

daawaan4U commented 4 years ago

Ohh rightt. I just noticed I don't actually have to listen to the application-closing and just make sure that the active file will be saved everytime the viewmodel is going to be closed. OnClose() is enough, Thanks!

daawaan4U commented 4 years ago

Hello again! I did what you suggested by overriding the CanCloseAsync() (instead of the OnClose() because I can't execute asynchronous operations inside OnClose()). I was able to notify the user before the app closed through a call from WindowManager.ShowMessageBox(). Unfortunately, I encountered some unexpected issues.

What I did was I called the RequestClose() of the ShellViewModel from the Closing event of the ShellView Window. In the child ViewModel of the ShellViewModel, I overrode the CanCloseAsync where I called the WindowManager.ShowMessageBox() and an additional asynchronous operation for saving the file. Unexpectedly, the CanCloseAsync() was being called twice when I step through my code causing the MessageBox to display again and eventually throwing an exception:

InvalidOperationException: Cannot set Visibility to Visible or call Show, ShowDialog, Close, or WindowInteropHelper.EnsureHandle while a Window is closing.

  at System.Windows.Window.VerifyNotClosing()
   at System.Windows.Window.InternalClose(Boolean shutdown, Boolean ignoreCancel)
   at System.Windows.Window.Close()
   at Stylet.WindowManager.WindowConductor.<Stylet-IChildDelegate-CloseItem>d__6.MoveNext() in /_/Stylet/WindowManager.cs:line 372
...

It seems like showing a message box while the main window is closing doesn't work. An answer from stackoverflow mentions setting the Cancel property of the CancelEventArgs. However, the CancelEventArgs is already handled within the WindowManager so I think there's nothing I can do by just using user code only.

daawaan4U commented 4 years ago

Just an update, I already fixed my issue. The cause of the CanCloseAsync() being called twice was because I registered a RequestClose() on the closing event of the Window while not being mindful enough that the WindowManager already handles the closing of the RootViewModel's Window. Overriding only the CanCloseAsync() of the child ViewModel was actually already enough. Thanks again!

canton7 commented 4 years ago

Thanks for the update, glad you solved it!