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

WPF视图切换时如何中止API调用?

WPF视图切换时中止API调用的解决方案

我的WPF应用顶部设有导航按钮,点击后可切换不同视图,但切换视图时,之前视图发起的API调用不会自动中止——就算已经切走,旧API返回404、500等错误时,错误弹窗仍会弹出。相关代码如下:

现有代码

XAML

<!-- FEE SURVEY BUTTON -->
<Menu:Btn
    Command="{Binding FeeSurveyCommand}"
    IsChecked="{Binding NavBar.FeeSurvey}"
    Style="{StaticResource NavButton}">
</Menu:Btn>

<!-- PRACTICE SNAPSHOT BUTTON -->
<Menu:Btn
    Command="{Binding PracticeSnapshotCommand}"
    IsChecked="{Binding NavBar.PracticeSnapshot}"
    Style="{StaticResource NavButton}">
</Menu:Btn>

<!-- 动态切换的内容区域 -->
<ContentControl
    x:Name="Pages"
    Grid.Column="1"
    Margin="25,0"
    Content="{Binding CurrentView}" />

MainViewModel

private object _currentView;
public object CurrentView
{
    get { return _currentView; }
    set { _currentView = value;
        OnPropertyChanged(); }
}    

public ICommand FeeSurveyCommand { get; set; }
public ICommand PracticeSnapshotCommand { get; set; }

private void FeeSurvey(object obj)
{
    CurrentView = new FeeSurveyVM();
}

private void PracticeSnapshot(object obj)
{
    CurrentView = new PracticeSnapshotVM();
}

// 构造函数
public MainViewVM()
{
    FeeSurveyCommand = new RelayCommand(FeeSurvey);
    PracticeSnapshotCommand = new RelayCommand(PracticeSnapshot);
}

FeeSurveyVM(子ViewModel示例)

// 构造函数
Dictionary<string, string> queryParams = new()
{
    {"startdate", "2023-01-01"},
    {"enddate", "2024-01-01"},
};

var res = await ApiCalls.AuthGetApiWithHeader<JObject>("/fee_surveys", queryParams);

// 处理API返回数据

ApiCalls.cs

public static async Task<T> AuthGetApiWithHeader<T>(string endpoint, Dictionary<string, string> queryParams = null)
{
    try
    {
        using var client = new HttpClient();

        // 构建请求URL的逻辑

        HttpResponseMessage response = await client.GetAsync(requestUrl);

        if (response.IsSuccessStatusCode)
        {
            if (response.StatusCode == HttpStatusCode.NoContent)
            {
                return default;
            }
            else
            {
                T result = await response.Content.ReadAsAsync<T>();
                return result;
            }
        }
        else
        {
            HandleApiCallErrorsAsync(response);  // 错误处理函数
            return default;
        }
    }
    catch (Exception)
    {
        MessageBoxHelper.ShowCustomMessageBox("error", "ERROR", "An error occurred. Please try again.");
        return default;
    }
}

解决方案:利用取消令牌中止API请求

核心思路是通过CancellationTokenSource跟踪未完成的API请求,在视图切换时取消旧请求并清理资源。

步骤1:让子ViewModel实现IDisposable接口

所有发起API请求的子ViewModel(如FeeSurveyVM、PracticeSnapshotVM)需持有取消令牌源,并在销毁时取消请求:

public class FeeSurveyVM : INotifyPropertyChanged, IDisposable
{
    private CancellationTokenSource _cts;

    public FeeSurveyVM()
    {
        _cts = new CancellationTokenSource();
        _ = LoadDataAsync(); // 异步加载数据,构造函数不允许async
    }

    private async Task LoadDataAsync()
    {
        try
        {
            Dictionary<string, string> queryParams = new()
            {
                {"startdate", "2023-01-01"},
                {"enddate", "2024-01-01"},
            };

            // 传入取消令牌
            var res = await ApiCalls.AuthGetApiWithHeader<JObject>("/fee_surveys", queryParams, _cts.Token);

            // 处理返回数据
        }
        catch (OperationCanceledException)
        {
            // 请求被取消,无需处理错误
        }
        catch (Exception)
        {
            MessageBoxHelper.ShowCustomMessageBox("error", "ERROR", "An error occurred. Please try again.");
        }
    }

    public void Dispose()
    {
        _cts?.Cancel(); // 取消未完成的请求
        _cts?.Dispose(); // 释放资源
    }

    // INotifyPropertyChanged实现逻辑...
}

步骤2:修改ApiCalls方法支持取消令牌

更新API调用方法,接受CancellationToken并传入HttpClient请求,同时跳过取消请求的错误弹窗:

public static async Task<T> AuthGetApiWithHeader<T>(string endpoint, Dictionary<string, string> queryParams = null, CancellationToken cancellationToken = default)
{
    try
    {
        using var client = new HttpClient();

        // 构建请求URL的逻辑

        // 传入取消令牌
        HttpResponseMessage response = await client.GetAsync(requestUrl, cancellationToken);

        if (response.IsSuccessStatusCode)
        {
            if (response.StatusCode == HttpStatusCode.NoContent)
            {
                return default;
            }
            else
            {
                T result = await response.Content.ReadAsAsync<T>(cancellationToken);
                return result;
            }
        }
        else
        {
            // 仅当请求未被取消时处理错误
            if (!cancellationToken.IsCancellationRequested)
            {
                HandleApiCallErrorsAsync(response);
            }
            return default;
        }
    }
    catch (OperationCanceledException)
    {
        // 请求被取消,直接返回默认值,不弹窗
        return default;
    }
    catch (Exception)
    {
        // 仅当请求未被取消时弹出错误
        if (!cancellationToken.IsCancellationRequested)
        {
            MessageBoxHelper.ShowCustomMessageBox("error", "ERROR", "An error occurred. Please try again.");
        }
        return default;
    }
}

步骤3:在MainViewModel切换视图时清理旧资源

修改CurrentView属性的setter,在设置新视图前,销毁旧ViewModel并触发取消:

private object _currentView;
public object CurrentView
{
    get { return _currentView; }
    set 
    { 
        // 清理旧ViewModel资源
        if (_currentView is IDisposable disposableOldView)
        {
            disposableOldView.Dispose();
        }
        _currentView = value;
        OnPropertyChanged(); 
    }
}    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 00:11:03