Mobile Zone is brought to you in partnership with:

Den is a DZone Zone Leader and has posted 460 posts at DZone. You can read more from them at their website. View Full User Profile

Basics of Using Local Storage on Windows Phone 8

12.20.2012
| 25719 views |
  • submit to reddit

Many application usage scenarios on Windows Phone are not only relying on data that is being fetched through third-party services or APIs, but also on storing that data either temporarily or permanently on individual devices. Windows Phone 7 offered developers the ability to use the Isolated Storage  - a sandboxed storage allocation that is directly associated with a given application. Windows Phone 8 extends on top of that content and offers additional options.

In this article I am going to cover the Windows.Storage namespace, exposed through the WinPRT APIs, and Microsoft.Phone.Storage, exposed through the standard .NET APIs.

On-Device Storage

Let's begin with ApplicationData. This class allows you to access the local application data cache. To some extent, this is something like the Isolated Storage - it is still associated with the application. And as with any other app-based content, it will be removed when the application is uninstalled, so if there is anything that the user might want to keep even if the application is uninstalled, offer some additional storage options, such as SkyDrive.

The local path for the Application Data folder might be something like this on the phone:

C:\Data\Users\DefApps\AppData\{5D93133B-3B39-4ABF-8D61-277D77B9F2AD}\Local

Where the GUID is the unique application identifier, declared in WMAppManifest.xml. If you've worked on Windows Store applications before, the approach - storing data with an ApplicationDataContainer , might seem familiar. However, there are specific limitations that you need to be aware of. For example, such goodies like ApplicationData.Current.LocalSettings and ApplicationData.Current.RoamingFolder are not implemented for Windows Phone.

Let's move on to general operations that can be performed with the folder, and we'll begin with storing content. The way to do it is very similar to the one used in Windows Store applications:

async void WriteData(string fileName, string content)
{
    byte[] data = Encoding.UTF8.GetBytes(content);

    StorageFolder folder = ApplicationData.Current.LocalFolder;
    StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);

    using (Stream s = await file.OpenStreamForWriteAsync())
    {
        await s.WriteAsync(data, 0, data.Length);
    }
}

In this sample snippet I am storing a string in an arbitrary file in the local Application Data folder. The core classes that you need to leverage here are StorageFolder and StorageFile. As StorageFolder gives you the access to the root container and allows you to perform specific operations, such as getting the file or creating a new one, StorageFile actually allows you to access the file content. In the example above, the file is opened for writing, after which WriteAsync is called with an associated data byte array - and this doesn't have to be a string. It can be an image, a music file or a video - virtually anything you would like to store.

When you are creating a new file, you can optionally specify a collision option, through CreationCollisionOption. The normal procedure for most scenarios would be overwriting the existing file. Also a recommendation in this case, if the file storage is exposed to the user and not used internally - warn the user that some data might be potentially lost when the new file creation process completes.

The reading process can be easily wrapped in an async function like this:

async Task<string> ReadData(string fileName)
{
    byte[] data;
    
    StorageFolder folder = ApplicationData.Current.LocalFolder;

    StorageFile file = await folder.GetFileAsync(fileName);

    using (Stream s = await file.OpenStreamForReadAsync())
    {
        data = new byte[s.Length];
        await s.ReadAsync(data, 0, (int)s.Length);
    }

    return Encoding.UTF8.GetString(data, 0, data.Length);
}

Notice that, once again, I am relying on StorageFolder and StorageFile to access the content, this time opening the base stream for reading instead of writing. Application Data will always be written on the device itself and not in the external storage.

External Storage

But as you also know, Windows Phone 8 now supports additional storage, such as SD cards. Here is where the classes exposed via Microsoft.Phone.Storage come in handy. A list of currently available external storage devices can be easily obtained via:

var storageAssets = await ExternalStorage.GetExternalStorageDevicesAsync();

Based on the assumption that there is only one external card available, you can obtain the representative instance with the help of LINQ (getting an ExternalStorageDevice):

ExternalStorageDevice item = storageAssets.FirstOrDefault();

The limitation, however, is the fact that for a third-party application, access to the external storage is limited to the read-only mode.

NOTE: Although you can get the current instance for the external storage device with no problem, if you try to get the list of folders and files available, you will get an exception unless you have the ID_CAP_REMOVABLE_STORAGE capability enabled for the application.

A simple way to enumerate existing folders in the root would look like this:

var storageAssets = await ExternalStorage.GetExternalStorageDevicesAsync();

ExternalStorageDevice item = storageAssets.FirstOrDefault();
ExternalStorageFolder folder = item.RootFolder;
var _folderList = await folder.GetFoldersAsync();
foreach (var _folder in _folderList)
{
    Debug.WriteLine(_folder.Name);
}

And even though querying the folder list is easy, you will not get access to the files that are stored in those folders unless you explicitly associate your application with a file type. To do this, you have to open the WMAppManifest.xml file and add create the Extensions node, with the information related to the associated file type:

<Extensions>
  <FileTypeAssociation TaskID="_default" Name="PSD" NavUriFragment="fileToken=%s">
    <Logos>
      <Logo Size="small" IsRelative="true">Assets/Tiles/FlipCycleTileSmall.png</Logo>
      <Logo Size="medium" IsRelative="true">Assets/Tiles/FlipCycleTileMedium.png</Logo>
      <Logo Size="large" IsRelative="true">Assets/Tiles/FlipCycleTileLarge.png</Logo>
    </Logos>
    <SupportedFileTypes>
      <FileType ContentType="application/psd">.psd</FileType>
    </SupportedFileTypes>
  </FileTypeAssociation>
</Extensions>

You can then query for existing files as you would do with any other folder:

var storageAssets = await ExternalStorage.GetExternalStorageDevicesAsync();

ExternalStorageDevice item = storageAssets.FirstOrDefault();
ExternalStorageFolder folder = item.RootFolder;
var imagesFolder = await folder.GetFolderAsync("images");
var fileList = await imagesFolder.GetFilesAsync();

REMEMBER: You will only get the ExternalStorageFile  instances that have the extension listed in the Extensions node, in WMAppManifest.xml.