13

:完全なソースが必要な場合、この質問のコードはdeSleeperの一部です。

私がコマンドに望んでいたことの 1 つは、非同期操作用のベイクド デザインでした。コマンドの実行中にボタンを押して無効にし、完了したら戻ってくるようにしました。実際の作業は 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;
    }
}

質問は次のとおりです。これはすべて必要ですか?データバインディングの非同期サポートが組み込まれていることに気付きましたが、コマンドを実行しないのはなぜですか? おそらく、次の質問であるパラメーターの質問に関連しています。

4

2 に答える 2

4

私は元のサンプルを改良し、同様の状況に遭遇した他の人にアドバイスをすることができました。

まず、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;
    }
}
于 2008-10-23T06:22:02.527 に答える
1

他の質問で答えたように、おそらくこれに同期的にバインドしてから、コマンドを非同期的に起動したいでしょう。そうすれば、今抱えている問題を回避できます。

于 2008-09-30T04:18:22.277 に答える