You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 20:06:03