CompositeCommand等待所有子命令完成

时间:2015-10-29 10:18:19

标签: c# wpf prism icommand delegatecommand

在我的Dialog中,TabControl使用Prism使用ChildViews。对于保存命令,我使用的是CompositeCommand。一切都按预期工作。 唯一的问题是:我想等待每个ChildViewModel完成异步保存过程,然后在一切完成后关闭对话框。

不幸的是,CompositeCommands不支持该功能。 那么在关闭对话框之前,我怎么能等到每个ViewModel完成工作?

enter image description here

2 个答案:

答案 0 :(得分:2)

好的。使用基于任务的异步模式您的要求是微不足道的事情。

你需要某种我们在框架中尚未拥有的AsynchronousCommand。我们可以毫不费力地自己创造它。

public interface IAsyncCommand : ICommand
{
     Task ExecuteAsync(object parameter);
}

您需要此接口的复合实现,如下所示

public class AsyncCompositeCommand : IAsyncCommand
{
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    private readonly IEnumerable<IAsyncCommand> commands;
    private readonly Action<object> executedCallback;
    public AsyncCompositeCommand(IEnumerable<IAsyncCommand> commands, Action<object> executedCallback)
    {
        this.commands = commands;
        this.executedCallback = executedCallback;
    }

    public bool CanExecute(object parameter)
    {
        return commands.Any(x => x.CanExecute(parameter));
    }     

    public async Task ExecuteAsync(object parameter)
    {
        var pendingTasks = commands.Select(c=> c.ExecuteAsync(parameter))
                                    .ToList();
        await Task.WhenAll(pendingTasks);

        executedCallback(parameter);//Notify
    }

    public async void Execute(object parameter)
    {
        await ExecuteAsync(parameter);
    }
}

在上面的类ExecuteAsync中并行启动其他子命令。如果您希望它是顺序的,则可以使用以下实现替换ExecuteAsync方法。

public async Task ExecuteAsync(object parameter)
{
    foreach (var cmd in commands)
    {
        await cmd.ExecuteAsync(parameter);
    }

    executedCallback(parameter);//Notify
}

executedCallback是一个委托,它将在完成所有子命令时被调用。您可以将窗口关闭代码包装在该委托中。

您的视图模型看起来像这样:

public class ViewModel
{
    public ICommand SaveCommand { get; private set; }

    public ViewModel()
    {
        SaveCommand = new AsyncCompositeCommand(new IAsyncCommand[] 
        {
            command1,
            command2,
            ...
        },
        param => Console.WriteLine("Done"));
    }
}

您可以在UI中绑定ViewModel.SaveCommand属性。

答案 1 :(得分:0)

我有同样的问题,在看了这个答案后,我想出了一个替代解决方案(但赞成接受的答案),假设使用了问题中提到的棱镜。

如果您需要注册和等待的命令不是异步的(常规的 DelegateCommand),您可以简单地从 CompositeCommand 继承并利用它提供的所有其他功能(IActiveAware 监控)。

    /// <summary>
/// Callback composite command.
/// </summary>
public class CallbackCompositeCommand : CompositeCommand
{
    /// <summary>
    /// The callback invoked when commands execution completes.
    /// </summary>
    private readonly Action<object> executedCallback;

    /// <summary>
    /// Initializes a new instance of the <see cref="CallbackCompositeCommand"/> class.
    /// </summary>
    /// <param name="executedCallback">
    /// The callback that will be invoked upon execution completion.
    /// </param>
    public CallbackCompositeCommand(Action<object> executedCallback)
        : this(executedCallback, false)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="CallbackCompositeCommand"/> class.
    /// </summary>
    /// <param name="executedCallback">
    /// The callback that will be invoked upon execution completion.
    /// </param>
    /// <param name="monitorCommandActivity">
    /// Indicates when the command activity is going to be monitored.
    /// </param>
    public CallbackCompositeCommand(Action<object> executedCallback, bool monitorCommandActivity)
        : base(monitorCommandActivity)
    {
        this.executedCallback = executedCallback;
    }

    /// <summary>
    /// Forwards <see cref="M:System.Windows.Input.ICommand.Execute(System.Object)" /> to the registered commands.
    /// </summary>
    /// <param name="parameter">Data used by the command.
    /// If the command does not require data to be passed, this object can be set to <see langword="null" />.
    /// </param>
    public override void Execute(object parameter)
    {
        base.Execute(parameter);
        this.executedCallback(parameter);
    }
}