title: Weird race condition by using ViewModel-first (binding) approach
body: <p>I am experimenting with a simple ViewModel-first based WPF application and some primitive navigation logic. The application consists of two views (screens). One screen contains a button "Go forward" and the other a button "Go backward". By pressing one of the buttons a delegate command is invoked, which in turn causes the shell view-model to switch the active screen. Screen 1 switches to Screen 2, whereas Screen 2 switches to Screen 1.</p>
  
  <p>The problem with this approach is, that it introduces a race condition. When  clicking fast enough there is a chance that the corresponding action (go forward/go backward) is executed twice, thus causing the application to fail. The interesting thing about that is that the screen has already been changed, but the UI doesn't reflect the state changes instantaneously. Until now I never had experienced this kind of gap and I made this experiment just to prove, that a single-threaded (dispatched) WPF application is automatically thread-safe. </p>
  
  <p>Does somebody have an explanation for this odd behavior? Is the WPF binding mechanism too slow, so that the button can be pressed a second time, until the UI has updated itself to represent the new screen state? </p>
  
  <p>I have no idea how to fix this according to the recommendations for developing mvvm applications. There is no way to synchronize the code, because there is only one thread. I hope you can help me, because now I feel very unsecure with relying on the WPF data binding and templating system.</p>
  
  <p><a href="http://en.file-upload.net/download-11230590/WPF_race_condition.zip.html" rel="nofollow noreferrer">Zip archive containing the project files</a></p>
  
  <p><a href="https://i.stack.imgur.com/ZeM7N.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ZeM7N.png" alt="Screen 1 switches to Screen 2, whereas Screen 2 switches to Screen 1. By clicking fast enough it is possible to introduce a race condition (that is going forward/backward twice)."></a></p>
  
  <p><strong>MainWindow.xaml:</strong></p>
  
  <pre><code>&lt;Window x:Class="MainWindow"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
          xmlns:local="clr-namespace:WpfApplication1"
          mc:Ignorable="d"
          Title="MainWindow" Height="350" Width="525"&gt;
      &lt;Window.Resources&gt;
          &lt;DataTemplate DataType="{x:Type local:Screen1}"&gt;
              &lt;local:View1/&gt;
          &lt;/DataTemplate&gt;
          &lt;DataTemplate DataType="{x:Type local:Screen2}"&gt;
              &lt;local:View2/&gt;
          &lt;/DataTemplate&gt;
      &lt;/Window.Resources&gt;
      &lt;Window.DataContext&gt;
          &lt;local:ShellViewModel/&gt;
      &lt;/Window.DataContext&gt;
      &lt;Grid&gt;
          &lt;ContentControl Content="{Binding CurrentScreen}"/&gt;
      &lt;/Grid&gt;
  &lt;/Window&gt;
  </code></pre>
  
  <p>The <strong>ShellViewModel</strong> containing a "go forward" and "go backward" method:</p>
  
  <pre><code>Public Class ShellViewModel
      Inherits PropertyChangedBase
  
      Private _currentScreen As Object
      Public Property Screens As Stack(Of Object) = New Stack(Of Object)()
  
      Public Sub New()
          Me.Screens.Push(New Screen1(Me))
          Me.GoForward()
      End Sub
  
      Property CurrentScreen As Object
          Get
              Return _currentScreen
          End Get
          Set(value)
              _currentScreen = value
              RaisePropertyChanged()
          End Set
      End Property
  
      Public Sub GoBack()
          Log("Going backward.")
          If (Me.Screens.Count &gt; 2) Then
              Throw New InvalidOperationException("Race condition detected.")
          End If
          Log("Switching to Screen 1")
          Me.Screens.Pop()
          Me.CurrentScreen = Me.Screens.Peek()
      End Sub
  
      Public Sub GoForward()
          Log("Going forward.")
          If (Me.Screens.Count &gt; 1) Then
              Throw New InvalidOperationException("Race condition detected.")
          End If
          Log("Switching to Screen 2.")
  
          Me.Screens.Push(New Screen2(Me))
          Me.CurrentScreen = Me.Screens.Peek()
      End Sub
  
  End Class
  </code></pre>
  
  <p>The <strong>Screen</strong> class containing just a delegate command to start the action:</p>
  
  <pre><code>Public Class Screen1
      Inherits PropertyChangedBase
  
      Private _clickCommand As ICommand
      Private _shellViewModel As ShellViewModel
  
      Public Sub New(parent As ShellViewModel)
          _shellViewModel = parent
      End Sub
  
  
      Public ReadOnly Property ClickCommand As ICommand
          Get
              If _clickCommand Is Nothing Then
                  _clickCommand = New RelayCommand(AddressOf ExecuteClick, AddressOf CanExecute)
              End If
              Return _clickCommand
          End Get
      End Property
  
      Private Function CanExecute(arg As Object) As Boolean
          Return True
      End Function
  
      Private Sub ExecuteClick(obj As Object)
          Threading.Thread.SpinWait(100000000)
          _shellViewModel.GoForward()
      End Sub
  
  End Class
  </code></pre>