example commandmanager c# mvvm .net-4.0 task icommand

c# - commandmanager - El comando de retransmisión puede ejecutarse y una tarea



command mvvm c# (5)

Podría tener una variable estática IsRunning , que puede establecer en True cuando la tarea comienza, en falso cuando termine, y simplemente vincular ese botón habilitado al estado de IsRunning

Quiero comenzar una tarea cuando se llama un comando de retransmisión, sin embargo, quiero desactivar el botón, siempre y cuando esa tarea se esté ejecutando.

toma este ejemplo

private ICommand update; public ICommand Update { get { if (update == null) { update = new RelayCommand( param => Task.Factory.StartNew(()=> StartUpdate()), param => true); //true means the button will always be enabled } return update; } }

¿Cuál es la mejor manera de verificar si esa tarea se está ejecutando?

aquí está mi solución, pero no estoy seguro si es la mejor manera

class Vm : ObservableObject { Task T; public Vm() { T = new Task(() => doStuff()); } private ICommand myCommand; public ICommand MyCommand { get { return myCommand ?? (myCommand = new RelayCommand( p => { T = new Task(() => doStuff()); T.Start(); }, p => T.Status != TaskStatus.Running)); } } private void doStuff() { System.Threading.Thread.Sleep(5000); } }

Actualización: todas las respuestas aquí funcionan bien, pero aún así no concuerdan entre sí, y acabo de alcanzar una reputación de 100, comienzo una recompensa cada vez que alcanzo 100, entonces lo que estoy buscando es una implementación para una pérdida de memoria óptima sin asincrónica RelayCommand que se ejecuta dentro de una tarea en .NET 4.0


Entonces su solución para usar RelayCommand casi funciona. El problema es que la interfaz de usuario no se actualizará inmediatamente después de que la tarea termine de ejecutarse. Esto se debe a que algo debe desencadenar el evento CanExecuteChanged de ICommand para que la UI se actualice correctamente.

Una forma de resolver este problema es creando un nuevo tipo de ICommand. Por ejemplo:

class AsyncRelayCommand : ICommand { private Func<object, Task> _action; private Task _task; public AsyncRelayCommand(Func<object,Task> action) { _action = action; } public bool CanExecute(object parameter) { return _task == null || _task.IsCompleted; } public event EventHandler CanExecuteChanged; public async void Execute(object parameter) { _task = _action(parameter); OnCanExecuteChanged(); await _task; OnCanExecuteChanged(); } private void OnCanExecuteChanged() { var handler = this.CanExecuteChanged; if (handler != null) handler(this, EventArgs.Empty); } }

Ahora su modelo de vista puede hacer algo como lo siguiente

private ICommand myCommand; public ICommand MyCommand { get { return myCommand ?? (myCommand = new AsyncRelayCommand(p => Task.Factory.StartNew(doStuff))); } } private void doStuff() { System.Threading.Thread.Sleep(5000); }

O podrías hacer que tu función doStuff sea una función "asíncrona" como esa

private ICommand myCommand2; public ICommand MyCommand2 { get { return myCommand2 ?? (myCommand2 = new AsyncRelayCommand(p => doStuff2())); } } private async Task doStuff2() { await Task.Delay(5000); }


Recomiendo encarecidamente que evite new Task así como Task.Factory.StartNew . La forma correcta de iniciar una tarea asincrónica en un hilo de fondo es Task.Run .

Puede crear un RelayCommand asíncrono fácilmente utilizando este patrón:

private bool updateInProgress; private ICommand update; public ICommand Update { get { if (update == null) { update = new RelayCommand( async () => { updateInProgress = true; Update.RaiseCanExecuteChanged(); await Task.Run(() => StartUpdate()); updateInProgress = false; Update.RaiseCanExecuteChanged(); }, () => !updateInProgress); } return update; } }


Creo que puedes usar esta implementación de AsyncCommand.

public class AsyncCommand : ICommand, IDisposable { private readonly BackgroundWorker _backgroundWorker = new BackgroundWorker {WorkerSupportsCancellation = true}; private readonly Func<bool> _canExecute; public AsyncCommand(Action action, Func<bool> canExecute = null, Action<object> completed = null, Action<Exception> error = null) { _backgroundWorker.DoWork += (s, e) => { CommandManager.InvalidateRequerySuggested(); action(); }; _backgroundWorker.RunWorkerCompleted += (s, e) => { if (completed != null && e.Error == null) completed(e.Result); if (error != null && e.Error != null) error(e.Error); CommandManager.InvalidateRequerySuggested(); }; _canExecute = canExecute; } public void Cancel() { if (_backgroundWorker.IsBusy) _backgroundWorker.CancelAsync(); } public bool CanExecute(object parameter) { return _canExecute == null ? !_backgroundWorker.IsBusy : !_backgroundWorker.IsBusy && _canExecute(); } public void Execute(object parameter) { _backgroundWorker.RunWorkerAsync(); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (_backgroundWorker != null) _backgroundWorker.Dispose(); } } }


Estoy tratando de evitar la biblioteca Prism para mantener mi control lo más simple posible desde el punto de vista del montaje de ensambles de referencia y terminé con esta solución

_cmd = new RelayCommand(async delegate { await Task.Run(() => <YourMethod>()); }, delegate { return !IsInProgress; }) );

Parece estar funcionando bien. (si no necesita pasar commandParameter en). Lamentablemente, esto sigue siendo un problema.

La clase RelayCommand hereda de ICommand

public class RelayCommand : ICommand { private Action<object> _execute; private Predicate<object> _canExecute; private event EventHandler CanExecuteChangedInternal; public RelayCommand(Action<object> execute) : this(execute, DefaultCanExecute) { } public RelayCommand(Action<object> execute, Predicate<object> canExecute) { if (execute == null) { throw new ArgumentNullException("execute"); } if (canExecute == null) { throw new ArgumentNullException("canExecute"); } _execute = execute; _canExecute = canExecute; } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; CanExecuteChangedInternal += value; } remove { CommandManager.RequerySuggested -= value; CanExecuteChangedInternal -= value; } } public bool CanExecute(object parameter) { return _canExecute != null && _canExecute(parameter); } public void Execute(object parameter) { _execute(parameter); } public void OnCanExecuteChanged() { EventHandler handler = CanExecuteChangedInternal; if (handler != null) { //DispatcherHelper.BeginInvokeOnUIThread(() => handler.Invoke(this, EventArgs.Empty)); handler.Invoke(this, EventArgs.Empty); } } public void Destroy() { _canExecute = _ => false; _execute = _ => { return; }; } private static bool DefaultCanExecute(object parameter) { return true; } }