Estoy construyendo una aplicación usando el patrón de diseño de MVVM y quiero hacer uso de los RoutedUICommands definidos en la clase ApplicationCommands. Como la propiedad CommandBindings de una Vista (léase UserControl) no es DependencyProperty, no podemos vincular CommandBindings definidos en un ViewModel a la Vista directamente. Lo resolví definiendo una clase abstracta View que vincula esto programáticamente, basada en una interfaz ViewModel que garantiza que cada ViewModel tenga una ObservableCollection de CommandBindings. Todo esto funciona bien, sin embargo, en algunos escenarios quiero ejecutar la lógica que se define en diferentes clases (el View y ViewModel) mismo comando. Por ejemplo, al guardar un documento.RoutedUICommand PreviewExecuted Bug?
En el modelo de vista del código guarda el documento en el 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 primera vista, el código anterior es todo lo que quería hacer, pero el cuadro de texto en la vista a la que el documento está obligado, sólo las actualizaciones su Fuente cuando pierde su foco. Sin embargo, puedo guardar un documento sin perder el foco presionando Ctrl + S. Esto significa que el documento se guarda antes de los cambios donde se actualizó en la fuente, haciendo caso omiso de los cambios. Pero como cambiar UpdateSourceTrigger a PropertyChanged no es una opción viable por motivos de rendimiento, algo más debe forzar una actualización antes de guardar. Así que pensé, vamos a utilizar el evento PreviewExecuted para forzar la actualización en caso PreviewExecuted, así:
//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;
};
}
}
Sin embargo, la asignación de un controlador para el evento PreviewExecuted parece cancelar el evento por completo, incluso cuando establece explícitamente la Propiedad manejada a falso. Entonces, el manejador de eventos executeSave que definí en la muestra del código anterior ya no se ejecuta. Tenga en cuenta que cuando cambio cb.PreviewExecuted a cb.Executed ambas piezas de código hacen ejecutan, pero no en el orden correcto.
Creo que esto es un error en .Net, porque debería poder agregar un controlador a PreviewExecuted y Executed y hacer que se ejecuten en orden, siempre que no marque el evento como manejado.
¿Alguien puede confirmar este comportamiento? ¿O estoy equivocado? ¿Hay alguna solución para este error?
La trama se complica ... Así que mirado el código fuente que ha mencionado y que hagan lo mismo cosa en OnCanExecute con PreviewCanExecute. Sin embargo, existe una diferencia importante entre CanExecuteRoutedEventArgs de OnCanExecute y ExecutedRoutedEventArgs de OnExecuted. Como cabría esperar, CanExecuteRoutedEventArgs contiene una propiedad ContinueRouting que hace exactamente eso, pero por alguna razón el ExecutedRoutedEventArgs tiene que prescindir. Realmente no puedo entender esta elección de Microsoft. – elmar
Creo que ContinueRouting no está involucrado en ese proceso; vea mi EDIT 2 en la publicación. En cuanto a por qué lo hicieron de esta manera ...Mire las dos partes del método CommandBinding.OnExecuted(), son casi exactamente iguales, podría ser el caso clásico de copiar/pegar :) y luego es un error. En serio, no creo que sea el caso. Realmente me gusta saber cuál fue su razón detrás de esto. –