Question

Remarque : le code de cette question fait partie de deSleeper si vous voulez la source complète.

Une des choses que je voulais dans les commandes était une conception cuite pour les opérations asynchrones. Je voulais que le bouton soit désactivé pour que la commande soit exécutée et que je revienne à la fin. Je voulais que le travail réel soit effectué dans un élément de travail ThreadPool. Enfin, je voulais un moyen de gérer les erreurs survenues lors du traitement asynchrone.

Ma solution était une commande asynchrone:

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

donc la question est: Est-ce que tout cela est nécessaire? J'ai remarqué la prise en charge asynchrone intégrée de la liaison de données, alors pourquoi ne pas exécuter la commande? Peut-être est-ce lié à la question de paramètre, qui est ma prochaine question.

Était-ce utile?

La solution

J'ai été en mesure d'affiner l'échantillon d'origine et de donner des conseils à quiconque se trouverait dans une situation similaire.

Tout d’abord, demandez-vous si BackgroundWorker répondra aux besoins. J'utilise toujours AsyncCommand souvent pour obtenir la fonction de désactivation automatique, mais si beaucoup de choses peuvent être faites avec BackgroundWorker.

Mais en enveloppant BackgroundWorker, AsyncCommand fournit une fonctionnalité semblable à celle d'une commande avec un comportement asynchrone (j'ai également un entrée de blog sur ce sujet )

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

Autres conseils

Comme je l’ai répondu dans votre autre question, vous souhaiterez probablement toujours vous connecter à cela de manière synchrone puis lancer les commandes de manière asynchrone. De cette façon, vous éviterez les problèmes que vous rencontrez maintenant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top