title: mvvm windows phone 8 confusion and how to capture when text is changed
body: <p>I'm trying to write a windows phone 8 app using the mvvm pattern but I'm struggling with it. </p>
  
  <p>I have a page with a list of persons which is binded to my PersonViewModel. That part is working fine. I then have 2 buttons in the application bar i.e. add or edit. When I want to edit a person, I select the person from the list, which then sets the CurrentPerson in my ViewModel. This in turns set a property in my MainViewModel which is used to store the currently selected person i.e. </p>
  
  <pre><code>App.MainViewModel.CurrentPerson = this.CurrentPerson;
  </code></pre>
  
  <p>When I want to add a new person, I use the same principal but I create a new person model.</p>
  
  <pre><code>App.MainViewModel.CurrentPerson = new PersonModel();
  </code></pre>
  
  <p>I then redirect to a page which contains the fields to handle a person, whether it is being added or edited and this is binded to a ViewModel called PersonEntryViewModel</p>
  
  <p>Before I explain my problem, I want to let you know what I'm trying to achieve. I want the "Save" button in my application bar to get enabled once a certain amount of criteria have been met i.e. Name has been filled and has x characters, etc...</p>
  
  <p>I can see what my problem is but I don't know how to resolve it.</p>
  
  <p>Here is a simplied version of my PersonEntryViewModel:</p>
  
  <pre><code>public class PersonEntryViewModel : BaseViewModel
  {
      private PersonModel _currentPerson;
      private bool _isNewPerson;
      private ICommand _savePersonCommand;
      private ICommand _cancelCommand;
      private ICommand _titleTextChanged;
  
      private bool _enableSaveButton;
  
      public PersonEntryViewModel()
      {
          this.CurrentPerson = App.MainViewModel.CurrentPerson ?? new PersonModel();
      }
  
      public ICommand SavePersonCommand
      {
          get
          {
              return this._savePersonCommand ?? (this._savePersonCommand = new DelegateCommand(SavePersonAction));
          }
      }
  
      public ICommand CancelCommand
      {
          get
          {
              return this._cancelCommand ?? (this._cancelCommand = new DelegateCommand(CancelAction));
          }
      }
  
      public ICommand NameTextChanged
      {
          get
          {
              return this._nameTextChanged ?? (this._nameTextChanged = new DelegateCommand(NameTextChangedAction));
          }
      }
  
      private void NameTextChangedAction(object actionParameters)
      {
          if (!string.IsNullOrEmpty(this._currentPerson.Name) &amp;&amp; _currentPerson.Name.Length &gt; 2)
          {
              EnableSaveButton = true;
          }            
      }
  
      private void CancelAction(object actionParameters)
      {
          Console.WriteLine("Cancel");
          INavigationService navigationService = this.GetService&lt;INavigationService&gt;();
          if (navigationService == null)
              return;
          navigationService.GoBack();
          navigationService = null;
      }
  
      private void SavePersonAction(object actionParameters)
      {
          Console.WriteLine("Saving");
      }
  
      public PersonModel CurrentPerson
      {
          get { return this._currentPerson; }
          set
          {
              if (this._currentPerson != value)
                  this.SetProperty(ref this._currentPerson, value);
          }
      }
  
      public string PageTitle
      {
          get { return this._pageTitle; }
          set { if (this._pageTitle != value) this.SetProperty(ref this._pageTitle, value); }
      }
  
      public bool IsNewPerson
      {
          get { return this._isNewPerson; }
          set
          {
              if (this._isNewPerson != value)
              {
                  this.SetProperty(ref this._isNewPerson, value);
                  if (this._isNewPerson)
                      this.PageTitle = AppResources.PersonEntryPageNewTitle;
                  else
                      this.PageTitle = AppResources.PersonEntryPageEditTitle;
              }
          }
      }
  
      public bool EnableSaveButton
      {
          get { return this._enableSaveButton; }
          set { if (this._enableSaveButton != value) this.SetProperty(ref this._enableSaveButton, value); }
      }
  }
  </code></pre>
  
  <p>Here is part of my XAML:</p>
  
  <pre><code>&lt;Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{StaticResource PersonEntryViewModel}" &gt;
  
      &lt;!--ContentPanel - place additional content here--&gt;
      &lt;Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" DataContext="{Binding CurrentPerson, Mode=TwoWay}"&gt;
          &lt;Grid Margin="0,0,0,5"&gt;
              &lt;Grid.RowDefinitions&gt;
                  &lt;RowDefinition Height="*"/&gt;
              &lt;/Grid.RowDefinitions&gt;
              &lt;Border BorderBrush="{StaticResource PhoneAccentBrush}"
                              HorizontalAlignment="Stretch"
                              VerticalAlignment="Stretch"
                              BorderThickness="5"
                              Background="Transparent"
                              CornerRadius="5"&gt;
                  &lt;Grid&gt;
                      &lt;Grid.RowDefinitions&gt;
                          &lt;RowDefinition Height="Auto"&gt;&lt;/RowDefinition&gt;
                          &lt;RowDefinition Height="Auto"&gt;&lt;/RowDefinition&gt;
                          &lt;RowDefinition Height="Auto"&gt;&lt;/RowDefinition&gt;
                          &lt;RowDefinition Height="Auto"&gt;&lt;/RowDefinition&gt;
                          &lt;RowDefinition Height="Auto"&gt;&lt;/RowDefinition&gt;
                          &lt;RowDefinition&gt;&lt;/RowDefinition&gt;
                      &lt;/Grid.RowDefinitions&gt;
                      &lt;TextBlock Text="Name:" 
                                         Grid.Row="0"
                                         Margin="12,0,0,0"/&gt;
                      &lt;TextBox Text="{Binding Name, Mode=TwoWay}" 
                                       Grid.Row="1"&gt;
                          &lt;!--&lt;i:Interaction.Triggers&gt;
                              &lt;i:EventTrigger EventName="TextChanged"&gt;
                                  &lt;i:InvokeCommandAction Command="{Binding NameTextChanged, Mode=OneWay}" CommandParameter="{Binding}" /&gt;
                              &lt;/i:EventTrigger&gt;
                          &lt;/i:Interaction.Triggers&gt;--&gt;
                      &lt;/TextBox&gt;
                      &lt;TextBlock Text="Address:" 
                                         Grid.Row="2"
                                         Margin="12,0,0,0"/&gt;
                      &lt;TextBox Text="{Binding Address, Mode=TwoWay}" 
                               AcceptsReturn="True" 
                               Height="200" 
                               VerticalScrollBarVisibility="Visible" 
                               TextWrapping="Wrap" 
                               Grid.Row="3"/&gt;
  </code></pre>
  
  <p>As you can see, my layoutRoot grid is binded to my ViewModel i.e. PersonEntryViewModel and the grid content panel containing my textboxes required for editing is binded to CurrentPerson.</p>
  
  <p>Is that the correct way to do it? I need to bind the control to the CurrentPerson property which will contain data if the person is being edited and it will contain a new empty PersonModel if a new person is being added.</p>
  
  <p>As it stands, that part is working. When I type some text in my field and click on the next one, it calls set the CurrentPerson relevant property which in turns calls the PersonModel. Click on the save button and I check the CurrentPerson, I can see it has all the various properties set.</p>
  
  <p>As you can see in my PersonEntryViewModel, I've got other properties which are required. For example the EnableSaveButton, which technically should be set to true or false based on the validation of the various properites from the CurrentPerson object but I need this to be checked as the user is typing text in the various textbox and this is where I'm having a problem.</p>
  
  <p>If I enable the following code:</p>
  
  <pre><code>&lt;i:Interaction.Triggers&gt;
      &lt;i:EventTrigger EventName="TextChanged"&gt;
          &lt;i:InvokeCommandAction Command="{Binding NameTextChanged, Mode=OneWay}" CommandParameter="{Binding}" /&gt;
      &lt;/i:EventTrigger&gt;
  &lt;/i:Interaction.Triggers&gt;
  </code></pre>
  
  <p>It doesn't get triggered in the PersonEntryViewModel where I really need it as this is where I want to set my EnableSaveButton property but I guess it makes sense as this code is binded to the Name textbox which is turn is binded to the CurrentPerson property which is my PersonModel.</p>
  
  <p>If I move the code from the PersonEntryViewModel to the PersonViewModel</p>
  
  <pre><code>private ICommand _personTextChanged;
  
  public ICommand PersonTextChanged
  {
     get
     {
         return this._personTextChanged ?? (this._personTextChanged = new DelegateCommand(PersonTextChangedAction));
     }
  }
  
  private void PersonTextChangedAction(object actionParameters)
  {
      if (!string.IsNullOrEmpty(this._name) &amp;&amp; this._name.Length &gt; 2)
      {
          //EnableSaveButton = true;
          Console.WriteLine("");
      }
  }
  </code></pre>
  
  <p>It gets triggered accordingly but then how do I get this information back to my PersonEntryViewModel which binded to the view where my 2 buttons (i.e. save &amp; cancel) are located and the EnableSaveButton property is responsible for enabling the save button accordingly when set assuming that the Name is valid i.e. set and minlen is match for example.</p>
  
  <p>Is the PersonEntryViewModel and using a CurrentPerson property with the current person being edited or added designed correctly or not and how am I to handle this scenario?</p>
  
  <p>I hope the above makes sense but if I'm not clear about something, let me know and I'll try to clarify it.</p>
  
  <p>Thanks.</p>
  
  <p>PS: I posted another posted related to how to detect text change, but I figured it out but it's obviously not the problem. The problem seems more related to design.</p>