C# 中继命令可以执行和执行任务
我想在调用中继命令时启动一个任务,但是只要该任务正在运行,我就想禁用该按钮 就拿这个例子来说C# 中继命令可以执行和执行任务,c#,mvvm,.net-4.0,task,icommand,C#,Mvvm,.net 4.0,Task,Icommand,我想在调用中继命令时启动一个任务,但是只要该任务正在运行,我就想禁用该按钮 就拿这个例子来说 private ICommand update; public ICommand Update { get { if (update == null) { update = new RelayCommand(
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;
}
}
检查该任务是否正在运行的最佳方法是什么
这是我的解决方案,但不确定这是否是最好的方法
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);
}
}
更新:这里的每一个答案都很好,但他们仍然不一致,而且我刚刚获得了100%的声誉,每当我达到100%时,我就会开始悬赏,因此,我要寻找的是一个在.net 4.0中的任务内执行的最佳无内存泄漏异步Relay命令的实现。您可以有一个静态变量
IsRunning
,您可以在任务开始时将其设置为True,在任务结束时将其设置为false,只需将启用的按钮绑定到正在运行的状态
,这样您使用RelayCommand的解决方案几乎可以正常工作。问题是在任务完成运行后,UI不会立即更新。这是因为需要触发ICommand的CanExecuteChanged事件才能正确更新UI
解决这个问题的一种方法是创建一种新的ICommand。例如:
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);
}
}
或者您可以将doStuff函数设置为“异步”函数,如下所示
private ICommand myCommand2;
public ICommand MyCommand2
{
get { return myCommand2 ?? (myCommand2 = new AsyncRelayCommand(p => doStuff2())); }
}
private async Task doStuff2()
{
await Task.Delay(5000);
}
我强烈建议您避免使用
新任务
以及任务.Factory.StartNew
。在后台线程上启动异步任务的正确方法是task.Run
您可以使用以下模式轻松创建异步RelayCommand
:
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;
}
}
我认为,您可以使用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();
}
}
}
公共类异步命令:ICommand,IDisposable
{
private readonly BackgroundWorker _BackgroundWorker=new BackgroundWorker{WorkerSupportsCancellation=true};
私有只读功能可执行;
public AsyncCommand(Action-Action,Func-canExecute=null,Action-completed=null,
操作错误=空)
{
_backgroundWorker.DoWork+=(s,e)=>
{
CommandManager.InvalidateRequestSuggested();
动作();
};
_backgroundWorker.RunWorkerCompleted+=(s,e)=>
{
如果(已完成!=null&&e.错误==null)
完成(即结果);
如果(错误!=null&&e.错误!=null)
错误(即错误);
CommandManager.InvalidateRequestSuggested();
};
_canExecute=canExecute;
}
公开作废取消()
{
如果(_backgroundWorker.IsBusy)
_backgroundWorker.CancelAsync();
}
公共布尔CanExecute(对象参数)
{
返回_canExecute==null
?!\u后台工作人员很忙
:!\u backgroundWorker.IsBusy&&u canExecute();
}
public void Execute(对象参数)
{
_backgroundWorker.RunWorkerAsync();
}
公共事件事件处理程序CanExecuteChanged
{
添加{CommandManager.RequerySuggested+=value;}
删除{CommandManager.RequerySuggested-=value;}
}
公共空间处置()
{
处置(真实);
总干事(本);
}
受保护的虚拟void Dispose(bool disposing)
{
如果(处置)
{
if(_backgroundWorker!=null)
_backgroundWorker.Dispose();
}
}
}
我试图避免使用Prism库,以从参考程序集的装载角度尽可能地简化我的控制,我最终得到了这个解决方案
_cmd = new RelayCommand(async delegate
{
await Task.Run(() => <YourMethod>());
}, delegate { return !IsInProgress; }) );
\u cmd=new RelayCommand(异步委托
{
等待任务。运行(()=>());
},委托{return!IsInProgress;});
看来效果不错。(如果不需要传入commandParameter)。不幸的是,这仍然是一个问题
RelayCommand类继承自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;
}
}
公共类RelayCommand:ICommand
{
私人行动——执行;
私有谓词_canExecute;
私有事件处理程序CanExecuteChangeInternal;
公共中继命令(操作执行)
:此(执行,默认CanExecute)
{
}
公共RelayCommand(操作执行,谓词canExecute)
{
if(execute==null)
{
抛出新的ArgumentNullException(“执行”);
}
如果(canExecute==null)
{
抛出新ArgumentNullException(“canExecute”);
}
_执行=执行;
_canExecute=canExecute;
}
公共事件事件处理程序CanExecuteChanged
{
添加
{
CommandManager.RequerySuggested+=值;
CanExecuteChangeInternal+=值;
}
去除
{
CommandManager.RequerySuggested-=值;
CanExecuteChangeInternal-=值;
}
}
公共布尔CanExecute(对象参数)
{
return _canExecute!=null&&u canExecute(参数);
}
public void Execute(对象参数)
{
_执行(参数);
}
CanExecuteChanged()上的公共无效
{
EventHandler handler=CanExecuteChangeInternal;
if(处理程序!=null)
{
//DispatcherHelper.BeginInvokeoUnithread(()=>handler.Invoke(this,EventArgs.Empty));
Invoke(this,EventArgs.Empty);
}
}
公共空间销毁()
{
_canExecute==>false;
_execute=\=>{return;};
}
私有静态bool DefaultCanExecute(对象参数)
{
返回true;
}
}
我已经这么做了,但是没有额外的变量就没有办法了吗?嗯,有人需要知道一些事情。。。否则你会怎么做?:)也许通过使任务全局化,这会令人不安,因为我有很多命令,只要相关任务正在运行,我就需要禁用按钮。静态标志只应在最简单的场景中使用,这种方法在出现问题时会很快失效
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;
}
}