Pergunta

Nota : O código nesta questão é parte de deSleeper se quiser que a fonte completo.

Uma das coisas que eu queria sair de comandos era um projeto cozido para operações assíncronas. Eu queria o botão pressionado para desativar enquanto o comando foi executado, e voltar quando completa. Eu queria que o trabalho real a ser realizado em um item de trabalho ThreadPool. E, finalmente, eu queria uma maneira de lidar com quaisquer erros que ocorreram durante o processamento assíncrono.

A minha solução foi um 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;
    }
}

Então a questão é: todos Isso é necessário? Tenho notado suporte embutido assíncrona para dados vinculativo, então por que não comandar a execução? Talvez ele está relacionado com a questão parâmetro, que é a minha próxima pergunta.

Foi útil?

Solução

Eu tenho sido capaz de refinar a amostra original para baixo e ter alguns conselhos para ninguém correr em situações semelhantes.

Em primeiro lugar, considerar se BackgroundWorker irá satisfazer as necessidades. Eu ainda uso AsyncCommand muitas vezes para obter a função desativar automática, mas se muitas coisas poderia ser feito com BackgroundWorker.

Mas por envolvimento BackgroundWorker, AsyncCommand fornece comando como funcionalidade com comportamento assíncrono (Eu também tenho um entrada de blog sobre este tema )

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;
    }
}

Outras dicas

Como eu respondeu em sua outra pergunta, você provavelmente ainda quer se ligam a esta forma síncrona e, em seguida, lançar os comandos de forma assíncrona. Dessa forma, você evitar os problemas que você está tendo agora.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top