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
相关产品推荐
相关产品推荐

