Più lo uso e più mi piace. Adesso che abbiamo un’infrastruttura che in qualche modo ci consente di intercettare il processo di Data Binding possiamo pensare di avere un ViewModel che non contiene più i command ma piuttosto una cosa del genere:
public Fact CanExecuteSample
{
    get
    {
        var fact = Fact.Create( o => !String.IsNullOrEmpty( this.SampleText ) );
return fact;
} } [KeyBinding( Key.Enter, Modifiers = ModifierKeys.Control )] public void ExecuteSample() { if( this.CanExecuteSample ) { //Command execution logic } }
mumble… mumble… se il tutto funziona come ci aspettiamo cosa ci vieta di fare ora così?:
protected override IBindableCommand GetCommand( DependencyObject target, DependencyProperty targetProperty )
{
    if( this.CanCreateCommand( target ) )
    {
        var dataContext = this.GetDataContext( target );
        var path = this.Path.Path;
        var methodName = path.EndsWith( "Command" ) ? path.Remove( path.IndexOf( "Command" ) ) : path;
        var factName = String.Concat( "Can", methodName );
        var method = dataContext.GetType().GetMethod( methodName );

        var definition = dataContext.GetType()
            .GetMethods()
            .Where( mi => mi.Name.Equals( methodName ) )
            .Select( mi =>
            {
                var prms = mi.GetParameters();

                return new
                {
                    DataContext = dataContext,
                    FastDelegate = mi.CreateVoidDelegate(),
                    Fact = dataContext.GetType()
                                .GetProperties()
                                .Where( pi =>
                                {
                                    return pi.PropertyType == typeof( Fact ) && pi.Name.Equals( factName );
                                } )
                                .Select( pi =>
                                {
                                    var fact = ( Fact )pi.GetValue( dataContext, null );
                                    return fact;
                                } )
                                .SingleOrDefault(),
                    HasParameter = prms.Length == 1,
                    ParameterType = prms.Length != 1 ? null : prms[ 0 ].ParameterType,
                    KeyBindings = mi.GetAttributes<KeyBindingAttribute>(),
                    Description = method.GetAttribute<CommandDescriptionAttribute>()
                };
            } )
            .SingleOrDefault();

        var text = ( definition.Description == null ) ? String.Empty : definition.Description.DisplayText;
        var cmd = DelegateCommand.Create( text )
            .OnCanExecute( o =>
            {
                return definition.Fact != null ?
                    definition.Fact.Eval( o ) :
                    true;
            } )
            .OnExecute( o =>
            {
                if( definition.HasParameter )
                {
                    var prm = Convert.ChangeType( o, definition.ParameterType );
                    definition.FastDelegate( definition.DataContext, new[] { prm } );
                }
                else
                {
                    definition.FastDelegate( definition.DataContext, null );
                }
            } );

        if( definition.KeyBindings != null )
        {
            definition.KeyBindings
                .ForEach( kb => cmd.AddKeyGesture( kb.Key, kb.Modifiers ) );
        }

        if( definition.Fact != null )
        {
            cmd.AddMonitor( definition.Fact );
        }

        target.SetValue( targetProperty, cmd );

        return cmd;
    }

    return null;
}
La classe base, per estrarre il command, si limitava a recuperare il valore della Dependency Property… che però ora non c’è più… cosa facciamo? un sacco di roba, proprio un sacco:
  • facciamo una prima valutazione per capire se possiamo creare/siamo interessati a ceare un comando:
protected virtual Boolean CanCreateCommand( DependencyObject target )
{
    if( DesignTimeHelper.GetIsInDesignMode() )
    {
        return false;
    }
    return this.Path != null && ( target is FrameworkElement || target is FrameworkContentElement );
}
  • estraiamo poi un po’ di dati e creiamo un tipo anonimo che faccia da dto per tutti i dati che abbiamo estratto;
  • la parte relativa al CreateVoidDelegate() è una delle tante implementazioni di “fast delegate” via Lambda Expression;
  • infine creiamo il vero e proprio DelegateCommand da settare sul target del Binding;
Questo in ultimo ci permette di scrivere:
<Button Height="23" Command="{cmd:AutoCommandBinding Path=ExecuteSample}"
Adesso direi che la domanda più interessante di tutte è: ma perchè tutto questo sbattimento? ha senso dannarsi semplicemente per evitare di creare un command?
In realtà la risposta ad entrambe le domande è no… ma c’è un problema di fondo che è legato a come funzinona il motore di data binding internamente e tutto questo rumore è finalizzato a capire come il signorino ragiona ogni volta che deve risolvere un binding… e vi garantisco che in base allo scenario il suo ragionamento interno cambia radicalmente.
Stay tuned :-)
.m