2015-06-09 8 views
12

मैं एमएसडीएन पत्रिका (Patterns for Asynchronous MVVM Applications) में स्टीफन क्लेरी द्वारा लेखों की उत्कृष्ट श्रृंखला का पालन कर रहा हूं और "हैलो वर्ल्ड" स्टाइल एप्लिकेशन में अपने IAsyncCommand पैटर्न का उपयोग कर रहा हूं।असिंक्रोनस एमवीवीएम कमांड

हालांकि, एक क्षेत्र जिसे वह संबोधित नहीं करता है वह है जब किसी को कमांड पैरामीटर (इस पैटर्न का उपयोग करके) में पास करने की आवश्यकता होती है। एक मामूली उदाहरण के लिए, प्रमाणीकरण लें जहां सुरक्षा कारणों से पासवर्ड नियंत्रण डेटा-बाध्य नहीं हो सकता है।

मुझे आश्चर्य है कि अगर कोई पैरामीटर के साथ काम करने के लिए AsyncCommand प्राप्त करने में कामयाब रहा है, और यदि ऐसा है, तो क्या वे अपने निष्कर्ष साझा करेंगे?

उत्तर

14

कार्य करने के लिए कार्य करने के दौरान पैरामीटर लेने वाले स्टीफन क्लेरी के IAsyncCommand पैटर्न को प्राप्त करने के लिए कार्य को अपने असिनक कॉमांड क्लास और स्थिर सहायक विधियों में केवल कुछ बदलाव की आवश्यकता होगी।

उपरोक्त लिंक में AsyncCommand4 नमूना में पाए गए अपने वर्गों से शुरू करते हुए, चलिए एक पैरामीटर के लिए इनपुट के साथ एक फ़ंक्शन लेने के लिए कन्स्ट्रक्टर को संशोधित करते हैं (प्रकार ऑब्जेक्ट - यह कमांड पैरामीटर होगा) साथ ही रद्दीकरण टोकन और लौटने एक कार्य। हमें ExecuteAsync विधि में एक भी परिवर्तन करने की आवश्यकता होगी ताकि हम आदेश निष्पादित करते समय पैरामीटर को इस फ़ंक्शन में पास कर सकें। मैंने AsyncCommandEx नामक एक वर्ग बनाई है (नीचे दिखाया गया है) जो इन परिवर्तनों को प्रदर्शित करता है।

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

यह भी कमान पैरामीटर अवगत IAsyncCommands आसान के निर्माण करने के लिए स्थिर AsyncCommand सहायक वर्ग अद्यतन करने के लिए उपयोगी होगा। कार्यों कि कर सकते हैं या एक कमान पैरामीटर हम तरीकों की संख्या दोगुनी हो जाएगा नहीं लेते लेकिन परिणाम बहुत बुरा नहीं है के संभावित संयोजन संभाल करने के लिए:

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

स्टीफन Cleary के नमूने के साथ जारी रखने के लिए, आप अब एक निर्माण कर सकते हैं AsyncCommand जो कमांड पैरामीटर (जो यूआई से बाध्य किया जा सकता है) से पारित ऑब्जेक्ट पैरामीटर लेता है:

CountUrlBytesCommand = AsyncCommandEx.Create((url,token) => MyService.DownloadAndCountBytesAsync(url as string, token)); 
संबंधित मुद्दे