Sto costruendo un'applicazione utilizzando il modello di progettazione MVVM e voglio utilizzare i RoutedUICommands definiti nella classe ApplicationCommands. Poiché la proprietà CommandBindings di una vista (leggi UserControl) non è DependencyProperty, non è possibile associare direttamente CommandBindings in ViewModel alla vista. Ho risolto questo problema definendo una classe View astratta che si lega a livello di codice, basata su un'interfaccia ViewModel che garantisce che ogni ViewModel abbia una ObservableCollection di CommandBindings. Tutto questo funziona bene, tuttavia, in alcuni scenari voglio eseguire la logica che è definita in diverse classi (il View e ViewModel) stesso comando. Ad esempio, quando si salva un documento.RoutedUICommand Anteprima Bug eseguito?
Nel ViewModel il codice salva il documento su disco:
private void InitializeCommands()
{
CommandBindings = new CommandBindingCollection();
ExecutedRoutedEventHandler executeSave = (sender, e) =>
{
document.Save(path);
IsModified = false;
};
CanExecuteRoutedEventHandler canSave = (sender, e) =>
{
e.CanExecute = IsModified;
};
CommandBinding save = new CommandBinding(ApplicationCommands.Save, executeSave, canSave);
CommandBindings.Add(save);
}
A prima vista il codice precedente è tutto quello che volevo fare, ma il controllo TextBox nella vista per cui il documento è destinato, solo gli aggiornamenti la sua fonte quando perde la concentrazione. Tuttavia, posso salvare un documento senza perdere il focus premendo Ctrl + S. Ciò significa che il documento viene salvato prima delle modifiche in cui è stato aggiornato nella sorgente, ignorando effettivamente le modifiche. Ma dal momento che la modifica di UpdateSourceTrigger a PropertyChanged non è un'opzione valida per motivi di prestazioni, qualcos'altro deve forzare un aggiornamento prima di salvare. Così ho pensato, permette di utilizzare l'evento PreviewExecuted per forzare l'aggiornamento in caso PreviewExecuted, in questo modo:
//Find the Save command and extend behavior if it is present
foreach (CommandBinding cb in CommandBindings)
{
if (cb.Command.Equals(ApplicationCommands.Save))
{
cb.PreviewExecuted += (sender, e) =>
{
if (IsModified)
{
BindingExpression be = rtb.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
e.Handled = false;
};
}
}
Tuttavia, l'assegnazione di un gestore per l'evento PreviewExecuted sembra annullare l'evento del tutto, anche se ho impostato in modo esplicito il Gestito la proprietà su false. Quindi il gestore di eventi executeSave che ho definito nell'esempio di codice precedente non viene più eseguito. Si noti che quando si modifica cb.PreviewExecuted su cb.Executed, entrambe le parti del codice do vengono eseguite, ma non nell'ordine corretto.
Penso che questo sia un bug in .Net, perché dovresti essere in grado di aggiungere un gestore a PreviewExecuted ed Execed e farli eseguire in ordine, purché tu non contrassegni l'evento come gestito.
Qualcuno può confermare questo comportamento? O mi sbaglio? C'è una soluzione alternativa per questo bug?
La trama si infittisce ... Così ho guardato il codice sorgente che lei ha citato e lo fanno lo stesso cosa in OnCanExecute con PreviewCanExecute. Tuttavia, esiste una differenza importante tra CanExecuteRoutedEventArgs da OnCanExecute e ExecutedRoutedEventArgs da OnExecuted. Come ci si aspetterebbe, CanExecuteRoutedEventArgs contiene una proprietà ContinueRouting che esegue esattamente questo, ma per qualche motivo a cui ExecutedRoutedEventArgs deve fare a meno. Davvero non riesco davvero a capire questa scelta di Microsoft. – elmar
Penso che ContinueRouting non sia coinvolto in quel processo - vedi il mio EDIT 2 nel post. Per quanto riguarda il motivo per cui l'hanno fatto in questo modo ...Guarda le due parti del metodo CommandBinding.OnExecuted(), sono quasi esattamente le stesse - potrebbe essere il classico caso di copia/incolla :) e quindi è un bug. Seriamente però, non penso sia così. Mi piace davvero sapere qual è stata la loro ragione dietro a questo. –