I have the following problem: I need to bind an asynchronous method to the Window.Closing event. Since the Closing event expects a CancelEventHandler with void signature, my WindowClosing method can't be of type Task. So I have this:
public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
DeinitializeThisFirst();
DeinitializeThisAfterwards();
}
For hardware reasons, DeinitializeThisFirst() must finish before DeinitializeThisAfterwards() is called. But DeinitializeThisFirst() is an awaitable Task. Since I can't change the signature of Window_Closing to be a Task, I can't do this:
public async Task Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
await DeinitializeThisFirst();
DeinitializeThisAfterwards();
}
And since I am on the UI thread, this will cause a deadlock:
public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
DeinitializeThisFirst().ConfigureAwait(false).GetAwaiter().GetResult();
DeinitializeThisAfterwards();
}
Executing it in a separate Task will cause DeinitializeThisAfterwards() to be executed prematurely:
public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Task.Run(async () => await DeinitializeThisFirst());
DeinitializeThisAfterwards();
}
And wrapping it in ContinueWith() will prevent both from being executed since the window is closed immediately:
public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
DeinitializeThisFirst().ContinueWith(_ =>
{
DeinitializeThisAfterwards();
});
}
I am at a loss here. Can anyone help?
You have to Cancel
the initial Closing
of the window, and Close
it programmatically later when the deinitialization has completed. The following example should give you an idea what to do:
private bool _deinitializeRunning = false;
private bool _deinitializeFinished = false;
public async void Window_Closing(object sender, CancelEventArgs e)
{
if (_deinitializeFinished) return; // Allow the window to close
e.Cancel = true;
if (_deinitializeRunning) return; // Cancel the close and return
_deinitializeRunning = true;
try
{
await DeinitializeThisFirst();
DeinitializeThisAfterwards();
}
finally { _deinitializeRunning = false; }
_deinitializeFinished = true;
await Task.Yield(); // Ensure that the Close is called asynchronously
Close();
}
You have to anticipate for three states of the deinitialization process:
Each state requires different actions inside the Closing
event handler.