如何在不导致InvalidOperationException的情况下延迟ICommand执行?

时间:2015-05-25 13:49:05

标签: c# wpf asynchronous delay icommand

我正在编写一个WPF'黑白棋'游戏,玩家在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();
        }
    }

我编写了一个人工智能,当它在玩家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);
                  });
          }
    }

但是这导致InvalidOperationException位于ClickCommand的{​​{1}}行(我在第一个代码示例中的相关行放了一个箭头)。这发生在2秒后(一旦Task.Delay继续)。

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:2)

异常是由在非UI线程上执行命令引起的,因为它现在是由线程池中的线程执行的任务的一部分。

要使其工作,请在任务或ViewModel中切换到UI线程。

根据设置,如果您使用正确的ViewModel,您可能会选择在ViewModel基类的UI线程上引发PropertyChanged,因为大多数时候响应该事件的主要原因是更新UI 。 请参阅https://stackoverflow.com/a/24882812/563088了解如何实现此目标。

答案 1 :(得分:1)

现在使用 .Net Framework 4.6.1 ,您可以通过添加如下的TaskScheduler.FromCurrentSynchronizationContext()参数来指示在当前上下文中运行线程的任务:

  Task.Delay(2000).ContinueWith(_ =>
  {
      rowDataContexts[nextMove.Y].SquareDataContexts[nextMove.X].SquareViewModel.Click.Execute(true);
  }, TaskScheduler.FromCurrentSynchronizationContext());