2015-06-09 16 views
12

MSDN dergisindeki (Patterns for Asynchronous MVVM Applications) Stephen Cleary'nin oldukça mükemmel makalelerini takip ediyorum ve "merhaba dünya" stili uygulamasında IAsyncCommand modelini kullanıyorum. Bununla birlikte, bir Komut Parametresinde (bu deseni kullanarak) geçiş yapılması gerektiğinde, ele almaması gereken bir alan olmasıdır. Önemsiz bir örnek olarak, şifre kontrolü güvenlik nedenleriyle veriye bağlı olmayacaksa Kimlik Doğrulama yapın.Asenkron MVVM komutları

Kimse onun parametrelerini çalışmak için onun AsyncCommand almayı başarmıştı ve eğer öyleyse, onların bulgularını paylaşır mı?

cevap

14

Stephen Cleary'nin IAsyncCommand desenini, çalıştırılacak olan Görevi üretirken bir parametre alan işlevlerle çalışmak, AsyncCommand sınıfına ve statik yardımcı yöntemlerine yalnızca birkaç ince ayar gerektirir.

Yukarıdaki bağlantıda bulunan AsyncCommand4 örneğinde bulunan sınıflarıyla başlayarak, bir parametrenin girişleri (tip nesnenin - bu Komut Parametresi olacaktır) yanı sıra bir CancellationToken ve geri dönüşü için bir işlev almak üzere kurucuyu değiştirelim. bir görev. Ayrıca, ExecuteAsync yönteminde tek bir değişiklik yapmamız gerekecek, böylece komutu çalıştırırken parametreyi bu işleve iletebiliriz. Bu değişiklikleri gösteren AsyncCommandEx (aşağıda gösterilen) adlı bir sınıf oluşturdum.

public class AsyncCommandEx<TResult> : AsyncCommandBase, INotifyPropertyChanged 
{ 
    private readonly CancelAsyncCommand _cancelCommand; 
    private readonly Func<object, CancellationToken, Task<TResult>> _command; 
    private NotifyTaskCompletion<TResult> _execution; 

    public AsyncCommandEx(Func<object, CancellationToken, Task<TResult>> command) 
    { 
     _command = command; 
     _cancelCommand = new CancelAsyncCommand(); 
    } 

    public ICommand CancelCommand 
    { 
     get { return _cancelCommand; } 
    } 

    public NotifyTaskCompletion<TResult> Execution 
    { 
     get { return _execution; } 
     private set 
     { 
      _execution = value; 
      OnPropertyChanged(); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    public override bool CanExecute(object parameter) 
    { 
     return (Execution == null || Execution.IsCompleted); 
    } 

    public override async Task ExecuteAsync(object parameter) 
    { 
     _cancelCommand.NotifyCommandStarting(); 
     Execution = new NotifyTaskCompletion<TResult>(_command(parameter, _cancelCommand.Token)); 
     RaiseCanExecuteChanged(); 
     await Execution.TaskCompletion; 
     _cancelCommand.NotifyCommandFinished(); 
     RaiseCanExecuteChanged(); 
    } 

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    private sealed class CancelAsyncCommand : ICommand 
    { 
     private bool _commandExecuting; 
     private CancellationTokenSource _cts = new CancellationTokenSource(); 

     public CancellationToken Token 
     { 
      get { return _cts.Token; } 
     } 

     bool ICommand.CanExecute(object parameter) 
     { 
      return _commandExecuting && !_cts.IsCancellationRequested; 
     } 

     void ICommand.Execute(object parameter) 
     { 
      _cts.Cancel(); 
      RaiseCanExecuteChanged(); 
     } 

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

     public void NotifyCommandStarting() 
     { 
      _commandExecuting = true; 
      if (!_cts.IsCancellationRequested) 
       return; 
      _cts = new CancellationTokenSource(); 
      RaiseCanExecuteChanged(); 
     } 

     public void NotifyCommandFinished() 
     { 
      _commandExecuting = false; 
      RaiseCanExecuteChanged(); 
     } 

     private void RaiseCanExecuteChanged() 
     { 
      CommandManager.InvalidateRequerySuggested(); 
     } 
    } 
} 

Ayrıca Komut Parametre farkında IAsyncCommands daha kolay yaratılması yapmak için statik AsyncCommand yardımcı sınıf güncellemek için yararlı olacaktır. yapmak ya da biz yöntemlerin sayısını iki katına çıkacak bir Komut Parametre yapmayız ama sonuç çok kötü değil fonksiyonların olası kombinasyonları işlemek için:

public static class AsyncCommandEx 
{ 
    public static AsyncCommandEx<object> Create(Func<Task> command) 
    { 
     return new AsyncCommandEx<object>(async (param,_) => 
               { 
                await command(); 
                return null; 
               }); 
    } 

    public static AsyncCommandEx<object> Create(Func<object, Task> command) 
    { 
     return new AsyncCommandEx<object>(async (param, _) => 
     { 
      await command(param); 
      return null; 
     }); 
    } 

    public static AsyncCommandEx<TResult> Create<TResult>(Func<Task<TResult>> command) 
    { 
     return new AsyncCommandEx<TResult>((param,_) => command()); 
    } 

    public static AsyncCommandEx<TResult> Create<TResult>(Func<object, Task<TResult>> command) 
    { 
     return new AsyncCommandEx<TResult>((param, _) => command(param)); 
    } 

    public static AsyncCommandEx<object> Create(Func<CancellationToken, Task> command) 
    { 
     return new AsyncCommandEx<object>(async (param, token) => 
               { 
                await command(token); 
                return null; 
               }); 
    } 

    public static AsyncCommandEx<object> Create(Func<object, CancellationToken, Task> command) 
    { 
     return new AsyncCommandEx<object>(async (param, token) => 
     { 
      await command(param, token); 
      return null; 
     }); 
    } 

    public static AsyncCommandEx<TResult> Create<TResult>(Func<CancellationToken, Task<TResult>> command) 
    { 
     return new AsyncCommandEx<TResult>(async (param, token) => await command(token)); 
    } 

    public static AsyncCommandEx<TResult> Create<TResult>(Func<object, CancellationToken, Task<TResult>> command) 
    { 
     return new AsyncCommandEx<TResult>(async (param, token) => await command(param, token)); 
    } 
} 

Stephen Cleary'nin örnek ile devam etmek için, şimdi bir inşa edebilirsiniz Komut Parametresi'nden (UI'ye bağlı olabilen) bir nesne parametresini alan AsyncCommand:

CountUrlBytesCommand = AsyncCommandEx.Create((url,token) => MyService.DownloadAndCountBytesAsync(url as string, token));