我们在开发中, 经常会有这样的需求:
- 点击按钮后, 进行一些耗时的工作
- 工作进行时, 按钮不可再次被点击
- 工作进行时, 会显示进度条, 或者 “加载中” 的动画
RelayCommand
CommunityToolkit.Mvvm 中的 RelayCommand 除了支持最简单的同步方法, 还支持以 Task 作为返回值的异步方法, 当我们为这样的异步方法标记上 RelayCommand
特性时, 它会生成一个对应的异步指令.
- 指令在执行时, 主要逻辑会在后台, 而不是运行在 UI 线程中. 具体可以参考 “异步和异步的线程切换”
- 指令在执行时, CanExecute 会变为 false, 此时使用该 Command 的 Button 或者其他控件, 也会变成 ‘被禁用’ 的状态.
- 如果方法参数中包含一个
CancellationToken
, 那么这个任务同样可以被取消. 只需要你方法内部有正确实现 “取消执行” 的逻辑就没问题.
下面是一个例子.
主窗体代码:
<Window x:Class="LearnMvvm.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:LearnMvvm"
xmlns:vm="clr-namespace:LearnMvvm.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="7![请添加图片描述](https://img-blog.csdnimg.cn/65ddbab8f917458bbfd40ebdb6bb6d16.gif)
00">
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<Grid>
<StackPanel Margin="50">
<Button Command="{Binding DoSomethingCommand}">Do something</Button>
<Button Command="{Binding CancelDoSomethingCommand}" Margin="0 5 0 0">Cancel</Button>
<ProgressBar Margin="0 10 0 0" Height="15" Value="{Binding Progress}"/>
</StackPanel>
</Grid>
</Window>
后台 ViewModel
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private int progress;
[RelayCommand]
public async Task DoSomething(CancellationToken token)
{
for (int i = 0; i <= 100; i++)
{
if (token.IsCancellationRequested)
return;
await Task.Delay(100);
Progress = i;
}
}
[RelayCommand]
public void CancelDoSomething()
{
DoSomethingCommand.Cancel();
}
}
效果:
在 CommunityToolkit.Mvvm 中, 支持以下样式的 RelayCommand 签名:
void 方法名()
无参同步指令void 方法名(类型 参数名)
有参同步指令Task 方法名()
, 无参, 不支持取消的异步指令Task 方法名(类型 参数名)
有参, 不支持取消的异步指令Task 方法名(CancellationToken token)
无参, 支持取消的异步指令Task 方法名(类型 参数名, CancellationToken token)
有参, 支持取消的异步指令