Асинхронные команды WPF - PullRequest
       23

Асинхронные команды WPF

13 голосов
/ 30 сентября 2008

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

Одной из вещей, которые я хотел от команд, был запеченный дизайн для асинхронных операций. Я хотел, чтобы нажатая кнопка отключалась во время выполнения команды и возвращалась после завершения. Я хотел, чтобы фактическая работа выполнялась в рабочем элементе 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;
    }
}

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

Ответы [ 2 ]

4 голосов
/ 23 октября 2008

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

Во-первых, подумайте, удовлетворяет ли 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;
    }
}
1 голос
/ 30 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...