C# 异步 WPF 命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/151686/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Asynchronous WPF Commands
提问by nedruod
Note: The code in this question is part of deSleeperif you want the full source.
注意:如果你想要完整的源代码,这个问题中的代码是deSleeper 的一部分。
One of the things I wanted out of commands was a baked design for asynchronous operations. I wanted the button pressed to disable while the command was executing, and come back when complete. I wanted the actual work to be performed in a ThreadPool work item. And lastly, I wanted a way to handle any errors that occurred during the asynchronous processing.
我想要的命令之一是异步操作的烘焙设计。我希望在命令执行时按下按钮禁用,并在完成后返回。我希望在 ThreadPool 工作项中执行实际工作。最后,我想要一种方法来处理异步处理期间发生的任何错误。
My solution was an AsyncCommand:
我的解决方案是 AsyncCommand:
public abstract class AsyncCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public event EventHandler ExecutionStarting;
public event EventHandler<AsyncCommandCompleteEventArgs> ExecutionComplete;
public abstract string Text { get; }
private bool _isExecuting;
public bool IsExecuting
{
get { return _isExecuting; }
private set
{
_isExecuting = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
protected abstract void OnExecute(object parameter);
public void Execute(object parameter)
{
try
{
IsExecuting = true;
if (ExecutionStarting != null)
ExecutionStarting(this, EventArgs.Empty);
var dispatcher = Dispatcher.CurrentDispatcher;
ThreadPool.QueueUserWorkItem(
obj =>
{
try
{
OnExecute(parameter);
if (ExecutionComplete != null)
dispatcher.Invoke(DispatcherPriority.Normal,
ExecutionComplete, this,
new AsyncCommandCompleteEventArgs(null));
}
catch (Exception ex)
{
if (ExecutionComplete != null)
dispatcher.Invoke(DispatcherPriority.Normal,
ExecutionComplete, this,
new AsyncCommandCompleteEventArgs(ex));
}
finally
{
dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() => IsExecuting = false));
}
});
}
catch (Exception ex)
{
IsExecuting = false;
if (ExecutionComplete != null)
ExecutionComplete(this, new AsyncCommandCompleteEventArgs(ex));
}
}
public virtual bool CanExecute(object parameter)
{
return !IsExecuting;
}
}
so the question is: Is all this necessary? I've noticed built in asynchronous support for data-binding, so why not command execution? Perhaps it's related to the parameter question, which is my next question.
所以问题是:这一切都是必要的吗?我注意到内置了对数据绑定的异步支持,那么为什么不执行命令呢?也许它与参数问题有关,这是我的下一个问题。
采纳答案by nedruod
I've been able to refine the original sample down and have some advice for anyone else running into similar situations.
我已经能够优化原始样本,并为遇到类似情况的其他人提供一些建议。
First, consider if BackgroundWorker will meet the needs. I still use AsyncCommand often to get the automatic disable function, but if many things could be done with BackgroundWorker.
首先考虑BackgroundWorker是否满足需求。我仍然经常使用 AsyncCommand 来获得自动禁用功能,但是如果使用 BackgroundWorker 可以完成很多事情。
But by wrapping BackgroundWorker, AsyncCommand provides command like functionality with asynchronous behavior (I also have a blog entry on this topic)
但是通过包装 BackgroundWorker,AsyncCommand 提供了类似命令的异步行为功能(我也有一个关于这个主题的博客条目)
public abstract class AsyncCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public event EventHandler RunWorkerStarting;
public event RunWorkerCompletedEventHandler RunWorkerCompleted;
public abstract string Text { get; }
private bool _isExecuting;
public bool IsExecuting
{
get { return _isExecuting; }
private set
{
_isExecuting = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
protected abstract void OnExecute(object parameter);
public void Execute(object parameter)
{
try
{
onRunWorkerStarting();
var worker = new BackgroundWorker();
worker.DoWork += ((sender, e) => OnExecute(e.Argument));
worker.RunWorkerCompleted += ((sender, e) => onRunWorkerCompleted(e));
worker.RunWorkerAsync(parameter);
}
catch (Exception ex)
{
onRunWorkerCompleted(new RunWorkerCompletedEventArgs(null, ex, true));
}
}
private void onRunWorkerStarting()
{
IsExecuting = true;
if (RunWorkerStarting != null)
RunWorkerStarting(this, EventArgs.Empty);
}
private void onRunWorkerCompleted(RunWorkerCompletedEventArgs e)
{
IsExecuting = false;
if (RunWorkerCompleted != null)
RunWorkerCompleted(this, e);
}
public virtual bool CanExecute(object parameter)
{
return !IsExecuting;
}
}
回答by Orion Adrian
As I answered in your other question, you probably still want to bind to this synchronously and then launch the commands asynchronously. That way you avoid the problems you're having now.
正如我在您的另一个问题中回答的那样,您可能仍然希望同步绑定到 this,然后异步启动命令。这样你就可以避免你现在遇到的问题。