DarioSantarelli.Blog("UgiDotNet");

<sharing mode=”On” users=”*” />
posts - 176, comments - 105, trackbacks - 3

My Links

News


This is my personal blog. These postings are provided "AS IS" with no warranties, and confer no rights.




Tag Cloud

Archives

Post Categories

My English Blog

[WPF] ICommand e CanExecute

Per chi sviluppa utilizzando il pattern M-V-VM l’interfaccia ICommand costituisce la base per costruire un’infrastruttura che permetta alla View di invocare comandi applicativi direttamente nel ViewModel. L’interfaccia mette a disposizione tre semplici membri: due metodi Execute(...), CanExecute(...) ed un evento CanExecuteChanged. E’ facile immaginare come in progetti di una certa consistenza sia necessario costruirsi delle implementazioni spesso particolari dell’interfaccia ICommand, cosa che da una parte implica tanta buona volontà, dall’altra ripaga quando si dormono sonni tranquilli pensando ad una View completamente svincolata dalla logica applicativa.
In genere un’ utile implementazione di ICommand poggia sull’ intercettazione dell’evento CanExecuteChanged in modo da poter esporre alla View informazioni sul fatto che il nostro command sia o meno eseguibile ( si pensi ad esempio ad un Button enabled o meno ). A mio modo di vedere, una limitazione dell’interfaccia ICommand risiede nel metodo CanExecute(…) , il quale appunto è un metodo ( con annesso parametro opzionale ) e non una proprietà, cosa che non mette a disposizione il classico pattern Property/PropertyChanged sfruttabile nei data binding tra ViewModel e View. 
E’ semplice ovviare a questa situazione: ad esempio si può fare in modo che il nostro ICommand implementi una sorta di proprietà “wrapper” per esporre un sistema binding-friendly CanExecute/CanExecuteChanged.
Vediamo un esempio pratico. In una mia applicazione WPF avevo bisogno di realizzare un VM per una Window di Login e volevo “bindare” la proprietà CanLogin del VM ( che semplicemente ritorna true se UserName e Password sono entrambi valorizzati ) direttamente con il metodo CanExecute(…) del mio ICommand.

Ho quindi realizzato un’implementazione di ICommand (chiamata senza fantasia CanExecuteCommand) che accetta:

  1. Un Delegate da eseguire all’interno dell’ Execute(…)
  2. Un oggetto di tipo Binding che rappresenta il binding tra una proprietà “wrapper” per il metodo CanExecute(…) dell’ ICommand (nello specifico CanExecuteAction) e una proprietà del ViewModel (nello specifico CanLogin) in modo da poter scatenare l’evento CanExecuteChanged proprio al variare della proprietà CanLogin del ViewModel.

Difficile da spiegare, facile da implementare :D

Command:


public class CanExecuteCommand : DependencyObject, ICommand
{
  
Action<object> _executeDelegate;
  
public event EventHandler CanExecuteChanged;

  
public bool CanExecuteAction
  
{
    
get { return (bool)GetValue(CanExecuteActionProperty); }
    
set { SetValue(CanExecuteActionProperty, value); }
  
}

  
public static readonly DependencyProperty CanExecuteActionProperty =
          DependencyProperty.Register("CanExecuteAction", typeof(bool), typeof(CanExecuteCommand),
                                      new PropertyMetadata(OnCanExecuteActionChanged));

  
public CanExecuteCommand(Action<object> executeDelegate) : this(executeDelegate, null) { }

  
public CanExecuteCommand(Action<object> executeDelegate, Binding canExecuteActionBinding)
  
{
     
_executeDelegate = executeDelegate;
     
if (canExecuteActionBinding != null) BindingOperations.SetBinding(this, CanExecuteActionProperty, canExecuteActionBinding);           
  
}

  
#region ICommand Members

  
public void Execute(object parameter) { _executeDelegate(parameter); }

  
public bool CanExecute(object parameter) { return CanExecuteAction; }

  
#endregion

  
private void RaiseCanExecuteActionChanged()
  
{
    
if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs());
  
}

  
static void OnCanExecuteActionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
  
{
   
(dependencyObject as CanExecuteCommand).RaiseCanExecuteActionChanged();
  
}                      
}



ViewModel:

 

public class LoginViewModel : ILoginViewModel, INotifyPropertyChanged
{
 
private string _username;
 
private string _password;

 
public event PropertyChangedEventHandler PropertyChanged;

 
public ICommand ReadyCommand { get; private set; } // In Binding con la View

 
#region ILoginViewModel Members

 
public string UserName // In Binding con la View
 
{
   
get { return _username; }
   
set
   
{
     
if (_username != value)
     
{
       
_username = value;
       
RaisePropertyChanged("UserName");
       
RaisePropertyChanged("CanLogin");
     
}
   
}
 
}

 
public string Password // In Binding con la View
 
{
   
get { return _password; }
   
set
   
{
     
if (_password != value)
     
{
       
_password = value;
       
RaisePropertyChanged("Password");
       
RaisePropertyChanged("CanLogin");                   
     
}
    
}
  
}

 
public bool CanLogin
 
{
   
get { return !string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password); }
 
}

 
public void Login()
 
{
   
//... Do Login...           
 
}

 
#endregion

 
public LoginViewModel()
 
{
   
Binding binding = new Binding("CanLogin");
   
binding.Source = this;
   
ReadyCommand = new CanExecuteCommand((arg) => Login(), binding);           
 
}

 
private void RaisePropertyChanged(string propertyName)
 
{
  
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
 
}      
}

 

Technorati Tag: ,

Print | posted on giovedì 2 aprile 2009 23:18 | Filed Under [ WPF ]

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET