Domanda

Nota : il codice in questa domanda fa parte di deSleeper se vuoi la fonte completa.

Una delle cose che volevo fuori dai comandi era un design cotto per operazioni asincrone. Volevo che il pulsante premuto si disabilitasse durante l'esecuzione del comando e tornasse al termine. Volevo che il vero lavoro fosse eseguito in un oggetto di lavoro ThreadPool. Infine, volevo un modo per gestire eventuali errori verificatisi durante l'elaborazione asincrona.

La mia soluzione era un 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;
    }
}

quindi la domanda è: tutto ciò è necessario? Ho notato il supporto asincrono incorporato per l'associazione dati, quindi perché non eseguire l'esecuzione? Forse è legato alla domanda sui parametri, che è la mia prossima domanda.

È stato utile?

Soluzione

Sono stato in grado di perfezionare il campione originale e di avere qualche consiglio per chiunque si imbatte in situazioni simili.

Innanzitutto, considera se BackgroundWorker soddisferà le esigenze. Uso ancora AsyncCommand spesso per ottenere la funzione di disabilitazione automatica, ma se si potrebbero fare molte cose con BackgroundWorker.

Ma avvolgendo BackgroundWorker, AsyncCommand fornisce funzionalità simili ai comandi con comportamento asincrono (ho anche un post di blog su questo argomento )

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

Altri suggerimenti

Come ho risposto nell'altra tua domanda, probabilmente vorrai comunque collegarti a questo in modo sincrono e quindi lanciare i comandi in modo asincrono. In questo modo eviti i problemi che stai riscontrando ora.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top