Home > front end >  Setting cancel flag of Window Closing event asynchronously
Setting cancel flag of Window Closing event asynchronously

Time:08-12

I currently have an asynchronous event handler on the Window.Closing event like

private async void View_Closing(object sender, CancelEventArgs e) {
    await OnClosingAsync(e);
}
private async Task OnClosing(CancelEventArgs e) {
    foreach (BaseConfigEditViewModel viewModel in ConfigEditViewModels) {
        if (!await viewModel.IsReadyToCloseAsync()) {
            e.Cancel = true;
            return;
        }
    }

    List<BaseConfigEditViewModel> tmpViewModels = new(ConfigEditViewModels);
    foreach (BaseConfigEditViewModel viewModel in tmpViewModels) {
        viewModel.Close();
    }
}
public async Task<bool> IsReadyToCloseAsync() {
    if (await GetHasChangedAsync()) {
        switch (MessageBox.Show($"{Header} has changed. Save changes?", "Info", MessageBoxButton.YesNoCancel, MessageBoxImage.Question)) {
            case MessageBoxResult.Yes:
                await SaveAsync();
                return true;
            case MessageBoxResult.Cancel:
                return false;
            case MessageBoxResult.No:
                return true;
        }
    }

    return true;
}

where the e.Cancel flag gets set in an asynchronous method that is awaited in the handler.

As the event handler itself doesn't return a task, yet is async, to my knowledge this implies a "dumb" fire-and-forget mechanism for the window who invokes the event.

How come it still knows how long to wait for the event to be handled by all subscribers? It would need to await the handling in order to know whether some subscriber has set the Cancel flag, doesn't it?

Edit: Included the whole call chain across various classes up to the async methods that perform various DB calls.

CodePudding user response:

As it turns out, this is not reproducible in a smaller application. While in my app even an arbitrary Task.Delay doesn't cause the canceling to file, i.e. a handler like

private async void View_Closing(object sender, CancelEventArgs e) {
    await Task.Delay(100);

    // ...

    e.Cancel = true;
}

a similar construct in a simple app without anything else fails to cancel.

Hence, it is actually dangerous to do what I was doing, as the dumb fire mechanism of the async void event doesn't guarantee that the source knows every subscriber had it's chance to cancel.

As to the app I am working on; seems like some other synchronous components of the app I am part of take long enough to handle their synchronous closing-Event, so it was never a problem, even with said arbitrary Task.Delay.

  • Related