Chapter 13. Isolated Storage

In Chapter 3, you learned that Microsoft Azure provides a reliable place to store and access data but using it requires Internet access. Sometimes it's more efficient to cache frequently accessed data on the Windows Phone device itself.

Isolated storage is a place on a Windows Phone 7 device where an application can save files, configuration information, and other data. Each application is allocated its own portion of the available space, but can't access file systems used by the operating system itself, a limitation that prevents a rogue application from accessing system data and possibly corrupting it. The amount of storage that can be assigned to any single application depends on the phone's available space.

With isolated storage, a Windows Phone application can create and maintain a virtual file storage system that can contain virtual folders and files; you won't have direct access to the underlying Windows Phone file system but the isolated storage will provide you with API to work with the file system. All input and output operations can be performed only on the application's isolated storage level.

Support for isolated storage on a Windows Phone is provided by the following two namespaces, whose features are depicted by Figure 13-1.

  • Isolated File Storage: System.IO.IsolatedStorage.IsolatedStorageFile allows you to create, use, and remove directories and files in the virtual isolated storage. The files can be added and retrieved through file stream using System.IO.IsolatedStorage.IsolatedFileStream, which can also cache images, sounds, and files that are dynamically loaded from the Web. In the first demo, you will learn how use isolated storage to cache an image loaded from the Web.

  • Isolated Local Settings: System.IO.IsolatedStorage.IsolatedStorageSettings provides APIs for storing and working with key-value pairs cached in isolated storage and for storing application settings and user specific settings.

Using the Isolated Storage APIs

Figure 13.1. Using the Isolated Storage APIs

In this chapter, you will learn how to work with a phone's isolated storage. The first example will show you a technique to cache an image downloaded from the Web into the isolated storage file. The second demo will show you how to save and retrieve name and value pairs using isolated storage settings.

Working with Isolated Directory Storage

You'll begin by building an application to work with the local storage on a phone. The application, named IsolatedStorageStoreImageDemo and shown in Figure 13-2, demonstrates the basic functions available through the isolated storage APIs, including the following:

  • Retrieving application-specific isolated storage.

  • Getting isolated storage quota.

  • Saving and retrieving isolated storage files.

In this demo, when the Get Image button is clicked for the first time, the application checks to see whether there is enough space available in isolated storage. If there is, the image will be downloaded from the web site and then saved to isolated storage via isolated storage file stream. If the button is clicked again, the image will be loaded into an isolated storage file.

IsolatedStorageStoreImageDemo

Figure 13.2. IsolatedStorageStoreImageDemo

You'll build the demo in three steps. First, you'll create a new Visual Studio project. Next, you'll build the project's user interface and finish up by adding code to respond to commands from the user.

Creating the IsolatedStorageStoreImageDemo Project

To set up the IsolatedStorageStoreImageDemo project, follow the steps you've used for previous examples in this book.

  1. Open Microsoft Visual Studio 2010 Express for Windows Phone on your workstation.

  2. Create a new Windows Phone Application by selecting File

    Creating the IsolatedStorageStoreImageDemo Project
Windows Phone Application template for creating IsolatedStorageStoreImageDemo

Figure 13.3. Windows Phone Application template for creating IsolatedStorageStoreImageDemo

Coding the User Interface

You'll first code the user interface, which we've chosen to implement in XAML. Sometimes it's faster to work with XAML than with managed code, especially when you're working with a simple example, like this one, which requires only a few controls. Go to the Solution Explorer, open MainPage.xaml, and replace the XAML you find there with the code that appears in the following sections.

Selecting the UI Resources

Begin by adding the following XAML markup to MainPage.xaml to identify where the resource to build the application's main page will be found:

<phone:PhoneApplicationPage

    x:Class="IsolatedStorageStoreImageDemo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

Referencing the namespace as xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" allows you to use common Windows Phone controls such as text boxes, buttons, and list boxes to create the main page. The code snippet also includes a reference to a codebehind class (x:Class="IsolatedStorageStoreImageDemo.MainPage") that will handle the main page controls' events.

Building the Main Page and Adding Components

Next, to create the main application page and populate it with controls, add the following XAML markup to the preceding block of code, also in MainPage.xaml:

<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitleGrid is the name of the application and page title-->
        <Grid x:Name="TitleGrid" Grid.Row="0">
            <TextBlock Text="IsolatedStorageStoreImageDemo"
                       x:Name="textBlockPageTitle"
                       Style="{StaticResource PhoneTextTitle1Style}"
                       FontSize="28" />
        </Grid>

        <!--ContentGrid is empty. Place new content here-->
        <Grid x:Name="ContentGrid" Grid.Row="1">

    <Image Height="458" HorizontalAlignment="Left"
                   Margin="20,134,0,0" Name="image1" Stretch="Uniform"
                   VerticalAlignment="Top" Width="423" />
            <Button Content="Get Image" Height="70"
                    HorizontalAlignment="Left" Margin="0,598,0,0"
Name="btnGetImage" VerticalAlignment="Top"
                    Width="443" Click="btnGetImage_Click" />
            <TextBox Height="72" HorizontalAlignment="Left"
                     Margin="12,29,0,0" Name="txtImageUrl"
                     Text="http://res1.newagesolution.net/Portals/0/twitter2_icon.jpg"
                     VerticalAlignment="Top" Width="460" />
        </Grid>
    </Grid>

</phoneNavigation:PhoneApplicationPage>

Once you've loaded the XAML code for the main page, you should see the layout shown in Figure 13-4. Now it's time to use the Isolated Storage APIs to add behavior to the application.

IsolatedStorageStoreImageDemo Design view

Figure 13.4. IsolatedStorageStoreImageDemo Design view

Coding the Application

In Solution Explorer, open MainPage.xaml.cs and replace the code there with the following C# code blocks, in precisely the order in which they are presented.

Specifying the Namespaces

Begin by specifying the namespaces the application will use. System.IO.IsolatedStorage and System.IO contain the APIs needed to work with the isolated file storage.

using System;
using System.Net;
using System.Windows;
using Microsoft.Phone.Controls;
using System.Windows.Media.Imaging;
using System.IO.IsolatedStorage;
using System.IO;

namespace IsolatedStorageStoreImageDemo
{

Initializing Variables

Now add the following block of code to MainPage.xaml.cs to initialize the application's variables:

public partial class MainPage : PhoneApplicationPage
{
    private string ImageFileName = null;


    WebClient _webClient; // Used for downloading the image first time from the web site
...

Initializing the Application

Now add the application's constructor, which uses the WebClient.OpenReadCompleted event to download its target image. The code contains logic to check whether enough space is available in the phone's isolated storage to save the downloaded image. If space is available, the image gets saved; otherwise it's loaded directly into the image control.

public MainPage()
{
    InitializeComponent();

    SupportedOrientations = SupportedPageOrientation.Portrait |
        SupportedPageOrientation.Landscape;

    _webClient = new WebClient();
// Handles when the image download is completed
     _webClient.OpenReadCompleted += (s1, e1) =>
         {
             if (e1.Error == null)
            {
                 try
              {
                     bool isSpaceAvailable =
                 IsSpaceIsAvailable(e1.Result.Length);

                     if (isSpaceAvailable)
                     {
                         // Save Image file to Isolated Storage
                         using (IsolatedStorageFileStream isfs =
                 new IsolatedStorageFileStream(ImageFileName,
                                 FileMode.Create,
                 IsolatedStorageFile.GetUserStoreForApplication()))
                         {
                             long imgLen = e1.Result.Length;
                             byte[] b = new byte[imgLen];
                             e1.Result.Read(b, 0, b.Length);
                             isfs.Write(b, 0, b.Length);
                             isfs.Flush();
                         }

                         LoadImageFromIsolatedStorage(ImageFileName);
                     }
                     else
                     {
                         BitmapImage bmpImg = new BitmapImage();
                         bmpImg.SetSource(e1.Result);
                         image1.Source = bmpImg;
                     }
                }
                catch (Exception ex)
                {
                     MessageBox.Show(ex.Message);
                }
            }
        };
}

Note

In order to create a sub-directory, you must create a directory path string, such as "MyDirectory1SubDirectory1" and pass it to the CreateDirectory method. To add a file to SubDirectory1, you must create a string that combines the file name with its path, such as "MyDirectory1SubDirectory1MyFileInSubDirectory1.txt", and then use IsolatedStorageFileStream to create a file. In order to add contents to the file, use StreamWriter.

When you use the Remove method in IsolatedStorageFile, use it with caution as it will delete all directories and files. To avoid accidently deleting everything in the isolated storage, create a warning prompt window to confirm with the user that it will be OK to delete everything in the isolated storage. Another possibility is to use IsolatedStorage.DeleteFile or IsolatedStorage.DeleteDirectory to delete a specific file or directory to avoid removing all files or directories. Please refer to MSDN documentation for more information at http://msdn.microsoft.com/en-us/library/kx3852wf(VS.85).aspx.

Tip

System.IO.Path.Combine provides a great way to combine directory paths and files without worrying if the backslashes () are properly added. Also, when searching files or directories, you can use a wild card (*) when building a directory or file path.

Checking Availability of Isolated Storage Space

Now, add code for the isSpaceAvailable helper method that the application uses to determine whether there is enough space available in isolated storage to store the image.

// Check to make sure there are enough space available on the phone
       // in order to save the image that we are downloading on to the phone
       private bool IsSpaceIsAvailable(long spaceReq)
       {
            using (IsolatedStorageFile store =
IsolatedStorageFile.GetUserStoreForApplication())
  {
                long spaceAvail = store.AvailableFreeSpace;
                if (spaceReq > spaceAvail)
                {
                   return false;
                }
                return true;
            }
       }

Adding a Button Event to Retrieve the Image from Isolated Storage

When the Get Image button is clicked, it checks to see if the image exists in the isolated storage. If the image exists, the image is loaded from the isolated storage; otherwise the image is downloaded from the web site.

private void btnGetImage_Click(object sender, RoutedEventArgs e)
        {
            using (IsolatedStorageFile isf =
                IsolatedStorageFile.GetUserStoreForApplication())
            {
                bool fileExist = isf.FileExists(ImageFileName);

                if (fileExist)
                {
                    LoadImageFromIsolatedStorage(ImageFileName);
                }
                else
                {
                              if (!string.IsNullOrEmpty(txtImageUrl.Text))
                    {
                // Use Uri as image file name
                        Uri uri = new Uri(txtImageUrl.Text);
                        ImageFileName =
uri.AbsolutePath.Substring(uri.AbsolutePath.LastIndexOf('/')+1);
                        _webClient.OpenReadAsync(new Uri(txtImageUrl.Text));
                    }

                }

            }

        }

Adding a Method to Retrieve the Image from Isolated Storage

The image is streamed directly from the isolated storage into the image control.

private void LoadImageFromIsolatedStorage(string imageFileName)
        {
            // Load Image from Isolated storage
            using (IsolatedStorageFile isf =
IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (IsolatedStorageFileStream isoStream =
isf.OpenFile(imageFileName, FileMode.Open))
                {
                    BitmapImage bmpImg = new BitmapImage();
                    bmpImg.SetSource(isoStream);
                    image1.Source = bmpImg;
                }
            }
        }

    }
}

Testing the Finished Application

To test the completed application, press F5 on your keyboard and run it.

In this brief demo, you've learned to work with isolated storage files by storing a downloaded image into the isolated storage and then retrieving the image from the isolated storage. In the next demo, you will interact with the name and value dictionary of the isolated storage settings.

Working with Isolated Storage Settings

In this section, you'll build an application, named IsolatedStorageSettingsDemo, that demonstrates CRUD operations (create, read, update, and delete) of System.IO.IsolatedStorage.IsolatedStorageSettings. Figure 13-5 shows how its UI will look on a Windows Phone.

IsolatedStorageSettingsDemo

Figure 13.5. IsolatedStorageSettingsDemo

In the IsolatedStorageSettingsDemo application, when the Save button is clicked, the value in the Value text box will be added to the isolated storage settings using the key in the Key text box. Whenever new key-value pair data is added to the isolated storage settings, the key will be added to the list box of keys. When any of the keys in the list box of keys is selected, the Key text box and the Value text box will be populated with the data retrieved from the isolated storage settings. The Delete button will delete the selected key from the isolated storage settings.

To build the demo, create a new project and then add XAML markup to create a new main page and its controls. Finally, you'll add behavior to the application with C# code that makes use of isolated storage APIs to save and retrieve key-value pairs.

Creating a New Project

To create the new IsolatedStorageSettingsDemo project, open Microsoft Visual Studio 2010 Express for Windows Phone. Select File

Creating a New Project

Now you'll build the application main page.

Windows Phone Application template for creating IsolatedStorageSettingsDemo

Figure 13.6. Windows Phone Application template for creating IsolatedStorageSettingsDemo

Building the Application UI (XAML)

To create the UI for IsolatedStorageSettingsDemo, go to Solution Explorer, open MainPage.xaml, and replace XAML with the following chunks of XAML markup in the sequence shown.

Selecting the UI Resources

The following code identifies where to find the UI controls that will be used to build this main page for this application. Using the namespace xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" will allow you to add common Windows Phone controls like text boxes, buttons, and list boxes, which will be used to create the main page. Also, you are adding a reference to a codebehind class (x:Class="IsolatedStorageSettingsDemo.MainPage") that will handle the main page controls' events.

<phone:PhoneApplicationPage
    x:Class="IsolatedStorageSettingsDemo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    shell:SystemTray.IsVisible="True">

Building the Main Page and Adding Controls

Now add the various controls and layouts you need to create the UI shown in Figure 13-5.

<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!--TitleGrid is the name of the application and page title-->
    <Grid x:Name="TitleGrid" Grid.Row="0">
        <TextBlock Text="Isolated Storage Settings Demo"
                   x:Name="textBlockListTitle"
                   Style="{StaticResource PhoneTextTitle1Style}"
                   FontSize="30" />
    </Grid>

    <!--ContentGrid is empty. Place new content here-->
    <Grid x:Name="ContentGrid" Grid.Row="1">
<TextBox Height="72" HorizontalAlignment="Left"
                     Margin="172,46,0,0" Name="txtKey" Text=""
                     VerticalAlignment="Top" Width="212" />
            <Button Content="Save" Height="70"
                    HorizontalAlignment="Left" Margin="78,228,0,0"
                    Name="btnSave" VerticalAlignment="Top" Width="160"
                    Click="btnSave_Click" />

        <ListBox Height="301" HorizontalAlignment="Left" Margin="94,392,0,0"
                     Name="lstKeys" VerticalAlignment="Top" Width="290" BorderBrush="White"
                     BorderThickness="1" SelectionChanged="lstKeys_SelectionChanged" />
            <TextBlock Height="39" HorizontalAlignment="Left" Margin="94,62,0,0"
                       Name="lblKey" Text="Key" VerticalAlignment="Top" />
            <TextBox Height="74" HorizontalAlignment="Left" Margin="172,124,0,0"
                     Name="txtValue" Text="" VerticalAlignment="Top" Width="212" />
            <TextBlock Height="39" HorizontalAlignment="Left" Margin="94,140,0,0"
                       Name="lblValue" Text="Value" VerticalAlignment="Top" />
            <Button Content="Delete" Height="70" HorizontalAlignment="Left"
                    Margin="224,228,0,0" Name="btnDelete" VerticalAlignment="Top"
                    Width="160" Click="btnDelete_Click" />
            <TextBlock Height="39" HorizontalAlignment="Left" Margin="94,347,0,0"
                       Name="lblListOfKeys" Text="List of Keys" VerticalAlignment="Top" />
        </Grid>
    </Grid>

</phoneNavigation:PhoneApplicationPage>

Once you've added the XAML markup blocks displayed in this section to MainPage.xaml, you should see the UI shown on the Visual Studio Design View tab in Figure 13-7.

IsolatedStorageSettingsDemo design view

Figure 13.7. IsolatedStorageSettingsDemo design view

Now it's time to add behavior to the application.

Coding Application Behavior (C#)

From Solution Explorer, open MainPage.xaml.cs and replace the code you find there with the following blocks of C# code.

Specifying the Namespaces

Begin by listing the namespaces the application will use. Note the inclusion of System.IO.IsolatedStorage in order to work with the isolated storage settings.

using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Phone.Controls;
using System.IO.IsolatedStorage;
namespace IsolatedStorageSettingsDemo
{
    public partial class MainPage : PhoneApplicationPage
    {
        private IsolatedStorageSettings _appSettings;

Initializing the Application

Now add the next code block to the MainPage class. Notice the use of the BindKeyList() method, which will retrieve all keys from the isolated storage settings and bind them to the list box created in the previous section.

public MainPage()
        {
            InitializeComponent();
            SupportedOrientations = SupportedPageOrientation.Portrait;
            _appSettings = IsolatedStorageSettings.ApplicationSettings;
    BindKeyList();
        }

Adding the Save Button Event Handler

Now add an event handler for the Save button. When the Save button is clicked, the application reads the key and the value from the text boxes into the isolated storage settings.

// Handles Create and Update
// If the key does not exist key-value pair will be added
// ElseIf the key exists the value will be updated
        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            if (!String.IsNullOrEmpty(txtKey.Text))
            {
                if (_appSettings.Contains(txtKey.Text))
                {
                    _appSettings[txtKey.Text] = txtValue.Text;
                }
                else
                {
                    _appSettings.Add(txtKey.Text, txtValue.Text);
                }
_appSettings.Save();
                BindKeyList();
            }
        }

Note

An isolated storage file will perform much better than isolated storage settings because a file can be streamed in and out of the isolated storage file as raw data by using StreamWriter and StreamReader, whereas the storage and retrieval of data in an isolated storage settings key-value pair dictionary requires serialization. However, there is complexity in using isolated storage files: you need to use the file stream to save and retrieve the data and you must be careful to dispose the stream after each use. In the isolated storage settings, there is inherent simplicity in using the key to save and retrieve the data.

Adding the Delete Button Event Handler

Next, add an event handler for the Delete button. When the Delete button is clicked, it removes the selected key from the isolated storage settings and rebinds the list box.

private void btnDelete_Click(object sender, RoutedEventArgs e)
        {
            if (lstKeys.SelectedIndex > −1)
            {
                _appSettings.Remove(lstKeys.SelectedItem.ToString());
_appSettings.Save();
                BindKeyList();
            }
        }

Adding the Listbox Changed Event

Finally, you'll use the Listbox changed event to update the Key and Value text boxes of the application interface. When the user selects a key from the list box, the application will load the key and its associated value from the isolated storage settings using the selected key and then populate the key and value text boxes.

// When key is selected value is retrieved from the storage settings
        private void lstKeys_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0)
            {
                string key = e.AddedItems[0].ToString();
                if (_appSettings.Contains(key))
                {
                    txtKey.Text = key;
                    txtValue.Text = _appSettings[key].ToString();
                }
            }
        }
private void BindKeyList()
        {
            lstKeys.Items.Clear();
            foreach (string key in _appSettings.Keys)
            {
                lstKeys.Items.Add(key);
            }
            txtKey.Text = "";
            txtValue.Text = "";
        }

    }
}

Testing the Finished Application

To test the finished application, press F5. The result should resemble the screenshot shown in Figure 13-5.

Summary

In this chapter, you learned how to save application data on a Windows Phone device using either an isolated storage file or isolated storage settings. From the demo applications, you learned to create, read, update, and delete isolated storage data.

In Chapter 14, you will learn to pinpoint the location of the phone by working with the Windows Phone's global positioning system (GPS), connected networks, and cellular telephone networks. You will also learn to interact with Microsoft's Bing Maps.

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

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