2015-05-25 5 views
1

Я программирую WPF «Реверси», в которой игроки нажимают на плитку в сетке 8x8, чтобы поместить камень.Как отложить выполнение ICommand, не вызывая InvalidOperationException?

Это команда, чтобы поместить камень на плитку:

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); 
        }); 
      } 
    } 

Но это приводит к InvalidOperationException в ClickCommand в соответствии с CanExecuteChanged(this, new EventArgs()) (я положил стрелку на соответствующей строке в первом образце кода). Это происходит через 2 секунды (как только Task.Delay продолжается).

Как я могу это решить?

ответ

2

Исключение вызвано выполнением команды в потоке, отличном от UI, потому что теперь это часть задачи, которая выполняется потоком из пула потоков.

Чтобы заставить его работать, переключитесь на поток пользовательского интерфейса либо в задачу, либо в ViewModel.

В зависимости от настройки, если вы используете подходящую ViewModel, вы можете выбрать для создания PropertyChanged в потоке пользовательского интерфейса в базовом классе ViewModel, поскольку большую часть времени основной причиной ответа на это событие является обновление UI. См. https://stackoverflow.com/a/24882812/563088 о том, как реализовать это.