This project has moved. For the latest updates, please go here.

ViewModel Status Tracking

  • Start by opening the result of the The previous tutorial (either do it yourself or download it)
  • A very important thing that is missing from the tutorials so far is updating the Model (and/or the web service). One way of doing this is by keeping track of the status of the objects in the ViewModel so when the time comes to update the Model/web service we know what to do with each object in the ViewModel.
  • We'll store the status of a ViewModel object in a special property called EntityStatus which is an enumeration.
  • Add a new C# file to the MVVM folder of the solution EntityStatus.cs:
namespace MVVM
{
    public enum EntityStatus
    {
        Original,
        Added,
        Modified,
        Deleted
    }
}
  • Then add a property to the Person.xml mapping file:
    <Properties>

      <Property name="ID"
                type="int">
        <ModelMapping propertyPath="ID"/>
      </Property>

      <Property name="FirstName"
                type="string">
        <ModelMapping propertyPath="FirstName"/>
      </Property>

      <Property name="LastName"
                type="string">
        <ModelMapping propertyPath="LastName"/>
      </Property>

      <Property name="EntityStatus"
                type="MVVM.EntityStatus"/>

    </Properties>
  • Click Transform All Templates.
  • Now we need to keep the value of this property in sync with the actual status of the object.
  • First keeping track of added persons. The place to manage that is in the Persons ViewModel class because that is where new Person objects are added to the collection.
  • Edit the Persons.cs partial class:
        partial void AddPersonCommandExecute(object parameter)
        {
            this.Collection.Add(this.NewPerson);
            this.NewPerson.EntityStatus = MVVM.EntityStatus.Added;
            this.NewPerson = new Person();
        }
  • Then keeping track of modified Person ViewModel objects. At the end of editing the EndEdit method of the IEditableObject interface is called. The implementation of this method that was generated by the MVMMapper calls two partial methods: BeforeEndEdit and AfterEndEdit. The BeforeEndEdit is called just before ending editing mode and allows you to cancel the ending of the editing mode (you could use this to do some validation). The AfterEndEdit is called after editing mode has been left and is usefull for our current purpose.
  • Add a partial class Person to the ViewModel folder and implement the AfterEndEdit partial method:

namespace PersonLookup.ViewModel
{
    public partial class Person
    {
        partial void AfterEndEdit()
        {
            if (this.EntityStatus == MVVM.EntityStatus.Original)
            {
                this.EntityStatus = MVVM.EntityStatus.Modified;
            }
        }
    }
}
  • Note that we do only change the status when the status is set to Original. The reason for this is that when we later update the Model or web service we must know whether we should 'insert' or 'update' an object. If we did change the status from Added to Modified we might endup trying to update a non existing object.
  • To be able to show all variations we'll add delete functionality as well.
  • Add a DeletePersonCommand command to the Persons.xml mapping file:
     <Commands>
      <Command name="AddPersonCommand"/>
      <Command name="EditPersonCommand"/>
      <Command name="DeletePersonCommand"/>
      <Command name="UpdateCommand"/>
    </Commands>
  • Click Transform All Templates.
  • In the Persons.cs file implement the DeletePersonCommandExecute partial method:
        partial void DeletePersonCommandExecute(object parameter)
        {
            this.SelectedPerson.EntityStatus = MVVM.EntityStatus.Deleted;
        }
  • To be able to delete the object we also need to add a button to the PersonsOverview view:
            <my:PersonEdit DataContext="{Binding EditPerson}" />
            <Button Content="Delete Selected Person"
                    Command="{Binding DeletePersonCommand}"
                    Margin="2,8,2,2" />
  • It is not easy to see what the status of a Person ViewModel object is, so let's add a visual clue in the PersonSummary View:
<UserControl x:Class="PersonLookup.View.PersonSummary"
             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"
             mc:Ignorable="d"
             d:DesignHeight="300"
             d:DesignWidth="400">

    <Grid x:Name="LayoutRoot"
          Background="White">
        <Border BorderBrush="Blue"
                Margin="2"
                BorderThickness="1"
                CornerRadius="3"
                Padding="3">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="auto" />
                    <ColumnDefinition Width="auto" />
                    <ColumnDefinition Width="auto" />
                    <ColumnDefinition Width="auto" />
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding LastName}"
                           Grid.Column="0" />
                <TextBlock Text=", "
                           Grid.Column="1" />
                <TextBlock Text="{Binding FirstName}"
                           Grid.Column="2" />
                <TextBlock Text="{Binding EntityStatus}"
                           Grid.Column="3"
                           Foreground="Gray"
                           Margin="4,0,0,0" />
            </Grid>
        </Border>
    </Grid>
</UserControl>
  • Now you can test and see how the EntityStatus changes when manipulating Person ViewModel objects.
  • Finally we'll have a look at updating the Model/web service.
  • Add an Update command to the Persons.xml mapping file:
    <Commands>
      <Command name="AddPersonCommand"/>
      <Command name="EditPersonCommand"/>
      <Command name="UpdateCommand"/>
    </Commands>
  • Add a button to the PersonsOverview View that will execute the UpdateCommand:
            <my:PersonEdit DataContext="{Binding EditPerson}" />
            <Button Content="Update"
                    Command="{Binding UpdateCommand}"
                    Margin="2,8,2,2" />
        </StackPanel>
  • Now all that is left is coding the UpdateCommandExecute partial method in the Persons.cs:
        partial void UpdateCommandExecute(object parameter)
        {
            var personsToDelete = new List<Person>();

            foreach (var person in Collection)
            {
                switch (person.EntityStatus)
                {
                    case MVVM.EntityStatus.Original:
                        // ignore
                        break;
                    case MVVM.EntityStatus.Added:
                        // call add person method
                        break;
                    case MVVM.EntityStatus.Modified:
                        // call update person method
                        break;
                    case MVVM.EntityStatus.Deleted:
                        // call delete person method
                        personsToDelete.Add(person);
                        break;
                    default:
                        break;
                }
            }

            foreach (var personToDelete in personsToDelete)
            {
                Collection.Remove(personToDelete);
            }
        }
  • As you can see there are some blanks to fill in; we'll do that one by one. We'll start with Added persons.
  • Add to the Person.xml mapping file a definition for the provider class and add a Save method to it:
  <ViewModelProviderClass name="PersonProvider"
                          namespace="PersonLookup.ViewModel.Providers">
    <Methods>
      <Method name="Save"
              generateCommand="true">
        <Result name="person"
                type="PersonLookup.ViewModel.Person"
                isViewModel="true"/>
        <Parameter name="person"
                   type="PersonLookup.ViewModel.Person"
                   isViewModel="true"/>
        <WrappedMethod name="AddPersonAsync"
                       providerName="PersonServiceClient"
                       providerNamespace="PersonLookup.PersonServiceReference">
          <Result>
            <Asynchronous methodCompletedEventName="AddPersonCompleted"
                          eventArgsType="PersonLookup.PersonServiceReference.AddPersonCompletedEventArgs"/>
          </Result>
        </WrappedMethod>
      </Method>
    </Methods>
  </ViewModelProviderClass>
  • Click Transform All Templates
  • Change the partial class Person ViewModel class in the Person.cs file:
        partial void SaveCompleted(Person person, System.Exception error, object userState)
        {
            if (error == null)
            {
                this.ID = person.ID;
                this.EntityStatus = MVVM.EntityStatus.Original;
            }
        }
  • If the call to the web service succeeds we update the ID of the ViewModel object with the ID that was passed back from the web service and we set its EntityStatus to Original because the ViewModel Person object is now in sync with the web service.
  • Finally update the UpdateCommandExecute method in the Persons partial class:
        partial void UpdateCommandExecute(object parameter)
        {
            var personsToDelete = new List<Person>();

            foreach (var person in Collection)
            {
                switch (person.EntityStatus)
                {
                    case MVVM.EntityStatus.Original:
                        // ignore
                        break;
                    case MVVM.EntityStatus.Added:
                        person.SaveCommand.Execute(null);
                        break;
                    case MVVM.EntityStatus.Modified:
                        // call update person method
                        break;
                    case MVVM.EntityStatus.Deleted:
                        // call delete person method
                        personsToDelete.Add(person);
                        break;
                    default:
                        break;
                }
            }
  • You can test the adding of Persons now by running the application.
  • Next: modified Person objects.
  • Add to the Person.xml mapping file a definition an Update method to the provider class:
      <Method name="Update"
              generateCommand="true">
        <Result name="rowsAffected"
                type="int"/>
        <Parameter name="person"
                   type="PersonLookup.ViewModel.Person"
                   isViewModel="true"/>
        <WrappedMethod name="UpdatePersonAsync"
                       providerName="PersonServiceClient"
                       providerNamespace="PersonLookup.PersonServiceReference">
          <Result>
            <Asynchronous methodCompletedEventName="UpdatePersonCompleted"
                          eventArgsType="PersonLookup.PersonServiceReference.UpdatePersonCompletedEventArgs"/>
          </Result>
        </WrappedMethod>
      </Method>
  • Click Transform All Templates
  • Change the partial class Person ViewModel class in the Person.cs file:
        partial void UpdateCompleted(int rowsAffected, System.Exception error, object userState)
        {
            if (error == null && rowsAffected == 1)
            {
                this.EntityStatus = MVVM.EntityStatus.Original;
            }
        }
  • If the call to the web service succeeds and one row has been updated set the EntityStatus to Original because the ViewModel Person object is now in sync with the web service.
  • Then update the UpdateCommandExecute method in the Persons partial class:
        partial void UpdateCommandExecute(object parameter)
        {
            var personsToDelete = new List<Person>();

            foreach (var person in Collection)
            {
                switch (person.EntityStatus)
                {
                    case MVVM.EntityStatus.Original:
                        // ignore
                        break;
                    case MVVM.EntityStatus.Added:
                        person.SaveCommand.Execute(null);
                        break;
                    case MVVM.EntityStatus.Modified:
                        person.UpdateCommand.Execute(null);
                        break;
                    case MVVM.EntityStatus.Deleted:
                        // call delete person method
                        personsToDelete.Add(person);
                        break;
                    default:
                        break;
                }
            }
  • You can test the modifying of Persons now by running the application.
  • Next: deleted Person objects.
  • Add to the Person.xml mapping file a definition a Delete method to the provider class:
      <Method name="Delete"
              generateCommand="true">
        <Result name="rowsAffected"
                type="int"/>
        <Parameter name="person"
                   type="PersonLookup.ViewModel.Person"
                   isViewModel="true"/>
        <WrappedMethod name="DeletePersonAsync"
                       providerName="PersonServiceClient"
                       providerNamespace="PersonLookup.PersonServiceReference">
          <Result>
            <Asynchronous methodCompletedEventName="DeletePersonCompleted"
                          eventArgsType="PersonLookup.PersonServiceReference.DeletePersonCompletedEventArgs"/>
          </Result>
        </WrappedMethod>
      </Method>
  • Click Transform All Templates
  • Change the partial class Person ViewModel class in the Person.cs file:
        partial void DeleteCompleted(int rowsAffected, System.Exception error, object userState)
        {
            if (error != null)
            {
                // some way of error messaging
            }
        }
  • If the call to the web service succeeds there is not much to do. All that is needed is removing the object from the Collection in the ViewModel which we already implemented.
  • Update the UpdateCommandExecute method in the Persons partial class:
        partial void UpdateCommandExecute(object parameter)
        {
            var personsToDelete = new List<Person>();

            foreach (var person in Collection)
            {
                switch (person.EntityStatus)
                {
                    case MVVM.EntityStatus.Original:
                        // ignore
                        break;
                    case MVVM.EntityStatus.Added:
                        person.SaveCommand.Execute(null);
                        break;
                    case MVVM.EntityStatus.Modified:
                        person.UpdateCommand.Execute(null);
                        break;
                    case MVVM.EntityStatus.Deleted:
                        person.DeleteCommand.Execute(null);
                        personsToDelete.Add(person);
                        break;
                    default:
                        break;
                }
            }

            foreach (var personToDelete in personsToDelete)
            {
                Collection.Remove(personToDelete);
            }
        }
  • This concludes this tutorial.

Last edited Jun 13, 2010 at 1:56 PM by Erno_de_Weerd, version 4

Comments

No comments yet.