wpf - El botón no se desactiva cuando el comando CanExecute es falso
mvvm command (2)
Tengo una ventana simple como se puede con un botón vinculado a un modelo de vista con un comando.
Espero que el botón se deshabilite si MyCommand.CanExecute () es falso. Pero parece que WPF solo establecerá la propiedad IsEnabled cuando se dibuje la ventana por primera vez. Cualquier acción posterior no afecta el estado visible del botón. Estoy usando un DelegateCommand de Prism.
Mi vista:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Click Here" Command="{Binding MyCommand}" Width="100" Height="50"/>
</Grid>
y mi ViewModel:
public class MyVM : NotificationObject
{
public MyVM()
{
_myCommand = new DelegateCommand(DoStuff, CanDoStuff);
}
private void DoStuff()
{
Console.WriteLine("Command Executed");
_myCommand.RaiseCanExecuteChanged();
}
private bool CanDoStuff()
{
var result = DateTime.Now.Second % 2 == 0;
Console.WriteLine("CanExecute is {0}", result);
return result;
}
private DelegateCommand _myCommand;
public ICommand MyCommand
{
get
{
return _myCommand;
}
}
}
El 50% de las veces, cuando se carga mi aplicación, el botón se desactiva correctamente. Sin embargo, si está habilitado cuando se carga la ventana, y hago clic en el botón para ejecutar el comando, espero que el 50% del tiempo se desactive, pero nunca lo hace. El comando no se ejecuta, pero aún puedo hacer clic en el botón. ¿Cómo entiendo que WPF entienda que el botón debería estar deshabilitado cuando CanExecute () es falso?
Puedes probar esto (es necesario Microsoft.Practices.Prism.dll
)
public class ViewModel
{
public DelegateCommand ExportCommand { get; }
public ViewModel()
{
ExportCommand = new DelegateCommand(Export, CanDoExptor);
}
private void Export()
{
//logic
}
private bool _isCanDoExportChecked;
public bool IsCanDoExportChecked
{
get { return _isCanDoExportChecked; }
set
{
if (_isCanDoExportChecked == value) return;
_isCanDoExportChecked = value;
ExportCommand.RaiseCanExecuteChanged();
}
}
private bool CanDoExptor()
{
return IsCanDoExportChecked;
}
}
Veo que está utilizando Prism y su NotificationObject
and DelegateCommand
, por lo que deberíamos esperar que no haya un error en RaiseCanExecuteChanged ().
Sin embargo, la razón del comportamiento es que RaiseCanExecuteChanged de Prism funciona sincrónicamente, por lo que CanDoStuff()
se CanDoStuff()
mientras todavía estamos dentro de la implementación de ICommand.Execute()
y el resultado parece ser ignorado.
Si crea otro botón con su propio comando y llama a _myCommand.RaiseCanExecuteChanged()
desde ese comando / botón, el primer botón se habilitará / deshabilitará como espera.
O bien, si prueba lo mismo con MVVM Light y RelayCommand su código funcionará porque MVVM Light''s RaiseCanExecuteChanged
llama a CommandManager.InvalidateRequerySuggested()
que invoca la devolución de llamada a CanDoStuff
forma asincrónica utilizando Dispatcher.CurrentDispatcher.BeginInvoke
, evitando el comportamiento que está viendo con Prism implementación.