使用ContinueWith顺序运行任务时ToggleButton提前取消选中问题
问题原因
- 你使用了
async void作为命令执行方法,async void仅适用于事件回调,当方法遇到第一个await时会直接返回调用方,Prism默认的DelegateCommand无法感知异步任务的执行状态,导致流程时序错乱。 ToggleButton的IsChecked是双向绑定,点击按钮时控件已经自动将isChecked赋值为true,你在Task.Run中重复赋值isChecked=true属于冗余操作,可能触发绑定的额外状态更新。ContinueWith默认运行在线程池线程,未指定UI调度器,且这种写法会导致isChecked=false的赋值时序不受控,最终出现任务还没执行完就先更新了按钮状态的问题。- 非UI线程直接修改绑定属性,即使
BindableBase会自动封送通知到UI线程,也可能因为线程调度优先级问题导致UI更新早于业务逻辑执行。
修复方案
XAML代码(无需修改)
<ToggleButton Command="{Binding StartProccessCommand}" Content="Proccessing" IsChecked="{Binding IsChecked,Mode=TwoWay}"></ToggleButton>
ViewModel修改后代码
public class ProccessViewModel: BindableBase { private bool _isChecked = false; public bool IsChecked { get => _isChecked; set => SetProperty(ref _isChecked, value); } public DelegateCommand StartProccessCommand { get; set; } public ProccessViewModel() { // 传入异步任务,避免async void的问题 StartProccessCommand = new DelegateCommand(async () => await ExecuteProcessAsync()); } // 改用返回Task的异步方法,时序可控 private async Task ExecuteProcessAsync() { // 第一部分后台任务 await Task.Run(() => { for (int i = 0; i < 50000; i++) { Console.WriteLine(i); } }); // 第二部分逻辑,可根据需求决定是否放到Task.Run中 for (int i = 50000; i < 100000; i++) { Console.WriteLine(i); } // 所有逻辑执行完成后,再修改按钮状态,await后自动回到UI线程,无线程安全问题 IsChecked = false; } }
修改说明
- 移除了冗余的
isChecked=true赋值,直接使用双向绑定自动更新的状态即可 - 将
async void方法改为async Task,配合await关键字顺序执行任务,替代逻辑混乱的ContinueWith写法 - await执行完后台任务后会自动切回UI线程,此时修改
IsChecked属性不会有线程安全问题,且能保证所有业务逻辑执行完成后才更新按钮状态
内容的提问来源于stack exchange,提问作者stack CVL
相关产品推荐
相关产品推荐

