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

WPF贪吃蛇点击新游戏后界面冻结不刷新问题排查

WPF贪吃蛇游戏界面冻结问题分析与解决方案

存在的错误

  • GameField属性未触发属性变更通知:你在InitGame方法中每次都会重新实例化ObservableCollection<ObservableCollection<Cell>>类型的GameField属性,但如果该属性未实现INotifyPropertyChanged的变更通知,UI层的绑定不会感知到数据源已经替换,仍然绑定第一次初始化的旧GameField实例。后续所有游戏逻辑都是修改新实例,自然不会同步到界面,这是你第一次启动正常、点击新游戏后冻结的核心原因。
  • DispatcherTimer资源泄漏与重复注册:构造函数首次初始化_timer并启动后,每次调用InitGame都会重新实例化_timer,旧的_timer没有停止、也没有移除Tick事件订阅,多个Timer同时运行会占用UI线程资源,甚至引发线程安全问题。
  • Cell类未实现属性变更通知:如果Cell类的Value和Color属性变更时没有触发INotifyPropertyChanged通知,UI无法感知单个单元格的属性变化,也会导致局部更新失效。

修复方案

1. 为GameField属性添加变更通知

确保你的ViewModel实现了INotifyPropertyChanged接口,修改GameField属性的定义:

private ObservableCollection<ObservableCollection<Cell>> _gameField;
public ObservableCollection<ObservableCollection<Cell>> GameField
{
    get => _gameField;
    set
    {
        _gameField = value;
        OnPropertyChanged(nameof(GameField));
    }
}

// 你的ViewModel需要继承INotifyPropertyChanged,实现以下方法
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

2. 清理旧的Timer资源

在InitGame方法开头添加旧Timer的清理逻辑:

private void InitGame()
{
    // 先清理已存在的Timer,避免资源泄漏
    if (_timer != null)
    {
        _timer.Stop();
        _timer.Tick -= _timer_Tick;
        _timer = null;
    }
    
    // 原有初始化逻辑保持不变
    _timer = new DispatcherTimer
    {
        Interval = new TimeSpan(0, 0, 0, 0, 500)
    };
    _timer.Tick += _timer_Tick;
    // ... 其余原有代码
}

3. 为Cell类添加属性变更通知

修改Cell类,实现INotifyPropertyChanged接口:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Media;

public class Cell : INotifyPropertyChanged
{
    private int _value;
    private Brush _color;

    public int Value
    {
        get => _value;
        set 
        { 
            _value = value; 
            OnPropertyChanged(); 
        }
    }

    public Brush Color
    {
        get => _color;
        set 
        { 
            _color = value; 
            OnPropertyChanged(); 
        }
    }

    public Cell(int value, Brush color)
    {
        Value = value;
        Color = color;
    }

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

4. 可选优化XAML绑定

将XAML中的DynamicResource替换为StaticResource,避免动态资源查找的额外开销,稳定性更高:

<ItemsControl ItemsSource="{Binding}" ItemTemplate="{StaticResource CellTemplate}" Background="Black">
<ItemsControl Grid.Column="0" Grid.RowSpan="10" ItemsSource="{Binding GameField}" ItemTemplate="{StaticResource GridTemplate}" >

(注:UpdateSourceTrigger=PropertyChanged对ItemsSource绑定没有必要,可以移除)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 01:54:03