Click here to Skip to main content
15,889,216 members
Articles / Mobile Apps / Windows Mobile

MVVM for Windows Phone Developers–Use it Guys! (Part 4)

Rate me:
Please Sign up or sign in to vote.
4.86/5 (3 votes)
29 Dec 2013CPOL31 min read 21.3K   5   7
MVVM for Windows Phone developers

In the last part (now really the last part) of this series, we added all the required classes and interfaces we need to wire anything together:

  • A bootstrapper to initialize our system
  • All required interfaces for data-access and services we want to implement
  • A base class for our view-models
  • A view-model-locater to locate available view-models we can use for data-binding

This part will cover all the “wiring” that is necessary to make the whole thing work.

Just to recap the structure, here is the current dependency-graph of our portable class implementation:

Solution Dependency Grap

Wiring It All Together

Doing It the TDD (Test Driven Development) Way

We have created two test projects, one for Windows Phone 8 and for Windows 8.

Let’s start to implement the Data-Access-Layer, and write some tests for it .

Navigate to our WP8 unit test project, right click the project and add a new class. Name it “DataAccessLayerTests” or anything you prefer.

To have everything available, we need to implement the tests we need to add additional using statements:

  • using Microsoft.VisualStudio.TestTools.UnitTesting => everything we need to create unit tests
  • using MVVMWindowsPhone.Core.Portable.DAL => Our portable core DAL implementation we want to test here
  • And a using statement for Moq => “Using Moq

Every class that represents a series of unit tests, needs the [TestClass] attribute to be attached.

Test methods need the [TestMethod] attribute attached to be recognized as such.

TIP: If you did not follow the other parts, please see the link to the GitHub repository at the end of this post.

We have everything in place now to test our repositories and to mockup the objects we want to run tests against.

TIP: If you work with solutions that contain multiple projects, and you want to avoid scrolling to focus on a specific project, right click that project in Solution Explorer and choose “Scope to this”. You can navigate to the complete solution tree again, by pressing the blue back-button. And you can use the forward button to return to your scoped view again.

Testing Our IUnitOfWork and IRepository

To be able to test our implementations, we need at first to do some additional work.

We need to add a fake context (database driver), to test our IUnitOfWork implementation.

Add a new folder to our WP8 unit test project and name it fakes. Right click the folder and add a new interface. Name the file “IFakeDBContext”.

<!--CRLF--><!--CRLF--><!--CRLF--><!--CRLF--><!--CRLF--><!--CRLF-->
<!--CRLF--><!--CRLF--><!--CRLF--><!--CRLF--><!--CRLF--><!--CRLF--><!--CRLF-->

Now simply copy all the method definitions from our IRepository class and insert them into our IFakeDBContext, and modify them a bit:

C#
using MVVMWindowsPhone.Core.Portable.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace UnitTesting.Fakes
{
    public interface IFakeDBContext<T>
    {
        /// <summary>
        /// A fake table for our users.
        /// </summary>
        IEnumerable<T> FakeTable {get;set;}

         /// <summary>
        /// Get all entries.
        /// </summary>
        /// <returns></returns>
        IQueryable<T> GetAllEntries();

        /// <summary>
        /// Get filtered entries.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        IQueryable<T> GetFilteredEntries(Expression<Func<T, bool>> filter);

        /// <summary>
        /// DeleteEntry
        /// </summary>
        /// <param name="entry"></param>
        /// <returns></returns>
        User DeleteEntry(T entry);

        /// <summary>
        /// Update Entry.
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="updateValue"></param>
        /// <returns></returns>
        User UpdateEntry(T entry, T updateValue);

        /// <summary>
        /// Add a new entry.
        /// </summary>
        /// <param name="Entry"></param>
        /// <returns></returns>
        User AddEntry(T Entry);
    }
}

Now, we need to implement the interface IFakeDBContext. Add a new class to the Fakes folder and name it FakeDBContext.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MVVMWindowsPhone.Core.Portable.Model;

namespace UnitTesting.Fakes
{
    public class FakeDBContext : IFakeDBContext<User>
    {
        /// <summary>
        /// Our fake table
        /// </summary>
        IEnumerable<User> fakeTable;

        /// <summary>
        /// Our fake table.
        /// </summary>
        public virtual IEnumerable<User> FakeTable
        {
            get
            {
                return fakeTable;
            }
            set
            {
                this.fakeTable = value;
            }
        }

        /// <summary>
        /// Constructor to add table data
        /// </summary>
        /// <param name="fakeTable"></param>
        public FakeDBContext(IEnumerable<User> fakeTable)
        {
            this.fakeTable = fakeTable;
        }

        /// <summary>
        /// Get all entries.
        /// </summary>
        /// <returns></returns>
        public IQueryable<User> GetAllEntries()
        {
            return this.fakeTable.AsQueryable<User>();
        }

        /// <summary>
        /// Get filtered entries.
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        public IQueryable<User> GetFilteredEntries
               (System.Linq.Expressions.Expression<Func<User, bool>> filter)
        {
            return fakeTable.AsQueryable<User>().Where(filter);
        }

        /// <summary>
        /// Delete a sepecific entry.
        /// </summary>
        /// <param name="entry"></param>
        /// <returns></returns>
        public User DeleteEntry(User entry)
        {
             if(this.fakeTable.Contains<User>(entry))
             {
                 var list = this.fakeTable.ToList<User>();
                 list.Remove(entry);
                 this.fakeTable = list;
                 return entry;
             }
             else
             {
                 return null;
             }
        }

        /// <summary>
        /// Update a specific entry.
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="updateValue"></param>
        /// <returns></returns>
        public User UpdateEntry(User entry, User updateValue)
        {
            if (this.fakeTable.Contains<User>(entry))
            {
                var list = this.fakeTable.ToList<User>();
                list[list.IndexOf(entry)] = updateValue;
                this.fakeTable = list;

                return updateValue;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Add a new entry.
        /// </summary>
        /// <param name="Entry"></param>
        /// <returns></returns>
        public User AddEntry(User Entry)
        {
           if(!this.fakeTable.Contains<User>(Entry))
           {
               var list = this.fakeTable.ToList<User>();
               list.Add(Entry);
               this.fakeTable = list;

               return Entry;
           }
           else
           {
               return null;
           }
        }
    }
}

Implementing the Unit Tests

Now that the FakeDBContext was implemented, we are ready to create our tests. These tests have only sample character and are not considered to be perfect unit tests. Select the UnitTesting project and add a new class. Name the class “DataAccessLayerTests” or whatever you like and add the following code:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MVVMWindowsPhone.Core.Portable.DAL;
using UnitTesting.Fakes;
using MVVMWindowsPhone.Core.Portable.Model;
using Microsoft.Phone.Testing;

namespace UnitTesting
{
    /// <summary>
    /// Testexample without
    /// using Moq, creating
    /// fakes manually.
    /// </summary>
    [TestClass]
    public class DataAccessLayerTests
    {

        /// <summary>
        /// The users
        /// </summary>
        List<User> users;

        /// <summary>
        /// The test repo
        /// </summary>
        IRepository<User,FakeDBContext> testRepo;

        /// <summary>
        /// Initializes the test.
        /// </summary>
        [TestInitialize]
        public void InitializeTest()
        {
            //Prepare the fake data
            users = new List<User>();

            users.Add(new User(){ Image="http://userimages.com/image1.png",
            Url="http://user1.com",UserName="TestUser1"});
            users.Add(new User() { Image = "http://userimages.com/image2.png",
            Url = "http://user2.com", UserName = "TestUser1" });
            users.Add(new User() { Image = "http://userimages.com/image3.png",
            Url = "http://user3.com", UserName = "TestUser2" });
            users.Add(new User() { Image = "http://userimages.com/image4.png",
            Url = "http://user4.com", UserName = "TestUser3" });
            users.Add(new User() { Image = "http://userimages.com/image5.png",
            Url = "http://user5.com", UserName = "TestUser4" });
            users.Add(new User() { Image = "http://userimages.com/image6.png",
            Url = "http://user6.com", UserName = "TestUser5" });
            users.Add(new User() { Image = "http://userimages.com/image7.png",
            Url = "http://user7.com", UserName = "TestUser6" });
            users.Add(new User() { Image = "http://userimages.com/image8.png",
            Url = "http://user8.com", UserName = "TestUser7" });
            users.Add(new User() { Image = "http://userimages.com/image9.png",
            Url = "http://user9.com", UserName = "TestUser8" });
            users.Add(new User() { Image = "http://userimages.com/image10.png",
            Url = "http://user10.com", UserName = "TestUser9" });
            users.Add(new User() { Image = "http://userimages.com/image11.png",
            Url = "http://user11.com", UserName = "TestUser10" });
            users.Add(new User() { Image = "http://userimages.com/image12.png",
            Url = "http://user12.com", UserName = "TestUser11" });
            users.Add(new User() { Image = "http://userimages.com/image13.png",
            Url = "http://user13.com", UserName = "TestUser12" });
            users.Add(new User() { Image = "http://userimages.com/image14.png",
            Url = "http://user14.com", UserName = "TestUser13" });
            users.Add(new User() { Image = "http://userimages.com/image15.png",
            Url = "http://user15.com", UserName = "TestUser14" });
            users.Add(new User() { Image = "http://userimages.com/image16.png",
            Url = "http://user16.com", UserName = "TestUser15" });
            users.Add(new User() { Image = "http://userimages.com/image17.png",
            Url = "http://user17.com", UserName = "TestUser16" });

            this.testRepo = new FakeUserRepository();

            var fakeUnitOfWork = new FakeUnitOfWork();

            fakeUnitOfWork.SetContext(new FakeDBContext(users));

            this.testRepo.Driver = fakeUnitOfWork;
        }

        /// <summary>
        /// Gets all users and count test.
        /// </summary>
        [TestMethod]
        public void GetAllUsersAndCountTest()
        {
            var count = this.testRepo.GetAllEntries().Count();
            Assert.AreEqual<int>(count,17);
        }

        /// <summary>
        /// Adds the user and count test.
        /// </summary>
        [TestMethod]
        public void AddUserAndCountTest()
        {
            this.testRepo.AddEntry(new User(){ Image="Image",
            Url="some url",UserName="Some UserName"});

            var count = testRepo.GetAllEntries().Count();

            Assert.AreEqual<int>(count,18);
        }

        /// <summary>
        /// Users the filter test.
        /// </summary>
        [TestMethod]
        public void UserFilterTest()
        {
            var filteredUsers = testRepo.GetFilteredEntries
            (user=>user.UserName.Equals("TestUser1") ||
                user.UserName.Equals("TestUser2")).ToList<User>();

            Assert.AreEqual<int>(filteredUsers.Count(),3);
        }

        /// <summary>
        /// Users the delete test.
        /// </summary>
        [TestMethod]
        [Tag("DeleteOnly")]
        public void UserDeleteTest()
        {
            var userToRemove = testRepo.GetFilteredEntries
            (user => user.UserName.Contains("TestUser1")).First();

            var deletedUser = testRepo.DeleteEntry(userToRemove);

            Assert.AreEqual<int>(testRepo.Driver.Context.FakeTable.Count(), 16);
        }

        /// <summary>
        /// Updates the user test.
        /// </summary>
        [TestMethod]
        public void UpdateUserTest()
        {
            var userToUpdate  = testRepo.GetFilteredEntries
            (user => user.UserName.Contains("TestUser1")).First();

            var updatedEntry = new User(){UserName=userToUpdate.UserName,
            Image="changed",Url = userToUpdate.Url};

            testRepo.Driver.Context.UpdateEntry(userToUpdate,updatedEntry);

            var updatedUser  = testRepo.GetFilteredEntries
            (user => user.UserName.Contains("TestUser1")).First();

            Assert.AreEqual<string>(updatedUser.Image, "changed");
        }
    }
}

We have 5 unit test methods:

  • GetAllUsersAndCountTest => Check if we have the added amount of users available
  • AddUserAndCountTest => After we add a user, do we have the right number of users?
  • UserFilterTest => Filter users by a certain criteria and check if the users meet the criteria
  • UserDeleteTest => Check if users can be deleted
  • UpdateUserTest => Check if users can be successfully updated

There is a special method named InitializeTest. You can name this method whatever you want, as long as you put the “[TestInitialize]” attribute on it. It initializes the repository for every test.

C#
/// <summary>
        /// Initializes the test.
        /// </summary>
        [TestInitialize]
        public void InitializeTest()
        {
            //Prepare the fake data
            users = new List<User>();

            users.Add(new User(){ Image="http://userimages.com/image1.png",
            Url="http://user1.com",UserName="TestUser1"});
            users.Add(new User() { Image = "http://userimages.com/image2.png",
            Url = "http://user2.com", UserName = "TestUser1" });
            users.Add(new User() { Image = "http://userimages.com/image3.png",
            Url = "http://user3.com", UserName = "TestUser2" });
            users.Add(new User() { Image = "http://userimages.com/image4.png",
            Url = "http://user4.com", UserName = "TestUser3" });
            users.Add(new User() { Image = "http://userimages.com/image5.png",
            Url = "http://user5.com", UserName = "TestUser4" });
            users.Add(new User() { Image = "http://userimages.com/image6.png",
            Url = "http://user6.com", UserName = "TestUser5" });
            users.Add(new User() { Image = "http://userimages.com/image7.png",
            Url = "http://user7.com", UserName = "TestUser6" });
            users.Add(new User() { Image = "http://userimages.com/image8.png",
            Url = "http://user8.com", UserName = "TestUser7" });
            users.Add(new User() { Image = "http://userimages.com/image9.png",
            Url = "http://user9.com", UserName = "TestUser8" });
            users.Add(new User() { Image = "http://userimages.com/image10.png",
            Url = "http://user10.com", UserName = "TestUser9" });
            users.Add(new User() { Image = "http://userimages.com/image11.png",
            Url = "http://user11.com", UserName = "TestUser10" });
            users.Add(new User() { Image = "http://userimages.com/image12.png",
            Url = "http://user12.com", UserName = "TestUser11" });
            users.Add(new User() { Image = "http://userimages.com/image13.png",
            Url = "http://user13.com", UserName = "TestUser12" });
            users.Add(new User() { Image = "http://userimages.com/image14.png",
            Url = "http://user14.com", UserName = "TestUser13" });
            users.Add(new User() { Image = "http://userimages.com/image15.png",
            Url = "http://user15.com", UserName = "TestUser14" });
            users.Add(new User() { Image = "http://userimages.com/image16.png",
            Url = "http://user16.com", UserName = "TestUser15" });
            users.Add(new User() { Image = "http://userimages.com/image17.png",
            Url = "http://user17.com", UserName = "TestUser16" });

            this.testRepo = new FakeUserRepository();

            var fakeUnitOfWork = new FakeUnitOfWork();

            fakeUnitOfWork.SetContext(new FakeDBContext(users));

            this.testRepo.Driver = fakeUnitOfWork;
        }

This method is called before any of the test methods are called. That way, it is guaranteed, that you have always a freshly initialized repository for every test you run.

Running the Unit Tests

Simply right click the UnitTesting project, select “debug” and select “Start new instance”:

Unit Testing

Just ignore the “Use tags” feature for now and click on the play button in the command bar. Your tests should run all successfully:

unittests_screen2

The topmost area shows you how many tests have successfully passed, failed and the total amount of tests. The “UnitTesting” area can be unfolded to show you each and every test that has been run by the unit test framework. You can click on each test to view the execution time and more.

You have now additional options like saving the test results to the isolated storage of the Windows Phone Emulator, or sending them via mail to a specific email address. But that is only possible with a valid account. If you want to send mails, just run the test on your phone, instead of the emulator.

Test Filtering Using Tags

You can filter tests by tagging them using the “Tag” attribute.

C#
/// <summary>
/// Users the delete test.
/// </summary>
[TestMethod]
[Tag("DeleteOnly")]
public void UserDeleteTest()
{
    var userToRemove = testRepo.GetFilteredEntries
           (user => user.UserName.Contains("TestUser1")).First();

    var deletedUser = testRepo.DeleteEntry(userToRemove);

    Assert.AreEqual<int>(testRepo.Driver.Context.FakeTable.Count(), 16);
}

In the small snippet (extracted from the current test source), you can see that the “UserDeleteTest” has a tag attribute with a constructor parameter “DeleteOnly”. When you start the test again, you can activate test-filtering by activating the “Use tags” switch:

test_tags_sample

Then you just hit the play button again, and you can see, that only tests with the tag-attribute set to “DeleteOnly” have been executed:

test_tags_sample2

Only the test we “tagged” has been run now.

If you want to learn more about the testing framework, here is a link to a blog-post where you can find out more: Windows Phone Toolkit overview.

Using Moq to Mock Our Fake Repository

IMPORTANT: To make Moq work on Windows Phone, you NEED to download an additional assembly from the Castle project. It’s Castle.Core 2.5.2. Download the first archive from this page: Castle Project Extract the archive, and browse the Silverlight 4 folder (“\Castle.Core.2.5.2\Silverlight4\”) and add a reference to the “Castle.Core.dll” to the Windows Phone test project to make it work.

First of all, a definition of what Moq is, from the GitHub repo:

Moq (pronounced “Mock-you” or just “Mock”) is the only mocking library for .NET developed from scratch to take full advantage of .NET Linq expression trees and lambda expressions, which makes it the most productive, type-safe and refactoring-friendly mocking library available. And it supports mocking interfaces as well as classes. Its API is extremely simple and straightforward, and doesn’t require any prior knowledge or experience with mocking concepts

Ok, That’s Nice, What Is Mocking Anyway?

When you search the internet about what mocking (in terms of testing, unit testing) really is, you will find various definitions. On StackOverflow, I found a simple and straight forward definition of what mocking is:

What is mocking on StackOverflow

Mocking is primarily used in unit testing. An object under test may have dependencies on other (complex) objects. To isolate the behavior of the object you want to test you replace the other objects by mocks that simulate the behavior of the real objects. This is useful if the real objects are impractical to incorporate into the unit test.

In short, mocking is creating objects that simulate the behavior of real objects.

In our case, we need to mock the fake repository to be able to test the behavior and functionality of the implementation. Usually, you write stories to define the expectations that have to be met by your users. For the sake of brevity, this step will be omitted.

Starting with Moq

Using Moq always starts with creating a new Mock<T> where T is the type of the interface (or class, also possible with Moq) that you want to mock. Add a new test to the UnitTesting project by right-clicking and choosing “Add=>Class …”. Name the class “DataAccessLayerTestsMoq” or whatever you want, and add the following code:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MVVMWindowsPhone.Core.Portable.DAL;
using UnitTesting.Fakes;
using MVVMWindowsPhone.Core.Portable.Model;
using Microsoft.Phone.Testing;
using Moq;

namespace UnitTesting
{
    /// <summary>
    /// Testexample without
    /// using Moq, creating
    /// fakes manually.
    /// </summary>
    [TestClass]
    public class DALTestsMoq
    {

         Mock<IRepository<User, IFakeDBContext<User>>> testRepo;

         List<User> userList;

         /// <summary>
         /// Initializes the test.
         /// </summary>
         [TestInitialize]
         public void InitializeTest()
         {

            IQueryable<User> users = null;

            userList =  new  List<User>();

            userList.Add(new User() { Image = "http://userimages.com/image1.png",
            Url = "http://user1.com", UserName = "TestUser1" });
            userList.Add(new User() { Image = "http://userimages.com/image2.png",
            Url = "http://user2.com", UserName = "TestUser1" });
            userList.Add(new User() { Image = "http://userimages.com/image3.png",
            Url = "http://user3.com", UserName = "TestUser2" });
            userList.Add(new User() { Image = "http://userimages.com/image4.png",
            Url = "http://user4.com", UserName = "TestUser3" });
            userList.Add(new User() { Image = "http://userimages.com/image5.png",
            Url = "http://user5.com", UserName = "TestUser4" });
            userList.Add(new User() { Image = "http://userimages.com/image6.png",
            Url = "http://user6.com", UserName = "TestUser5" });
            userList.Add(new User() { Image = "http://userimages.com/image7.png",
            Url = "http://user7.com", UserName = "TestUser6" });
            userList.Add(new User() { Image = "http://userimages.com/image8.png",
            Url = "http://user8.com", UserName = "TestUser7" });
            userList.Add(new User() { Image = "http://userimages.com/image9.png",
            Url = "http://user9.com", UserName = "TestUser8" });
            userList.Add(new User() { Image = "http://userimages.com/image10.png",
            Url = "http://user10.com", UserName = "TestUser9" });
            userList.Add(new User() { Image = "http://userimages.com/image11.png",
            Url = "http://user11.com", UserName = "TestUser10" });
            userList.Add(new User() { Image = "http://userimages.com/image12.png",
            Url = "http://user12.com", UserName = "TestUser11" });
            userList.Add(new User() { Image = "http://userimages.com/image13.png",
            Url = "http://user13.com", UserName = "TestUser12" });
            userList.Add(new User() { Image = "http://userimages.com/image14.png",
            Url = "http://user14.com", UserName = "TestUser13" });
            userList.Add(new User() { Image = "http://userimages.com/image15.png",
            Url = "http://user15.com", UserName = "TestUser14" });
            userList.Add(new User() { Image = "http://userimages.com/image16.png",
            Url = "http://user16.com", UserName = "TestUser15" });
            userList.Add(new User() { Image = "http://userimages.com/image17.png",
            Url = "http://user17.com", UserName = "TestUser16" });

            users = userList.AsQueryable<User>();

            testRepo = new Mock<IRepository<User, IFakeDBContext<User>>>();

            testRepo.SetupProperty(repo => repo.Driver.Context.FakeTable,userList);

            testRepo.Setup(repo => repo.GetAllEntries()).Returns(users);

            testRepo.Setup(repo => repo.AddEntry(It.IsAny<User>())).Returns((User user) => {
                userList.Add(user);
                return user;
            });

            testRepo.Setup(repo => repo.DeleteEntry(It.IsAny<User>())).Returns
            ((User user) => { userList.Remove(user); return user; });

            testRepo.Setup(repo => repo.GetFilteredEntries
            (It.IsAny<System.Linq.Expressions.Expression<Func<User, bool>>>()))
                .Returns(
                (System.Linq.Expressions.Expression<Func<User, bool>> filter) =>
                {
                    return userList.AsQueryable<User>().Where(filter);
                });

            testRepo.Setup(repo => repo.UpdateEntry(It.IsAny<User>(),
            It.IsAny<User>())).Returns((User orig, User update) => {

                if (userList.Contains<User>(orig))
                {
                    List<User> list = userList.ToList<User>();
                    list[list.IndexOf(orig)] = update;
                    userList = list;

                    return update;
                }
                else
                {
                    return null;
                }
            });
        }

        /// <summary>
        /// Gets all users and count test.
        /// </summary>
        [TestMethod]
        [Tag("Moq")]
        public void GetAllUsersAndCountTest()
        {
            var allEntries = testRepo.Object.GetAllEntries().Count();
            Assert.AreEqual<int>(allEntries, 17);
        }

        /// <summary>
        /// Adds the user and count test.
        /// </summary>
        [TestMethod]
        [Tag("Moq")]
        public void AddUserAndCountTest()
        {
            User userToAdd =  new User() { Image = "Image",
            Url = "some url", UserName = "TestUser18" };
            var user = testRepo.Object.AddEntry(userToAdd);

            Assert.AreEqual<int>(testRepo.Object.GetAllEntries().Count(), 18);
        }

        ///// <summary>
        ///// Users the filter test.
        ///// </summary>
        [TestMethod]
        [Tag("Moq")]
        public void UserFilterTest()
        {
            var filteredUser = testRepo.Object.GetFilteredEntries
            (user => user.UserName.Contains("TestUser1")).First();

            Assert.AreEqual<string>(filteredUser.UserName,"TestUser1");
        }

        ///// <summary>
        ///// Users the delete test.
        ///// </summary>
        [TestMethod]
        [Tag("Moq")]
        public void UserDeleteTest()
        {
            var filteredUser = testRepo.Object.GetFilteredEntries
            (user => user.UserName.Contains("TestUser1")).First();

             var userDeleted = testRepo.Object.DeleteEntry(filteredUser);

             Assert.IsNotNull(userDeleted);
        }

        ///// <summary>
        ///// Updates the user test.
        ///// </summary>
        [TestMethod]
        [Tag("Moq")]
        public void UpdateUserTest()
        {
            var userToUpdate = testRepo.Object.GetFilteredEntries
            (user => user.UserName.Contains("TestUser1")).First();

            var updatedEntry = new User()
            { UserName = userToUpdate.UserName, Image = "changed", Url = userToUpdate.Url };

            testRepo.Object.Driver.Context.UpdateEntry(userToUpdate, updatedEntry);

            var updatedUser = testRepo.Object.GetFilteredEntries
            (user => user.UserName.Contains("TestUser1")).First();

            Assert.Equals(updatedUser.Image, "changed");
        }
    }
}

As you can see, there are two private properties defined:

  • testRepo – the Moq “Mock” object
  • userList – The list of users to work with

In the first few lines, the userList is initialized and filled with 17 test users. Then, we cast the userList to IQueryable<User> because the repository is expecting and IQueryable<T>.

Then the Mock is created. An IRepository<User, IFakeDBContext<User>>> is set as the type parameter for the generic Moq “Mock” class.

This will create a virtual type of T (IRepository<User, IFakeDBContext<User>>>) that we can use to bind expectations to, by using the Setup Method of the Moq “Mock” instance. Expectations in short can be explained like this (usually they are the user stories):

When I do this, I expect the following result….

If you take a look at the first SetupProperty call, you can see that it is expected for the FakeTable property to contain all users from the userList.

The next line, binds the AddEntry-Method of the IRepository interface by specifying that the parameter passed to add Entry can be any type of User object. We don’t expect the user to have a certain UserName or picture for example. Then, using fluent configuration, we define (Using a Func<T>) what we expect to be returned, if adding the new user to the list was successful. We return the new User itself.

The next few lines do exactly the same thing, to complete our CRUD scenario for the test based on Moq. Please don’t get confused about all the Lambdas and Linq expressions. Just check the code of the IRepository interface and you will see that it is not much different from what you see there. Very intuitive.

This is the way you add the implementation by setting up expectations using Moq. It is not so hard at all.

The tests are not much different from the tests we created before, using the fake implementations for our repository. If you take a close look, you can see, that I decorated each test method using the Tag attribute. That way, you can easily filter your tests, and try only the Moq based ones.

As Moq is not supported in Windows 8/8.1, we will not use it for the Windows 8.1 testing. I have copied the fake classes from the Windows Phone 8 testing project to the “UnitTestingWin8” project, using the same folder “Fakes” and the same name for the test class “DataAccessLayerTests”.

Running Your Windows 8/8.1 Unit Tests

To run your Windows 8/8.1 tests, go to the Visual Studio 2013 “TEST” menu and select Windows=>Test Explorer. Then rebuild the project, so that the unit tests can be discovered by Visual Studio 2013. Then click on the “Run All” link to run all of the unit tests for Windows 8/8.1.

TestExplorerWin8Tests

Finalizing the Implementation

Now that you have seen, how to mock repositories using Moq and mocking them manually, it’s time to go on with our implementation.

All the important stuff is in the MVVMWindowsPhone.Core.Portable assembly. Just expand the project to recall the parts that we need to implement and finalize the project:

importantpartsmvvm

If you are not sure what all this means, please read part 1, part 2 and part 3 of this article series in order to understand what’s going on.

  • Bootstrapping => Everything we need to use DI (utilizing Ninject) in our project and decouple as good as possible.
  • DAL => This is the data-access layer we use, you have seen it already in the tests. This time, we implement it using SQLite for Windows Phone 8 and Windows 8/8.1.
  • Model => These are our data classes. In this sample, we are working with users.
  • Services => The folder for the services we want to inject. Here, we have a VERY simple implementation of a navigation service, that will be used in our view-models to navigate from page to page using commands.
  • ViewModel => A base view-model and base view-model locater that we will use with MVVM-Light.

Before we start anything else, let’s implement the most important parts, the repository and the bootstrapper.

Implementing the Repository using SQLite

Based on our IUnitOfWork<T> interface, we can implement a SQLiteDriver class. The idea behind it is that we can implement any kind of data-access driver and abstract it through the IUnitOfWork<T> interface and our IRepository<T,U> interface. However, here is the implementation of the SQLiteDriver:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SQLite;
using MVVMWindowsPhone.Core.Portable.DAL;
using Ninject;

namespace MVVMWindowsPhone.DAL
{
    public class SQLiteDriver : IUnitOfWork<SQLiteAsyncConnection>
    {
        private SQLiteAsyncConnection context;

        [Inject]
        public SQLiteAsyncConnection Context
        {
            get
            {
                return this.context;
            }
            set
            {
                this.context = value;
            }
        }

        public SQLiteDriver()
        {

        }

        public void SetContext(SQLiteAsyncConnection context)
        {
            if(context == null)
            {
                throw new ArgumentException("Parameter cannot be null.", "context");
            }

            this.Context = context;
        }
    }
}

Now, we have two possibilities of setting the SQLiteAsyncConnection. Using the SetContext<T> method or via property injection using Ninject. As you can see, we have an [Inject] attribute placed on the context property, that will automagically inject an object of type SQLiteAsyncConnection, if we register that in our bootstrapper.

This means that Ninject will create and resolve the SQLiteAsyncConnection for us at runtime, and we decouple our code with this method!

Before we do that, let’s implement the SQLiteRepository.

Implementing the SQLiteRepository

Now that we have our driver ready, we need to add an additional class to our project. Let’s call it SQLiteRepository (or whatever you want). Add it to the DAL folder, and add the following code:

C#
using MVVMWindowsPhone.Core.Portable.DAL;
using MVVMWindowsPhone.Core.Portable.Model;
using MVVMWindowsPhone.Model;
using Ninject;
using SQLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMWindowsPhone.DAL
{
    /// <summary>
    /// The SQLiteRepository
    /// </summary>
    public class SQLiteRepository : ITAPRepository<SQLiteUser, SQLiteAsyncConnection>
    {
        IUnitOfWork<SQLiteAsyncConnection> driver;

        /// <summary>
        /// Gets or sets the driver.
        /// </summary>
        /// <value>
        /// The driver.
        /// </value>
        [Inject]
        public IUnitOfWork<SQLiteAsyncConnection> Driver
        {
            get
            {
                return this.driver;
            }
            set
            {
                this.driver = value;
            }
        }

        /// <summary>
        /// Gets all entries.
        /// </summary>
        /// <returns></returns>
        public async Task<IQueryable<SQLiteUser>> GetAllEntries()
        {
            var list = await Driver.Context.Table<SQLiteUser>().ToListAsync();

            if (list != null)
            {
                return list.AsQueryable<SQLiteUser>();
            }

            return null;
        }

        /// <summary>
        /// Gets the filtered entries.
        /// </summary>
        /// <param name="filter">The filter.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentException">
        /// Parameter cannot be null or empty.;filter</exception>
        public async Task<IQueryable<SQLiteUser>> 
        GetFilteredEntries(System.Linq.Expressions.Expression<Func<SQLiteUser, bool>> filter)
        {
            if (filter == null)
            {
                throw new ArgumentException("Parameter cannot be null or empty.", "filter");
            }

            var list = await Driver.Context.Table<SQLiteUser>().ToListAsync();

            if (list != null)
            {
                return list.AsQueryable<SQLiteUser>().Where(filter);
            }

            return null;
        }

        /// <summary>
        /// Deletes the entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentException">
        /// Parameter cannot be null or empty.;entry</exception>
        public async Task<SQLiteUser> DeleteEntry(SQLiteUser entry)
        {

            if (entry == null)
            {
                throw new ArgumentException("Parameter cannot be null or empty.", "entry");
            }

            var deleted = await Driver.Context.DeleteAsync(entry);

            if (deleted != 0)
            {
                return entry;
            }

            return null;
        }

        /// <summary>
        /// Updates the entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="updateValue">The update value.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentException">
        /// Parameter cannot be null or empty.;entry
        /// or
        /// Parameter cannot be null or empty;updateValue
        /// </exception>
        public async Task<SQLiteUser> UpdateEntry(SQLiteUser entry, SQLiteUser updateValue)
        {
            if (entry == null)
            {
                throw new ArgumentException("Parameter cannot be null or empty.", "entry");
            }

            if (updateValue == null)
            {
                throw new ArgumentException("Parameter cannot be null or empty", "updateValue");
            }

            entry.Image = updateValue.Image;
            entry.Url = updateValue.Url;
            entry.UserName = updateValue.UserName;

            int updateSuccess = await Driver.Context.UpdateAsync(entry);

            if (updateSuccess != 0)
            {
                return entry;
            }

            return null;
        }

        /// <summary>
        /// Adds the entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentException">
        /// Parameter cannot be null or empty.;entry</exception>
        public async Task<SQLiteUser> AddEntry(SQLiteUser entry)
        {
            if (entry == null)
            {
                throw new ArgumentException("Parameter cannot be null or empty.", "entry");
            }

            var addSuccess = await Driver.Context.InsertAsync(entry);

            if(addSuccess != 0)
            {
                return entry;
            }

            return null;
        }
    }
}

As you can see, the SQLiteRepository is implemented as ITAPRepository<SQLiteUser, SQLiteAsyncConnection> (to use the TAP async/await pattern).

And we implemented a special SQLiteUser class, that has a primary key attribute on it, inheriting from our User class (it has also an auto-increment option). This is necessary to allow SQLLite to identify objects that need to be updated and/or otherwise modified.

Now imagine that you can exchange T and U with anything you want to implement CRUD operations on specific objects (T) using a specific type of “Driver” (U). Comfortable, isn’t it?

We inject the type of U driver we need, using Ninjects [Inject] attribute. This ensures that an instance of the SQLite driver is injected at runtime.

The Bootstrapper using Ninject

In the previous posts, a basic bootstrapper was already added. The bootstrapper for Windows Phone 8 will inherit this one, to make it easier for us.

This is the code of the SimpleBootstrapper class:

C#
using MVVMWindowsPhone.Core.Portable.Bootstrapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject;
using Ninject.Modules;

namespace MVVMWindowsPhone.Core.Portable.Bootstrapping
{
    /// <summary>
    /// This is a simple implementation of our
    /// bootstrapper for Ninject. This needs to
    /// be
    /// </summary>
    public abstract class SimpleBootstrapper:IBootstrapper
    {

        /// <summary>
        /// The Ninject-Kernel,
        /// our Container in terms
        /// of
        /// </summary>
        private IKernel container;

        /// <summary>
        /// The list of modules to import.
        /// </summary>
        private IList<INinjectModule> modules;

        /// <summary>
        ///
        /// </summary>
        private IViewModelLocator viewModelLocator;

        /// <summary>
        /// The container (Ninject Kernel)
        /// used to bind the types to
        /// the interfaces.
        /// </summary>
        public IKernel Container
        {
            get
            {
                return this.container;
            }
            set
            {
                this.container = value;
            }
        }

        /// <summary>
        /// The ninject modules
        /// to be loaded by the
        /// container (Ninject Kernel)
        /// </summary>
        public IList<INinjectModule> Modules
        {
            get
            {
                return this.modules;
            }
            set
            {
                this.modules = value;
            }
        }

        /// <summary>
        /// The ViewModel-Locator
        /// that holds the instantiated
        /// ViewModels to bind the
        /// XAML against.
        /// </summary>
        public IViewModelLocator ViewModelLocator
        {
            get
            {
                return this.viewModelLocator;
            }
            set
            {
                this.viewModelLocator = value;
            }
        }

        /// <summary>
        /// The standard constructor.
        /// </summary>
        public SimpleBootstrapper()
        {
           //Nothing here currently.
        }

        /// <summary>
        /// This method is defined
        /// as virtual, to enable
        /// an entry point for Ninject
        /// like stated by Owen on Twitter.
        /// </summary>
        public virtual void ConfigureBootstrapper()
        {

        }
    }
}

Based on this class, we implement the bootstrapper for the Windows Phone project:

C#
using MVVMWindowsPhone.Core.Portable.Bootstrapping;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject;
using Ninject.Modules;
using MVVMWindowsPhone.NinjectModules;

namespace MVVMWindowsPhone.Bootstrapper
{
    public class Bootstrapper:SimpleBootstrapper
    {
        public override void ConfigureBootstrapper()
        {
            //Initialize the modules
            this.Modules = new List<INinjectModule>();

            this.Modules.Add(new ModuleRuntime());
            this.Modules.Add(new ModuleViewModels());

            //Add the modules we need
            //One debug module, and one runtime module
            //for our repository
            this.Container = new StandardKernel(this.Modules.ToArray<INinjectModule>());
        }
    }
}

To de-clutter the bootstrap process, we can add Ninject modules. Those are like little sub-bootstrapper, that can help to “name” the different scenarios one is using for his app. Create a new folder called NinjectModules within the Windows Phone 8 project. Add a new class to the folder and call it “ModuleRuntime” (or call it whatever you want). Add the following code to the module:

C#
using Ninject.Modules;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MVVMWindowsPhone.Core.Portable.DAL;
using MVVMWindowsPhone.Core.Portable.Model;
using MVVMWindowsPhone.DAL;
using SQLite;
using System.IO;
using Windows.Storage;
using MVVMWindowsPhone.Model;

namespace MVVMWindowsPhone.NinjectModules
{
    /// <summary>
    /// The runtime module.
    /// </summary>
    public class ModuleRuntime:NinjectModule
    {

        /// <summary>
        /// Loads the module into the kernel.
        /// </summary>
        public override void Load()
        {
           string dbPath = Path.Combine(Path.Combine
                  (ApplicationData.Current.LocalFolder.Path, "users.db"));

           SQLiteAsyncConnection con = new SQLiteAsyncConnection(dbPath, true);

             var exists = con.ExecuteScalarAsync<int>
             ("select count(type) from sqlite_master where type='table' and name='SQLiteUser';");

              exists.Wait();

              if(!(exists.Result == 1))
              {
                  var created = con.CreateTableAsync<SQLiteUser>();

                  created.Wait();

              }

           this.Bind<SQLiteAsyncConnection>().ToConstant(con).InSingletonScope();

           this.Bind<IUnitOfWork<SQLiteAsyncConnection>>().To<SQLiteDriver>().InSingletonScope();

           this.Bind<ITAPRepository<SQLiteUser, 
                     SQLiteAsyncConnection>>().To<SQLiteRepository>().InSingletonScope();

        }
    }
}

We use this module to create the database itself, and to bind instances of SQLiteAsyncConnection, IUnitOfWork<SQLiteAsyncConnection> and IUnitOfWork<SQLiteAsyncConnection> as singletons. This means that the instances are exactly available once in the Windows Phone project.

Activating the Bootstrapper

Open App.xaml.cs and add a static property of type MVVMWindowsPhone.Bootstrapper.Bootstrapper and initialize it.

Then call the configureBootstrapper() method in Application_Activated and Application_Launching.

C#
public static MVVMWindowsPhone.Bootstrapper.Bootstrapper bootstrapper = 
                                        new Bootstrapper.Bootstrapper();
C#
bootstrapper.ConfigureBootstrapper();
            ModelLocator = bootstrapper.Container.GetService
                    (typeof(ViewModelLocator)) as ViewModelLocator;

That’s it so far for the bootstrapper and the production repository.

The Real Stuff – Short Timeout

Looks like this post has nothing in common with MVVM. Really? This is the usual procedure (or parts of it, let’s come down) when you do enterprise level development. One big exception (from my personal experience) is, that we are not creating something like our own “In house” framework. Makes me laugh every time when I think about it.

Time to enlighten the architecture a bit and to move to the “Real Stuff” like view-models, locators, bindings, commands and all the other funny things you are confronted every day with.

The View Model Locator

People who have worked with MVVM-Light think that the class locating the view-models is an essential, not exchangeable part of MVVM-Light. It is not, it is simply a class that uses the Locator Pattern – Mark Seemann aka “ploeh” even discusses that Service Locator is an Anti-Pattern (link to his blog-post, he’s well know expert in the field of unit testing, architecture and much much more). I highly recommend to read his blog, his books and look for his posts also on StackOverflow.

You can replace the MVVM-Light view-model locator anytime with your own implementation. This is exactly what happened here.

Since XAML is able to bind to dynamic properties and indexers, I have chosen a different approach. This enables you to register any kind of view-model that implements ViewModelBase (all classes are inside the PCL project) and bind it to your views. One thing you have to take care about, is to use the exact property names and view-model names. Just to recall, here the IViewModelLocator, the ViewModelLocator and the BaseViewModel class.

C#
using System;
using System.Collections.Generic;
using GalaSoft.MvvmLight;

namespace MVVMWindowsPhone.Core.Portable.Bootstrapping
{
    /// <summary>
    /// The base for our Viewmodel locator.
    /// </summary>
    public interface IViewModelLocator
    {
        /// <summary>
        /// The indexer to get the
        /// right ViewModel.
        /// </summary>
        /// <param name="viewModelName"></param>
        /// <returns></returns>
        dynamic this[string viewModelName] { get; }

        /// <summary>
        /// The dictionary to
        /// save the ViewModels to.
        /// </summary>
        Dictionary<string, dynamic> ViewModels { get; set; }

        /// <summary>
        /// Casts the specified view model to the desired type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="viewModelName">Name of the view model.</param>
        /// <returns></returns>
        T Cast<T>(string viewModelName);

    }
}
C#
using GalaSoft.MvvmLight;
using MVVMWindowsPhone.Core.Portable.Bootstrapper;
using MVVMWindowsPhone.Core.Portable.DAL;
using MVVMWindowsPhone.Core.Portable.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using MVVMWindowsPhone.Core.Portable.Bootstrapping;
using Ninject;

namespace MVVMWindowsPhone.Core.Portable.ViewModel
{
    /// <summary>
    /// The ViewModel locator.
    /// </summary>
    public class ViewModelLocator:IViewModelLocator
    {
        /// <summary>
        /// Our view models.
        /// </summary>
        Dictionary<string, dynamic> viewModels;

        /// <summary>
        /// Our view models.
        /// </summary>
        [Inject]
        public Dictionary<string, dynamic> ViewModels
        {
            get { return viewModels; }
            set { viewModels = value; }
        }

        /// <summary>
        /// Standard constructor.
        /// </summary>
        public ViewModelLocator()
        {

            ViewModels = new Dictionary<string,dynamic>();

        }

        /// <summary>
        /// Set and get your ViewModels
        /// here.
        /// </summary>
        /// <param name="viewModelName">The name of the viewmodel to get or set.</param>
        /// <returns>The viewmodel selected.</returns>
        public dynamic this[string viewModelName]
        {
            get
            {
                if(ViewModels.ContainsKey(viewModelName))
                {
                    return this.ViewModels[viewModelName];
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Casts the specified view model to the desired type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="viewModelName">Name of the view model.</param>
        /// <returns></returns>
        public T Cast<T>(string viewModelName)
        {
            if (ViewModels.ContainsKey(viewModelName))
            {
                return (T) this.ViewModels[viewModelName];
            }
            else
            {
                return default(T);
            }
        }
    }
}
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GalaSoft.MvvmLight;
using MVVMWindowsPhone.Core.Portable.DAL;
using System.Collections.ObjectModel;
using MVVMWindowsPhone.Core.Portable.Services;
using Ninject;

namespace MVVMWindowsPhone.Core.Portable.ViewModel
{
    /// <summary>
    /// Our base-view model
    /// based on the ViewModelBase
    /// of MVVM-Light portable, with
    /// two generic type parameters
    /// to support our IRepository.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="U"></typeparam>
    public class BaseViewModel<T,U>:ViewModelBase where U:class where T:class
    {

        /// <summary>
        /// The data we need for our ViewModel.
        /// </summary>
        private  ObservableCollection<T> data;

        /// <summary>
        /// Our navigation service we need.
        /// </summary>
        private readonly INavigationService navigationService;

        /// <summary>
        /// The data we need for our ViewModel.
        /// </summary>
        public ObservableCollection<T> Data
        {
            get { return data; }
            set { data = value; }
        }

        /// <summary>
        /// The repository we use.
        /// </summary>
        protected readonly ITAPRepository<T, U> repository;

        /// <summary>
        /// Our constructor.
        /// This one will be used
        /// to inject our repository.
        /// </summary>
        /// <param name="repo"></param>
          public BaseViewModel(ITAPRepository<T, U> repo, INavigationService navService)
        {
            this.repository = repo;
            this.navigationService = navService;
            this.Data = new ObservableCollection<T>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseViewModel{T, U}"/> class.
        /// </summary>
        public BaseViewModel()
        {

        }
    }
}

One little change will be made on the BaseViewModel class. I decided to support only the ITAPRepository interface, as it allows one to use async and await. It is still based on the excellent ViewModelBase class by Laurent Bugnion.

Add a New Ninject Module for the ViewModel-Locator and the View-Models

Add a new class to the NinjectModules folder within the MVVMWindowsPhone project, name it “ModuleViewModels” (or anything you want), and make it inherit from NinjectModule and let Visual Studio implement the abstract class. Do this by selecting the “NinjectModule” after the “:” and press CTRL + “.” . Then select “Implement Abstract Class”. That will create an override for the Load() method.

What we need to do now, is tell Ninject to bind the ViewModelLocator. Until now, we don’t have any view-models.

This is done by telling the Ninject-Module to bind IViewModelLocator to ViewModelLocator. As we want only one instance of it hanging around, we choose InSingletonScope. The type of configuration we use here is called “Fluent Configuration”.

To finalize the view-model story, we add a new view-model called UserViewModel (folder ViewModel, Windows Phone project), let it inherit from our BaseViewModel class. Here is the final class:

C#
using GalaSoft.MvvmLight.Command;
using MVVMWindowsPhone.Core.Portable.DAL;
using MVVMWindowsPhone.Core.Portable.Services;
using MVVMWindowsPhone.Core.Portable.ViewModel;
using MVVMWindowsPhone.DAL;
using MVVMWindowsPhone.Model;
using Ninject;
using SQLite;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Text;
using System.Windows;
using Windows.Storage;
using Windows.UI.Core;

namespace VVMWindowsPhone.ViewModel
{
    /// <summary>
    /// The user view model class.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="U"></typeparam>
    public class UserViewModel:BaseViewModel<SQLiteUser, SQLiteAsyncConnection>
    {

        /// <summary>
        /// Gets or sets the add new user command.
        /// </summary>
        /// <value>
        /// The add new user command.
        /// </value>
        public RelayCommand<SQLiteUser> AddNewUserCommand { get; set; }

        /// <summary>
        /// Gets or sets the delete user command.
        /// </summary>
        /// <value>
        /// The delete user command.
        /// </value>
        public RelayCommand DeleteUserCommand { get; set; }

        /// <summary>
        /// Gets or sets the edit user command.
        /// </summary>
        /// <value>
        /// The edit user command.
        /// </value>
        public RelayCommand EditUserCommand { get; set; }

        /// <summary>
        /// Gets or sets the filter users command.
        /// </summary>
        /// <value>
        /// The filter users command.
        /// </value>
        public RelayCommand<object> FilterUsersCommand { get; set; }

        /// <summary>
        /// Gets or sets the dialog result command.
        /// </summary>
        /// <value>
        /// The dialog result command.
        /// </value>
        public RelayCommand<string> DialogResultCommand { get; set; }

        /// <summary>
        /// Gets or sets the initialize.
        /// </summary>
        /// <value>
        /// The initialize.
        /// </value>
        public RelayCommand Init { get; set; }

        private SQLiteUser crudUser;

        /// <summary>
        /// Gets or sets the crud user.
        /// </summary>
        /// <value>
        /// The crud user.
        /// </value>
        public SQLiteUser CrudUser
        {
            get { return crudUser; }
            set
            {
                if (crudUser != value)
                {
                    crudUser = value;

                    RaisePropertyChanged("CrudUser");
                }
            }
        }

        private string dialogTitle;

        /// <summary>
        /// Gets or sets the dialog title.
        /// </summary>
        /// <value>
        /// The dialog title.
        /// </value>
        public string DialogTitle
        {
            get { return dialogTitle; }
            set
            {
                if (dialogTitle != value)
                {
                    dialogTitle = value;

                    RaisePropertyChanged("DialogTitle");
                }
            }
        }

        private string filterText;

        public string FilterExpression
        {
            get { return filterText; }
            set
            {

                    filterText = value;
                    RaisePropertyChanged("FilterText");

                this.FilterUsersCommand.RaiseCanExecuteChanged();
            }
        }

        private bool userControlVisibility;

        /// <summary>
        /// Gets or sets a value indicating whether [user control visibility].
        /// </summary>
        /// <value>
        /// <c>true</c> if [user control visibility]; otherwise, <c>false</c>.
        /// </value>
        public bool UserControlVisibility
        {
            get { return userControlVisibility; }
            set
            {
                if (userControlVisibility != value)
                {
                    userControlVisibility = value;
                    RaisePropertyChanged("UserControlVisibility");
                }
            }
        }

        private object selectedItem;

        /// <summary>
        /// Gets or sets the selected item.
        /// </summary>
        /// <value>
        /// The selected item.
        /// </value>
        public object SelectedItem
        {
            get { return selectedItem; }
            set
            {
                if (selectedItem != value)
                {
                    selectedItem = value;
                    RaisePropertyChanged("SelectedItem");
                }

                this.EditUserCommand.RaiseCanExecuteChanged();
                this.DeleteUserCommand.RaiseCanExecuteChanged();
            }
        }

        private bool AddOrEdit;

        /// <summary>
        /// Initializes a new instance of the <see cref="UserViewModel"/> class.
        /// </summary>
        /// <param name="repo">The repo.</param>
        /// <param name="navService">The nav service.</param>
        [Inject]
        public UserViewModel(ITAPRepository<SQLiteUser, SQLiteAsyncConnection> repo, 
              INavigationService navService)
            : base(repo, navService)
        {

            this.AddNewUserCommand = new RelayCommand<SQLiteUser>
                      (this.AddNewUser, this.CanExecuteAddNewUser);
            this.DeleteUserCommand = new RelayCommand
                      (this.DeleteUser, this.CanExecuteEditCommand);
            this.EditUserCommand = new RelayCommand
                      (this.EditUser, this.CanExecuteEditCommand);
            this.FilterUsersCommand = new RelayCommand<object>
                      (this.FilterUsers, this.CanExecuteFilterCommand);
            this.DialogResultCommand = new RelayCommand<string>
                      (this.DialogResultCheck, this.CanExecuteCommandWithParameter);
            this.Init = new RelayCommand(this.InitData, this.CanExecuteCommand);

            this.CrudUser = new SQLiteUser();

            var users = this.repository.GetAllEntries();

        }

        /// <summary>
        /// Determines whether this instance 
        /// [can execute filter command] the specified argument.
        /// </summary>
        /// <param name="arg">The argument.</param>
        /// <returns></returns>
        private bool CanExecuteFilterCommand(object arg)
        {

                    var evtArgs = (System.Windows.Input.KeyEventArgs)arg;

                   if (evtArgs.Key == System.Windows.Input.Key.Enter)
                   {
                       return true;
                   }

           return false;
        }

        /// <summary>
        /// Initializes the data.
        /// </summary>
        /// <exception cref="System.NotImplementedException"></exception>
        private async void InitData()
        {
                var users = await this.repository.GetAllEntries();

                if (users != null)
                {
                    RefreshDataSource(users);
                }
        }

        /// <summary>
        /// Dialogs the result check.
        /// </summary>
        /// <param name="obj">The object.</param>
        private async void DialogResultCheck(string obj)
        {
           if(obj == "ok")
           {
               if(this.AddOrEdit)
               {
                    var user = await this.repository.AddEntry(this.CrudUser);

                    this.Data.Clear();

                    var users = await this.repository.GetAllEntries();

                    RefreshDataSource(users);

                    this.CrudUser = new SQLiteUser();
               }
               else
               {
                   var updatedUser = await this.repository.UpdateEntry(
                           (SQLiteUser)this.SelectedItem, this.CrudUser
                       );

                   this.Data.Clear();

                   var users = await this.repository.GetAllEntries();

                   RefreshDataSource(users);

                   this.CrudUser = new SQLiteUser();
               }

               this.UserControlVisibility = false;
               this.RaiseExecuteChangedDialog();
           }

           if(obj == "cancel")
           {
               this.CrudUser = new SQLiteUser();
               this.UserControlVisibility = false;
               this.RaiseExecuteChangedDialog();
           }
        }

        /// <summary>
        /// Raises the execute changed dialog.
        /// </summary>
        private void RaiseExecuteChangedDialog()
        {
            this.AddNewUserCommand.RaiseCanExecuteChanged();
            this.EditUserCommand.RaiseCanExecuteChanged();
            this.DeleteUserCommand.RaiseCanExecuteChanged();
            this.DialogResultCommand.RaiseCanExecuteChanged();
        }

        /// <summary>
        /// Determines whether this instance [can execute add new user] the specified argument.
        /// </summary>
        /// <param name="arg">The argument.</param>
        /// <returns></returns>
        private bool CanExecuteAddNewUser(SQLiteUser arg)
        {
            if (this.CanExecuteCommand())
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Adds the new user.
        /// </summary>
        /// <param name="obj">The object.</param>
        private  void AddNewUser(SQLiteUser obj)
        {
            this.DialogTitle = "Add new user";
            this.AddOrEdit = true;
            this.UserControlVisibility = true;
            this.RaiseExecuteChangedDialog();
        }

        /// <summary>
        /// Refreshes the data source.
        /// </summary>
        /// <param name="list">The list.</param>
        public void RefreshDataSource(IQueryable<SQLiteUser> list)
        {
            list.ToList().ForEach(this.Data.Add);
        }

        /// <summary>
        /// Determines whether this instance 
        /// [can execute command with parameter] the specified argument.
        /// </summary>
        /// <param name="arg">The argument.</param>
        /// <returns></returns>
        private bool CanExecuteCommandWithParameter(string arg)
        {
            if (!arg.Equals("cancel") && !arg.Equals("ok"))
            {
                if (this.CanExecuteCommand())
                {
                    if (string.IsNullOrEmpty(arg) || string.IsNullOrWhiteSpace(arg))
                    {
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }
            }
            else
            {
                if (this.CanExecuteCommandDialogResult())
                {
                    if (string.IsNullOrEmpty(arg) || string.IsNullOrWhiteSpace(arg))
                    {
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }
            }

           return false;
        }

        /// <summary>
        /// Filters the users.
        /// </summary>
        /// <param name="obj">The object.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        private async void FilterUsers(object obj)
        {
            if (this.FilterExpression.Length == 1 || 
                      string.IsNullOrEmpty(this.FilterExpression))
            {

                    //clear
                    this.Data.Clear();

                    var users = await this.repository.GetAllEntries();

                    RefreshDataSource(users);

            }
            else
            {
                //clear
                this.Data.Clear();

                var users = await this.repository.GetFilteredEntries
                  (u => u.UserName.ToLower().Contains(this.FilterExpression.ToLower())
                    || u.Image.ToLower().Contains(this.filterText.ToLower()) || 
                       u.Url.ToLower().Contains(this.FilterExpression.ToLower())
                    );

                RefreshDataSource(users);
            }
        }

        /// <summary>
        /// Edits the user.
        /// </summary>
        private void EditUser()
        {
            var user = new SQLiteUser();

            user.Image = ((SQLiteUser)this.SelectedItem).Image;
            user.Url = ((SQLiteUser)this.SelectedItem).Url;
            user.UserName = ((SQLiteUser)this.SelectedItem).UserName;

            this.CrudUser = user;

            this.DialogTitle = "Edit user";
            this.AddOrEdit = false;
            this.UserControlVisibility = true;
            this.RaiseExecuteChangedDialog();
        }

        /// <summary>
        /// Deletes the user.
        /// </summary>
        /// <exception cref="System.NotImplementedException"></exception>
        private async void DeleteUser()
        {
            var result = MessageBox.Show
                 ("Delete this item?","CAUTION",MessageBoxButton.OKCancel);

            if(result == MessageBoxResult.OK)
            {
                   var deletedUser = 
                       this.repository.DeleteEntry((SQLiteUser)this.SelectedItem);

                   this.Data.Clear();

                   var users = await this.repository.GetAllEntries();

                   RefreshDataSource(users);
            }
        }

        /// <summary>
        /// Determines whether this instance [can execute command].
        /// </summary>
        /// <returns></returns>
        private bool CanExecuteCommand()
        {
            string dbPath = Path.Combine(Path.Combine
                  (ApplicationData.Current.LocalFolder.Path, "users.db"));

            return IsolatedStorageFile.GetUserStoreForApplication().FileExists(dbPath) && 
                                    !this.UserControlVisibility;
        }

        /// <summary>
        /// Determines whether this instance [can execute edit command].
        /// </summary>
        /// <returns></returns>
        private bool CanExecuteEditCommand()
        {
            return this.CanExecuteCommand() && !this.userControlVisibility && 
                                (this.SelectedItem != null);
        }

        /// <summary>
        /// Determines whether this instance [can execute command dialog result].
        /// </summary>
        /// <returns></returns>
        private bool CanExecuteCommandDialogResult()
        {

            return  this.UserControlVisibility;
        }
    }
}

There is only one small improvement to add to solve. Since we have different view-models of different types, all inheriting from BaseViewModel, and the view-model locator does have a specific type to return (BaseViewModel). Some of you may like to see a method to cast from everything, to everything, to get the concrete type of the view-model (strongly typed) instead of dynamically. I changed the ViewModel locator to use dynamic for the Dictionary (as value), where the view-models reside.

To cast the dynamic to the desired type, just use the Cast<T> method, passing the name of the view-model (the exact name). If it is unable to cast it, it will return the default of T.

Now we need to find a comfortable way to add our view-models to the view-model locator. This is again done, by using Ninject. Open the ModuleViewModels, and change it like this:

C#
using MVVMWindowsPhone.Core.Portable.Bootstrapping;
using MVVMWindowsPhone.Core.Portable.DAL;
using MVVMWindowsPhone.Core.Portable.ViewModel;
using MVVMWindowsPhone.Model;
using MVVMWindowsPhone.Services;
using Ninject.Modules;
using SQLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VVMWindowsPhone.ViewModel;

namespace MVVMWindowsPhone.NinjectModules
{
    public class ModuleViewModels : NinjectModule
    {
        public override void Load()
        {
            var  viewModels = new Dictionary<string, dynamic>();

            var repo = Kernel.GetService
                (typeof(ITAPRepository<SQLiteUser, SQLiteAsyncConnection>)) 
                 as ITAPRepository<SQLiteUser, SQLiteAsyncConnection>;

            var navigationService = new NavigationService();

            var userViewModel = new UserViewModel(repo,navigationService);

            viewModels.Add("UserViewModel", userViewModel);

            this.Bind<Dictionary<string, dynamic>>().ToConstant(viewModels).InSingletonScope();

            this.Bind<IViewModelLocator>().To<ViewModelLocator>().InSingletonScope();

        }
    }
}

A new Dictionary<string,dynamic> was added to the ModuleViewModels Ninject-Module and it is bound and injected into the view-model locator.

Now we need to tell Ninject to add the ModuleViewModels module to the kernel. Open Bootstrapper.cs (from within the Windows Phone 8 project) and add a new entry to the Modules list.

A Simple Navigation Service

To be able to navigate from within the view-models, a simple navigation service is needed. Add a new class to the Services folder of the Windows Phone 8 project and call it “NavigationService” (or whatever your want). Implement the interface INavigation service, and add the following code:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MVVMWindowsPhone.Core.Portable.Services
{
    /// <summary>
    /// The navigation service to
    /// enable page navigation.
    /// For all our platforms.
    /// </summary>
    public interface INavigationService
    {
        /// <summary>
        /// Navigate to a specific page.
        /// Used for Windows phone.
        /// </summary>
        /// <param name="page">The absolute uri to the page to navigate to.</param>
        void NavigateTo(Uri page);

        /// <summary>
        /// Used for Windows 8.
        /// </summary>
        /// <param name="pageToNavigateTo"></param>
        void NavigateTo(Type pageToNavigateTo);

        /// <summary>
        /// Go back to
        /// the previous page.
        /// Used for Windows Phone and Windows 8.
        /// </summary>
        void GoBack();
    }
}

The base idea is very simple to use one interface for Windows 8 and Windows Phone 8. Therefore, the NavigateTo(Type pageToNavigateTo) is not implemented and should not be used. Otherwise, a NotImplemented exception will be thrown.

The implementation is very simple. The navigation-service tries to get the current root-visual of the current app, which is the current PhoneApplicationFrame. If that was successful, it utilizes the Navigate and GoBack methods. That’s it.

All dependencies that are needed have been added to Ninject and all the required repositories and services have been implemented. The next step is now, to make the view-model locator globally available, so that we can avoid passing the view-models in code-behind.

Locating Our View-Models

Since it is not possible on Windows Phone to bind to a static value, we need another solution to locate our view-models, using a workaround.

First of all, we need a single ViewModelLocator static instance in App.xaml.cs. Open App.xaml.cs and add the following property code:

C#
ModelLocator = bootstrapper.Container.GetService(typeof(ViewModelLocator)) as ViewModelLocator;

Because we bound already an instance (singleton) of ViewModelLocator in ModuleViewModel.cs, we can use our bootstrapper in App.xaml.cs to request that specific instance and assign it to our static ModelLocator property.

This is done by calling the GetService<T> method of the bootstrapper.Container property, which is an implementation of Ninject.IKernel.

We do that in Application_Activated and in Application_Launched.

Now we need a comfortable way to access the static ModelLocator property using static resources in our views. To be able to do that, we create a value converter and pass it a static resource of type system.string.

Inside the value-converter, we access the current app, and use a bit of reflection to access the static property ModelLocator via a string constant “ModelLocator”. We cast the object that is returned by GetType().GetProperty().GetValue() to a ViewModelLocator instance.

After that is done, and the ViewModelLocator instance is not null, we take the “value” parameter of the Convert method, and check if it is a string. If so, and the string is not empty, we pass the value to the indexer of the ModelLocator instance and return the ViewModel if we can find it. If not, we return null. Add a new folder to our Windows Phone 8 project, and name it “Converters”. Then add a new class named “ViewModelConverter”. Add the following code:

C#
using MVVMWindowsPhone.Core.Portable.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace MVVMWindowsPhone.Converter
{
    /// <summary>
    /// Nice solution to access static members
    /// and bind them.
    /// Found the solution here:
    /// http://shineasilverlight.wordpress.com/2010/01/01/
    /// accessing-members-in-app-as-a-xaml-resource/
    /// </summary>
    public class ViewModelConverter:IValueConverter
    {
        /// <summary>
        /// Modifies the source data before passing it to the target for display in the UI.
        /// </summary>
        /// <param name="value">The source data being passed to the target.</param>
        /// <param name="targetType">The <see cref="T:System.Type" /> 
        /// of data expected by the target dependency property.</param>
        /// <param name="parameter">An optional parameter to be used 
        /// in the converter logic.</param>
        /// <param name="culture">The culture of the conversion.</param>
        /// <returns>
        /// The value to be passed to the target dependency property.
        /// </returns>
        public object Convert(object value, Type targetType, 
              object parameter, System.Globalization.CultureInfo culture)
        {

           App current = (App)Application.Current;

           var locator = current.GetType().GetProperty("ModelLocator").GetValue
                         (current, null) as ViewModelLocator;

           if(value == null)
           {
               return null;
           }

           if(value is string)
           {
               var viewModelName = (string) value;
               var viewModel = locator[viewModelName];

               if(viewModel == null)
               {
                   return null;
               }

               return viewModel;
           }

           return null;
        }

        /// <summary>
        /// Modifies the target data before passing it to the source object. 
        /// This method is called only in 
        /// <see cref="F:System.Windows.Data.BindingMode.TwoWay" /> bindings.
        /// </summary>
        /// <param name="value">The target data being passed to the source.</param>
        /// <param name="targetType">The <see cref="T:System.Type" /> 
        /// of data expected by the source object.</param>
        /// <param name="parameter">An optional parameter to be used 
        /// in the converter logic.</param>
        /// <param name="culture">The culture of the conversion.</param>
        /// <returns>
        /// The value to be passed to the source object.
        /// </returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public object ConvertBack(object value, Type targetType, 
               object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

In App.xaml, we first add a namespace sys that points to the System namespace (assembly: mscorlib). We need that to create a static resource of type string, that will contain the name of the ViewModel to bind to. The next step is to add another namespace conv, that will point to clr-namespace:MVVMWindowsPhone.Converter. Now we add a new static resource of type ViewModelConverter.

With these two resources in place, we can now assign the data-context to the PhoneApplicationPage:

C#
<sys:String x:Key="UserViewModel">UserViewModel</sys:String>
            <conv:ViewModelConverter x:Key="ViewModelConverter"/>
C#
DataContext="{Binding Converter={StaticResource ViewModelConverter}, 
              Source={StaticResource UserViewModel}}"

Time to Extend UserViewModel

Let’s extend our UserViewModel class a bit, to allow the following functions:

  • Add a new user to our SQLite-Database using our SQLiteRepository
  • Delete a selected user
  • Filter users
  • And edit a user

This is where the relationship to the MVVM-Light ViewModelBase class, will save us a lot of work. We can use the complete feature set here, including commanding, property change notifications and more.

Adding the Required Commands

Let’s start with the commands to implement the CRUD operations on our data. MVVM-Light offers an implementation of ICommand, called RelayCommand, that is very easy to use.

Open our UserViewModel to see the implementations.

As you can see, we added four RelayCommands:

  • AddNewUserCommand => Add a new user (this is another type of RelayCommand, that can accept a parameter. We need to pass the filter string here.)
  • DeleteUserCommand => Delete the selected User (it will take the user from a SelectedItem property.)
  • EditUserCommand => Edit the selected User (this is another type of RelayCommand, that can accept a parameter. We need to pass the filter string here.)
  • FilterUsersCommand => To filter the list of users (this is another type of RelayCommand, that can accept a parameter. We need to pass the filter string here)

To tell the RelayCommand what to do, we need to assign an action. In fact, we can apply two actions. One action to execute pre-conditions (can execute) to see, if the command is actually executable and another action that will perform the work, if the command is executable.

In our case, the commands are pretty useless, if we are unable to access the database. Therefore, one action to check, if the data-source parameter of our SQLAsyncConnection was set or not. If not, the command cannot be executed and vice versa.

We can use also Lambda expressions to assign the command functionality. But for better readability, we will avoid that and add the actions manually.

The Bindable Application Bar

To execute the various commands, we will use the bindable application bar. You can download it here:

To save some lines here, I have pasted the original sample code from the codeplex website to demonstrate how to use the BindableAppllicationBar:

XML
<bar:Bindable.ApplicationBar>
    <bar:BindableApplicationBar
        IsVisible="{Binding BarIsVisible}"
        IsMenuVisible="{Binding IsMenuVisible, Mode=TwoWay}"
        IsMenuEnabled="{Binding IsMenuEnabled}"
        ForegroundColor="{Binding ForegroundColor, 
        Converter={StaticResource DoubleToColorConverter}}"
        BackgroundColor="{Binding BackgroundColor, 
        Converter={StaticResource DoubleToColorConverter}}"
        BindableOpacity="{Binding Opacity}"
        Mode="{Binding Mode}"
        MenuItemsSource="{Binding MenuItems}"
        ButtonsSource="{Binding Buttons}">
        <!--<bar:BindableApplicationBar.MenuItemTemplate>
            <DataTemplate>
                <bar:BindableApplicationBarMenuItem
                    Text="{Binding Text}"
                    Command="{Binding Command}"
                    CommandParameter="{Binding CommandParameter}"/>
            </DataTemplate>
        </bar:BindableApplicationBar.MenuItemTemplate>-->
        <bar:BindableApplicationBarButton
            Text="{Binding IconButtonText}"
            IconUri="{Binding IconUri, FallbackValue=/Icons/Dark/appbar.add.rest.png}"
            IsEnabled="{Binding ButtonIsEnabled}" />
        <bar:BindableApplicationBarButton
            Text="XAML Btn 2"
            IconUri="/Icons/Dark/appbar.next.rest.png"
            Command="{Binding TestCommand}"
            CommandParameter="{Binding TestCommandParameter}" />
        <bar:BindableApplicationBar.MenuItems>
            <bar:BindableApplicationBarMenuItem
                Text="{Binding MenuItemText}"
                IsEnabled="{Binding MenuItemIsEnabled}" />
            <bar:BindableApplicationBarMenuItem
                Text="XAML MnuIt 2"
                Command="{Binding TestCommand2}"
                CommandParameter="{Binding TestCommand2Parameter}" />
        </bar:BindableApplicationBar.MenuItems>
    </bar:BindableApplicationBar>
</bar:Bindable.ApplicationBar>

It should give you a good overview of how to use it. It allows us to bind commands and parameters to the application bar. That’s the most important fact.

After you have downloaded it, extract the archive, open the solution (ignore the source code dialog) and add a reference to the output assembly to our Windows Phone 8 project and rebuild the solution.

Now add the required namespace to MainPage.xaml. I used “appb” as the namespace. You can choose whatever you want.

Now add the required XAML code for 3 of the commands, except for the FilteredUserCommand. We will use a textbox for that later on.

Now create a new “icons” folder and add some standard application-bar icons to that folder. You can find the sample icons in:

C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v8.0\Icons

Access the sub-directory “Dark” and add the following 3 icons:

  • delete.png
  • add.png
  • edit.png

Here is the snippet to build app the BindableApplicationBar.

XML
<appb:Bindable.ApplicationBar>
        <appb:BindableApplicationBar IsVisible="True" Background="Azure">
            <appb:BindableApplicationBarButton IconUri="/Icons/add.png" 
             Text="add" Command="{Binding AddNewUserCommand}"/>
            <appb:BindableApplicationBarButton IconUri="/Icons/edit.png" 
             Text="edit" Command="{Binding EditUserCommand}"/>
            <appb:BindableApplicationBarButton IconUri="/Icons/delete.png" 
             Text="delete" Command="{Binding DeleteUserCommand}"/>
        </appb:BindableApplicationBar>
    </appb:Bindable.ApplicationBar>

If you set now a break-point in AddNewUser() (in UserViewModel.cs) and CanExecute(), you will see that can execute will check always if the command can be executed as soon as the binding is complete. And if you hit F5 to continue, you can see that CanExecute() is called for all three buttons. You see as well, that our view-model-binding is working, and that the SQLite db has been successfully created during our Ninject initialization. The view-model-locator works as well. You can ignore the XAML errors. The code is ok. XAML is complaining about the cast in our ValueConverter that is resolving the view-models.

Payday

Can you feel it? All of the previous work starts to payoff now. We will be rewarded with a clean and simple design, and methods that require only a few lines of code. And a great side-effect is, that we have ZERO code in our code-behind files! Yeah!

Back to Work – Implement the Commands

Ok. Let’s implement the add/edit user functionality. First, we need something like a simple popup that can be used as input mask, to add a new user. We will not add any sanity checks for the input, so be careful. Maybe good for some testing and exploring.

The user control is fairly simple. Create a new folder, name it “UserControls” (or whatever you want) and add a new item of type “Windows Phone User Control”, name it “AddEditUserControl” (or, you know what I mean…).

Now add a canvas and resize it to fill the whole area of the user control. To do that, right-click the canvas and choose Layout=>Reset all.

Place a textblock-control on the canvas for the dialog title. Then we need 3 additional textboxes and 3 additional labels. We will edit or add a new SQLiteUser item here, and we don’t need a field for the id. SQLite will manage that for us. Here my version of the dialog:

customcontroldesign

Now we need to extend the UserViewModel, and add a new property of type SQLiteUser. Call it “CrudUser”. We will use this property to add a new user or edit a current user. And we need another property of type string, for the dialog title (Edit, Add). Both of the properties need to notify when their values have been changed to get updated values.

MVVM-Light has already implemented all the required interfaces for us (ViewModelBase) and we only need to call the method RaisePropertyChanged(“PROPERTYNAME”) to indicate a value change. We need to do this with standard and not auto-properties, using a backing-field. Here is how to do it:

C#
/// <summary>
/// Gets or sets the crud user.
/// </summary>
/// <value>
/// The crud user.
/// </value>
public SQLiteUser CrudUser
{
    get { return crudUser; }
    set
    {
        if (crudUser != value)
        {
            crudUser = value;

            RaisePropertyChanged("CrudUser");
        }
    }
}

Now we need to bind the dialog-title and the textboxes to the CrudUser property of the UserViewModel.

So far, so good. But how to show/hide the user-control? We need another property on our view-model named UserControlVisibility of type Boolean.

TIP: To save you some time, you can just type “propfull” where you want to add the new property and Visual Studio will expand a template to create a property with a backing-field for you (a full property).

Add the property change notification, and bind the visibility property of the AddEditUserControl property to it. That will look good in XAML, but will not work. Why? Because the Visibility property is not of type Boolean, it is an enumeration. Therefore, we need what’s called a BooleanToVisibilityConverter. If you fire up a search in your favorite search engine, you will see thousands of entries and sample code. Anyway, here is the implementation used in this project:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace MVVMWindowsPhone.Converter
{
    /// <summary>
    /// Convert a boolean value to
    /// Visibility.Visible, or Visibility.Collapsed.
    /// </summary>
    public class BooleanToVisibilityConverter:IValueConverter
    {
        /// <summary>
        /// Modifies the source data before passing it to the target for display in the UI.
        /// </summary>
        /// <param name="value">The source data being passed to the target.</param>
        /// <param name="targetType">The <see cref="T:System.Type" /> 
        /// of data expected by the target dependency property.</param>
        /// <param name="parameter">An optional parameter to be used in 
        /// the converter logic.</param>
        /// <param name="culture">The culture of the conversion.</param>
        /// <returns>
        /// The value to be passed to the target dependency property.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, 
               System.Globalization.CultureInfo culture)
        {
            if(value != null)
            {
                var collapse = (bool)value;

                if(collapse)
                {
                    return Visibility.Visible;
                }
                else
                {
                    return Visibility.Collapsed;
                }
            }

            return Visibility.Collapsed;
        }

        /// <summary>
        /// Modifies the target data before passing it to the source object.
        /// This method is called only in 
        /// <see cref="F:System.Windows.Data.BindingMode.TwoWay" /> bindings.
        /// </summary>
        /// <param name="value">The target data being passed to the source.</param>
        /// <param name="targetType">The <see cref="T:System.Type" /> 
        /// of data expected by the source object.</param>
        /// <param name="parameter">An optional parameter 
        /// to be used in the converter logic.</param>
        /// <param name="culture">The culture of the conversion.</param>
        /// <returns>
        /// The value to be passed to the source object.
        /// </returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public object ConvertBack(object value, Type targetType, 
                    object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

To use the converter, add a new static resource to App.xaml using the “conv” namespace. Afterwards, add the converter to the Visibility binding in AddEditUserControl.xaml.

Now we need one additional command:

  • DialogResultCommand

We need to know what’s going on. Add this command, and give it a string parameter. We will use this parameter to distinguish between an ok and cancel, using a string. The value is “ok” for the ok button and “cancel” for the cancel button. Now bind that command to the OK and Cancel buttons of our user control, and set the parameter values.

That’s fine, you might say, but how do we know, if it is an edit or add thing? Simple! We just add a boolean private property to our UserViewModel and set it accordingly to false for edit, and to true for add. Add a new private boolean property named “addOrEdit”. And set it to true, when the AddNewuser command is called, and to false, when the edit command is called.

Just one thing is missing. We need to bind the currently selected value of the ListBox that is showing our users to a property of our UserViewModel. We add a new property called “SelectedItem” of type object and add property changed notification. That way, we have always the selected value to manipulate in our view-model. And set the binding-mode to Two-Way.

Adding and Editing a New User

Let’s pimp-up now our MainPage. First, add a ListBox to show our available and future user-entries. Then bind that baby (had a beer, sorry) to the Data property of our UserViewModel.

To make the list look better, we add a new items-template. Just a simple one with a stack-panel and the three properties of interest, a background-color and some spacing between the items. The username will be in bold. Not very sexy, but useful. And we add an ItemContainer style (for TargetType ListBoxItem) to stretch the content to full width.

This is what it could look like (one test item added):

mainpagedesign

Add the AddEditUserControl

Now add the AddEditUserControl to MainPage.xaml and add move it between the title-panel and the content-panel. Then move the content-panel above it, using the Document Outline Window in Visual Studio. It will make it the top-most element. Stretch it to fill the whole page and set the Visibility to Collapsed. This is how it should look before you set the visibility to collapsed.

addeditcontrolbeforecollapsed

Set the DataContext of the AddEditUserControl to {Binding}. That way, we can remote-control the AddEditUserControl from within our UserViewModel.

The code to implement adding a new user is very straight forward:

C#
{
    RefreshDataSource(users);
}
}

/// <summary>
/// Dialogs the result check.
/// </summary>
/// <param name="obj">The object.</param>
private async void DialogResultCheck(string obj)
{
   if(obj == "ok")
   {
       if(this.AddOrEdit)
       {
            var user = await this.repository.AddEntry(this.CrudUser);

            this.Data.Clear();

            var users = await this.repository.GetAllEntries();

            RefreshDataSource(users);

            this.CrudUser = new SQLiteUser();
       }
       else
       {
           var updatedUser = await this.repository.UpdateEntry(
                   (SQLiteUser)this.SelectedItem, this.CrudUser
               );

           this.Data.Clear();

           var users = await this.repository.GetAllEntries();

           RefreshDataSource(users);

           this.CrudUser = new SQLiteUser();
       }

       this.UserControlVisibility = false;
       this.RaiseExecuteChangedDialog();
   }

   if(obj == "cancel")
   {
       this.CrudUser = new SQLiteUser();
       this.UserControlVisibility = false;
       this.RaiseExecuteChangedDialog();
   }
}

First it checks, if we have an “ok” or “Cancel” result from our AddEditUserControl. Then, we use the previously mentioned AddOrEdit (Boolean) property to see if we add a user or edit one. If we add a new user, we simply call the AddEntry method (awaitable) and add what we found in CrudUser.

SQLite gives the inserted object automatically and ID and increases it. You can check that, if you set a breakpoint after the call to AddEntry and inspect the user variable. Then we clear the ObservableCollection Data, and call GetAllEntries to get all the current entries in our SQLite table.

And to refresh the ListBox with the data from the table, we call RefreshDataSource – a very cool method, found a sample here: Add Elements from IList to ObservableCollection – SO and adapted it.

Afterwards, the visibility of the AddEditUserControl is set to false, so that it disappears again. Then, I call RaiseExecuteChangedDialog to refresh the can execute values, so that all commands are available again on the AppBar.

Imagine all the wired-up events, not using MVVM!

Editing an entry is nearly similar. There are only two differences. The first is, that it is necessary to create a copy of the selected entry. Assigning the selected entry from the ListBox to the CrudUser property would give us just the same version of the object caused by reference.

When the app is starting, the ListBox is empty. This could indicate to the user that there is something wrong, after a new record was added to the database.

Therefore the existing entries in the database need to be loaded when the ViewModel is ready. Unfortunately, we can not do this in the constructor, because it is not async and Ninject does not like it when it is creating an instance of the UserViewModel (or any other class, at least on Windows Phone).

What can we do now? One possibility cloud be to do it in code-behind – meh. There is something better – EventToCommand, offered by MVVM-Light. This will give us the possibility to bind an event to an command.

The best event to do that is the loaded event of the PhoneApplicationPage. When this event is fired, it is already present in and was added to the object tree. Usually, EventToComman’s are added using Expression Blend. I will show you how to do it manually. That way, you will understand it better how to use it.

First, we need to add two new namespaces to Main.xaml:

  • xmlns:i=”clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity”
  • xmlns:cmd=”clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform.WP8″

The first namespace gives us access to the triggers that are currently loaded for the specific element. In our case, the PhoneApplicationPage, because we are in that “scope” when defining the EventToCommand. Those triggers are EventTriggers that are bound to specific RoutedEvents like the Loaded-Event (this is the event type used in XAML, but that is another story). What we do now here, is to add an EventTrigger to the collection of triggers through XAML, that is fired when the Loaded-Event is fired.

Read more details about Event-Triggers here: EventTrigger Class –MSDN

The second namespace gives us access to the command implementations of MVVM-Light, where EventToCommand can be found. It allows us to bind the event to a command. We don’t need a parameter here or something else. Let’s just add a new command called “Init” and read the existing data from the SQLite table into our Data property of the UserView model.

C#
/// <summary>
/// Initializes the data.
/// </summary>
/// <exception cref="System.NotImplementedException"></exception>
private async void InitData()
{
        var users = await this.repository.GetAllEntries();

        if (users != null)
        {
            RefreshDataSource(users);
        }
}

That was not too heavy.

Now let’s add the delete functionality. Thanks to MVVM-Light and our architecture, another piece of cake!

Firing Up Message Boxes

Delete functionality should always be backed by a “security” question. Easy to use extensions like the a message-box are the perfect candidates.

Here is the implementation of the delete-command functionality:

C#
/// <summary>
/// Deletes the user.
/// </summary>
/// <exception cref="System.NotImplementedException"></exception>
private async void DeleteUser()
{
    var result = MessageBox.Show("Delete this item?","CAUTION",MessageBoxButton.OKCancel);

    if(result == MessageBoxResult.OK)
    {
           var deletedUser = this.repository.DeleteEntry((SQLiteUser)this.SelectedItem);

           this.Data.Clear();

           var users = await this.repository.GetAllEntries();

           RefreshDataSource(users);
    }
}

Implementing Filtering

To implement filtering (or searching), we add a TextBlock and a TextBox to MainPage.xaml:

FilterLayout

Then we add another EventToCommand. This time, we pass the EventArgs to our ViewModel. The filter will be executed, when the user hits Enter on the virtual keyboard. We bind the EventToCommand to the KeyUp event:

C#
<i:Interaction.Triggers>
     <i:EventTrigger EventName="KeyUp">
          <cmd:EventToCommand Command="{Binding FilterUsersCommand}" 
           PassEventArgsToCommand="True"/>
     </i:EventTrigger>
</i:Interaction.Triggers>

This time, we have set the PassEventArgsToCommand property to true. This will ensure that we have the value of the current key that was pressed available. We add an additional property for the filter text. Name the property “FilterExpression” and implement the property-change notification.

Filtering should happen, when the enter-key is pressed by the user. To remove the filter and show all records again, the user clears the TextBox and hits the enter-key. That’s all fine. The standard behavior of a Textbox on Windows Phone is, that the property changed notification is fired after the TextBox has lost its focus. This will not work for our solution.

Behaviors to the Rescue!

Behaviors have been introduced with Expression Blend. They are like extensions or overrides for controls, to change an existing behavior or attach a new one.

In our case, we need to modify the binding behavior of the TextBox Text-Property. We want the TextBox to update the binding source when the OnTextChanged event is fired, and not only when losing focus.

The Behaviour<T> generic class allows us to set the type parameter to TextBox. When we add the behavior in XAML, it will add itself to the Behaviors attached property of the scope where it is added to (within the filter-textbox).

I found a great implementation of such a behavior on StackOverflow. It was a behavior defined in Prism, and modified for Silverlight.

C#
using System;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;

namespace MVVMWindowsPhone.Behaviours
{
    /// <summary>
    /// Custom behavior that updates the source of a binding on a text box as the text changes.
    /// Found here: http://stackoverflow.com/questions/5569768/
    /// textbox-binding-twoway-doesnt-update-until-focus-lost-wp7
    /// </summary>
    public class UpdateTextBindingOnPropertyChanged : Behavior<TextBox>
    {
        /// <summary>
        /// Binding expression this behavior is attached to.
        /// </summary>
        private BindingExpression _expression;

        /// <summary>
        /// Called after the behavior is attached to an AssociatedObject.
        /// </summary>
        /// <remarks>
        /// Override this to hook up functionality to the AssociatedObject.
        /// </remarks>
        protected override void OnAttached()
        {
            base.OnAttached();

            // Hook events to change behavior
            _expression = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
            AssociatedObject.TextChanged += OnTextChanged;
        }

        /// <summary>
        /// Called when the behavior is being detached from its AssociatedObject, 
        /// but before it has actually occurred.
        /// </summary>
        /// <remarks>
        /// Override this to unhook functionality from the AssociatedObject.
        /// </remarks>
        protected override void OnDetaching()
        {
            base.OnDetaching();

            // Un-hook events
            AssociatedObject.TextChanged -= OnTextChanged;
            _expression = null;
        }

        /// <summary>
        /// Updates the source property when the text is changed.
        /// </summary>
        private void OnTextChanged(object sender, EventArgs args)
        {
            _expression.UpdateSource();
        }
    }
}

Essentially, it grabs the binding expression of a TextBox Text-Property, registers itself to the TextChanged event of the TextBox and updates the source of the BindingExpression OnTextChanged. Simple, effective and works.

The FilterUsersCommand will only fire, if the key-value passed by the EventToCommand is the enter key, this is how the CanExecuteFilterCommand-Method was implemented:

C#
/// <summary>
/// Determines whether this instance [can execute filter command] the specified argument.
/// </summary>
/// <param name="arg">The argument.</param>
/// <returns></returns>
private bool CanExecuteFilterCommand(object arg)
{

            var evtArgs = (System.Windows.Input.KeyEventArgs)arg;

           if (evtArgs.Key == System.Windows.Input.Key.Enter)
           {
               return true;
           }

   return false;
}

The FilterUsers-Method implementation:

C#
/// <summary>
/// Filters the users.
/// </summary>
/// <param name="obj">The object.</param>
/// <exception cref="System.NotImplementedException"></exception>
private async void FilterUsers(object obj)
{
    if (this.FilterExpression.Length == 1 || string.IsNullOrEmpty(this.FilterExpression))
    {
            //clear
            this.Data.Clear();

            var users = await this.repository.GetAllEntries();

            RefreshDataSource(users);
    }
    else
    {
        //clear
        this.Data.Clear();

        var users = await this.repository.GetFilteredEntries
         (u => u.UserName.ToLower().Contains(this.FilterExpression.ToLower())
            || u.Image.ToLower().Contains(this.filterText.ToLower()) ||
               u.Url.ToLower().Contains(this.FilterExpression.ToLower())
            );

        RefreshDataSource(users);
    }
}

The solution in action

Final Words

You should be able now without any problems to understand the rest of the source code without any problems.

I leave the rest of the implementation up to you. You can re-use most of the source (about 80-90%) to implement the Windows 8 solution.

This post is my (late) Christmas gift in 2013 for all the readers waiting so long for this last part of the series! Thank you again for being so patient!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer ExGrip LCC
Germany Germany
Working as professional freelancer for the last 5 years. Specialized on and addicted to .NET and a huge fan of Windows Azure from the beginning. Socializing people of all areas, from CEO's to co-workers. Consider myself as a social architect.

Now the proud owner of ExGrip LLC - building mobile cloud experiences. Our latest product "Tap-O-Mizer" is shortly in Beta2. It enables you to see what really needs to be changed in your Windows Phone 8 or Windows 8 app (in real-time, if needed), to ensure customer satisfaction.

Started authorship for "Pluralsight - Hardcore Developer Training" and going to publish the first course on Windows Azure soon.

A few years ago I made a major shift from developer to "devsigner".Focusing my creativity also on great user experiences on Windows, Windows 8 and Windows Phone. Utilizing Expression Design, Expression Blend, Photoshop and Illustrator.

I started developing my first programs on a Commodore C64 (basic and assembly) at the age of fourteen years. Later on an Amiga 500 (C++). After that I made the shift to DOS and Windows from version 3.11 and up.

To me the most important part of developing new experiences is to work with creative and outstanding people and to bring new, exciting ideas to life.

I strongly believe that everyone can be a hero if he/she get's pushed and motivated and valued. Therefore, and that under any circumstances: "People first!"

Specialties:Extremely motivated and pushing people to create results.

Comments and Discussions

 
QuestionBlendability Issue Pin
Programm3r19-Jun-15 3:53
Programm3r19-Jun-15 3:53 
QuestionHow to use this architecture with multiple tables? Pin
RockStar1718-Mar-14 10:15
RockStar1718-Mar-14 10:15 
AnswerRe: How to use this architecture with multiple tables? Pin
IInjac18-Mar-14 10:56
IInjac18-Mar-14 10:56 
GeneralRe: How to use this architecture with multiple tables? Pin
RockStar1719-Mar-14 0:20
RockStar1719-Mar-14 0:20 
GeneralRe: How to use this architecture with multiple tables? Pin
IInjac19-Mar-14 7:17
IInjac19-Mar-14 7:17 
QuestionInteresting approach for your ViewModelLocator implementation Pin
ArchieCoder3-Jan-14 17:40
ArchieCoder3-Jan-14 17:40 
AnswerRe: Interesting approach for your ViewModelLocator implementation Pin
IInjac3-Jan-14 17:54
IInjac3-Jan-14 17:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.