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

WPF异步命令完成/取消后按钮状态切换的MVVM实现方案

解决WPF异步命令按钮任务完成后MVVM方式恢复初始状态的问题

核心思路是通过ViewModel中的布尔属性绑定ToggleButton的IsChecked状态,在异步任务的**最终块(finally)**中强制重置该属性,确保无论任务成功、取消还是失败,按钮都会切回初始状态。以下是具体实现步骤和代码:


1. ViewModel核心逻辑实现

首先在ViewModel中添加绑定属性、异步命令,并处理任务完成后的状态重置:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;

public class MainViewModel : INotifyPropertyChanged
{
    private bool _isConnecting;
    private CancellationTokenSource _cts;

    // 绑定ToggleButton的IsChecked属性,控制按钮状态
    public bool IsConnecting
    {
        get => _isConnecting;
        set
        {
            _isConnecting = value;
            OnPropertyChanged();
            // 命令可执行状态变化时通知UI更新
            (ConnectCommand as AsyncCommand)?.RaiseCanExecuteChanged();
        }
    }

    public IAsyncCommand ConnectCommand { get; }

    public MainViewModel()
    {
        ConnectCommand = new AsyncCommand(ExecuteConnectAsync, CanExecuteConnect);
    }

    // 控制命令是否可执行(避免重复点击)
    private bool CanExecuteConnect()
    {
        return !IsConnecting;
    }

    private async Task ExecuteConnectAsync(CancellationToken token)
    {
        IsConnecting = true;
        _cts = CancellationTokenSource.CreateLinkedTokenSource(token);

        try
        {
            // 替换为实际的异步连接逻辑
            await Task.Delay(5000, _cts.Token);
            // 连接成功后的业务处理
        }
        catch (OperationCanceledException)
        {
            // 取消操作后的业务处理
        }
        catch
        {
            // 异常处理逻辑
        }
        finally
        {
            // 无论任务结果如何,强制重置状态
            IsConnecting = false;
            _cts?.Dispose();
            _cts = null;
        }
    }

    // 取消操作方法(绑定到ToggleButton的Unchecked事件)
    public void CancelConnect()
    {
        _cts?.Cancel();
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. AsyncCommandBase扩展实现

确保你的AsyncCommand支持可执行状态通知,这里给出基础实现:

public abstract class AsyncCommandBase : IAsyncCommand
{
    public abstract bool CanExecute(object parameter);
    public abstract Task ExecuteAsync(object parameter);

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        ExecuteAsync(parameter).ConfigureAwait(false);
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

public interface IAsyncCommand : ICommand
{
    Task ExecuteAsync(object parameter);
}

public class AsyncCommand : AsyncCommandBase
{
    private readonly Func<CancellationToken, Task> _execute;
    private readonly Func<bool> _canExecute;

    public AsyncCommand(Func<CancellationToken, Task> execute, Func<bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public override bool CanExecute(object parameter)
    {
        return _canExecute?.Invoke() ?? true;
    }

    public override async Task ExecuteAsync(object parameter)
    {
        await _execute(CancellationToken.None);
    }
}

3. XAML中ToggleButton绑定

通过Trigger切换按钮文本,并绑定取消操作:

<ToggleButton 
    Command="{Binding ConnectCommand}"
    IsChecked="{Binding IsConnecting, Mode=TwoWay}"
    Width="100" Height="30">
    <ToggleButton.Style>
        <Style TargetType="ToggleButton">
            <Setter Property="Content" Value="Connect"/>
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Content" Value="Cancel"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </ToggleButton.Style>
    <!-- 使用交互库绑定Unchecked事件到取消方法 -->
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Unchecked">
            <i:InvokeCommandAction Command="{Binding CancelCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ToggleButton>

注:如果没有单独的CancelCommand,也可以直接在Unchecked事件中调用ViewModel的CancelConnect方法,或者通过自定义行为实现。


关键说明

  • 状态重置的可靠性:通过finally块确保异步任务无论成功、取消还是抛出异常,都会重置IsConnecting为false,避免按钮卡死在Cancel状态。
  • 命令可执行性控制:CanExecuteConnect方法确保按钮在连接中时无法重复点击,防止多次启动异步任务。
  • MVVM合规性:所有状态变更和业务逻辑都在ViewModel中处理,UI仅通过数据绑定响应状态变化,符合MVVM设计模式。

内容的提问来源于stack exchange,提问作者GreezXII

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 16:58:35