2011-12-23 21 views
5

Tutti i menu/contextmenus/barre degli strumenti che uso in WPF vengono dichiarati nel codice ViewModel più o meno in questo modo:MenuItem scorciatoie da tastiera in MVVM 'puro'?

MenuService.Add(new MenuItem() 
    { 
    Header = "DoStuff", 
    Command = new relayCommand(DoStuff,() => CanDoStuffExecute()) 
    // some more properties like parent item/image/... 
    }); 

Il MenuService fornisce un unico punto di legame che è un elenco gerarchico di MenuItem e viene legato alla effettiva menu del ItemsSource in xaml.

Questo funziona molto bene e ora mi piacerebbe aggiungere scorciatoie da tastiera nello stesso modo conveniente. Idealmente MenuItem otterrebbe una proprietà di tipo System.Windows.Input.KeyGesture così posso scrivere semplicemente

Shortcut = new KeyGesture(Key.D, ModifierKeys.Control) 

che comporterebbe il Comando della voce di essere chiamati premendo Ctrl + D nella finestra che possiede il menu, e che avrebbe anche portare a visualizzare automaticamente "Ctrl + D" nel menu.

Tuttavia, mi sono perso qui: volevo impostare la raccolta MenuItem.InputBindings tramite il collegamento dati, ma è di sola lettura. Come posso ottenere comunque degli oggetti? O c'è un framework MVVM che supporta già qualcosa di simile? La maggior parte di q & a che ho trovato sulle scorciatoie da tastiera riguardano l'impostazione delle scorciatoie tramite xaml, il che non aiuta.

Aggiornamento

Cerca per 'relaycommand vs routeduicommand e 'relaycommand keygesture' etc ha rivelato informazioni sufficienti per trovare una soluzione di lavoro se hacky. Ci sono sicuramente altri e migliori modi là fuori, ma al momento questa è una priorità ultra bassa per me e fa il lavoro perfettamente. Ho aggiunto due proprietà alla classe MenuItem come questo:

//Upon setting a Gesture, the original command is replaced with a RoutedCommand 
//since that automatically gives us proper display of the keyboard shortcut. 
//The RoutedCommand simply calls back into the original Command. 
//It also sets the CommandBinding property, which still has to be added to the 
//CommandBindingCollection of either the bound control or one of it ancestors 
public InputGesture Gesture 
{ 
    set 
    { 
    var origCmd = Command; 
    if(origCmd == null) 
     return; 
    var routedCmd = new RoutedCommand(Header, 
     typeof(System.Windows.Controls.MenuItem), 
     new InputGestureCollection { value }); 
    CommandBinding = new CommandBinding(routedCmd, 
     (sender, args) => origCmd.Execute(args.Parameter), 
     (sender, args) => { args.CanExecute = origCmd.CanExecute(args.Parameter); }); 
    Command = routedCmd; 
    } 
} 

//CommandBinding created when setting Gesture 
public CommandBinding CommandBinding { get; private set; } 

Quindi questo dà la funzionalità che ho chiesto in origine (cioè l'aggiunta di scorciatoie da tastiera nel codice dove sono facilmente configurabili, ecc). Non resta che registrare i commandbindings. Al momento questo viene fatto semplicemente aggiungendoli tutti a Application.Current.MainWindow.CommandBindings.

risposta

3

Questo in realtà non si qualifica come una "risposta" (non sono in grado di aggiungere un commento evidentemente) - ma suggerirei che ciò che state facendo non è il metodo previsto in WPF. Stai facendo questo in modo Windows Form (e come in molti altri toolkit) - definendo il tuo UX in codice. Hai ottenuto quanto hai fatto, ma ora hai incontrato un muro di mattoni: i gesti chiave sono puramente UX, sicuramente non devono essere specificati nel code-behind. L'aspetto (in funzione del modello di vista) e l'interazione dell'utente con esso (i modi di eseguire un dato comando) sono per la definizione XAML.

I valori delle proprietà e i comandi sono per il modello della vista, in modo che sia possibile riutilizzare questo modello di vista per altre viste e creare facilmente test delle unità per esso. In che modo l'implementazione delle scorciatoie da tastiera nel modello di visualizzazione aiuta la verificabilità? E per l'uso in altre viste, si potrebbe sostenere che le scorciatoie effettive potrebbero non essere applicabili a una nuova vista, quindi non è quella a cui appartengono. Potresti avere altri motivi, naturalmente, ma ti suggerirei di prendere in considerazione solo la definizione di questi in XAML.

-Aggiunte, in risposta al vostro comment-

Hai ragione - e ho visto alcuni piuttosto grandi progetti WPF UX che hanno cercato difficile da evitare qualsiasi codice e feriscono inutilmente ottuso. Cerco semplicemente di usare qualsiasi approccio produca un risultato di lavoro, ed è il più semplice possibile.

Ecco uno snippet di esempio che crea semplicemente il MenuItem ..

<Menu x:Name="miMain" DockPanel.Dock="Top"> 
    <MenuItem Command="{Binding Path=MyGreatCommand}" Header="DoSomething"/> 

Questo crea il menu. Qui, MyGreatCommand è un ICommand ed è semplicemente una proprietà sul modello di visualizzazione. Io generalmente luogo che all'interno di un DockPanel, per gestire la StatusBar, ecc

Per assegnare il gesto chiave ..

<Window.InputBindings> 
    <KeyBinding Key="X" Modifiers="ALT" Command="{Binding Path=MyGreatCommand}"/> 

Tuttavia, dal momento che lei ha detto che hai già cercato risposte e si trova solo XAML - Suppongo tu abbia già provato questo approccio. Ho usato RoutedUICommand s invece di ICommand s, definito dall'utente, per ottenere quel bel gesto di tasto allineato a destra nel testo dell'intestazione, ma non ho trovato come fare entrambe le cose. Se insisti a creare comandi e gesti chiave tutti in codice, potresti dover creare RoutedUICommand s.

Perché desideri impostare i movimenti delle chiavi in ​​altro rispetto a XAML?

Se si desidera che alcuni voci di menu per appaiono solo quando alcuni stati dominano all'interno della vostra vista modello, allora si può associare la proprietà Visibility di una voce di menu (che può contenere altri voci di menu) per Collapsed o Visible .

+0

Potete fornire un esempio di codice? Vuoi dire che dovrei definire il menu completo in xaml? O solo la scorciatoia? Mi piacerebbe vedere come ... Ho bisogno di cose come un menu 'Strumenti' globale in cui i plug-in possono aggiungere sottomenu a volontà - può anche essere fatto in xaml? Supponiamo anche che possa in qualche modo sporcare il codice con xaml, no? Ora tutto ciò di cui ho bisogno per aggiungere un comando di menu è il codice come mostrato. Questo è il più breve e maneggevole che può ottenere e funziona meravigliosamente. A proposito, il tuo muro di mattoni funziona in due direzioni: devo ancora vedere un'app su larga scala in cui nessun pezzo UX singolo è definito nel codice. Questo è un mito. – stijn

+0

Hai assolutamente ragione - e ho visto alcuni progetti UX WPF piuttosto grandi che hanno cercato davvero di evitare qualsiasi codice - e sono finiti inutilmente ottusi. Cerco di usare solo l'approccio che funziona ed è più semplice. Ecco un frammento del campione (pardon la mancanza di finese - io sono sicuro come formattare questo su questo sito) .. '

Modifica – JamesWHurst

+1

la tua risposta, incolla il codice, selezionalo e fai clic sul pulsante * Esempio di codice *. – stijn

Problemi correlati