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

UI线程卡顿约4秒,Dispatcher.BeginInvoke无效,ListBox选机场加载遇阻

解决WPF ListBox选中后UI卡顿的问题

哥们,你现在的问题核心是把所有耗时操作都塞到UI线程里执行了——哪怕用了Dispatcher.BeginInvoke,本质上还是让UI线程去读大文件、遍历几万行的DataTable,这肯定会卡啊!Dispatcher的作用是把UI相关的操作扔回UI线程,而不是让它干脏活累活。

给你几个针对性的解决方案,按优先级来:

1. 把耗时操作移到后台线程,只让UI线程干UI的活

用Task.Run把读文件、查找数据的逻辑放到后台线程,找到目标数据后,再用Dispatcher把更新UI的操作抛回UI线程。这样UI线程全程不被阻塞,自然不会卡顿。

2. 提前缓存数据,避免每次选中都重读文件

你现在每次选中机场都重新读一遍r5_IOS.txt文件、生成DataTable,这完全是重复劳动!应该在程序启动或者初始化的时候,把文件内容加载一次,缓存成高效的查找结构(比如字典),之后每次选中直接查缓存就行。

3. 优化查找逻辑,从O(n)降到O(1)

遍历几万行DataTable找匹配项是O(n)的时间复杂度,换成字典(Key设为机场的唯一标识,比如ICAO代码),查找速度直接变成O(1),这能大幅减少耗时。

修改后的代码示例

// 提前缓存的字典,程序初始化时加载一次
private Dictionary<string, DataRow> _airportDataCache;

// 初始化方法(比如窗口Loaded事件里)
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // 后台线程加载数据,避免启动时卡顿
    Task.Run(() => 
    {
        DataTable dt = TxtFileToTable(Path.Combine(Environment.CurrentDirectory, "r5_IOS.txt"));
        // 假设你的机场唯一标识在"ICAO"列,根据实际情况修改
        _airportDataCache = dt.Rows.Cast<DataRow>()
            .ToDictionary(row => row["ICAO"].ToString(), row => row);
    });
}

private void ListAirport_SelectedIndexChanged(object sender, SelectionChangedEventArgs e)
{
    // 先判断选中项是否有效,以及缓存是否加载完成
    if (ListAirport.SelectedItem == null || _airportDataCache == null)
        return;

    // 获取选中机场的唯一标识(比如从SelectedItem里取ICAO)
    string selectedAirportId = GetSelectedAirportId(ListAirport.SelectedItem);
    if (string.IsNullOrEmpty(selectedAirportId))
        return;

    // 后台线程查找数据(虽然字典查找很快,但还是放后台更稳妥)
    Task.Run(() => 
    {
        if (_airportDataCache.TryGetValue(selectedAirportId, out var targetRow))
        {
            // 把需要更新UI的操作抛回UI线程
            Dispatcher.Invoke(() => 
            {
                // 这里写更新UI的逻辑,比如绑定跑道信息等
                LoadRunwayInfo(targetRow);
            });
        }
    });
}

// 辅助方法:从选中项提取机场唯一标识,根据你的实际数据结构修改
private string GetSelectedAirportId(object selectedItem)
{
    // 假设你的ListBox绑定的是自定义Airport对象,或者是DataRowView
    // 示例:如果是DataRowView
    var drv = selectedItem as DataRowView;
    return drv?["ICAO"].ToString();
}

// 辅助方法:更新UI显示跑道等信息
private void LoadRunwayInfo(DataRow row)
{
    // 比如:
    txtRunwayLength.Text = row["RunwayLength"].ToString();
    txtRunwayDirection.Text = row["RunwayDirection"].ToString();
    // ...其他UI更新逻辑
}

额外优化:处理快速切换选中项的情况

如果用户快速点击多个机场,之前的后台任务可能还在执行,这时候可以用CancellationTokenSource取消之前的任务,避免无效操作:

private CancellationTokenSource _cts;

private void ListAirport_SelectedIndexChanged(object sender, SelectionChangedEventArgs e)
{
    // 取消之前的任务
    _cts?.Cancel();
    _cts = new CancellationTokenSource();
    var token = _cts.Token;

    if (ListAirport.SelectedItem == null || _airportDataCache == null)
        return;

    string selectedAirportId = GetSelectedAirportId(ListAirport.SelectedItem);
    if (string.IsNullOrEmpty(selectedAirportId))
        return;

    Task.Run(() => 
    {
        if (token.IsCancellationRequested) return;
        
        if (_airportDataCache.TryGetValue(selectedAirportId, out var targetRow))
        {
            if (token.IsCancellationRequested) return;
            
            Dispatcher.Invoke(() => 
            {
                if (token.IsCancellationRequested) return;
                LoadRunwayInfo(targetRow);
            });
        }
    }, token);
}

// 窗口关闭时记得释放资源
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    _cts?.Cancel();
    _cts?.Dispose();
}

这样处理后,UI卡顿的问题应该就彻底解决了——后台干耗时的活,UI线程只负责渲染,两者互不干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:24:45