Estendere il motore di DataBinding, a oggi, è impossibile perchè è tutto internal e/o sealed… immagino abbiano avuto i loro buoni motivi e posso immaginare anche quali siano, sta di fatto che è una gran menata…
Possiamo però partire da questo post, come suggerito da Giuseppe, e wrappare la classe Binding in una nostra classe Binding esponendo le stesse identitche proprietà che saranno dei meri proxy verso la classe Binding reale; facendo infine semplicemente questo:
public override object ProvideValue( IServiceProvider provider )
{
    return binding.ProvideValue( provider );
}
Nel momento in cui la nostra markup extension viene effettivamente invocata l’unica cosa che possiamo fare è passare la palla alla vera markup extension con cui Wpf è in grado di interagire.
Ma non è ancora tutto perso… se usiamo M-V-VM siamo probabilmente molto avvezzi al concetto di DelegateCommand/RelayCommand e alla possibilità di scrivere una cosa di questo genere:
var command = DelegateCommand.Create()
    .AddKeyGesture(
            System.Windows.Input.Key.S,
            System.Windows.Input.ModifierKeys.Control )
    .OnExecute( o => { /* execution logic */ } );
con lo scopo di creare un ICommand a cui sia implicitamente associata la combinazione Ctrl+S peccato che quella cosa non funzioni perchè nessuno (aka la classe Binding) si preoccupa di eseguire la registrazione degli Input Bindings…
Sfruttando quindi la nostra bella markup extension perchè non realizzare qualcosa del genere:
public class CommandBinding : BindingDecoratorBase
{
    public override object ProvideValue( IServiceProvider provider )
    {
        var b = base.ProvideValue( provider );
        this.OnProvideValue( provider, b );
        return b;
    }

    protected virtual void OnProvideValue( IServiceProvider provider, Object value )
    {
        FrameworkElement fe;
        DependencyProperty dp;

        if( this.TryGetTargetItems<FrameworkElement>( provider, out fe, out dp ) )
        {
            RoutedEventHandler reh = null;
            reh = ( s, e ) =>
            {
                fe.Loaded -= reh;
                this.OnTargetLoaded( fe, dp );
            };
            fe.Loaded += reh;
        }
        else
        {
            FrameworkContentElement fce;
            if( this.TryGetTargetItems<FrameworkContentElement>( provider, out fce, out dp ) )
            {
                RoutedEventHandler reh = null;
                reh = ( s, e ) =>
                {
                    fce.Loaded -= reh;
                    this.OnTargetLoaded( fce, dp );
                };
                fce.Loaded += reh;
            }
        }
    }
Con TryGetTargetItems definita così:
protected virtual bool TryGetTargetItems( IServiceProvider provider, out T target, out DependencyProperty dp )
    where T : DependencyObject
{
    target = null;
    dp = null;
    if( provider == null ) return false;

    var service = provider.GetService( typeof( IProvideValueTarget ) ) as IProvideValueTarget;
    if( service == null ) return false;

    target = service.TargetObject as T;
    dp = service.TargetProperty as DependencyProperty;
    return target != null && dp != null;
}
Abbiamo la necessità di sapere quando il target del Binding è “Loaded”, tracciamo sia FrameworkElement (eg Button) che FrameworkContentElement (eg Hyperlink) perchè purtroppo () non hanno lo stesso ancestor e quindi hanno 2 eventi Loaded diversi… poco male, basta saperlo.
    protected virtual void OnTargetLoaded( DependencyObject target, DependencyProperty targetProperty )
    {
        var source = target as ICommandSource;
        var command = this.GetCommand( target, targetProperty );
        this.SetInputBindings( target, source, command );
    }
A questo punto quando il target è Loaded ci limitiamo ad estrarre dalla DepenedencyProperty (omesso per brevità) il valore dell’ICommand e non facciamo altro che impostare gli imput bindings sul root element, anche qui la funzione ricorsiva che va alla ricerca del root element è omessa:
protected virtual void SetInputBindings( DependencyObject target, ICommandSource source, IBindableCommand command)
{
    if( source != null && command != null && command.InputBindings != null )
    {
        var rootElement = this.GetRootElement( target as FrameworkElement );
        foreach( InputBinding ib in command.InputBindings )
        {
            if( ib.CommandParameter != source.CommandParameter )
            {
                ib.CommandParameter = source.CommandParameter;
            }

            rootElement.InputBindings.Add( ib );
        }
    }
}
Togo, adesso lo possiamo usare così:
<Button Height="23" Command="{cmd:CommandBinding Path=ExecuteSampleCommand}" />
Ottenendo gratis la registrazione degli input bindings.
Ma non è finita… Stay tuned!
.m