Вопрос

Примечание:Код в этом вопросе является частью Дезодорант если вам нужен полный исходный код.

Одной из вещей, которые я хотел получить от commands, был продуманный дизайн для асинхронных операций.Я хотел, чтобы нажатая кнопка отключалась во время выполнения команды и возвращалась после завершения.Я хотел, чтобы фактическая работа выполнялась в рабочем элементе ThreadPool.И, наконец, мне нужен был способ обработки любых ошибок, которые возникали во время асинхронной обработки.

Моим решением была команда AsyncCommand:

public abstract class AsyncCommand : ICommand
{
    public event EventHandler CanExecuteChanged;
    public event EventHandler ExecutionStarting;
    public event EventHandler<AsyncCommandCompleteEventArgs> ExecutionComplete;

    public abstract string Text { get; }
    private bool _isExecuting;
    public bool IsExecuting
    {
        get { return _isExecuting; }
        private set
        {
            _isExecuting = value;
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }
    }

    protected abstract void OnExecute(object parameter);

    public void Execute(object parameter)
    {   
        try
        {
            IsExecuting = true;
            if (ExecutionStarting != null)
                ExecutionStarting(this, EventArgs.Empty);

            var dispatcher = Dispatcher.CurrentDispatcher;
            ThreadPool.QueueUserWorkItem(
                obj =>
                {
                    try
                    {
                        OnExecute(parameter);
                        if (ExecutionComplete != null)
                            dispatcher.Invoke(DispatcherPriority.Normal, 
                                ExecutionComplete, this, 
                                new AsyncCommandCompleteEventArgs(null));
                    }
                    catch (Exception ex)
                    {
                        if (ExecutionComplete != null)
                            dispatcher.Invoke(DispatcherPriority.Normal, 
                                ExecutionComplete, this, 
                                new AsyncCommandCompleteEventArgs(ex));
                    }
                    finally
                    {
                        dispatcher.Invoke(DispatcherPriority.Normal, 
                            new Action(() => IsExecuting = false));
                    }
                });
        }
        catch (Exception ex)
        {
            IsExecuting = false;
            if (ExecutionComplete != null)
                ExecutionComplete(this, new AsyncCommandCompleteEventArgs(ex));
        }
    }

    public virtual bool CanExecute(object parameter)
    {
        return !IsExecuting;
    }
}

итак, вопрос заключается в следующем:Необходимо ли все это?Я заметил встроенную асинхронную поддержку привязки данных, так почему бы не выполнить команду?Возможно, это связано с вопросом о параметрах, который является моим следующим вопросом.

Это было полезно?

Решение

Мне удалось доработать исходный образец и дать несколько советов всем, кто еще сталкивался с подобными ситуациями.

Во-первых, подумайте, удовлетворит ли BackgroundWorker ваши потребности.Я все еще часто использую AsyncCommand, чтобы получить функцию автоматического отключения, но если бы многое можно было сделать с помощью BackgroundWorker.

Но, оборачивая BackgroundWorker, AsyncCommand предоставляет функциональность, подобную команде, с асинхронным поведением (у меня также есть запись в блоге на эту тему)

public abstract class AsyncCommand : ICommand
{
    public event EventHandler CanExecuteChanged;
    public event EventHandler RunWorkerStarting;
    public event RunWorkerCompletedEventHandler RunWorkerCompleted;

    public abstract string Text { get; }
    private bool _isExecuting;
    public bool IsExecuting
    {
        get { return _isExecuting; }
        private set
        {
            _isExecuting = value;
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }
    }

    protected abstract void OnExecute(object parameter);

    public void Execute(object parameter)
    {   
        try
        {   
            onRunWorkerStarting();

            var worker = new BackgroundWorker();
            worker.DoWork += ((sender, e) => OnExecute(e.Argument));
            worker.RunWorkerCompleted += ((sender, e) => onRunWorkerCompleted(e));
            worker.RunWorkerAsync(parameter);
        }
        catch (Exception ex)
        {
            onRunWorkerCompleted(new RunWorkerCompletedEventArgs(null, ex, true));
        }
    }

    private void onRunWorkerStarting()
    {
        IsExecuting = true;
        if (RunWorkerStarting != null)
            RunWorkerStarting(this, EventArgs.Empty);
    }

    private void onRunWorkerCompleted(RunWorkerCompletedEventArgs e)
    {
        IsExecuting = false;
        if (RunWorkerCompleted != null)
            RunWorkerCompleted(this, e);
    }

    public virtual bool CanExecute(object parameter)
    {
        return !IsExecuting;
    }
}

Другие советы

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top