wpf - soccer - fivethirtyeight texas senate
Actualizar Comando WPF (6)
Conocí CommandManager.InvalidateRequerySuggested () hace mucho tiempo y lo usé, pero a veces no me funcionaba. ¡Finalmente descubrí por qué era este el caso! Aunque no arroje como otras acciones, TIENE que invocarlo en el hilo principal.
Llamar a un hilo de fondo parece funcionar, pero a veces deja la IU inhabilitada. Realmente espero que esto ayude a alguien, y les ahorre las horas que acabo de perder.
¿Alguien sabe cómo puedo forzar a que CanExecute
sea llamado a un comando personalizado ( RelayCommand
Josh Smith)?
Normalmente, se llama a CanExecute
siempre que se produce una interacción en la UI. Si hago clic en algo, mis comandos se actualizan.
Tengo una situación en la que la condición de CanExecute
se activa o desactiva mediante un temporizador detrás de escena. Debido a que esto no está impulsado por la interacción del usuario, CanExecute
no se llama hasta que el usuario interactúa con la interfaz de usuario. El resultado final es que mi Button
permanece habilitado / deshabilitado hasta que el usuario haga clic en él. Después del clic, se actualiza correctamente. A veces, el Button
aparece habilitado, pero cuando el usuario hace clic cambia a deshabilitado en lugar de disparar.
¿Cómo puedo forzar una actualización en el código cuando el temporizador cambia la propiedad que afecta a CanExecute
? Intenté INotifyPropertyChanged
PropertyChanged
( INotifyPropertyChanged
) en la propiedad que afecta a CanExecute
, pero eso no ayudó.
Ejemplo XAML:
<Button Content="Button" Command="{Binding Cmd}"/>
Código de ejemplo detrás:
private ICommand m_cmd;
public ICommand Cmd
{
if (m_cmd == null)
m_cmd = new RelayCommand(
(param) => Process(),
(param) => EnableButton);
return m_cmd;
}
// Gets updated from a timer (not direct user interaction)
public bool EnableButton { get; set; }
Gracias chicos por los consejos. Aquí hay un poco de código sobre cómo ordenar esa llamada desde un hilo BG al hilo de la interfaz de usuario:
private SynchronizationContext syncCtx; // member variable
En el constructor:
syncCtx = SynchronizationContext.Current;
En el hilo de fondo, para activar la nueva consulta:
syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );
Espero que ayude.
-- Miguel
Para actualizar solo un solo GalaSoft.MvvmLight.CommandWpf.RelayCommand, puede usar
mycommand.RaiseCanExecuteChanged();
y para mí he creado un método de extensión:
public static class ExtensionMethods
{
public static void RaiseCanExecuteChangedDispatched(this RelayCommand cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
public static void RaiseCanExecuteChangedDispatched<T>(this RelayCommand<T> cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
}
Probablemente esta variante te convenga:
public interface IRelayCommand : ICommand
{
void UpdateCanExecuteState();
}
Implementación:
public class RelayCommand : IRelayCommand
{
public event EventHandler CanExecuteChanged;
readonly Predicate<Object> _canExecute = null;
readonly Action<Object> _executeAction = null;
public RelayCommand( Action<object> executeAction,Predicate<Object> canExecute = null)
{
_canExecute = canExecute;
_executeAction = executeAction;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
return _canExecute(parameter);
return true;
}
public void UpdateCanExecuteState()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
public void Execute(object parameter)
{
if (_executeAction != null)
_executeAction(parameter);
UpdateCanExecuteState();
}
}
Usando simple:
public IRelayCommand EditCommand { get; protected set; }
...
EditCommand = new RelayCommand(EditCommandExecuted, CanEditCommandExecuted);
protected override bool CanEditCommandExecuted(object obj)
{
return SelectedItem != null ;
}
protected override void EditCommandExecuted(object obj)
{
// Do something
}
...
public TEntity SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
//Refresh can execute
EditCommand.UpdateCanExecuteState();
RaisePropertyChanged(() => SelectedItem);
}
}
XAML:
<Button Content="Edit" Command="{Binding EditCommand}"/>
Una solución para eso es vincular IsEnabled
a una propiedad:
<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>
y luego implementar esta propiedad en su ViewModel. Esto también hace que sea más fácil para UnitTesting trabajar con las propiedades y no con los comandos para ver si el comando se puede ejecutar en un determinado momento.
Personalmente, lo encuentro más conveniente.