【发布时间】:2016-12-07 10:11:12
【问题描述】:
我刚开始用 C# 编程,想从一开始就做任何事情。 所以我了解了 MVVM 模式并试图让我的程序使用它。 为此,我使用了来自 ICommand 的 Delegate 和 lambda 函数。
这很好用,但由于我的程序使用 HTML 请求,我必须找到一种方法来异步调用命令。
所以我找到了一个很好的实现,我试图理解它,但我有一次失败了……下面是实现:
public interface IRaiseCanExecuteChanged
{
void RaiseCanExecuteChanged();
}
// And an extension method to make it easy to raise changed events
public static class CommandExtensions
{
public static void RaiseCanExecuteChanged(this ICommand command)
{
var canExecuteChanged = command as IRaiseCanExecuteChanged;
if (canExecuteChanged != null)
canExecuteChanged.RaiseCanExecuteChanged();
}
}
public class DelegateCommand : DelegateCommand<object>
{
public DelegateCommand(Action executeMethod)
: base(o => executeMethod())
{
}
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
: base(o => executeMethod(), o => canExecuteMethod())
{
}
}
/// <summary>
/// A command that calls the specified delegate when the command is executed.
/// </summary>
/// <typeparam name="T"></typeparam>
public class DelegateCommand<T> : ICommand, IRaiseCanExecuteChanged
{
private readonly Func<T, bool> _canExecuteMethod;
private readonly Action<T> _executeMethod;
private bool _isExecuting;
public DelegateCommand(Action<T> executeMethod)
: this(executeMethod, null)
{
}
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
{
if ((executeMethod == null) && (canExecuteMethod == null))
{
throw new ArgumentNullException("executeMethod", @"Execute Method cannot be null");
}
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
bool ICommand.CanExecute(object parameter)
{
return !_isExecuting && CanExecute((T)parameter);
}
void ICommand.Execute(object parameter)
{
_isExecuting = true;
try
{
RaiseCanExecuteChanged();
Execute((T)parameter);
}
finally
{
_isExecuting = false;
RaiseCanExecuteChanged();
}
}
public bool CanExecute(T parameter)
{
if (_canExecuteMethod == null)
return true;
return _canExecuteMethod(parameter);
}
public void Execute(T parameter)
{
_executeMethod(parameter);
}
}
public interface IAsyncCommand : IAsyncCommand<object>
{
}
public interface IAsyncCommand<in T> : IRaiseCanExecuteChanged
{
Task ExecuteAsync(T obj);
bool CanExecute(object obj);
ICommand Command { get; }
}
public class AwaitableDelegateCommand : AwaitableDelegateCommand<object>, IAsyncCommand
{
public AwaitableDelegateCommand(Func<Task> executeMethod)
: base(o => executeMethod())
{
}
public AwaitableDelegateCommand(Func<Task> executeMethod, Func<bool> canExecuteMethod)
: base(o => executeMethod(), o => canExecuteMethod())
{
}
}
public class AwaitableDelegateCommand<T> : IAsyncCommand<T>, ICommand
{
private readonly Func<T, Task> _executeMethod;
private readonly DelegateCommand<T> _underlyingCommand;
private bool _isExecuting;
public AwaitableDelegateCommand(Func<T, Task> executeMethod)
: this(executeMethod, _ => true)
{
}
public AwaitableDelegateCommand(Func<T, Task> executeMethod, Func<T, bool> canExecuteMethod)
{
_executeMethod = executeMethod;
_underlyingCommand = new DelegateCommand<T>(x => { }, canExecuteMethod);
}
public async Task ExecuteAsync(T obj)
{
try
{
_isExecuting = true;
RaiseCanExecuteChanged();
await _executeMethod(obj);
}
finally
{
_isExecuting = false;
RaiseCanExecuteChanged();
}
}
public ICommand Command { get { return this; } }
public bool CanExecute(object parameter)
{
return !_isExecuting && _underlyingCommand.CanExecute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add { _underlyingCommand.CanExecuteChanged += value; }
remove { _underlyingCommand.CanExecuteChanged -= value; }
}
public async void Execute(object parameter)
{
await ExecuteAsync((T)parameter);
}
public void RaiseCanExecuteChanged()
{
_underlyingCommand.RaiseCanExecuteChanged();
}
}
现在我有两个问题。 1. 像他们那样实施它好不好?正如我所看到的,对象 T 只是用作函数参数,我可以将其传递给我的 lambda 函数。
我可以用这段代码做什么:
private ObservableList<string, string> dict;
private IAsyncCommand searchCommand;
public async Task myFunction() {
//changes global variable dict bound to view
public IAsyncCommand MyCommand
{
get
{
if (myCommand == null)
{
myCommand = new AwaitableDelegateCommand(
() =>
{
return myFunction(myParameter);
});
}
return searchCommand;
}
}
其中 myFunction 编辑一个全局变量 dict,该变量通过带有 getter 和 setter 的函数 MyFunction 绑定到视图。我希望这是常见的做法,因为我只是发现它是这样的。 我们在这里返回任务,AwaitableDelegateCommand 在内部等待它,所以我们不必关心。但是我们无法访问任务及其结果,对吧?
如果 myFunction 不返回 void 而是一个变量,我怎么能做到这一点。 然后我必须得到任务的结果(在等待异步之后)并将其分配给列表。
代码如下所示:
public async Task<ObservableDictionary<string, string>> myFunction() {
var dict = new ObservableDictionary<string, string>();
//do work....
return dict;
}
public IAsyncCommand MyCommand
{
get
{
if (myCommand == null)
{
//what to do here to assign dict the result of the Task?
}
return searchCommand;
}
}
这将使我的代码更可重用,我希望它是好的 :) 希望有人可以帮助我。
编辑: 由于我的问题似乎不是很清楚,我尝试再次解释它。
我的方法返回一个值,我希望我的方法与 AwaitableDelegateCommand 异步运行
现在的问题...返回值的方法通常对其余代码不执行任何操作,但返回的值... 在我的例子中,它从 HTML 请求中填充字典并返回它。 如果我现在在 lambda 中运行异步方法,它什么也不做......因为我无法在后台获得任务的结果。 所以我现在通过重写函数解决了这个问题。它不是填充一个局部字典变量,而是填充一个全局变量。 这是我能弄清楚访问函数数据的唯一方法。 我只是想知道是否有其他方法可以做到这一点,或者这是否是唯一/最好的方法。
希望这会更好。
【问题讨论】: