Как использовать DerralManager GetDeferral () в событии закрытия WPF? - PullRequest
0 голосов
/ 08 ноября 2018

Wpf

Я пытаюсь отложить закрытие окна, пока все задачи не будут выполнены с помощью библиотека асинхронных / ожидающих файлов StephenCleary https://github.com/StephenCleary/AsyncEx.

Определения делегата обработчика события и аргументов события:

public delegate void CancelEventHandlerAsync(object sender, CancelEventArgsAsync e);

public class CancelEventArgsAsync : CancelEventArgs
{
    private readonly DeferralManager _deferrals = new DeferralManager();

    public IDisposable GetDeferral()
    {
        return this._deferrals.GetDeferral();
    }

    public Task WaitForDefferalsAsync()
    {
        return this._deferrals.SignalAndWaitAsync();
    }
}

Затем в коде NewWindowDialog.xaml я переопределяю событие OnClosing:

public NewWindowDialog()
        {
            InitializeComponent();

        }

        protected override async void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            e.Cancel = true;
            base.OnClosing(e);
            await LaunchAsync();
        }

        private async Task LaunchAsync()
        {
            var vm =(NewProgressNoteViewModel)DataContext;
            var cancelEventArgs = new CancelEventArgsAsync();
            using (var deferral = cancelEventArgs.GetDeferral())
            {
                // a very long procedure!
                await vm.WritingLayer.CompletionAsync();
            }

        }

Очевидно, что это не удалось, так как e.Cancel = true выполняется перед ожиданием. Так что я упускаю, чтобы правильно использовать GetDeferral (), чтобы задержать закрытие окна во время выполнения задач (в WPF).

ТИА

Редактировать: с помощью всех, я в настоящее время использую это. Однако есть ли у кого-нибудь хороший пример паттерна отсрочки при закрытии окна?

Спасибо всем.

private bool _handleClose = true;
        protected override async void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            using (new BusyCursor())
            {
                if (_handleClose)
                {
                    _handleClose = false;
                    IsEnabled = false;
                    e.Cancel = true;

                    var vm = (NewProgressNoteViewModel)DataContext;

                    await vm.WritingLayer.SaveAsync();

                    e.Cancel = false;
                    base.OnClosing(e);
                }
            }
        }

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Я полагаю, что это более удобный для пользователя способ показать модальное сообщение "Пожалуйста, подождите ..." в вашем обработчике Window.Closing, которое исчезает после завершения Task. Таким образом, поток управления не покидает ваш обработчик Closing, пока не будет безопасно закрыть приложение.

Ниже приведен полный пример того, как это можно сделать в WPF. Обработка ошибок пропущена для краткости. Вот также связанный вопрос , связанный с аналогичной проблемой.

using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace WpfApp
{
    public partial class MainWindow : Window
    {
        Task _longRunningTask;

        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (_longRunningTask?.IsCompleted != false)
            {
                return;
            }

            var canClose = false;

            var dialog = new Window
            {
                Owner = this,
                Width = 320,
                Height = 200,
                WindowStartupLocation = WindowStartupLocation.CenterOwner,
                Content = new TextBox {
                    Text = "Please wait... ",
                    HorizontalContentAlignment = HorizontalAlignment.Center,
                    VerticalContentAlignment = VerticalAlignment.Center
                },
                WindowStyle = WindowStyle.None
            };

            dialog.Closing += (_, args) =>
            {
                args.Cancel = !canClose;
            };

            dialog.Loaded += async (_, args) =>
            {
                await WaitForDefferalsAsync();
                canClose = true;
                dialog.Close();
            };

            dialog.ShowDialog();
        }

        Task WaitForDefferalsAsync()
        {
            return _longRunningTask;
        }

        public MainWindow()
        {
            InitializeComponent();

            this.Closing += MainWindow_Closing;

            _longRunningTask = Task.Delay(5000);
        }
    }
}
0 голосов
/ 08 ноября 2018

Вам не нужна отсрочка. Просто установите для свойства CancelEventArgs.Cancel значение true, дождитесь длительной операции и затем закройте. Вы можете использовать флаг, чтобы избежать повторения одного и того же:

private bool _handleClose = true;
protected override async void OnClosing(System.ComponentModel.CancelEventArgs e)
{
    if (_handleClose)
    {
        e.Cancel = true;
        await Task.Delay(5000);// a very long procedure!
        _handleClose = false;
        Close();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...