Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Building a simple Isolated Storage explorer system in a Windows Phone 7 application

DZone's Guide to

Building a simple Isolated Storage explorer system in a Windows Phone 7 application

· Mobile Zone
Free Resource

Discover how to focus on operators for Reactive Programming and how they are essential to react to data in your application.  Brought to you in partnership with Wakanda

Applications are using Isolated Storage for a multitude of reasons, including temporary storage (cached content) and permanent storage (reusable content and databases). Sometimes, it is hard to figure out what's where, especially when the developer operates with lots of files and folders. My solution was to build a small explorer sub-system (if you want to call it so) that will make it a bit easier to go through existing content. The ultimate goal is to build a custom full-fledged control, but for now - enjoy this functional proof of concept.

Let's take a look at the basic structure I need.

I am binding the ObservableCollection that keeps a list of StorageItem instances to the visual list, represented by a ListBox with a custom DataTemplate. So let's take a look at the XAML part that is the foundation of the UI:

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="600"/>
            <RowDefinition Height="80"></RowDefinition>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Button Click="Button_Click" Grid.Row="1" Width="109" Content="^" HorizontalAlignment="Left"></Button>
        <TextBlock Grid.Row="1" Margin="140,0,0,0" Text="{Binding ElementName=ExplorerPage,Path=currentPath}"></TextBlock>
        <ListBox x:Name="StorageList" ItemsSource="{Binding ElementName=ExplorerPage,Path=storageItems}" SelectionChanged="StorageList_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid Height="39">
                        <Image Source="{Binding ContentType,Converter={StaticResource ConverterMain}}" Height="32" Width="32" HorizontalAlignment="Left" Margin="10,0,0,0"></Image>
                        <TextBlock Margin="45,0,0,0" Height="Auto" Text="{Binding Name}" Width="370"></TextBlock>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Click="Button_Click_1" Grid.Row="2" Content="Refresh"></Button>
    </Grid>

Notice that the DataTemplate I am using for ListBox.ItemTemplate is composed of a single grid with an Image and a Button. You might be wondering - why I need an image when listing storage content? That will be the indicator, showing whether the entity listed is a file or a folder. Ignore the bound converter at this point. 

So the ListBox is bound to the collection in the code-behind, which is referenced through a DependencyProperty:

ObservableCollection<StorageItem> storageItems
        {
            get
            {
                return (ObservableCollection<StorageItem>)GetValue(_ST_ITEMS);
            }
            set
            {
                SetValue(_ST_ITEMS, value);
            }
        }
public DependencyProperty _ST_ITEMS = DependencyProperty.Register("storageItems", typeof(ObservableCollection<StorageItem>),
    typeof(PhoneApplicationPage),
    new PropertyMetadata(new ObservableCollection<StorageItem>()));

A StorageItem only haas two properties - a ContentType and a Name.

public class StorageItem
{
    public StorageType ContentType { get; set; }
    public string Name { get; set; }
}

The ContentType is nothing but a flag determined through an enum:

public enum StorageType
{
    File,
    Folder
}

While working with all the content inside the Isolated Storage, it is safe to assume that not everything will be located in the root folder, and there might be secondary folder structures. To keep track of it, I introduced a currentPath property, also backed by a DependencyProperty, since I am binding to it in the UI:

string currentPath
{
    get
    {
        return (string)GetValue(_ST_CURPATH);
    }
    set
    {
        SetValue(_ST_CURPATH, value);
    }
}
public DependencyProperty _ST_CURPATH = DependencyProperty.Register("currentPath", typeof(string),
    typeof(PhoneApplicationPage),
    new PropertyMetadata(string.Empty));

I am getting the data regarding the Isolated Storage contents with the help of a GetStorageItems method:

public void GetStorageItems()
{
    storageItems = new ObservableCollection<StorageItem>();

    string[] files = file.GetFileNames(currentPath + @"\*.*");
    string[] directories = file.GetDirectoryNames(currentPath + @"\*.*");

    foreach (string directory in directories)
    {
        storageItems.Add(new StorageItem() { ContentType = StorageType.Folder, Name = directory });
    }

    foreach (string _file in files)
    {
        storageItems.Add(new StorageItem() { ContentType = StorageType.File, Name = _file });
    }
}

The \*.* suffix is added to the path to query against all content, no matter what the extension or name is, both for files and for folders. For now, the path does not change, but it should. So when the selection changes in the list, I am dynamically modifying the path, and in case the user selected a folder, I need to get the content from that specific folder.

private void StorageList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (e.AddedItems.Count != 0)
    {
        StorageItem item = (StorageItem)e.AddedItems[0];
        if (item.ContentType == StorageType.Folder)
        {
            currentPath += @"\" + item.Name;
            GetStorageItems();
        }
    }
}

Now back to the converter that was bound to the Image in the DataTemplate for custom list items. I need to convert the ContentType property to an icon. There are only two choices I have - it's either going to be a file or a folder, so I have two predefined icons located in the Images folder inside my application - file.png and folder.png.

The converter looks like this:

public class StorageTypeConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((StorageType)value == StorageType.Folder)
        {
            return new BitmapImage(new Uri("Images/folder.png", UriKind.Relative));
        }
        else
        {
            return new BitmapImage(new Uri("Images/file.png", UriKind.Relative));
        }

    }

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

It is declared as a static resource in the main application page:

<phone:PhoneApplicationPage.Resources>
    <local:StorageTypeConverter x:Key="ConverterMain"></local:StorageTypeConverter>
</phone:PhoneApplicationPage.Resources>

The icon is shown correctly, so is the path and the contents of each and every folder. What to do in case the user wants to navigate back, to a folder that is the container for the current file/folder set? All that needs to be done is path editing to the last folder separator and invoking the GetStorageItems method again:

currentPath = currentPath.Remove(currentPath.LastIndexOf(@"\"));
GetStorageItems();

That is it! Once you run the application, you should see something similar to this:

Some ideas I will be working on to extend this to a full-fledged control:

  • Ability to navigate by entering the path manually
  • Add file operations (rename, change extension, open, delete, copy, move)
  • A visually appealing toolbar
  • Add size information for files and folders

Learn how divergent branches can appear in your repository and how to better understand why they are called “branches".  Brought to you in partnership with Wakanda

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}