ListView perchè sei tu, ListView… (part 2)
Siccome abbiamo scoperto, almeno io ;-), la potenza delle attached properties facciamocelo.
ItemDoubleClickCommand
Partiamo come al solito dal risultato che vogliamo ottenere:
Molto semplice: poter dichiarare un nuovo Command e metterlo in binding con qualcosa che implementi l’interfaccia ICommand; per arrivare li, nell’ormai vecchio e familiare ListViewManager, cominciamo a dichiarare l’attached property:<ListView local:ListViewManager.ItemDoubleClickCommand="{Binding Path=Select}" />
Riporto anche la parte Get/Set perchè faccio notare che Visual Studio sfrutta le informazioni sul “tipo” del parametro value di SetXxxx, e del valore di ritorno di GetXxxx, per capire in fase di compilazione se la dichiarazione nello xaml sia sensata o meno, questo in combinazione con il tipo che avete dichiarato nella attached property (il secondo parametro del metodo RegisterAttached).public static DependencyProperty ItemDoubleClickCommandProperty = DependencyProperty.RegisterAttached( "ItemDoubleClickCommand", typeof( ICommand ), typeof( ListViewManager ), new UIPropertyMetadata( OnItemDoubleClickCommandChanged ) ); public static void SetItemDoubleClickCommand( DependencyObject target, ICommand value ) { target.SetValue( ItemDoubleClickCommandProperty, value ); } static ICommand GetItemDoubleClickCommand( DependencyObject target ) { return ( ICommand )target.GetValue( ItemDoubleClickCommandProperty ); }
Continuiamo poi l’handler che viene invocato in fase di modifica del valore della proprietà:
Verifichiamo innanzitutto che siamo “attached” su una ListView dopodichè agganciamo o sganciamo l’handler dall’evento MouseDoubleClick in base alla situazione in cui siamo. L’event handler, dichiarato sempre nel costruttore statico della classe ListViewManager, altro non fa che gestire il doppio click:static void OnItemDoubleClickCommandChanged( DependencyObject target, DependencyPropertyChangedEventArgs e ) { Ensure.That( target.GetType() ) .WithMessage( "This behavior can be attached to ListView(s) only." ) .Is<ListView>(); var lv = ( ListView )target; if( ( e.NewValue != null ) && ( e.OldValue == null ) ) { lv.MouseDoubleClick += onListViewDblClick; } else if( ( e.NewValue == null ) && ( e.OldValue != null ) ) { lv.MouseDoubleClick –= onListViewDblClick; } }
Per prima cosa verifichiamo di essere su un ListViewItem, questa verifica è necessaria perchè l’evento MouseDoubleClick viene scatenato per il doppio click su qualsiasi parte della ListView ma a noi interessano solo quelli avvenuti in un ListViewItem, andando a recuperare l’elemento che ha originato il doppio click (OriginalSource) e risalendo il VisualTree finchè non troviamo quello che ci interessa:onListViewDblClick += ( s, e ) => { if( IsInsideListViewItem( e.OriginalSource as DependencyObject ) ) { var command = GetItemDoubleClickCommand( ( DependencyObject )s ); if( command.CanExecute( null ) ) { command.Execute( null ); } } };
Ripercorrere all’indietro il visual tree è necessario perchè associato a quel ListViewItem ci potrebbe essere un complesso DataTemplate e quindi la OriginalSource dell’evento essere un elemento nested nel template.static Boolean IsInsideListViewItem( DependencyObject obj ) { if( obj == null ) { return false; } else if( obj is ListViewItem ) { return true; } else { var parent = VisualTreeHelper.GetParent( obj ); return IsInsideListViewItem( parent ); } }
Risaliamo quindi il visual tree e se effettivamente siamo su un ListViewItem recuperiamo, dalla attached property, una reference all’ICommand con cui siamo in binding, verifichiamo che CanExecute() sia true e nel caso invochiamo l’esecuzione del command.
.m