Follow @PDSAInc November 2010 - Posts - Paul Sheriff's Blog for the Real World

Paul Sheriff's Blog for the Real World

This blog is to share my tips and tricks garnered over 25+ years in the IT industry

Paul's Favorites

November 2010 - Posts

Using Isolated Storage on Windows Phone

On a Windows Phone application, or actually in any application, you will sometimes want to keep information around from one session to another. On a Windows Phone it is sometimes especially important as it is a little difficult to type on these small devices. If you need the user to login, fill in their name and email, or enter other information each time they use the application, then you might want to store that data on the phone and retrieve it each time they come back in to the application. Isolated storage is the place where you are can store this information for your application.

This article assumes that you have VS.NET 2010 and the Windows Phone tools installed along with it. The Windows Phone tools must be downloaded separately and installed with VS.NET 2010. You may also download the free VS.NET 2010 Express for Windows Phone developer environment.

Store Data On Your Phone 

Figure 1: Store Data on your Phone

The LastUser Class

For this example, I will be taking the data from the screen and putting it into a class called “LastUser”. This class is shown in the code below:

public class LastUser : INotifyPropertyChanged {
  #region INotifyPropertyChanged Event
  public event PropertyChangedEventHandler PropertyChanged;

  protected void RaisePropertyChanged(string propertyName)  {
    if (PropertyChanged != null)
      PropertyChanged(this,
        new PropertyChangedEventArgs(propertyName));
  }

  private string mFirstName = string.Empty;
  private string mLastName = string.Empty;
  private string mEmail = string.Empty;

  public string FirstName  {
    get { return mFirstName; }
    set {
      mFirstName = value;
      RaisePropertyChanged("FirstName");
    }
  }

  public string LastName  {
    get { return mLastName; }
    set {
      mLastName = value;
      RaisePropertyChanged("LastName");
    }
  }

  public string Email  {
    get { return mEmail; }
    set {
      mEmail = value;
      RaisePropertyChanged("Email");
    }
  }
}

This class implements the INotifyPropertyChanged interface as I want the user interface to automatically update when I load this data into an instance of this class.

Hooking up this Class to the Page

In the sample page (shown in Figure 1) you will need to bind the LastUser class to the DataContext of this page. In the page class create an instance of the LastUser class like the shown here:

private LastUser _User = new LastUser();

In the Loaded event procedure for the page you will set the DataContext of the page to the _User variable as shown here:

private void PhoneApplicationPage_Loaded(object sender,
 RoutedEventArgs e)
{
  this.DataContext = _User;
}

The first time in, this user class will be completely blank. All of the text boxes on the XAML page that are bound to each of the properties in the LastUser class will also be blank. Later you will add two additional lines of code to the Loaded event procedure to retrieve user data stored, but first let’s learn to store a user into isolated storage.

Storing Data into Isolated Storage

After the user has filled in some information on this phone page and click the Submit button you will save the data from the LastUser class into isolated storage. After you save the user data you will then redirect back to the main page.

private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
  SaveUser();
  NavigationService.Navigate(new Uri("/MainPage.xaml",
     UriKind.Relative));
}

The SaveUser Method

The SaveUser method will take the data from the _User variable and serialize it as an XML string so it can be stored into isolated storage. The process for serializing an object into XML is the same on Windows Phone as in any other .NET application.

You create an instance of the XmlSerializer class passing in the Type of LastUser. You call the Serialize method on the XmlSerializer object to convert all the properties of the LastUser object to Xml and store the result into a MemoryStream. Next, use a StreamReader class to read the MemoryStream and convert that memory stream into an XML string (stored in the “xml” variable). Finally you use the IsolatedStorageSettings.ApplicationSettings to create a new key (in this case a constant USER_KEY defined as “LastUser”) and store the data in the “xml” variable into storage.

private void SaveUser()
{
  string xml = string.Empty;

  using (MemoryStream ms = new MemoryStream())
  {
    XmlSerializer serializer = new XmlSerializer(typeof(LastUser));
    serializer.Serialize(ms, _User);
    ms.Position = 0;

    using (StreamReader reader = new StreamReader(ms))
      xml = reader.ReadToEnd();
  }

  // Store user in Isolated Storage
  IsolatedStorageSettings.ApplicationSettings[USER_KEY] = xml;
}

NOTE: The USER_KEY is a constant defined in the page class as shown below:

private const string USER_KEY = "LastUser";

Checking if Data Exists

The next time you come into this phone page, after storing the user data, you will want to retrieve that data and fill it into the _User variable. You will now add two more lines to the Loaded event procedure to detect if the data has been stored into isolated storage with the key name of USER_KEY (“LastUser”). For this you use the ApplicationSettings.Contains method to check to see if the USER_KEY key name is in isolated storage. If the key exists, you will call the GetUser method to retrieve the data.

private void PhoneApplicationPage_Loaded(object sender,
  RoutedEventArgs e)
{
  if (IsolatedStorageSettings.ApplicationSettings.
        Contains(USER_KEY))
    GetUser();

  this.DataContext = _User;
}

The GetUser Method

The GetUser method is just the opposite of the SaveUser method. This method will use the IsolatedStorageSettings.ApplicationSettings to retrieve the data as a string from the key USER_KEY. You now need to take the string and convert it into a stream. I am using a MemoryStream object in the code below and calling the GetBytes method on the Unicode object to convert the string into a byte array. Once in the byte array, you create an instance of the XmlSerializer and use the Deserialize method to convert the byte array in the memory stream back into an instance of the LastUser class.

private void GetUser()
{
  string xml;

  xml =
  IsolatedStorageSettings.ApplicationSettings[USER_KEY].ToString();
  using (MemoryStream ms = new
          MemoryStream(Encoding.Unicode.GetBytes(xml)))
  {
    XmlSerializer serializer = new XmlSerializer(typeof(LastUser));
    _User = (LastUser)serializer.Deserialize(ms);
  }
}

After you have deserialized the data from isolated storage and placed it into the _User variable, the data will automatically display in the bound text boxes on the page.

Summary

You have now seen how easy it is to store data from one invocation of your phone app to another. I added two pages within the phone application so you can see that the data does indeed stick around in between calls to the pages. You need to do this because on the emulator you can’t save the data in isolated storage because it is wiped out each time you stop and start your project in VS.NET. However, on a real Windows Phone, the storage will stay around in between each instance of running the application.

NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "Using Isolated Storage on Windows Phone" from the drop-down.

Good Luck with your Coding,
Paul Sheriff

** SPECIAL OFFER FOR MY BLOG READERS **
Visit http://www.pdsa.com/Event/Blog for a free video on Silverlight entitled Silverlight XAML for the Complete Novice - Part 1.

 

Change Templates When Orientation Changes on Windows Phone

Developing for Windows Phone does require you to think a little differently. For example on a regular computer you only have to worry about the screen orientation being in portrait mode. However, on a phone the user can turn the phone sideways and even upside down. If the user switches from portrait orientation (Figure 1) to landscape orientation (Figure 2) you end up with more room to display your data horizontally. You can take advantage of this extra room by switching between two different XAML templates when the phone orientation changes.

This article assumes that you have VS.NET 2010 and the Windows Phone tools installed along with it. The Windows Phone tools must be downloaded separately and installed with VS.NET 2010. You may also download the free VS.NET 2010 Express for Windows Phone developer environment.

 

Figure 1: Windows Phone in Portrait Mode

Figure 2: Windows Phone in Landscape Mode

The Product Class

For this example, I will be using a Product class with three properties, and a collection class of Product objects using the Generic List class. You can create this sample by creating a Product class as shown in the following code:

public class Product {

  public Product() { }

  public Product(string name, decimal price, string imageUri)
  {
    this.ProductName = name;
    this.Price = price;
    this.ImageUri = imageUri;
  }

  public string ProductName { get; set; }
  public decimal Price { get; set; }
  public string ImageUri { get; set; }
}

Create a collection class that initializes a property called DataCollection with some sample data as shown in the code below:

public class Products : List<Product> {
  public Products() {
    BuildCollection();
  }

  public List<Product> DataCollection { get; set; }

  List<Product> BuildCollection() {
    DataCollection = new List<Product>();

    DataCollection.Add(new Product(
      "Haystack Code Generator for .NET", 799, "Haystack.jpg”));
    DataCollection.Add(new Product(
      "Fundamentals of N-Tier eBook", 19.95, "NTier.jpg"));

    // MORE PRODUCTS HERE – REMOVED FOR BREVITY

    return DataCollection;
  }
}

Value Converter

As you can see in Figure 1 and Figure 2, the Price is shown in a currency format. Unfortunately in the Windows Phone version of Silverlight, the StringFormat attribute on the Binding class is absent. So you will need to create a value converter for displaying a value in a currency format. Create the following class that implements the IValueConverter interface. In the Convert method you will then take the “value” parameter passed in, convert it to a decimal value, then return that value as a string formatted with the ”c” option in the ToString() method.

public class PriceConverter : IValueConverter
{
  public object Convert(object value, Type targetType,
     object parameter, System.Globalization.CultureInfo culture)
  {
    decimal price;

    price = (decimal)value;

    return price.ToString("c");
  }

  public object ConvertBack(object value, Type targetType,
    object parameter, System.Globalization.CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

Create Classes in XAML

Now that you have the Product class, the Products class and the PriceConverter classes created, you now need to create an instance of the Products collection class and the PriceConverter class in your XAML. In your MainPage.xaml, add an xml namespace to the name of your project as shown here:

xmlns:local="clr-namespace:WPListBoxImage"

Next you create an instance of the Products and PriceConverter class in XAML. The constructor for the Products class will create the initial collection of product objects.

<phone:PhoneApplicationPage.Resources>
  <local:Products x:Key="productCollection" />
  <local:PriceConverter x:Key="priceConvert" />
</phone:PhoneApplicationPage.Resources>

These two classes may now be used within the rest of your XAML by referencing them by their Key name.

Create Portrait Template

Also in the Resources section of your phone application page is where you will create two different XAML templates for displaying data in a list box. The first template you will create is for when the phone is in portrait mode.

<DataTemplate x:Key="listPortrait">
  <StackPanel>
    <TextBlock Margin="8"
                Width="250"
                TextWrapping="Wrap"
                VerticalAlignment="Top"
                HorizontalAlignment="Left"
                Text="{Binding Path=ProductName}" />
    <TextBlock Width="100"
                Margin="8,0,8,8"
                VerticalAlignment="Top"
                HorizontalAlignment="Left"
                Text="{Binding
                   Path=Price,
                   Converter={StaticResource priceConvert}}" />
  </StackPanel>
</DataTemplate>

This <DataTemplate> with the key name “listPortrait” has a <StackPanel> control to display the text for the product name and the price stacked vertically on top of each other. Notice in the text block that displays the actual price the use of the value converter class you created earlier. You bind to the Price property using the Path attribute of the Binding class. However, before the Binding class displays the Price it passes it through the converter. The Convert() method of the PriceConverter class formats the price and returns it as a string to be displayed in the TextBlock.

Create Landscape Template

The next XAML template you create in the Resources section of the phone application page has a key name of “listLandscape”. The XAML for this <DataTemplate> is shown below.

<DataTemplate x:Key="listLandscape">
  <StackPanel Orientation="Horizontal">
    <Image Margin="8"
            VerticalAlignment="Top"
            Source="{Binding Path=ImageUri}"
            Width="100"
            Height="100" />
    <StackPanel>
      <TextBlock Margin="8"
                  Width="400"
                  TextWrapping="Wrap"
                  VerticalAlignment="Top"
                  HorizontalAlignment="Left"
                  Text="{Binding Path=ProductName}" />
      <TextBlock Width="100"
                  Margin="8,0,8,8"
                  VerticalAlignment="Top"
                  HorizontalAlignment="Left"
                  Text="{Binding
                     Path=Price,
                     Converter={StaticResource priceConvert}}" />
    </StackPanel>
  </StackPanel>
</DataTemplate>

In this template you add a <StackPanel> control around the <StackPanel> you created in the “listPortrait” template. Set the Orientation property of this stack panel to be horizontal. Then you place an <Image> control that will display the image for the product before the product name and price. Notice also that the Width property of the product name TextBlock control has also been increased so the product name may not have to wrap as it might have to in portrait mode.

The List Box Definition

The List Box control used for this phone application page is fairly simple. You simply set the ItemsSource property and the ItemsTemplate property to the appropriate static resources defined in the Resources section of this page.

<ListBox x:Name="lstData"
     ItemsSource="{Binding
                    Source={StaticResource productCollection},
                    Path=DataCollection}"
     ItemsTemplate="{StaticResource listPortrait}" />

The ItemsTemplate is set to listPortrait to start out because in the Page definition the Orientation attribute is set to “Portrait”.

OrientationChanged Event

Now all you need to do is to hook up an event so when the user changes the orientation of the phone you can switch between the two templates you have created. There are just a couple of things you have to do to prepare the page. First you must set the attribute in the PhoneApplicationPage called SupportedOrientation to the value “PortraitOrLandscape”. This attribute will allow the OrientationChanged event to fire.

SupportedOrientations="PortraitOrLandscape"

So, next you add the OrientationChanged event to the Page. Type in OrientationChanged in the page XAML and have it build the event procedure for you.

OrientationChanged="PhoneApplicationPage_OrientationChanged"

In the OrientationChanged event procedure you will write the following code:

private void PhoneApplicationPage_OrientationChanged(
   object sender, OrientationChangedEventArgs e)
{
  if (e.Orientation.ToString().Contains("Portrait"))
    lstData.ItemTemplate =
      (DataTemplate)this.Resources["listPortrait"];
  else
    lstData.ItemTemplate =
      (DataTemplate)this.Resources["listLandscape"];
}

When the OrientationChanged event is fired the new orientation is passed in the “e” argument as the Orientation property. This property can be one of 7 enumeration values; None, Portrait, PortraitUp, PortraitDown, Landscape, LandscapeRight, and LandscapeLeft. Since we are just concerned with Portrait or Landscape, you will just convert the Orientation property to a string and see if the value contains “Portrait”. If it contains “Portrait”, then you find the “listPortrait” resource, cast it as a DataTemplate and assign it to the list box’s ItemTemplate property. This causes the list box to redraw itself using the defined data template.

Summary

In this article you learned how to respond to the OrientationChanged event and switch between two pre-defined templates. Since the user can physically move the phone into almost any orientation it is useful for us to take advantage of the extra screen width. By creating different XAML templates and responding to just one event you can give your users a great experience when using your application.

NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "Change Orientation/Templates on Windows Phone" from the drop-down.

Good Luck with your Coding,
Paul Sheriff

** SPECIAL OFFER FOR MY BLOG READERS **
Visit http://www.pdsa.com/Event/Blog for a free videos on Silverlight entitled Silverlight XAML for the Complete Novice - Part 1.

Posted: Nov 17 2010, 12:00 PM by psheriff | with 6 comment(s)
Filed under:
More Posts