AutoCommandBinding :-)
mumble… mumble… se il tutto funziona come ci aspettiamo cosa ci vieta di fare ora così?: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 } }
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: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; }
- 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;
Adesso direi che la domanda più interessante di tutte è: ma perchè tutto questo sbattimento? ha senso dannarsi semplicemente per evitare di creare un command?<Button Height="23" Command="{cmd:AutoCommandBinding Path=ExecuteSample}"
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