2013-02-22 5 views
8

Ho una semplice finestra con un pulsante legato a un ViewModel con un comando.Il pulsante non si disabilita quando il comando CanExecute è falso

Mi aspetto che il pulsante sia disabilitato se MyCommand.CanExecute() è falso. Ma sembra che WPF imposterà la proprietà IsEnabled solo quando la finestra viene disegnata per la prima volta. Qualsiasi azione successiva non influisce sullo stato visibile del pulsante. Sto usando un DelegateCommand di Prism.

mio punto di 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> 

e il mio 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; 
     } 
    } 
} 

50% del tempo, quando i miei applicazione carica, il pulsante è disabilitato correttamente. Tuttavia, se è abilitato al caricamento della finestra, e faccio clic sul pulsante per eseguire il comando, mi aspetto che il 50% del tempo per il pulsante venga disattivato, ma non lo è mai. Il comando non viene eseguito, ma posso ancora fare clic sul pulsante. Come faccio a capire a WPF che il pulsante deve essere disattivato quando CanExecute() è falso?

+0

prima cosa che devi fare è alzare i messaggi di debug per l'associazione dati: http://i.stack.imgur.com/MF8i5.png Avanti, rieseguire e controllare la finestra di output e vedere quali errori ci sono. Se nessuno si riferisce al bind del comando, l'implementazione di 'RaiseCanExecuteChanged()' non è corretta/errata. – Will

+0

Il tuo metodo CanDOStuff è davvero strano! Potrebbe rendere il tuo pulsante disabilitato, ma nel prossimo secondo il tuo comando potrebbe essere eseguito, ma il pulsante è disabilitato ... Davvero strano. ma dovresti chiamare [CommandManager.InvalidateRequerySuggested()] (http://msdn.microsoft.com/en-us/library/system.windows.input.commandmanager.invalidaterequerysuggested.aspx) se CanExecute può essere cambiato e l'interfaccia utente non è aggiornato perché CanExecuteChanged non è stato generato. –

+0

@Viktor So che è davvero strano, dovrebbe essere un esempio sciocco che restituisce vero/falso casualmente. CommandManager.InvalidateRequerySuggested non ha alcun effetto. –

risposta

6

Vedo che stai usando Prism e il suo NotificationObject e DelegateCommand, quindi dovremmo aspettarci che non ci sia un bug in RaiseCanExecuteChanged().

Tuttavia, il motivo per il comportamento è che RaiseCanExecuteChanged di Prism opera in modo sincrono, in modo CanDoStuff() è chiamata mentre siamo ancora dentro l'attuazione ICommand.Execute() e il risultato poi sembra essere ignorato.

Se si crea un altro pulsante con un proprio comando e si chiama _myCommand.RaiseCanExecuteChanged() da tale comando/pulsante, il primo pulsante verrà abilitato/disabilitato come previsto.

Oppure, se si prova la stessa cosa con MVVM Luce e RelayCommand il codice funzionerà perché RaiseCanExecuteChanged chiamate di MVVM luce CommandManager.InvalidateRequerySuggested() che invoca la callback per CanDoStuff in modo asincrono utilizzando Dispatcher.CurrentDispatcher.BeginInvoke, evitando il comportamento che stai vedendo con l'attuazione di Prism.

+0

Grazie per la spiegazione. Ho provato entrambi i tuoi suggerimenti e sono perfetti. Sembra una sfortunata limitazione di Prism. –

0

Si può provare questo (Microsoft.Practices.Prism.dll è necessario)

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; 
    } 
} 
Problemi correlati