C H A P T E R  6

images

Windows Phone Advanced UI Development

Building a great user interface for a professional application is challenging, given all of the competing priorities, such as presenting data, interaction models, network connectivity, security, managing background tasks, and localization. It’s important to build a rich application on a solid architecture that helps the different pieces work together.

This chapter delves more deeply into user interface development with Silverlight, covering a wide range of topics. It builds on previous chapters, but especially on Chapter 2, which covered Silverlight user interface development. This chapter extends the topic of user interface development to include how to architect an application with separation of concerns without sacrificing support for tooling or ease of development.

This chapter starts with an introduction of the most prevalent application architecture model in Silverlight development, Model-View-ViewModel (MVVM), which provides excellent support for well-designed user interfaces with separation of concerns. With Windows Phone OS 7.1 (Mango), Windows Phone adds compatibility with the Silverlight 4 programming model including much improved commanding support.

After MVVM, the Silverlight for Windows Phone Toolkit is next, covering the additional controls and capabilities including the new controls available in the update for Windows Phone OS 7.1 (Mango). The section following is on creating transitions and interactivity in Expression Blend. The final section is on the Microsoft Advertising SDK, which provides an excellent vehicle to monetize applications.

The Model-View-ViewModel Architecture

The Model-View-ViewModel (MVVM) architecture originated when the Microsoft Windows Presentation Foundation (WPF) team were building the first version of Expression Blend. WPF is Microsoft’s desktop XAML development model, and Expression Blend is written in WPF. MVVM is similar to other separation-of-concerns architectures, like the tried-and-true Model-View-Controller (MVC) model; however, MVVM is optimized to take advantage of XAML’s rich data binding, data templates, commands, and event routing capabilities. The next section covers the architecture in more detail.

MVVM Overview

In this section, the MVVM pattern is defined to help you grasp how it works with XAML. If you are familiar with MVC, MVVM will look somewhat familiar to you—but it is much more than just MVC. MVVM relies heavily on XAML data-binding capabilities to allow the UI to bind to both data and commands. Figure 6-1 depicts the MVVM architecture.

images

Figure 6-1. The MVVM architecture

In Chapter 4, there is a simple example that displays a list of fake Vendor data made available via JSON. The REST+JSON service project, named WcfRemoteServicesSimpleRestJSON, is added to the Chapter 6 solution. The BasicMVVM sample re-architects the AdventureWorksRestJSONPage.xaml page from the Chapter 4 CallingRemoteServices project to use MVVM in the Chapter 6 project named BasicMVVM.

The BasicMVVM and the WcfRemoteServicesSimpleRestJSON projects are configured as the startup project. Three folders are added to the project, named Model, View, and ViewModel. The sections that follow cover the major components of MVVM in the BasicMVVM example.

BasicMVVM - Model

The Model contains the building blocks of the application. It consists of the underlying data objects that are populated via a data access layer. Examples of Model classes are Customer, Store, Product, and so on. When you create a class to represent an object in an application, it most likely belongs as part of the Model. The Model sits behind the ViewModel. The View will data bind to lists or individual objects based on classes in the Model.

To get started, copy over the Vendor class from the WcfRemoteServicesSimpleRestJSON services project to the BasicMVVM Models folder. The class implements the INotifyPropertyChanged interface to support data binding at the class level. The INotifyPropertyChanged interface ensures that changes to the underlying object are propagated to the UI and vice versa. See Listing 6-1 for the code.

Listing 6-1. Vendor Model Class Code File

using System;
using System.ComponentModel;
using System.Runtime.Serialization;

namespace BasicMVVM.Model
{
  //Copied from services project
  [DataContract()]
  public class Vendor : INotifyPropertyChanged
  {
    private string AccountNumberField;
    private byte CreditRatingField;
    private string NameField;

    [DataMemberAttribute()]
    public string AccountNumber
    {
      get
      {
        return this.AccountNumberField;
      }
      set
      {
        this.AccountNumberField = value;
        NotifyPropertyChanged("AccountNumber");
      }
    }

    [DataMemberAttribute()]
    public byte CreditRating
    {
      get
      {
        return this.CreditRatingField;
      }
      set
      {
        this.CreditRatingField = value;
        NotifyPropertyChanged("CreditRating");
      }
    }

    [DataMemberAttribute()]
    public string Name
    {
      get
      {
        return this.NameField;
      }
      set
      {
        this.NameField = value;
        NotifyPropertyChanged("Name");
      }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
      if (null != PropertyChanged)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
    }
  }
}
BasicMVVM - ViewModel

The fact that the View or UI data binds to the ViewModel suggests that a ViewModel consists of the data containers for the application, which is correct. Lists of objects defined in the Model are created and managed by the ViewModel. In addition, the ViewModel consists of the majority of application logic as well.

Next create the VendorViewModel class. The VendorViewModel class in the BasicMVVM project supports the following four major features:

  • Vendor-specific business logic
  • UI Data binding via INotifyPropertyChanged
  • Design-time support
  • REST+JSON data loading

The Vendor-specific business logic is pretty straightforward. It consists of a read-only collection of Vendor objects from the Model and a couple of update operations that we cover in just a bit.

images Note While the VendorViewModel.Vendors collection is read-only— it has just a get property accessor— you can still add and remove Vendor objects in the collection. You just cannot assign a new collection to the property.

It is critical to implement INotifyPropertyChanged for data binding to work. Otherwise, changes are not propagated back to the UI, and vice versa. It is simple enough to do. Add an instance of the PropertyChangedEventHandler class named PropertyChanged and a method that takes a property name as a string and then fires the PropertyChanged event instance.

To detect design time, the System.ComponentModel.DesignerProperties class has a static bool property named IsInDesignTool that indicates whether the code is running in a design-time tool. The VendorViewModel constructor checks if an instance of the class is running at design-time. If it is at design-time, the constructor calls the LoadSampleData method. Otherwise, at run-time, it calls LoadData, which invokes a remote REST+JSON service.

The VendorViewModel class implements two data operations on the Vendors collection, one to insert a vendor and another to delete a vendor. The data operations are implemented via two ICommand implementations, one to add a Vendor object and another to remove a Vendor object from the VendorViewModel.Vendors collection. The ICommand interface is newly available in Windows Phone OS 7.1, brought over from Silverlight 4. This interface enables rich control over business logic execution via data binding. Commanding is an advanced feature of Silverlight that enables stronger separation of concerns between the View and the ViewModel.

In the previous edition of the book, event handlers had to be wired up in the View’s code-behind class file, and code like this executed in the handler:

private void insertVendorAppBarBtn_Click(object sender, EventArgs e)
{
  VendorViewModel vm = LayoutRoot.DataContext as VendorViewModel;
  vm.AddVendor();
}

Instead, you can now data bind in XAML to the ICommand directly without having to write any code. I’ll discuss how that works when I cover the VendorsView view object. The last major functionality for the VendorsViewModel class is making the remote service call, which is covered in detail in Chapter 4. The interesting change for this scenario is that the service call and asynchronous callback live in the VendorsViewModel class, so the code does not have direct access to the View and the View UI elements as in Chapter 4. The callback cannot have code like the following:

vendorsListbox.Dispatcher.BeginInvoke(…);

The solution is to make the call using this line of code instead:

Deployment.Current.Dispatcher.BeginInvoke(..);

This code ensures that the correct Dispatcher instance is used to notify the UI that data changes occurred. The next challenge is that the callback function needs to update the Vendors collection property. Remember that Vendors is a read-only collection, because we do not want external classes to be able to assign a new collection to it. We want the data to only come from the remote services. The code instead assigns the collection to the underlying _vendors collection private member variable.

The final issue is that the code still needs to notify the UI that data changes occurred—that is, that the Vendors collection is loaded. Since the _vendors collection is updated directly, NotifyPropertyChanged("Vendors") is called in the anonymous delegate by BeginInvoke. Again, the code could make Vendors read/write and have a set accessor function like this, but maintaining data integrity is preferred, so the set function is commented out, as in the following:

set
{
  _vendors = value;
  NotifyPropertyChanged("Vendors");
}

Listing 6-2 has the full source code for VendorViewModel as well as the two ICommand implementations in the AddVendorCommand and RemoveVendorCommand classes.

Listing 6-2. VendorViewModel Class Code File

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Windows;
using BasicMVVM.Models;
using System.Windows.Input;

namespace BasicMVVM.ViewModels
{
  public class VendorViewModel : INotifyPropertyChanged
  {
    public VendorViewModel()
    {
      if (InDesignTime)
      {
        LoadSampleData();
      }
      else
      {
        LoadData();
      }
    }

    #region Design-time support
    private bool InDesignTime
    {
      get
      {
        return DesignerProperties.IsInDesignTool;
      }
    }

    private void LoadSampleData()
    {
      _vendors = new ObservableCollection<Vendor>()
      {
        new Vendor(){AccountNumber="111111", CreditRating=65,
          Name="DesignTime - Fabrikam Bikes" },
        new Vendor(){AccountNumber="222222", CreditRating=40,
          Name="Contoso Sports" },
        new Vendor(){AccountNumber="333333", CreditRating=30,
          Name="Duwamish Surfing Gear" },
        new Vendor(){AccountNumber="444444", CreditRating=65,
          Name="Contoso Bikes" },
        new Vendor(){AccountNumber="555555", CreditRating=40,
          Name="Fabrikam Sports" },
        new Vendor(){AccountNumber="666666", CreditRating=30,
          Name="Duwamish Golf" },
        new Vendor(){AccountNumber="777777", CreditRating=65,
          Name="Fabrikam Sun Sports" },
        new Vendor(){AccountNumber="888888", CreditRating=40,
          Name="Contoso Lacross" },
        new Vendor(){AccountNumber="999999", CreditRating=30,
          Name="Duwamish Team Sports" },
      };
    }
    #endregion

    #region Vendors Data Load
    HttpWebRequest httpWebRequest;
    private void LoadData()
    {
      httpWebRequest =
        HttpWebRequest.CreateHttp(
        "http://localhost:9191/AdventureWorksRestJSON.svc/Vendors");
      httpWebRequest.BeginGetResponse(new AsyncCallback(GetVendors), null);
    }

    //add a reference to System.Servicemodel.web to get DataContractJsonSerializer
    void GetVendors(IAsyncResult result)
    {
      HttpWebResponse response =
        httpWebRequest.EndGetResponse(result) as HttpWebResponse;
      DataContractJsonSerializer ser =
        new DataContractJsonSerializer(typeof(ObservableCollection<Vendor>));
      _vendors =
        ser.ReadObject(response.GetResponseStream()) as ObservableCollection<Vendor>;
      //Vendors is read-only so cannot set directly
      //Must call NotifyPropertyChanged notifications on UI thread
      //to update the UI and have data binding work properly
      Deployment.Current.Dispatcher.BeginInvoke(() =>
      {
        NotifyPropertyChanged("Vendors");
      });
    }
    #endregion

    #region Vendors Business Logic
    private ObservableCollection<Vendor> _vendors;
    public ObservableCollection<Vendor> Vendors
    {
      get
      {
        return _vendors;
      }
      //set
      //{
      //  _vendors = value;
      //  NotifyPropertyChanged("Vendors");
      //}
    }

    public Vendor GetVendorByAccountNumber(string accountNumber)
    {
      var vendor = from v in _vendors
                   where v.AccountNumber == accountNumber
                   select v;

      return vendor.First<Vendor>();
    }

    public ICommand AddVendor
    {
      get { return new AddVendorCommand(this); }
    }

    public ICommand RemoveVendor
    {

      get { return new RemoveVendorCommand(this); }
    }
    #endregion

    #region INotifyPropertyChanged interface members
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(String property)
    {
      if (PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(property));
      }
    }
    #endregion
  }

  public class AddVendorCommand : ICommand
  {
    VendorViewModel _vendorViewModel;
    public AddVendorCommand(VendorViewModel vendorViewModel)
    {
      _vendorViewModel = vendorViewModel;
    }

    public bool CanExecute(object parameter)
    {
      if (_vendorViewModel != null)
        return true;
      else
        return false;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
      _vendorViewModel.Vendors.Add(new Vendor()
      {
        AccountNumber = "111111",
        CreditRating = 65,
        Name = "Fabrikam Bikes - Added"
      });
    }
  }

  public class RemoveVendorCommand : ICommand
  {
    VendorViewModel _vendorViewModel;
    public RemoveVendorCommand(VendorViewModel vendorViewModel)
    {
      _vendorViewModel = vendorViewModel;
    }

    public bool CanExecute(object parameter)
    {
      if (_vendorViewModel != null)
        return true;
      else
        return false;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
      Vendor vendor = parameter as Vendor;
      if (null != vendor)
        _vendorViewModel.Vendors.Remove((Vendor)vendor);
    }
  }
}

Commanding is implemented by first declaring an ICommand object as part of the ViewModel class, in this case VendorViewModel.AddVendor and VendorViewModel.RemoveVendor. Both return an instantiation of the appropriate ICommand implementation, passing in the current instance of the VendorViewModel. The next section covers how to make the Model and ViewModel objects available to the UI as well as how to wire in the ICommand support

BasicMVVM - View

The View is the actual XAML of an application. It is the MainPage.xaml file in a typical Windows Phone project, and is what the user interacts with directly, presenting the underlying data and application logic. The View data binds to the ViewModel, which is covered in the previous section.

The goal when building the View is not to have any code in the code-behind for the .xaml file, if possible, to maximize separation of concerns. This means that all logic is in the ViewModel, which is nonvisual, making it much more unit-testable. The other advantage of the separation of concerns here is that the design team can focus on building out the View without interfering with business logic in event handlers. A View always has a reference to the ViewModel, because it data binds to it.

Remove the MainPage.xaml from the BasicMVVM project and add a new View (.xaml page) to the Views folder named VendorsView.xaml. Next, edit the WMAppManifest.xml file by changing the NavigationPage attribute to point to the new default task, as in the following:

<DefaultTask  Name ="_default" NavigationPage="Views/CustomersView.xaml"/>.

images Note In general, the WMAppManifest.xml file should not be manually edited, but in this case it is required.

In Expression Blend, add a ListBox to VendorsView.xaml and configure the ItemsSource to data bind to the VendorViewModel.Vendors collection by clicking the Advanced Options button next to the ItemsSource property in the Expression Blend Properties window and selecting Data Binding to bring up the Create Data Binding dialog. Click the +CLR Object button, select VendorViewModel, and then click OK.

images Tip If the VendorViewModel class— or any .NET CLR class that you want to data bind—does not show up in the dialog box, make sure to compile the application. Static collections will not appear either.

This generates a new Data Source named VendorViewModelDataSource in the left pane. Select Vendors in the right pane and then click OK. This configuration updates the XAML in three places. It adds a new resource to the VendorsView page, as in the following:

<phone:PhoneApplicationPage.Resources>
  <BasicMVVM_ViewModels:VendorViewModel
  x:Key="VendorViewModelDataSource" d:IsDataSource="True"/>
</phone:PhoneApplicationPage.Resources>

It configures LayoutRoot Grid’s DataContext property to point to the VendorViewModel class:

DataContext="{Binding Source={StaticResource VendorViewModelDataSource}}"

Next, Configure the vendorsListBox ItemsSource property to data bind to the VendorViewModel.Vendors collection like so: ItemsSource="{Binding Vendors}"   in Expression Blend. Generally in Windows Phone, developers should use the Application Bar as much as possible as the standard UI to implement in-page functionality. In this case we use two standard buttons to demonstrate the concept of commanding. The ListBox configured above is added to a StackPanel container, and another StackPanel with Orientation set to Horizontal is added below the ListBox with two Buttons objects. Figure 6-2 shows the UI.

images

Figure 6-2. BasicMVVM running in the emulator

One of the goals of MVVM and separating concerns is to make the View as “thin” as possible. Windows Phone OS 7.1 (Mango) now supports UI element Buttons to data bind to methods on the ViewModel via Commanding and the ICommand interface. This means that instead of having event handlers in the code-behind for the view, everything is instead configured via data binding in XAML.

As mentioned above, Windows Phone OS 7.1 with Silverlight 4 compatibility includes support for Commanding. ButtonBase and Hyperlink support Command and CommandParameter properties. The Command property can reference an ICommand implementation that comes from a view-model data source, through a {Binding} usage. The command is then interpreted at run-time. CommandParameter can pass data into the Command, such as which record to delete.

In Expression Blend, select each button and bring up the data binding UI, and both of the VendorViewModel ICommand objects are displayed. For the “Remove Vendor” button, also data bind the CommandParameter property to the ListBox.SelectedItem property. Save the changes and run the project to test out Commanding support, and you’ll see that it works like a charm.

While using Commanding with Button objects is great, unfortunately support for Commanding is not available with the Application Bar Buttons. Luckily, there are third-party, open-source frameworks that provide extensions to Silverlight that enable better support for MVVM, which we cover in the next section.

Pick an MVVM Helper SDK

Because Silverlight for Windows Phone is based on Silverlight 3, it falls short of the MVVM capabilities available in Silverlight 4 and WPF, such as full Commanding support. Luckily, there are quite a few third-party MVVM frameworks available to choose from that provide Commanding support and more. The following lists a few in no particular order:

  • Caliburn
  • nRoute
  • Composite Application Block/PRISM
  • SilverlightFX
  • GalaSoft’s MVVM Light Toolkit

The major benefit that these frameworks have in varying degrees is increasing separation of concerns, unit testing support, support for Messaging, and additional Commanding support. You can find arguments for and against the available frameworks, so please investigate the available options. For this example, let’s take MVVM Light Toolkit for a spin, as it is this author’s opinion that MVVM Light provides a nice balance of power and simplicity that is a great fit for phone application development. Many others would suggest Caliburn instead for similar reasons. The most important thing is to pick a helper SDK, learn it, and use it.

GalaSoft MVVM Light Toolkit

For Windows Phone development, my preference is GalaSoft’s MVVM Light Toolkit. In my opinion it has the right combination of power and as the name says, lightness, for mobile application development.

The MVVM Light Toolkit is up to version 4 Beta, which has quite a few enhancements over the previous version. All of the code has been updated to work with this latest version as of the time of this writing.

The MVVM Light Toolkit was originally developed to address the Commanding shortfalls in Silverlight 2 and Silverlight 3. The MVVM Light Toolkit also includes customized Visual Studio 2010 templates to help you get started right away. First, download the MVVM Light Toolkit and follow the instructions at this page:

http://galasoft.ch/mvvm/getstarted/

If you like the MVVM Light toolkit, I encourage you to click the Donate button at the bottom of the above page, which goes towards the cost of running the site and the rest goes to charity. The source code is available in CodePlex here:

http://mvvmlight.codeplex.com/

Once you have the toolkit downloaded and installed, you can run the MVVMLightSample available in Chapter 6 to see it in action. The entire range of features of the MVVM Light Toolkit are not described end-to-end in this book, but the next couple of sections cover the features of MVVM Light used to migrate the BasicMVVM to MVVM Light.

MVVM Light Sample

In Visual Studio 2010, you can select File  New Project, and click on Silverlight for Windows Phone to filter to the Windows Phone project templates. Select MvvmLight (WP71) as the project template to get started with the Mango version of MVVM Light. If you don’t see that option, check to ensure that you installed the toolkit correctly before proceeding.

Once the project is created, run it and the screen in Figure 6-3 should appear, which indicates that all is configured correctly.

images

Figure 6-3. Testing your MVVM Light installation

The page title, subtitle, and text are all data bound to the MainViewModel class. Let’s go through the architecture of MVVM Light so that you have a basic understanding as we migrate the BasicMVVM app to MVVM Light. After a new project is created, the project includes the default App.xaml and MainPage.xaml as well as the other items expected in a Silverlight for Windows Phone application but with a couple of additions. There is a Model folder with three code files present that we discuss shortly. There is also a ViewModel folder that contains two ViewModel classes named MainViewModel and ViewModelLocator. I cover these in the next section.

ViewModelLocator

The ViewModelLocator class contains a reference to every ViewModel class in the project. This provides a centralized way to manage creation and allow XAML configuration via a single application resource. By default, in App.xaml a resource is added for the ViewModelLocator class. A namespace is added to the <Application> object that hosts the PhoneApplicationFrame that contains the XAML pages or View classes as they are navigated:

xmlns:vm="clr-namespace:MvvmLightSample.ViewModel"

The ViewModelLocator class is configured as an application level resource like this:

<Application.Resources>
    <vm:ViewModelLocator x:Key="Locator"
                          d:IsDataSource="True" />
</Application.Resources>

This resource is available throughout the entire application, just like any other application-level resource, making the referenced ViewModel objects available now. Now we move on to explain how MainPage.xaml data binds to MainViewModel within this architecture. In the XAML, the <PhoneApplicationPage> element’s DataContext property data binds to the Locator resource discussed above via this XAML code:

DataContext="{Binding Main, Source={StaticResource Locator}}"

The Path property for the Binding object is configured with the value of Main using the default syntax (it can also be written as Path=Main). This configuration makes an instance of the MainViewModel available within MainPage.xaml and allows the page title, sub-title, and text to data bind to properties available on the MainViewModel. Here’s an example of one of the Application Title Bindings:

<TextBlock x:Name="ApplicationTitle"
    Text="{Binding ApplicationTitle}"
    Style="{StaticResource PhoneTextNormalStyle}" />

Listing 6-3 shows the default ViewModelLocator class.

Listing 6-3. Default ViewModelLocator Class Code File

namespace MvvmLightSample.ViewModel
{
  /// <summary>
  /// This class contains static references to all the view models in the
  /// application and provides an entry point for the bindings.
  /// <para>
  /// Use the <strong>mvvmlocatorproperty</strong> snippet to add ViewModels
  /// to this locator.
  /// </para>
  /// <para>
  /// See http://www.galasoft.ch/mvvm/getstarted
  /// </para>
  /// </summary>
  public class ViewModelLocator
  {
    static ViewModelLocator()
    {
      ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

      if (ViewModelBase.IsInDesignModeStatic)
      {
        SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
      }
      else
      {
        SimpleIoc.Default.Register<IDataService, DataService>();
      }

      SimpleIoc.Default.Register<MainViewModel>();
    }

    /// <summary>
    /// Gets the Main property.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
        "CA1822:MarkMembersAsStatic",
        Justification = "This non-static member is needed for data binding purposes.")]
    public MainViewModel Main
    {
      get
      {
        return ServiceLocator.Current.GetInstance<MainViewModel>();
      }
    }

    /// <summary>
    /// Cleans up all the resources.
    /// </summary>
    public static void Cleanup()
    {
    }
  }
}

Notice in Listing 6-3 how the MainViewModel is made available. It uses the SimpleIoc class to register the class MainViewModel within the application. For the Main property, the ServiceLocator class gets the instance of MainViewModel that is available.

The class name SimpleIoc is short for Simple Inversion of Control, which is a technique to simplify the creation and the resolution of services by allowing a consumer to request an interface and have SimpleIoc provide a matching concrete class, in this case the ViewModels; it allows for a cleaner syntax in the ViewModelLocator than in previous versions of MVVM Light.

To add additional ViewModel objects, manually edit the ViewModelLocator class and follow the pattern for the MainViewModel as an example. There is a code snippet available to automate this process. Type mvvmlocatorproperty and tap the Tab key twice to quickly add the property using Visual Studio 2010’s code snippet template UI. Essentially, type new values for the default and click the Tab key to move through the template to quickly add the property combination in the correct format.

The MainViewModel takes a type of IDataService in its constructor. Generally ViewModels need not take parameters for XAML support. With SimpleIoc, as long as the service is registered, in this case IDataService, and the ViewModel is registered, SimpleIoc will handle this for you and pass the registered instance into the constructor and create the MainViewModel.

Notice also in the ViewModelLocator class it registers a service for the IDataService interface in the constructor using an if statement to provide design-time or run-time data depending on the current environment. What’s nice about this method is that the application can simply retrieve an instance of IDataService and it will be properly populated with data as needed; you won’t have to worry about design-time vs. run-time. You could use the same technique to register an IDataService that provides different sets of data to authenticated or unauthenticated users, as well as design-time data.

MvvmLightSample – Model

A reference is added to System.Runtime.Serialization assembly and to System.ServiceModel.Web to set up the work needed to create the Model and the related data service.

A question that comes up when building MVVM is where to put the code to load up your data collections. MVVM Light provides an answer to this question with the concept of an IDataService and the inversion-of-control functionality provided by SimpleIoc. The idea is that you register your data service and ViewModels in the view model locator page, and it connects the ViewModel with the corresponding service. In MVVM Light, the service is placed with the related Model in the Model folder.

We first modify the IDataService provided by the default project template to return the ApplicationTitle and PageName so that the MainPage.xaml View has something to bind to for the Title and Page Name. Note that the data binding code is already there in the default MainPage.xaml View, but it is not present in the MainViewModel or the IDataService. Here is the updated interface:

public interface IDataService
{
  void GetApplicationTitle(Action<DataItem, Exception> callback);
  void GetPageName(Action<DataItem, Exception> callback);
}

The implementation class, DataService, is not much more complex. It simply returns the values for the properties:

public class DataService : IDataService
{
  public void GetApplicationTitle(Action<DataItem, Exception> callback)
  {
    var item = new DataItem("CHAPTER SIX");
    callback(item, null);
  }

  public void GetPageName(Action<DataItem, Exception> callback)
  {
    var item = new DataItem("mvvm light sample");
    callback(item, null);
  }
}

While a pretty simple modification, it demonstrates that the DataService provides a callback of the desired data. Let’s now create the IVendorService, which will return a listing of all of the Vendor objects:

public interface IVendorsService
{
  void GetVendors(Action<IList<Vendor>, Exception> callback);
}

In this case, instead of returning a DataItem class object, GetVendors returns an IList containing Vendor objects. Where this gets interesting is in the concrete implementation in the VendorService class shown here:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net;
using Newtonsoft.Json;

namespace MvvmLightSample.Model
{
  public class VendorsService : IVendorsService
  {
    private const string ServiceUri =
"http://localhost:9191/AdventureWorksRestJSON.svc/Vendors";
    public void GetVendors(Action<IList<Vendor>, Exception> callback)
    {
      var client = new WebClient();
      client.DownloadStringCompleted += ClientDownloadStringCompleted;
      client.DownloadStringAsync(new Uri(ServiceUri), callback);
    }

    private static void ClientDownloadStringCompleted(object sender,
DownloadStringCompletedEventArgs e)
    {
      var callback = e.UserState as Action<IList<Vendor>, Exception>;

      if (callback == null)
      {
        return;
      }

      if (e.Error != null)
      {
        callback(null, e.Error);
        return;
      }

      ObservableCollection<Vendor> vendors;
      vendors = JsonConvert.DeserializeObject<ObservableCollection<Vendor>>(e.Result);
      callback(vendors, null);
    }
  }
}

There are a few things going on in this implementation to note, but the first is that the code uses JSON.NET to deserialize the data. JSON.NET has been tested informally to have better performance than the built-in DataContractSerializer class. JSON.NET is very flexible with quite a few options that I won’t go into here. I do take advantage of the very simple code it takes to deserialize a string into a collection of objects:

ObservableCollection<Vendor> vendors;
vendors = JsonConvert.DeserializeObject<ObservableCollection<Vendor>>(e.Result);

Otherwise, the code uses the new and improved WebClient class in Windows Phone OS 7.1 (Mango) to call the REST service and return the data in a string as shown earlier in the code. The last item I cover is the modifications to the Vendor class. It inherits from ObservableObject, a new base class available in MVVM Light version 4. Here is the updated Vendor class:

using System.Runtime.Serialization;
using GalaSoft.MvvmLight;

namespace MvvmLightSample.Model
{
  [DataContract()]
  public class Vendor : ObservableObject
  {
    private string AccountNumberField;
    private byte CreditRatingField;
    private string NameField;

    [DataMemberAttribute()]
    public string AccountNumber
    {
      get
      { return this.AccountNumberField; }
      set
      { this.AccountNumberField = value;
        RaisePropertyChanged("AccountNumber");}
    }

    [DataMemberAttribute()]
    public byte CreditRating
    {
      get
      { return this.CreditRatingField; }
      set
      {this.CreditRatingField = value;
        RaisePropertyChanged("CreditRating");}
    }

    [DataMemberAttribute()]
    public string Name
    {
      get
      {return this.NameField;}
      set
      {this.NameField = value;
        RaisePropertyChanged("Name");}
    }
  }
}

The other item is design-time data. The ViewModelLocator class checks for design time with a call to ViewModelBase.IsInDesignModeStatic. Depending on whether it is design time or not, the Design data service is registered with SimpleIoc or the production data service is registered in the ViewModelLocator constructor:

if (ViewModelBase.IsInDesignModeStatic)
{
  SimpleIoc.Default.Register<IVendorsService, Design.DesignVendorService>();
  SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
  SimpleIoc.Default.Register<IVendorsService, VendorsService>();
  SimpleIoc.Default.Register<IDataService, DataService>();
}

The default project template for MVVM Light has a Design folder with a default design-time data source class named DesignDataService. I modified it to return the ApplicationTitle and PageName values at design time. The design-time data class for Vendors is DesignVendorDataService.cs in the Design folder. It creates five records to have some data at design-time. As you can see, having Interface-based services allow for simplified code to instantiate the correct data source as needed. This completes coverage of the Model and data service. Next up on our MVVM journey is the ViewModel.

MvvmLightSample – VendorsViewModel

With the changes in MVVM Light version 4, I rewrote the ViewModel class from BasicMVVM to take advantage of the new features.

images Note The first edition of this book leveraged MVVM Light version 3, which does not compile in the new version. Be sure to update to the latest sample code available as a download for this edition.

The VendorViewModel class inherits from ViewModelBase to take advantage of the built-in INotifyPropertyChange implementation. I’ve also added a reference to System.ServiceModel.Web to make the DataContractJsonSerializer class available within the VendorsViewModel.

The new VendorsViewModel class includes a Vendors property of type ObservableCollection as before. The constructor for VendorsViewModel takes an instance of IVendorsService passed in by SimpleIoc. In the constructor the data is loaded up as shown here:

public VendorsViewModel(IVendorsService dataService)
{
  _vendorsService = dataService;

  Vendors = new ObservableCollection<Vendor>();
  _vendorsService.GetVendors(
      (vendors, error) =>
      {
        if (error != null)
        {
          // Report error here
          return;
        }
        Vendors = (ObservableCollection<Vendor>)vendors;
      });
}

Whether at design time or runtime, the appropriate concrete implementation of IVendorService is provided and the appropriate data service provides data to the UI. Next up is the VendorsView object where the data is displayed.

MvvmLightSample – VendorsView

A new folder named View is added to the MVVMLightSample project, and the VendorsView.xaml page is copied from the BasicMVVM project to the MVVMLightSample view folder. Do a Find / Replace with BasicMVVM to replace it with MvvmLightSample with the “Look in” set to Current Project. That fixes up namespace references to compile the project successfully.

Next, MainViewModel is modified to return a more appropriate page title and subtitle as well as string text for the Welcome message property for the MainPage.xaml page to display the application title and page name. A TextBlock is added to MainPage.xaml to provide a link to navigate to the Vendors View page via the NavigateToPageAction behavior. Clicking on that text will navigate you to the VendorsView.xaml page. Fix up the margins so that everything aligns at 24px on the left.

If you run the project, it works as before, but let’s configure the VendorsView to take advantage of the MVVM Light toolkit capabilities. First, add a property combination to the ViewModelLocator via the mvvmlocatorproperty code snippet. The code snippet quickly generates this code for you in the ViewModelLocator class:

static ViewModelLocator()
{
  ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

  if (ViewModelBase.IsInDesignModeStatic)
  {
    SimpleIoc.Default.Register<IVendorsService, Design.DesignVendorService>();
    SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
  }
  else
  {
    SimpleIoc.Default.Register<IVendorsService, VendorsService>();
    SimpleIoc.Default.Register<IDataService, DataService>();
  }

  SimpleIoc.Default.Register<MainViewModel>();
  SimpleIoc.Default.Register<VendorsViewModel>();
}

public VendorsViewModel Vendors
{
  get
  {
    return ServiceLocator.Current.GetInstance<VendorsViewModel>();
  }
}

As you can see, if you are not familiar with Visual Studio 2010 the code snippet is quite handy! It adds an implementation of the constructor that registers the ViewModel. We delete that version and edit the existing constructor as shown above. It also adds a property that uses the ServiceLocator to return the VendorsViewModel instance.

Now that we have the VendorsView added to the ViewModelLocator, we can configure the VendorsView to data bind the MVVM Light Toolkit way using Expression Blend. First compile the project to make sure everything is up to date, and then remove the DataContext binding on the LayoutRoot Grid. Also remove the VendorViewModelDataSource from the PhoneApplicationPage.Resources section in the VendorsView.xaml file.

In Expression Blend, select the PhoneApplicationPage root item in the Objects and Timeline tool window. Find the DataContext property, click the Advanced Options button, and select Data Binding to bring up the Create Data Binding dialog. Select the Locator data source and then select Vendors, as shown in Figure 6-4.

images

Figure 6-4. Data bind DataContext to the VendorsViewModel

Run the application and navigate to the VendorsView, and it displays the data as before. We still have the event handlers in the code-behind. In the next subsection the event handlers are removed and instead the application takes advantage Commanding support provided by the MVVM Light toolkit.

Commanding and RelayCommand

The MVVM Light Toolkit supports Commanding, or data binding events to ViewModel methods, via the RelayCommand and RelayCommand<T> classes. With Windows Phone OS 7.1, the programming model is improved with full support for ICommand and Commanding within the XAML programming model. I demonstrated the command support in the BasicMVVM project. This section covers how to leverage MVVM Light’s Commanding support.

In the VendorViewModel class, two RelayCommand instances are added in a region named Commanding, one that is parameter-less and one that takes a parameter. Here is the declaration:

#region Commanding
public RelayCommand AddAVendorCommand
{
  get;
  private set;
}

public RelayCommand<Vendor> RemoveAVendorCommand
{
  get;
  private set;
}
#endregion

The commands are instantiated in the VendorViewModel() constructor in this code:

//Instantiate Commands
AddAVendorCommand = new RelayCommand(
  () => AddVendor());

RemoveAVendorCommand = new RelayCommand<Vendor>(
  param => RemoveVendor(param));

The RelayCommand objects bridge between the ViewModel methods and the UI events. RelayCommand has support for one parameter only so if you need to pass more info, consider encapsulating into an object. Now it’s time to data bind the commands in the UI. Currently the application uses the application bar to execute Add and Remove. Unfortunately, the ApplicationBarIconButton class does not inherit from FrameworkElement so the ButtonBaseExtension cannot be attached to a DependencyObject. You can still call the relay in code-behind as before. Here is an example from the sample:

private void insertVendorAppBarBtn_Click(object sender, EventArgs e)
{
  var vm = DataContext as VendorViewModel;
  if (vm != null)
  {
    vm.AddAVendorCommand.Execute(null);
  }
}

For this sample, the two Buttons are added so that we can demonstrate the EventToCommand Expression Blend behavior, as well as via the code-behind for the application bar buttons. In Expression Blend, switch to the Assets tab and select Behaviors to filter the list. Drag the EventToCommand behavior on to the Button objects and configure the correct command on each button. For the Remove Button, data bind the EventToCommand Command property to the RemoveAVendorCommand RelayCommand. Also data bind the CommandParameter for the Remove EventToCommand object to the vendorsListBox.SelectedItem method, which returns a Vendor object. Here is the resulting markup for Remove:

<Button x:Name="RemoveButton" Content="Remove" HorizontalAlignment="Right"
VerticalAlignment="Bottom" Margin="0,0,8,7">
  <Custom:Interaction.Triggers>
    <Custom:EventTrigger EventName="Click">
      <GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding RemoveAVendorCommand,
Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=VendorsListBox}"/>
    </Custom:EventTrigger>
  </Custom:Interaction.Triggers>
</Button>

The RelayCommand class also supports CanExecute and CanExecuteChanged members as well to determine whether to enable or disable the element, in this case a Button object. The CanExecute method can be passed in to the constructor as a second parameter. Here’s an example:

AddAVendorCommand = new RelayCommand(
  () => AddVendor(), () => CheckEnabled);

We have now completely migrated the sample over to MVVM. Listing 6-4 shows the source code for the updated VendorViewModel class.

Listing 6-4. Updated VendorViewModel Code File

using System.Collections.ObjectModel;
using System.Linq;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using MvvmLightSample.Model;

namespace MvvmLightSample.ViewModel
{
  public class VendorsViewModel : ViewModelBase
  {
    private readonly IVendorsService _vendorsService;

    public const string VendorsPropertyName = "Vendors";
    private ObservableCollection<Vendor> _vendors = null;
    public ObservableCollection<Vendor> Vendors
    {
      get
      {
        return _vendors;
      }

      set
      {
        if (_vendors == value)
        {
          return;
        }

        _vendors = value;
        RaisePropertyChanged(VendorsPropertyName);
      }
    }

    /// <summary>
    /// Initializes a new instance of the VendorsViewModel class.
    /// </summary>
    public VendorsViewModel(IVendorsService dataService)
    {
      _vendorsService = dataService;

      Vendors = new ObservableCollection<Vendor>();
      _vendorsService.GetVendors(
          (vendors, error) =>
          {
            if (error != null)
            {
              // Report error here
              return;
            }
            Vendors = (ObservableCollection<Vendor>)vendors;
          });

      //Instantiate Commands
      AddAVendorCommand = new RelayCommand(
        () => AddVendor());

      RemoveAVendorCommand = new RelayCommand<Vendor>(
        param => RemoveVendor(param));

    }

    #region Business Logic
    public Vendor GetVendorByAccountNumber(string accountNumber)
    {
      var vendor = from v in Vendors
                   where v.AccountNumber == accountNumber
                   select v;

      return vendor.First<Vendor>();
    }

    public void AddVendor()
    {
      Vendors.Add(new Vendor()
      {
        AccountNumber = "111111",
        CreditRating = 65,
        Name = "Fabrikam Bikes - Added"
      });
    }

    public void RemoveVendor(object vendor)
    {
      if (null != vendor)
        Vendors.Remove((Vendor)vendor);
    }
    #endregion

    #region Commanding
    public RelayCommand AddAVendorCommand
    {
      get;
      private set;
    }

    public RelayCommand<Vendor> RemoveAVendorCommand
    {
      get;
      private set;
    }
    #endregion

    ////public override void Cleanup()
    ////{
    ////    // Clean up if needed

    ////    base.Cleanup();
    ////}
  }

}In the next couple of sections, I cover additional features of the MVVM Light Toolkit.

The MVVM Light Messenger Class

The Messenger class provides a means to communicate within an application in a decoupled way. Classes can register to receive messages of different types. The message can be anything from simple values to complex objects. Likewise, messages can specify a target type that should receive the message for fine-tuned control.

MVVM Light includes multiple message classes. The following is a list of possible messages from the docs:

  • MessageBase: A simple message class, carrying optional information about the message’s sender.
  • GenericMessage<T>: A simple message with a Content property of type T.
  • NotificationMessage: Used to send a notification (as a string) to a recipient. For example, define notifications as constants in a Notifications class, and then send Notifications.Save to recipients.
  • NotificationMessage<T>: Same as the previous, but with a generic Content property. It can be used to pass a parameter to the recipient together with the notification.
  • NotificationMessageAction: Sends a notification to a recipient and allows the recipient to call the sender back.
  • NotificationMessageAction<T>: Sends a notification to a recipient and allows the recipient to call the sender back with a generic parameter.
  • DialogMessage: Used to request that a recipient (typically a View) displays a dialog, and passes the result back to the caller (using a callback). The recipient can choose how to display the dialog, either with a standard MessageBox, with a custom popup, or something similar.
  • PropertyChangedMessage<T>: Used to broadcast that a property changed in the message sender. Fulfills the same purpose as the PropertyChanged event, but in a decoupled manner.

The Messenger class provides a powerful means to pass data and notifications between application layers and within ViewModels in a decoupled way.

Silverlight for Windows Phone Toolkit

The Silverlight Toolkit has been in existence for several years. The idea behind it is to be able to more frequently release control and other updates with full source code to developers out-of-band from the normal product release cycle. The toolkit has been updated for Windows Phone OS 7.1 (Mango) in August 2011. It is available at the following URL:

http://silverlight.codeplex.com/

The updated version adds quite a few additional controls highlighted in Table 6.1 in the next section. It also includes improved transitions and ContextMenu performance as well as various bug fixes. The toolkit also includes localized resources to provide support for multiple languages.

Installation and Overview

Installation is simply a matter of running the MSI and making the controls available in the Toolbox within Visual Studio 2010. Right-click on the Toolbox, select Choose Items, and then put a check box next to controls you want to show up in the Toolbox. You can sort the controls alphabetically as well. The Silverlight for Windows Phone Toolkit includes several phone-specific controls that most developers will want to take advantage of to some degree; they are listed in Table 6-1.

Images

Images

The controls in Table 6-1 were frequently requested by developers during the beta-test phase of the Windows Phone Developer Tools. The product team created the toolkit to supplement the SDK to help match the built-in native UI controls’ look and feel.

images Note GestureService and GestureListener are covered in Chapter 3.

The best way to proceed is to dive right in and show you how to put the controls to work within a sample project. Luckily, the Silverlight for Windows Phone Toolkit includes a very robust sample that is included with the source code download titled “Silverlight for Windows Phone Toolkit Source & Sample - Aug 2011.zip” available here:

http://silverlight.codeplex.com/releases/view/71550

There isn’t any documentation beyond the sample application, so this section provides an overview of the available additional controls available in the toolkit. There is one additional sample project in the Chapter 6 solution that demonstrates how to data bind the WrapPanel control.

When you first run the sample app that ships with the source code available for download at the link I just gave you, it launches into a menu shown in Figure 6-5.

images

Figure 6-5. Silverlight for Windows Phone Toolkit sample main menu

Each control is covered in the sections that follow.

The AutoCompleteBox Control

The AutoCompleteBox control allows a user to type letters, which brings up matches from the data source resource configured on the ItemSource attribute. Here is an example:

<toolkit:AutoCompleteBox VerticalAlignment="Top" ItemsSource="{StaticResource words}"Images
 Margin="0,12"/>

Figure 6-6 shows the AutoCompleteBox in action. The top AutoCompleteBox is configured with the above XAML.

images

Figure 6-6. AutoCompleteBox in action

The bottom AutoCompleteBox is configured with an ItemTemplate to display two lines of text:

<toolkit:AutoCompleteBox
    InputScope="Url"
    ItemsSource="{StaticResource websites}"
    Margin="0,12"
    ValueMemberPath="Item1">
    <toolkit:AutoCompleteBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Margin="0,7">
                <TextBlock
                    Margin="8,0"
                    Text="{Binding Item1}"/>
                <TextBlock
                    FontSize="{StaticResource PhoneFontSizeNormal}"
                    Foreground="#ff666666"
                    Margin="8,-6,8,2"
                    Text="{Binding Item2}"/>
            </StackPanel>
        </DataTemplate>
    </toolkit:AutoCompleteBox.ItemTemplate>
</toolkit:AutoCompleteBox>

The words come from these resources, configured in the PhoneApplicationPage.Resources section:

<phone:PhoneApplicationPage.Resources>
    <data:LoremIpsum x:Key="words"/>
    <data:LoremIpsumWebsites x:Key="websites"/>
</phone:PhoneApplicationPage.Resources>

In the Data folder of the toolkit sample solution PhoneToolkitSample project, there are two classes, LoremIpsum.cs and LoremIpsumWebsites.cs, which generate a random collection of words in an IEnumerable collection. You can data bind to any collection of strings and display the values as the user types.

The AutoCompleteBox provides a great way to improve UI by populating text fields with most likely values, saving users from having to type.

The ContextMenu Control

The ContextMenu control provides a user interaction unique to Windows Phone with the tap-and-hold gesture. Figure 6-7 shows the test page with in-text hints on functionality, as well as results after tap-and-hold actions.

images

Figure 6-7. ContextMenu control test page

The ContextMenu control is bindable to ICommand objects, so it can work quite nicely with GalaSoft’s MVVM support for Commanding, allowing context menu items to invoke methods on the data bound ViewModel class.

The DatePicker and TimePicker Controls

The DatePicker and TimePicker controls make it easy for users to pick date and time within a Windows Phone application. Figure 6-8 shows the controls in action.

images

Figure 6-8. DatePicker and TimePicker controls in action

You can attach an event hander to the Click event and assign commands to the Command property. Here is example XAML for ICommand support:

<toolkit:ContextMenuService.ContextMenu>
    <toolkit:ContextMenu>
        <toolkit:MenuItem
        Header="Always-on item"
        Command="{Binding AlwaysCommand}"/>
        <toolkit:MenuItem
        Header="Intermittent item"
        Command="{Binding IntermittentCommand}"/>
        <toolkit:MenuItem
        Header="Always-on item with param"
        Command="{Binding AlwaysCommand}"
        CommandParameter="param1"/>
        <toolkit:MenuItem
        Header="Intermittent item with param"
        Command="{Binding IntermittentCommand}"
        CommandParameter="param2"/>
    </toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>

The controls are dependent on having the correct application bar icons in a folder named Toolkit.Content. The icons are ApplicationBar.Cancel.png and ApplicationBar.Check.png and their build action must be set to Content.

The ListPicker Control

The ListPicker control provides a full-page, touch-friendly drop-down scrollable list to select an item. Figure 6-9 shows the example.

images

Figure 6-9. ListPicker control in action

The ListPicker control can display a set of inline strings like this:

<toolkit:ListPicker Header="background">
    <sys:String>dark</sys:String>
    <sys:String>light</sys:String>
    <sys:String>dazzle</sys:String>
</toolkit:ListPicker>

The ListPicker control also has ItemSource and ItemTemplate attributes to support data binding and full customization of how items are displayed. Here is example XAML:

<toolkit:ListPicker ItemsSource="{Binding}" Header="accent color"
                  FullModeHeader="ACCENTS" CacheMode="BitmapCache">
    <toolkit:ListPicker.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Rectangle Fill="{Binding}" Width="24" Height="24"/>
                <TextBlock Text="{Binding}" Margin="12 0 0 0"/>
            </StackPanel>
        </DataTemplate>
    </toolkit:ListPicker.ItemTemplate>
    <toolkit:ListPicker.FullModeItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="16 21 0 20">
                <Rectangle Fill="{Binding}" Width="43" Height="43"/>
                <TextBlock Text="{Binding}" Margin="16 0 0 0" FontSize="43"
                           FontFamily="{StaticResource PhoneFontFamilyLight}"/>
            </StackPanel>
        </DataTemplate>
    </toolkit:ListPicker.FullModeItemTemplate
</toolkit:ListPicker>

The ToggleSwitch Control

The ToggleSwitch control configures a boolean value as On or Off. It can take a simple Header attribute for a text value to display across the top above the switch and current status as shown in the first two ToggleSwitch controls in Figure 6-10.

images

Figure 6-10. The ToggleSwitch control

The last ToggleSwitch control is much more customized than the first two. It includes a ToggleSwitch.HeaderTemplate to adjust the Font for the header. The ToggleSwitch.ContentTemplate customizes the ToggleSwitch status info on the left with additional detail. Simply embed a <ContentControl Content="{Binding}"/> control inside the ToggleSwitch.ContentTemplate to have the On/Off status display correctly, as in the following:

<toolkit:ToggleSwitch Header="5:45 AM">
    <toolkit:ToggleSwitch.HeaderTemplate>
        <DataTemplate>
            <ContentControl FontSize="{StaticResource PhoneFontSizeLarge}"
                  Foreground="{StaticResource PhoneForegroundBrush}" Content="{Binding}"/>
        </DataTemplate>
    </toolkit:ToggleSwitch.HeaderTemplate>
    <toolkit:ToggleSwitch.ContentTemplate>
        <DataTemplate>
            <StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Alarm: " FontSize="{StaticResource PhoneFontSizeSmall}"/>
                    <ContentControl HorizontalAlignment="Left"
                            FontSize="{StaticResource PhoneFontSizeSmall}"
Content="{Binding}"/>
                </StackPanel>
                <TextBlock Text="every schoolday"
                            FontSize="{StaticResource PhoneFontSizeSmall}"
                            Foreground="{StaticResource PhoneSubtleBrush}"/>
            </StackPanel>
        </DataTemplate>
    </toolkit:ToggleSwitch.ContentTemplate>
</toolkit:ToggleSwitch>

The WrapPanel Control

The WrapPanel control works very similarly to the same named control available in the desktop Silverlight toolkit. It arranges child items left to right, row by row or top to bottom, column by column. Figure 6-11 shows the UI.

images

Figure 6-11. The WrapPanel control

The WrapPanel control has a Children collection that allows you to add child items to the control via code, which is how the toolkit sample adds items. There may be situations in which you prefer to data bind to an ItemSource property and an ItemTemplate, as you can in the ListBox control. The WrapPanelDataBinding project sample in the Chapter 6 solution demonstrates how to do this.

You can use an ItemsControl to provide this functionality by changing the ItemsControl.ItemsPanel to a WrapPanel. Otherwise, data binding works just like with a ListBox. Here is the XAML markup:

<ItemsControl ItemsSource="{Binding Strings}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <toolkit:WrapPanel ItemWidth="69"/>
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <Grid Width="60" Height="60" Margin="4">
        <Rectangle Fill="#FF2A2AC8" Stroke="Black"/>
        <TextBlock
          Text="{Binding Text}" TextWrapping="Wrap"/>
      </Grid>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

The WrapPanelDataBinding project has a sample data source generated in Expression Blend to display random text over rectangles. The ItemsControl.ItemsSource points to the Strings collection in the sample data source. Figure 6-12 shows the output.

images

Figure 6-12. Data binding the WrapPanel control

The HubTile Control

In Table 6-1, I’ve listed the new controls available in the August toolkit for Windows Phone 7.5, but I do not highlight every control in this chapter. However, one control that provides an amazing UI for very little effort is HubTile. Figure 6-13 shows its UI.

images

Figure 6-13. The HubTile Control In Action

The configuration for the control is very straightforward and can be done via data binding. Here is the XAML for the HubTileSample.xaml file, which is part of the Phone Toolkit sample project available for download from CodePlex:

<toolkit:HubTile Grid.Row="1" Grid.Column="0"
                                         Margin="12,12,0,0"
                                         Source="/Images/Dessert.jpg"
                                         Title="Dessert"
                                         Notification="2 New&#x0d;&#x0a;Receipes"
                                         DisplayNotification="True"
                                         GroupTag="Food"/>
                        <toolkit:HubTile Grid.Row="1" Grid.Column="1"
                                         Margin="12,12,0,0"
                                         Source="/Images/Fruits.jpg"
                                         Title="Fruits"
                                         GroupTag="Food"/>
                        <toolkit:HubTile Grid.Row="2" Grid.Column="0"
                                         Margin="12,12,0,0"
                                         Source="/Images/Pretzel.jpg"
                                         Title="Pretzel"
                                         Notification="w/fixings"
                                         DisplayNotification="True"
                                         GroupTag="Food"/>
                        <toolkit:HubTile Grid.Row="2" Grid.Column="1"
                                         Margin="12,12,0,0"
                                         Source="/Images/Shrimp.jpg"
                                         Title="Shrimp"
                                         Message="&quot;Just an amazing work by the
chef&quot;"
                                         GroupTag="Food"/>
                        <toolkit:HubTile Grid.Row="3" Grid.Column="0"
                                         Margin="12,12,0,0"
                                         Source="/Images/SteakSandwich.jpg"
                                         Title="Steak&#x0d;&#x0a;Sandwich"
                                         Message="@ a great cafe"
                                         GroupTag="Food"/>
                        <toolkit:HubTile Grid.Row="3" Grid.Column="1"
                                         Margin="12,12,0,0"
                                         Source="/Images/Beignets.jpg"
                                         Title="Beignets"
                                         Notification="New&#x0d;&#x0a;Orleans"
                                         DisplayNotification="True"
                                         GroupTag="Food"/>

As you can see, configuration of the HubTile control is very simple but can yield an amazing user interface addition to your application.

A control that we won’t dive into here is PerformanceProgressBar, which was added to the toolkit in February 2011 and is now a part of the Windows Phone OS 7.1 SDK. Using the control is pretty straightforward, as it models the functionality of the built-in Progressbar control. I mention it because you will want to use the toolkit PerformanceProgressBar to improve rendering performance in your Silverlight or Windows Phone applications.

The LongListSelector Control

The LongListSelector control is the uber-ListBox control available for Windows Phone. It supports flat lists (like in a ListBox), but it can also support complex grouping and list navigation, which is very useful on the small screen. Try it for your scenario, especially in Windows Phone 7.5. The control has been completely rewritten to take advantage of performance enhancements around scrolling and off-thread touch support available in the Windows Phone OS 7.1 SDK. The LongListSelector control also supports grouped list, inline “more like these” buttons, and jump list UI.

Given the complexity and power of this control, the next couple of sections describe its visual capabilities, key properties and methods, and coding approaches.

LongListSelector - IsFlatList

The quickest way to take advantage of the LongListSelector control’s potential performance and list virtualization advantages is to replace existing ListBox controls with LongListSelector controls and set the attribute IsFlatList to True. When in IsFlatList=true mode, the UI looks just like a ListBox control, though you can provide a header and footer to the list via the ListHeaderTemplate and ListFooterTemplate properties, as shown in Figure 6-14.

images

Figure 6-14. LongListSelector with header and footer

Listing 6-5 shows the XAML for the control and the three templates that are configured.

Listing 6-5. LongListSelector XAML Markup File

<phone:PhoneApplicationPage
    x:Class="LongListSelectorSample.pages.LongListSelectorPage2"
…<!—removed standard namespaces for clarity Images
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
    shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded"
    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Images
Microsoft.Phone.Controls.Toolkit">
  <phone:PhoneApplicationPage.Resources>
    <DataTemplate x:Key="ItemTemplate">
      <StackPanel Margin="0,0,0,20">
        <TextBlock Text="{Binding Name}"
                   Style="{StaticResource PhoneTextExtraLargeStyle}"/>
        <TextBlock Text="{Binding Description}"
                   Style="{StaticResource PhoneTextSmallStyle}"/>
        <TextBlock Text="{Binding Quantity}"
                   Style="{StaticResource PhoneTextAccentStyle}"/>
      </StackPanel>
    </DataTemplate>
    <DataTemplate x:Key="HeaderDataTemplate">
      <Border Background="#FF0027FF">
        <TextBlock TextWrapping="Wrap" Text="Chapter Six"
          HorizontalAlignment="Center" VerticalAlignment="Center"
          Style="{StaticResource PhoneTextLargeStyle}"/>
      </Border>
    </DataTemplate>
    <DataTemplate x:Key="FooterDataTemplate">
      <Border Background="#FF0027FF">
        <TextBlock TextWrapping="Wrap" Text="Advanced Silverlight UI"
         HorizontalAlignment="Center" VerticalAlignment="Center"
         Style="{StaticResource PhoneTextLargeStyle}"/>
      </Border>
    </DataTemplate>
  </phone:PhoneApplicationPage.Resources>
  <!–LayoutRoot is the root grid where all page content is placed–>
  <Grid x:Name="LayoutRoot" Background="Transparent"
    DataContext="{Binding Source={StaticResource longlistDataSource}}">
    <Grid.RowDefinitions>  <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>  </Grid.RowDefinitions>
    <!–TitlePanel contains the name of the application and page title–>
    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
      <TextBlock x:Name="ApplicationTitle" Text="CHAPTER 6-LONGLISTSELECTORSAMPLE"
                 Style="{StaticResource PhoneTextNormalStyle}"/>
      <TextBlock x:Name="PageTitle" Text="longlistselector" Margin="9,-7,0,0"
                 Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>
    <!–ContentPanel - place additional content here–>
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
      <toolkit:LongListSelector IsFlatList="True"
        ItemTemplate="{StaticResource ItemTemplate}"
        ItemsSource="{Binding Collection}"
        ListHeaderTemplate="{StaticResource HeaderDataTemplate}"
        ListFooterTemplate="{StaticResource FooterDataTemplate}"/>
    </Grid>
  </Grid>
</phone:PhoneApplicationPage>

If you forget to set IsFlatList equal to True, it will generate an error when data bound to a flat list. This is because by default the LongListSelector control expects a data structure with a grouped set of items that permits the control to display the long list of content segmented by available groups.

LongListSelector via LINQ

The PhoneToolkit Sample Solution downloaded from CodePlex when you install the toolkit includes a page named LongListSelectorSample.xaml. This page demonstrates several ways a developer can implement grouping with the LongListSelector control. The UI for the LongListSelectorSample.xaml is a Pivot control with three PivotItem pages, titled linq, code, and buddies. The LongListSelector implementation for the PivotItem titled linq has the following XAML:

<toolkit:LongListSelector x:Name="linqMovies" Background="Transparent"
    ListHeaderTemplate="{StaticResource movieListHeader}"
    GroupHeaderTemplate="{StaticResource movieGroupHeader}"
    GroupItemTemplate="{StaticResource groupItemHeader}"
    ItemTemplate="{StaticResource movieItemTemplate}">
</toolkit:LongListSelector>

Note that it does not implement a GroupFooterTemplate. The PivotItem page titled linq displays movie data via Category using LINQ to demonstrate how to group items in the correct format using LINQ. The item that is displayed in the LongListSelector for both the linq and code PivotItem pages is a Movie class that has fields like Title, Description, Year, and so on. The movies are grouped by Category, which is of type string. In the code-behind file, the LoadLinqMovies method creates a flat collection of movies with random data and then builds a LINQ to Object query to group the movies by category.

The LINQ query is dazzlingly simple, taking advantage of the built-in group by support in LINQ that is based on the IGrouping Interface. Here is the LoadLinqMovies method

private void LoadLinqMovies()
{
    List<Movie> movies = new List<Movie>();

    for (int i = 0; i < 50; ++i)
    {
        movies.Add(Movie.CreateRandom());
    }

    var moviesByCategory = from movie in movies
                            group movie by movie.Category into c
                            orderby c.Key
                            select new PublicGrouping<string, Movie>(c);

    linqMovies.ItemsSource = moviesByCategory;
}

The class PublicGrouping implements the IGrouping Interface, which has this definition from metadata:

namespace System.Linq
{
  // Summary:
  //     Represents a collection of objects that have a common key.
  //
  // Type parameters:
  //   TKey:
  //     The type of the key of the System.Linq.IGrouping<TKey,TElement>.
  //
  //   TElement:
  //     The type of the values in the System.Linq.IGrouping<TKey,TElement>.
  public interface IGrouping<TKey, TElement> : IEnumerable<TElement>, IEnumerable
  {
    // Summary:
    //     Gets the key of the System.Linq.IGrouping<TKey,TElement>.
    //
    // Returns:
    //     The key of the System.Linq.IGrouping<TKey,TElement>.
    TKey Key { get; }
  }
}

An IEnumerable collection of PublicGrouping items is the output from the previous LINQ query:

var moviesByCategory = from movie in movies
                        group movie by movie.Category into c
                        orderby c.Key
                        select new PublicGrouping<string, Movie>(c);

The PublicGrouping class is generic: Here is the class declaration and constructor:

public class PublicGrouping<TKey, TElement> : IGrouping<TKey, TElement>

public PublicGrouping(IGrouping<TKey, TElement> internalGrouping)
{
    _internalGrouping = internalGrouping;
}

The LINQ query obtains the Enumerator from the LINQ Query, in this case the ‘c’ variable, which is defined as Category. Use the PublicGrouping class as a basis for your usage of the LongListSelector control.

Given the correctly formatted data structure, the LongListSelector class renders the UI in a grouped format. If you click a group item, it displays a menu of available groups. Figure 6-15 shows the linq PivotItem in action, with the screenshot on the right showing the results of clicking the group item. Select a new group item like Comedy to jump to the portion of the list containing the Comedy movies.

images

Figure 6-15. LongListSelector using LINQ

LongListSelector via Code

The “code PivotItem” in the PhoneToolkit project LongListSelectorSample.xaml page implements an additional feature with the LongListSelector control. The GroupFooterTemplate is modified to include a Button to display “more” of a particular category. Here is the XAML for the code PivotItem LongListSelector control:

<toolkit:LongListSelector x:Name="codeMovies" Background="Transparent"
            ItemsSource="{StaticResource movies}"
            ListHeaderTemplate="{StaticResource movieListHeader}"
            GroupHeaderTemplate="{StaticResource movieGroupHeader}"
            GroupItemTemplate="{StaticResource groupItemHeader}"
            ItemTemplate="{StaticResource movieItemTemplate}">
  <toolkit:LongListSelector.GroupFooterTemplate>
    <DataTemplate>
      <local:CommandButton DataContext="{Binding}" Content="{Binding GetMore}"
        Command="{StaticResource moreCommand}" CommandParameter="{Binding}"/>
    </DataTemplate>
  </toolkit:LongListSelector.GroupFooterTemplate>
</toolkit:LongListSelector>

Notice that the GroupFooterTemplate includes a DataTemplate with a CommandButton class instance, which is included in the sample code. The LongListSelector implementation on the code PivotItem does not use LINQ to generate the grouped item list. It binds to a StaticResource defined on the page named moreCommand, which is a class located in the MoviesByCategory.cs class file in the Data folder. More on that in a bit.

Also notice that ItemsSource data binds to a StaticResource named movies, which points to a class named MoviesByCategory located in the Data folder. The MoviesByCategory class is fairly simple. It obtains the Categories, and then randomly generates a set of fake objects for each Category using the MoviesInCategory class. It demonstrates how to create properly formatted groups in code and can provide a useful starter example.

The MoreCommand class implements the ICommand interface. The ICommand.Execute method adds additional random objects to the currently selected group that is passed in via a parameter. For a real implementation, some tracking is necessary to identify which records are already displayed so that the correct next set of records is retrieved, if present. Figure 6-16 shows the UI with the More Command button.

images

Figure 6-16. LongListSelector with Command button displayed.

The last PivotItem, named buddies, also generates a collection in code. It randomly generates a list of people names via the AllPeople class, sorts, them, and then generates groups by the alphabet and related group of names starting with each letter in the PeopleByFirstName class. The GroupItemTemplate in this case is the alphabet as shown in Figure 6-17.

images

Figure 6-17. LongListSelector with alphabetized list

In this section, we performed an in-depth review of the LongListSelector control, because it provides rich and very much needed functionality for Windows Phone applications that display lists of data. This section concludes the review of the Silverlight for Windows Phone toolkit except for the transition animations, which we cover as part of the next section.

Creating Transitions and Interactivity

In previous sections, we used the NavigateToPageAction behavior to navigate from one page to another. In this section, I focus on showing how to create more interactivity with Expression Blend for page transitions, status changes, and orientation changes. We start by investigating how to add interactivity using the Silverlight for Windows Phone Toolkit. We next focus on the Visual State Manager to create animations and transitions using a state-based management system.

Toolkit Page Transitions

The Silverlight for Windows Phone toolkit enables transitions that match the built-in native transitions available in Windows Phone, allowing your application to have the same look and feel without a lot of work. It is very simple to make the transitions available within an application, which is demonstrated in the Chapter 6 project named ToolkitTransitions.

Sample Project

The ToolkitTransitions project has three XAML pages. MainPage.xaml navigates to TestTransitionsPage.xaml, which data binds to a collection of sample data. The sample data is present to make the transitions more obvious than with just a mostly blank screen. When an item is selected in the TestTransitionsPage.xaml’s sampleDataListBox, the code in the sampleDataListBox_SelectionChanged event appends the index of the selected item to a query string and then navigates to TestTransitionsPage2.xaml. The TestTransitionsPage2.xaml page displays the full details of the selected item.

An extra page is added to the project beyond MainPage.xaml because transitions from the Silverlight for Windows Phone toolkit do not override the standard application loading and exiting page transitions, so we need another page to fully demonstrate the transitions. The page in the middle, TestTransitionsPage.xaml, will have the transitions configured on it to demonstrate the full navigating to and from page transition capabilities because it is not the default item or main page for the application.

To get started, first add a project reference to the Microsoft.Phone.Controls.Toolkit assembly. Next open the App.xaml.cs file and change this line of code in the InitializePhoneApplication() method.

RootFrame = new PhoneApplicationFrame();

to this line of code:

RootFrame = new Microsoft.Phone.Controls.TransitionFrame();

Next, add a second page named toolkittransitionpage.xaml to a pages folder. A TextBlock is added to MainPage.xaml and the NavigateToPageAction behavior is applied to the TextBlock, so that when it is touched, the toolkittransitionpage.xaml is displayed. Run the project and check to make sure the navigation from MainPage to the toolkittranstiionpage.xaml is working. It is working, but it still does not have a nice transition to the second page. Now you can add any transition within the application.

Configuring Transitions

There isn’t any Expression Blend support for visually applying transitions via behaviors. You edit the XAML to apply transitions. For each page, you can define four types of transitions:

  • NavigationInTransition: Applies when navigating to the current page, either via a forward navigation to the page or via clicking the Back button to navigate back to the page.
  • NavigationOutTransition: Applies when the current page is navigating out to another page, either via forward navigation to another page or when clicking the Back button on the current page.
  • DatePickerPage: Applies when the current page is navigating to a DatePicker control page.
  • TimePickerPage: Applies when the current page is navigating to a TimePicker control page.

To add a transition in XAML, add a namespace reference to the toolkit:

xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Images
Microsoft.Phone.Controls.Toolkit"

Next, type <toolkit:TransitionService to bring up IntelliSense to show the five available transition types. Select NavigationInTransition and then close the tag to generate the ending element </toolkit:TransitionService.NavigationInTransition>. A warning appears: “Property ‘NavigationInTransition’ does not have a value,” and blue squiggly lines appear as shown in Figure 6-18.

images

Figure 6-18. XAML Intellisense for NavigationInTransition

The only option that displays is toolkit:Navigation:InTransition. Within that element you can add the following two additional elements:

  • NavigationInTransition.Backward
  • NavigationInTransition.Forward

Within the .Backward and .Forward transitions, you can configure one of five options, as shown in Figure 6-19.

images

Figure 6-19. XAML Intellisense for actual transition types

We discuss the five possible transitions in detail in the next subsection. NavigationOutTransition also has a .Backward and .Forward option. Figure 6-20 describes the relationship for all four configurations.

images

Figure 6-20. Possible TransitionService Page navigation configurations

In Figure 6-20, all five transitions are configured on the middle page and have independent behavior depending on the relative navigation to the middle page. This is the same configuration as in the ToolkitTransitions sample project’s TestTransitionsPage.xaml page, where all five options are configured. Here is the XAML for the ToolkitTransitions sample transitions:

<toolkit:TransitionService.NavigationInTransition>
  <toolkit:NavigationInTransition>
    <toolkit:NavigationInTransition.Backward>
      <toolkit:RollTransition />
    </toolkit:NavigationInTransition.Backward>
    <toolkit:NavigationInTransition.Forward>
      <toolkit:RotateTransition  />
    </toolkit:NavigationInTransition.Forward>
  </toolkit:NavigationInTransition>
</toolkit:TransitionService.NavigationInTransition>
<toolkit:TransitionService.NavigationOutTransition>
  <toolkit:NavigationOutTransition>
    <toolkit:NavigationOutTransition.Backward>
      <toolkit:TurnstileTransition Mode="BackwardOut" />
    </toolkit:NavigationOutTransition.Backward>
    <toolkit:NavigationOutTransition.Forward>
      <toolkit:TurnstileTransition Mode="ForwardOut" />
    </toolkit:NavigationOutTransition.Forward>
  </toolkit:NavigationOutTransition>
</toolkit:TransitionService.NavigationOutTransition>

To help further explain, let’s configure transitions on the page right. Since it is a “leaf” page without any additional forward navigation actions, only two transitions need to be configured. One is for when the page is being navigated to and the other when on the page and the back button is touched and the page is navigated from. The two transitions are the forward “in” transition, NavigationInTransition.Forward, and the backward “out” transition, NavigationOutTransition.Backward. Here is the XAML:

<toolkit:TransitionService.NavigationInTransition>
  <toolkit:NavigationInTransition>
    <toolkit:NavigationInTransition.Forward>
      <toolkit:RotateTransition  />
    </toolkit:NavigationInTransition.Forward>
  </toolkit:NavigationInTransition>
</toolkit:TransitionService.NavigationInTransition>
<toolkit:TransitionService.NavigationOutTransition>
  <toolkit:NavigationOutTransition>
    <toolkit:NavigationOutTransition.Backward>
      <toolkit:TurnstileTransition Mode="BackwardOut" />
    </toolkit:NavigationOutTransition.Backward>
  </toolkit:NavigationOutTransition>
</toolkit:TransitionService.NavigationOutTransition>

Run the project in the emulator or, better yet, on a device where the transitions are more apparent. You will see that the transitions are “chained,” meaning that the NavigationOutTranstion.Forward for page TestTransitionsPage.xaml does not override the NavigationIn.Forward for page TestTransitionsPage2.xaml. Instead, the transitions are chained. Again, this is more visible on a real device than in the emulator.

Transitions in Depth

We briefly mentioned above the five possible types of transitions that can be applied in any configuration, either “in,” or “out,” Forward or Backward:

  • RollTransition
  • RotateTransition
  • SlideTransition
  • SwivelTransition
  • TurnstileTransition

All of the transitions except the RollTransition take a Mode property that can have these values:

  • BackwardIn
  • BackwardOut
  • ForwardIn
  • ForwardOut

The Mode attribute allows you to tell the transition how it should appear based on whether it is a Forward “in” transition, and so on, so that it matches the native transitions correctly.

In addition to the Backward and Forward properties, the NavigationInTransition and NavigationOutTransition objects also have two events:

  • BeginTransition
  • EndTransition

These events allow you to hook into the transition at the Begin and End portion to perform actions such as data loading, unloading, and the like. Because these are events, you can use the MVVM Light Toolkit EventToCommand Behavior to bind the transition events to Commands declared in the ViewModel for your application.

Transitions and UI Elements

Transitions can be applied to any UI Element object. In the ToolkitTransitions project, click apply transition to rectangle TextBlock in MainPage.Xaml to load the corresponding page, and click the single Application Bar button. The Rectangle object will slide down and fade in. The Rectangle is named targetRectangle and its Opacity is set to 0 in XAML:

private void ApplyTransitionAppBarBtn_Click(object sender, EventArgs e)
{
  RotateTransition rotateTransition =
    new RotateTransition { Mode = RotateTransitionMode.In180Clockwise};

  ITransition transition = rotateTransition.GetTransition(targetRectangle);

  transition.Completed +=
    (s, eventarg) => { transition.Stop(); targetRectangle.Opacity = 1; };

  transition.Begin();
}

You can of course simply create a Storyboard using Expression Blend and apply it to the Rectangle as well, but this section demonstrates how to leverage the existing animations available in the Silverlight for Windows Phone toolkit. The next section describes how to create a transition based on a custom Storyboard object.

Custom Transitions

In this section I describe how to create a new transition class that leverages a custom Storyboard object. The steps are to implement the ITransition Interface with a custom class and implement another class that inherits from the TransitionElement base class. In the Chapter 6 Solution ToolkitTransitions project’s MainPage.xaml, there is a TextBlock titled “custom transition” that navigates to the CustomTransitionPage.xaml page. This page demonstrates a custom Transition.

The first step is to create a custom Storyboard animation that applies to the entire page named CustomPageTransitionStoryboard in Expression Blend. The CustomPageTransitionStoryboard uses translation to move the Page content from off-screen lower left sliding diagonally into place. The Storyboard is moved into the App.Resources section of App.xaml to make it globally available throughout the application.

Next create a class named TheTransition that implements ITransition, as shown in Listing 6-6.

Listing 6-6. TheTransition Class

public class TheTransition : ITransition
{
  private Storyboard _storyboard;

  public TheTransition(Storyboard storyBoard)
  {
    _storyboard = storyBoard;
  }

  public void Begin()
  {
    _storyboard.Begin();
  }

  public event EventHandler Completed;

  public ClockState GetCurrentState()
  {
    return _storyboard.GetCurrentState();
  }

  public TimeSpan GetCurrentTime()
  {
    return _storyboard.GetCurrentTime();
  }

  public void Pause()
  {
    _storyboard.Pause();
  }

  public void Resume()
  {
    _storyboard.Resume();
  }

  public void Seek(TimeSpan offset)
  {
    _storyboard.Seek(offset);
  }

  public void SeekAlignedToLastTick(TimeSpan offset)
  {
    _storyboard.SeekAlignedToLastTick(offset);
  }

  public void SkipToFill()
  {
    _storyboard.SkipToFill();
  }
  public void Stop()
  {
    _storyboard.Stop();
  }
}

It is a pretty simple class that essentially wraps the Storyboard object. The class that is actually added in XAML is named MyTransition, and is shown in Listing 6-7.

Listing 6-7. MyTransition Class

public class MyTransition : TransitionElement
{
  public override ITransition GetTransition(UIElement element)
  {
    Storyboard myStoryboard = App.Current.Resources["CustomPageTransitionStoryboard"] as
Storyboard;

    Storyboard.SetTarget(myStoryboard, element);

    return new TheTransition(myStoryboard);
  }

Notice how the MyTransition class obtains the Storyboard via the App.Current.Resources collection. In the CustomTransitionPage.xaml, a namespace named thisPage is added, as is the custom transition:

xmlns:thisPage="clr-namespace:ToolkitTransitions.pages"

<toolkit:TransitionService.NavigationInTransition>
  <toolkit:NavigationInTransition>
    <toolkit:NavigationInTransition.Forward>
      <thisPage:MyTransition/>
    </toolkit:NavigationInTransition.Forward>
  </toolkit:NavigationInTransition>
</toolkit:TransitionService.NavigationInTransition>

Run the toolkitTransitions project to see the custom transition in action. It would make sense to add a Mode parameter to the MyTransitions class and then apply a custom Storyboard, depending on whether it is Backward, Forward, “in,” or “out,” but this sample demonstrates how to get started if you wish to create a custom transition.

This concludes coverage of the Silverlight for Windows Phone Toolkit and how to create interactivity for page transitions. The next section demonstrates how to use the Visual State Manager to provide interactivity within a page.

Visual State Manager

The Visual State Manager (VSM) is a tool available within Expression Blend. It allows the developer / designer to visually define Visual State Groups that represent UI state for controls in that state, represented as a Storyboard. The best way to learn how to work with the VSM is by demonstration, most of which will be in Expression Blend.

Orientation Aware

This section demonstrates how to create an Orientation-aware application using the VSM. A new project named VSMVideoPlayer is added to the Chapter 6 solution based on the MVVMLight (WP71 Application project template. The project is customized to be a basic video player that can play a list of videos from a recent vacation— videos without ownership rights issues that I can use for this example.

A Grid containing a MediaElement control named mediaPlayer is added to the ContentPanel Grid. Below the Grid containing the MediaElement, a ListBox named videosListBox is added and the Application Bar is enabled. The Application Bar is wired up to provide video controls for the mediaPlayer MediaElement.

A Model named Video is added to the Model folder and a collection of Video objects is added to the MainViewModel class. Finally, three videos are added to a folder named Content and added to the project. Figure 6-21 shows the VSMVideoPlayer project in Expression Blend with the States tab opened.

images

Figure 6-21. VSMVideoPlayer and States tab

The videosListBox is data bound to the Videos collection on the MainViewModel. The Grid containing the MediaElement is data bound to the SelectedItem of the videosListBox. The Source property for the mediaPlayer MediaElement is data bound to the Url property of the SelectedItem Video object. Run the project, and when you select a video in the videosListBox it plays in the MediaElement. Listing 6-8 shows the XAML for the Content Grid and Application Bar from MainPage.xaml.

Listing 6-8. Content Grid and Application Bar Code

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
      <StackPanel d:LayoutOverrides="Width">
        <Grid Height="213" DataContext="{Binding SelectedItem, ElementName=videosListBox}">
          <MediaElement Source="{Binding Url}" Margin="5,9,7,-9" Name="mediaPlayer"
MediaFailed="mediaPlayer_MediaFailed" Stretch="UniformToFill" />
        </Grid>
        <ListBox x:Name="videosListBox" ItemsSource="{Binding Main.Videos}"
         Margin="12,24,0,0" ItemTemplate="{StaticResource VideoDataTemplate}" />
      </StackPanel>
    </Grid>
  </Grid>
  <phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
      <shell:ApplicationBarIconButton x:Name="rewindAppBarBtn"
       IconUri="/icons/appbar.transport.rew.rest.png" Text="rewind"
       Click="rewindAppBarBtn_Click"/>
      <shell:ApplicationBarIconButton x:Name="stopAppBarBtn"
       IconUri="/icons/appbar.transport.pause.rest.png" Text="pause"
       Click="stopAppBarBtn_Click"/>
      <shell:ApplicationBarIconButton x:Name="playAppBarBtn"
       IconUri="/icons/appbar.transport.play.rest.png" Text="play"
       Click="playAppBarBtn_Click"/>
      <shell:ApplicationBarIconButton x:Name="ffAppBarBtn"
       IconUri="/icons/appbar.transport.ff.rest.png" Text="fastforward"
        Click="ffAppBarBtn_Click"/>
    </shell:ApplicationBar>
  </phone:PhoneApplicationPage.ApplicationBar>

Listing 6-9 shows the MainPage.xaml.cs code-behind file where the event handlers control the mediaPlayer MediaElement.

Listing 6-9. MainPage.xaml.cs Code File

using Microsoft.Phone.Controls;
using System;
using System.Windows;

namespace VSMVideoPlayer
{
  public partial class MainPage : PhoneApplicationPage
  {
    // Constructor
    public MainPage()
    {
      InitializeComponent();
    }

    private void rewindAppBarBtn_Click(object sender, System.EventArgs e)
    {
      if (mediaPlayer.CanSeek)
      {
        mediaPlayer.Position = mediaPlayer.Position - new TimeSpan(0, 0, 5);
        mediaPlayer.Play();
      }
    }

    private void stopAppBarBtn_Click(object sender, System.EventArgs e)
    {
      mediaPlayer.Pause();
    }

    private void playAppBarBtn_Click(object sender, System.EventArgs e)
    {

      mediaPlayer.Play();
    }

    private void ffAppBarBtn_Click(object sender, System.EventArgs e)
    {

      if (mediaPlayer.CanSeek)
      {
        mediaPlayer.Position = mediaPlayer.Position + new TimeSpan(0, 0, 5);
        mediaPlayer.Play();
      }
    }

    private void mediaPlayer_MediaFailed(object sender,
      System.Windows.ExceptionRoutedEventArgs e)
    {
      MessageBox.Show("Media Failed: " + e.ErrorException.Message);
    }
  }
}

The last item to highlight is that the SupportedOrientations attribute on the PhoneApplicationPage element in XAML is changed from Portrait to PortraitOrLandscape. This allows the page to respond to orientation changes via the PhoneApplicationPage.OrientationChanged event.

Now that everything is configured properly, let’s configure the project to support a full-screen mode for video playback in Expression Blend. With the States tab opened in Expression Blend, the Add state group button creates a Visual State Group that can contain one or more Visual States. The Turn on transition preview button dynamically shows the changes when different states are selected at design time.

Click the Add State Group button and name the Visual State Group “Orientations.” Next, create an “Add state” button to create a Visual State for PortraitUp. Create another state named LandscapeRight. The VSM should look like Figure 6-22.

images

Figure 6-22. VSM with one State Group and two Visual States

Figure 6-22 also defines the UI available in the VSM to create states as well as customize the transitions. EasingFunctions provide for a more realistic animation flow. EasingFunctions are literally functions like quadratic, cubic, bounce, elastic, and so on. In general, EasingFunctions are available in Storyboards. Developers can create custom EasingFunctions as well. Think of an EasingFunction as altering the animation speed from a straight line to a curve, where sharp bends represent an increase in speed so that animations appear to better model real-world movement with acceleration and bounce. Figure 6-23 shows the EasingFunction selector.

images

Figure 6-23. EasingFunction selector for Storyboards

The other buttons shown in Figure 6-23 are transition duration, which represents how long the animation should take in real time, regardless of EasingFunction, and a button to turn on FluidLayout. FluidLayout provides an engine that takes a look at the start state, the end state, and then creates a smooth animation between the states based on the selected EasingFunction. FluidLayout can help make animations created by non-designers look great. Now that we have the Blend functionality covered, let’s create our states based on current orientation for the sample project.

Look at Figure 6-22, and you see a state at the top called “Base.” When working in Blend previously, you may not have been aware, but this is the visual state that you were working in. “Base” represents the default or initial state. All other state modifications are referenced to the Base state.

images Tip Be sure to select the Base state if you want to make modifications that apply across all states.

In Expression Blend, switch to Landscape in the Device tab and then switch back to the States tab. With the LandscapeRight Visual State selected, select the TitlePanel Grid and set Visibility to Collapsed, and set Visibility to Collapsed for the videosListBox as well. For the Grid that contains the mediaPlayer MediaElement, set the Height property on the Grid to 480px. The Objects and Timeline window indicates the changes visually in the timeline keyframe editor as shown in Figure 6-24.

images

Figure 6-24. Object and Timeline window

When you run the application, it works as expected when flipped to LandscapeRight, as shown in Figure 6-25.

images

Figure 6-25. Project running in LandScapeRight orientation

If you switch the Emulator to LandscapeLeft, the UI remains as the Portrait layout, as shown in Figure 6-26.

images

Figure 6-26. Project running in LandScapeLeft orientation

You could build another state manually, but an easier option is to right-click on the LandscapeRight state, select Copy State To, and then select New State. Rename it LandscapeLeft, and that is all that’s required to have both Landscape Orientation values display correctly.

The Visual State Manager provides a powerful tool to customize transitions and animations via a state-based mechanism. The next section covers the Microsoft Advertising SDK, which supports advertising revenue for both Silverlight and XNA Framework applications.

The RichTextBox Control

Now available in Windows Phone OS 7.1 SDK is the RichTextBox control. This is a read-only control that you can use to render Rich Text Format (RTF) content. The control does not accept user input or edits, but you can update the content programmatically or via data binding.

The RichTextBox control supports formatted text, inline images, and hyperlinks. To put this into context, Windows ships with WordPad, which is a feature-rich text editor, built on the RTF format.

The RichTextBox control supports the FlowDirect property available in Silverlight 4 for localization. This means that text can flow RightToLeft or LeftToRight as needed to support the desired language. The RichTextBox control supports sub-elements for content formatting including the following:

  • Paragraph: Starts a new text paragraph in the content.
  • Run: Represents a discrete section of formatted or unformatted text.
  • HyperLink: Web link.
  • InlineUIContainer: Can be used to render an Image element.
  • LineBreak: Causes a new line within the content.
  • Span: Groups other Inline descendent content elements.
  • Bold, Italic, Underline: Specify additional text formatting within the content.

The RichTextBox control supports a block-based content model where a block is a collection of Paragraph elements. A Paragraph element can contain elements that derive from inline such as Run, Span, text formatting like Bold, Hyperlink and InLineUIContainer. Here is an example that displays some text with an inline image:

<RichTextBox>
    <Paragraph>
        Displaying text with inline image
        <InlineUIContainer>
            <Image Source="./Sports.jpg" Height="75" Width="75" />
        </InlineUIContainer>            
    </Paragraph>
</RichTextBox>

The RichTextBox control is great when you have RTF content to render, such as help documentation. In general, Microsoft recommends using the RichTextBox control instead of the WebBrowser control if possible since it presents native rendering.

The Microsoft Advertising SDK

The Microsoft Advertising SDK now ships directly in the Windows Phone OS 7.1 SDK. It provides mobile advertising support for Windows Phone. You are not required to use Microsoft’s advertising SDK and associated advertising network. If you have an existing advertising network or an in-house creative and sales force, you can continue to use those resources for advertising revenue.

If you are new to mobile applications or simply want a fast and easy mobile advertising solution, the Microsoft Advertising SDK can get you up and running in about 20 minutes and about 50 lines of code. This is explained next.

Getting Started

While Windows Phone is a new platform, Microsoft’s advertising solution is not. With the world’s first real-time bidding Mobile Ad Exchange with targeted advertising, multiple purchase models and leading resellers including Microsoft’s sales force, you can tap into the existing adCenter marketplace by simply adding the control to your application.

Register on pubCenter at the https://pubcenter.microsoft.com link to set up your advertising account. You create a software property where ads are displayed, create a mobile ad unit, and set up a targeting category. After registering in pubCenter, you will have an application ID and an Ad unit ID that you can use to request ads from the Microsoft Advertising Servers.

You can also try out the mobile advertising control and receive test ads without signing up, but you will soon want to take advantage of Microsoft’s contextual advertising platform with over 20,000 advertisers and resellers to get paid.

Adding the Advertising Control

You can drag the control onto your Silverlight UI from the Toolbox window to add advertising to a Silverlight page. You can receive test ads using the test Application ID and test Ad Unit ID to get a feel for how the advertising works. There are three types of ads you can receive in your application, summarized in Table 6-2.

Images

Even though there is support for a 300 × 50 ad size, it is recommended to always set the size of the AdControl instance to 480px wide by 80px height. For the X-Large Image Banner, the control will automatically size to display the full 300 × 50, centered at the center of the AdControl instance.

The AdControl also has a Location property to receive targeted ads by supplying a Latitude and Longitude value as a Location type. You can use a GeoCoordinateWatcher instance to collect location data as shown in Chapter 3 on input. The AdControl class supports the following events:

  • AdControlError: Fires when there is a problem receiving or displaying an advertisement.
  • AdEngaged: Fires when the user clicks the ad and the action dialog appears.
  • AdDisengaged: Fires when the user clicks any button in the action dialog to dismiss the dialog.
  • NewAd: Fires when a new advertisement is displayed. Can use to animate the AdControl to catch the user’s attention.
AdControl in Silverlight

In this section, the AdControl is leveraged in a sample project named AdvertisingSLSample. For the Silverlight sample project, start with the Windows Phone Data bound Application project template so that you have some data to display. Next, simply drag and drop the control at the bottom of the MainPage.xaml page, adjusting its settings so it just fits across the bottom of the screen.

It is recommended to place the AdControl at the top or bottom of a page. For the Panorama or Pivot controls, you can place the AdControl instance inside or over top of the Panorama or Pivot control. If placed inside either control, the ad will only display in a single pane. You can choose to have a unique ad on each pane if desired. To have the AdControl always visible, even when scrolling, place the instance outside of the Panorama control on top.

images Tip It is not recommended to change the parent control of an AdControl instance at run time.

As you can see, the control is simple to configure. Here is the XAML for the configured ad control in the Silverlight AdvertisingSample project:

<my:AdControl  Height="80" HorizontalAlignment="Left" Margin="-16,527,0,0" Name="adControl1"
  VerticalAlignment="Top" Width="480"  ApplicationId="test_client" AdUnitId="Image480_80"
  RenderTransformOrigin="0.5,0.5"  IsAutoCollapseEnabled="True" >
  <my:AdControl.RenderTransform>
    <CompositeTransform/>
  </my:AdControl.RenderTransform>
</my:AdControl>

Figure 6-27 shows the control in action, using the test configuration in the previous XAML.

images

Figure 6-27. Advertising in a Silverlight application

We want to grab the user’s attention when a new ad is available. One possible way to do that is to create an animation that indicates something happened, such as to make the control increase in size slightly and then return to its original size via a Storyboard animation.

In Expression Blend, create the AnimateAdControl Storyboard resource by clicking the New button in the Objects and Timeline window. Add a keyframe at zero time and then slide the yellow timeline over to 100 milliseconds. Click on the Transform section in the Properties window for the AdControl and switch to the Scale transform tab. Change X and Y to 1.1 from 1. Slide the yellow timeline bar to 200 milliseconds, right-click the first keyframe at zero seconds and then right-click and select Paste.

Switch back to Visual Studio and create an event handler for the NewAd event on the adControl1 instance and then add this code:

AnimateAdControl.Begin();

For a multi-page application, it is recommended to unload the control when a page is unloaded to minimize resource consumption. This can be done in the PhoneApplicationPage.Unloaded event handler. Simply set the AdControl instance to null.

Although this chapter is focused on Silverlight UX, the next subsection covers how to add advertising support to a Windows Phone XNA Framework-based game.

AdControl in the XNA Framework

Adding support for the AdControl in a Windows Phone XNA Framework application is just as easy. Start by adding a reference to the Microsoft.Advertising.Mobile.Xna.dll using the Add Reference functionality.

Next add a using Microsoft.Advertising.Mobile.Xna; statement to game1.cs. Next add a DrawableAd object as a private member of the Game1 class:

DrawableAd bannerAd;

In the constructor for the Game1 class, add this line of code to initialize and add the static Current AdGameContent to the Components list for the game:

AdGameComponent.Initialize(this, "test_client");
Components.Add(AdGameComponent.Current);

Notice that the same application Id, test_client, is used as before for the testing functionality. Finally, we want to load an ad, which is done in the LoadContent() method where all game assets are generally loaded for most games:

bannerAd = AdGameComponent.Current.CreateAd(
"Image300_50",
  new Rectangle(
    10, 390, GraphicsDevice.Viewport.Bounds.Width, 120), true);

That is all that is required to add advertising to a Windows Phone XNA Framework game. Figure 6-28 shows the results.

images

Figure 6-28. Test advertising in an XNA application

As you can see from this section, adding support for Advertising revenue to your applications is very straight-forward. For a free or trial application, advertising revenue is a great way to get paid for your hard work.

Conclusion

This chapter began with an overview of the Model-View-ViewModel pattern, which is the architecture to use in XAML-based applications. We next moved on to covering one of the third-party MVVM frameworks that improve upon the built in support for MVVM when building property architected applications.

This was followed by a discussion of the Silverlight for Windows Phone toolkit, including coverage on all of the major controls including detailed coverage on the LongListSelector control as well as on page transitions. We then explored how to use the Visual State Manager in your applications to greatly simplify UI development.

The coverage of advertising concludes Chapter 6 on advanced user interface development. The next chapter covers advanced programming model topics that allow you to more deeply integrate with Windows Phone, leverage media, as well as other topics.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset