如何在不导致InvalidOperationException的情况下延迟iccommand的执行
本文关键字:情况下 延迟 iccommand 执行 InvalidOperationException | 更新日期: 2023-09-27 17:50:37
我正在编写一个WPF 'Reversi'游戏,在这个游戏中,玩家按下8x8网格中的瓷砖来放置石头。
这是在瓷砖上放置石头的命令:
private class Click : ICommand
{
private readonly SquareViewModel squareViewModel;
public ClickCommand(SquareViewModel squareViewModel)
{
this.squareViewModel = squareViewModel;
squareViewModel.Square.IsValidMove.PropertyChanged += (sender, args) =>
{
if (CanExecuteChanged != null)
{
/*->*/ CanExecuteChanged(this, new EventArgs());
}
};
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return squareViewModel.Square.IsValidMove.Value;
}
public void Execute(object parameter)
{
squareViewModel.Square.PlaceStone();
}
}
我已经编写了一个AI,当轮到玩家2时放置石头:
void CurrentPlayer_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
Player player = ((ICell<Player>)(sender)).Value;
if (player != null && player.Equals(Player.TWO))
{
Vector2D nextMove = ai.FindBestMove(boardViewModel.Game);
rowDataContexts[nextMove.Y].SquareDataContexts[nextMove.X].SquareViewModel.Click.Execute(null);
}
}
}
这工作得很好。然而,我希望ai在2秒后做出动作,而不是立即。
我已经尝试实现这样的延迟:
void CurrentPlayer_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
Player player = ((ICell<Player>)(sender)).Value;
if (player != null && player.Equals(Player.TWO))
{
Vector2D nextMove = ai.FindBestMove(boardViewModel.Game);
Task.Delay(2000).ContinueWith(_ =>
{
rowDataContexts[nextMove.Y].SquareDataContexts[nextMove.X].SquareViewModel.Click.Execute(true);
});
}
}
但这导致ClickCommand
中CanExecuteChanged(this, new EventArgs())
行的InvalidOperationException
(我在第一个代码示例中的相关行中放置了一个箭头)。这将在2秒后发生(只要Task。继续拖延)。
我该如何解决这个问题?
该异常是由在非ui线程上执行命令引起的,因为现在它是由线程池中的线程执行的任务的一部分。
要使其工作,切换到任务或ViewModel中的UI线程。
根据设置,如果您正在使用适当的ViewModel,您可能会选择在ViewModel基类中的UI线程上引发PropertyChanged,因为大多数时候响应该事件的主要原因是更新UI。
现在用。. Net framework 4.6.1可以通过添加TaskScheduler.FromCurrentSynchronizationContext()
参数来指示在当前上下文中运行线程的任务,如下所示:
Task.Delay(2000).ContinueWith(_ =>
{
rowDataContexts[nextMove.Y].SquareDataContexts[nextMove.X].SquareViewModel.Click.Execute(true);
}, TaskScheduler.FromCurrentSynchronizationContext());