C# 如何使用异步命令MVVM WPF
因此,我最近开始与MVVM合作,最终开始了解这个概念。 我似乎偶然发现了一个关于命令的小问题。 我创建了一个有按钮和文本框的项目,计划是单击按钮,然后生成一些文本到文本框。 当然,使用MVVM。 所以我设法让它工作,唯一的问题是我的按钮被绑定到一个命令,而不是什么,当点击按钮时,我的UI冻结了,这有明显的原因,但这让我思考。为了防止UI死锁,您通常会使用async&await,但这样一来,它开始抱怨C# 如何使用异步命令MVVM WPF,c#,.net,wpf,mvvm,C#,.net,Wpf,Mvvm,因此,我最近开始与MVVM合作,最终开始了解这个概念。 我似乎偶然发现了一个关于命令的小问题。 我创建了一个有按钮和文本框的项目,计划是单击按钮,然后生成一些文本到文本框。 当然,使用MVVM。 所以我设法让它工作,唯一的问题是我的按钮被绑定到一个命令,而不是什么,当点击按钮时,我的UI冻结了,这有明显的原因,但这让我思考。为了防止UI死锁,您通常会使用async&await,但这样一来,它开始抱怨Execute()中没有等待的方法在该命令中。 处理这种情况下的UI死锁的正确方法是什么? 我要发
Execute()中没有等待的方法代码>在该命令中。
处理这种情况下的UI死锁的正确方法是什么?
我要发出异步命令还是?
另外,如果我需要一个AsyncCommand,创建它的最佳方法是什么
看法
视图模型
public class ViewModel : INotifyPropertyChanged
{
private ProductService _productService;
private ProductModel _myProduct;
public AddProductCommand AddProductCommand { get; set; }
public ViewModel()
{
_productService = new ProductService();
_myProduct = new ProductModel();
AddProductCommand = new AddProductCommand(this);
}
//Bind a button to a command that invokes this method.
public void FillDescription()
{
_myProduct.ProductName = _productService.GetProductName();
}
public ProductModel MyProduct
{
get { return _myProduct; }
set
{
_myProduct = value;
OnPropertyChanged("MyProduct");
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
服务
public class ProductService
{
public ProductService()
{
}
public string GetProductName()
{
var web = new HtmlWeb();
var doc = web.Load("https://shop.supersimpleonline.com/products/baby-shark-official-plush");
var title = doc.DocumentNode.SelectNodes("//h1[@itemprop = 'name']").FirstOrDefault(x => x != null).InnerText;
return title;
}
}
命令
public class AddProductCommand : ICommand
{
public ViewModel ViewModel { get; set; }
public AddProductCommand(ViewModel viewModel)
{
ViewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
ViewModel.FillDescription();
}
public event EventHandler CanExecuteChanged;
}
这里有一个中继命令,它允许您执行异步工作,并防止命令多次触发/还显示按钮禁用等。它非常简单
public class AsyncRelayCommand : ICommand
{
public Func<object, Task> ExecuteFunction { get; }
public Predicate<object> CanExecutePredicate { get; }
public event EventHandler CanExecuteChanged;
public void UpdateCanExecute() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public bool IsWorking { get; private set; }
public AsyncRelayCommand(Func<object, Task> executeFunction) : this(executeFunction, (obj) => true) { }
public AsyncRelayCommand(Func<object, Task> executeFunction, Predicate<object> canExecutePredicate)
{
ExecuteFunction = executeFunction;
CanExecutePredicate = canExecutePredicate;
}
public bool CanExecute(object parameter) => !IsWorking && (CanExecutePredicate?.Invoke(parameter) ?? true);
public async void Execute(object parameter)
{
IsWorking = true;
UpdateCanExecute();
await ExecuteFunction(parameter);
IsWorking = false;
UpdateCanExecute();
}
}
希望这有帮助:)创建一个通用命令类,该类接受执行命令时要调用的操作:
public class DelegateCommand : System.Windows.Input.ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute)
: this(execute, null)
{
_execute = execute;
}
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
return true;
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged;
}
如果在execute方法中调用Task.Run(),会发生什么?我还没有检查过,但我想应该检查一下work@mahlatse这不好,只是触发并忘记了任务,我可能会丢失一个异常,或者有人可能会再次按下你的按钮。。突然竞争条件对于异常部分,可以使用continuewith并获取异常对象为什么构造函数为空?第一个是。。公共异步无效不是坏习惯吗?我想你应该总是回一封信Task@MarkDenom:构造函数不应为空,FillDescription
不应为公共。我把这个修好了。FillDescription
方法标记为async
的唯一原因是您可以在其中使用wait
关键字。由于ICommand
的Execute
方法不是异步的,因此在框架执行命令时不会等待它。
public class AsyncRelayCommand : ICommand
{
public Func<object, Task> ExecuteFunction { get; }
public Predicate<object> CanExecutePredicate { get; }
public event EventHandler CanExecuteChanged;
public void UpdateCanExecute() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public bool IsWorking { get; private set; }
public AsyncRelayCommand(Func<object, Task> executeFunction) : this(executeFunction, (obj) => true) { }
public AsyncRelayCommand(Func<object, Task> executeFunction, Predicate<object> canExecutePredicate)
{
ExecuteFunction = executeFunction;
CanExecutePredicate = canExecutePredicate;
}
public bool CanExecute(object parameter) => !IsWorking && (CanExecutePredicate?.Invoke(parameter) ?? true);
public async void Execute(object parameter)
{
IsWorking = true;
UpdateCanExecute();
await ExecuteFunction(parameter);
IsWorking = false;
UpdateCanExecute();
}
}
public class ViewModel : INotifyPropertyChanged
{
private bool asyncCommandWorking;
public event PropertyChangedEventHandler PropertyChanged;
public void Notify([CallerMemberName] string name = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
public ViewModel()
{
AsyncCommand = new AsyncRelayCommand(Execute, CanExecute);
}
private Task Execute(object obj)
{
return Task.Run(() =>
{
// do some work...
});
}
private bool CanExecute(object obj)
{
AsyncCommandWorking = AsyncCommand.IsWorking;
// process other can execute logic.
// return the result of CanExecute or not
}
public AsyncRelayCommand AsyncCommand { get; }
public bool AsyncCommandWorking
{
get => asyncCommandWorking;
private set
{
asyncCommandWorking = value;
Notify();
}
}
}
public class DelegateCommand : System.Windows.Input.ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute)
: this(execute, null)
{
_execute = execute;
}
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
return true;
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged;
}
public class ViewModel : INotifyPropertyChanged
{
public DelegateCommand AddProductCommand { get; set; }
public ViewModel()
{
_productService = new ProductService();
_myProduct = new ProductModel();
AddProductCommand = new DelegateCommand(FillDescription);
}
async void FillDescription(object _)
{
try
{
await Task.Run(() => _myProduct.ProductName = _productService.GetProductName());
}
catch(Exception)
{
//...
}
}
}