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

Developing SevenDrops - A Dropbox-based photo uploader for Windows Phone 7 [1/6]

DZone's Guide to

Developing SevenDrops - A Dropbox-based photo uploader for Windows Phone 7 [1/6]

· Mobile Zone
Free Resource

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

If you didn't know this yet, Dropbox is a cloud storage service service that allows you to store files on their servers and then access those from anywhere. This service exposes a public API that I decided to integrate in a Windows Phone 7 application to backup my photos. SkyDrive integration is great but I was just curious about an implementation for Dropbox, so this set of articles describes the way I built the application.

Enough intro. Where is the code? So here is the XAML for the main window:

<phone:PhoneApplicationPage 
    x:Class="SevenDrops.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"
    x:Name="Main">

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="60" />
            <RowDefinition Height="*"/>
            <RowDefinition Height="80"></RowDefinition>
        </Grid.RowDefinitions>

        <TextBlock Grid.Row="1" Text="Current Account:" Style="{StaticResource PhoneTextTitle3Style}" Margin="20,15,0,0" HorizontalAlignment="Left" Width="171"></TextBlock>
        <TextBlock Grid.Row="1" Text="NAME" Margin="201,15,102,0" Style="{StaticResource PhoneTextTitle3Style}"></TextBlock>
        <Button Grid.Row="1" Height="80" Width="80" Margin="380,-10,0,0">
            <Image Height="48" Width="48" Source="/Images/pick.png" Margin="-8,0,0,0"></Image>
        </Button>
        
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <Image Source="/Images/drops.png" Height="164" Width="164" Margin="339,0,0,0"></Image>
            <TextBlock x:Name="ApplicationTitle" Margin="0,-150,0,0" Text="SevenDrops" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <Grid x:Name="ContentPanel" Grid.Row="2" Margin="12,29,12,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="309*" />
                <ColumnDefinition Width="147*" />
            </Grid.ColumnDefinitions>
            <ListBox ItemsSource="{Binding ElementName=Main,Path=ImageCollection}" Grid.ColumnSpan="2">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid Width="460" Height="80">
                            <Image Height="128" Width="128" Source="{Binding Path=Source}" Margin="-310,0,0,0"></Image>
                            <TextBlock Text="{Binding Path=ImageName}" Margin="70,0,0,0" Width="220" TextWrapping="Wrap"></TextBlock>
                            <Button Width="80" Height="80" Margin="380,0,0,0">
                                <Image Source="/Images/delete.png" Height="48" Width="48" Margin="-8,0,0,0"></Image>
                            </Button>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
        
        <Button Grid.Row="3" Content="Add Image" Click="Button_Click"></Button>
    </Grid>
</phone:PhoneApplicationPage>

If you are curious how the actual UI looks like, here is a screenshot:

The list itself is missing on the screenshot right now since there are no items in the collection, although it is bound to ImageCollection (explained below) - its purpose is to list selected images that will later on be passed to the method that will store the images externally. There is a custom ItemTemplate defined for each list item to show the image icon (also known as the thumbnail) and the name, plus the Remove button that will exclude the image from the queue.

To add some testing elements, first of all I added a local collection for image objects that will be user-retrieved. Since there is no complex interaction logic involved, I can easily put the collection in the main window class:

ObservableCollection<ImageUnit> Images;

The collection is initialized when the window itself is initialized:

// Constructor
public MainPage()
{
InitializeComponent();
Images = new ObservableCollection<ImageUnit>();
}

But this would be unusable directly from the application code - I need to have a DependencyProperty to bind against. Therefore, I register one:

DependencyProperty _collection = DependencyProperty.Register("ImageCollection", typeof(ObservableCollection<ImageUnit>), typeof(MainPage), null);

Subsequently, I need to modify the collection to be a property tied to the DependencyProperty:

public ObservableCollection<ImageUnit> Images
{
get
{
return (ObservableCollection<ImageUnit>)GetValue(_collection);
}
set
{
SetValue(_collection, value);
}
}

So there is this ImageUnit class that is kind of confusing - after all I didn't mention what it is. Instead of just temporarily storing the image, I will also define the image name (which by the way will be a GUID). So the class itself looks like this:

public class ImageUnit
{
public byte[] ImageContents { get; set; }
public string ImageName { get; set; }
public BitmapImage Source
{
get
{
BitmapImage image = new BitmapImage();
image.SetSource(new MemoryStream(ImageContents));
return image;
}
set;
}
}

For now it is really basic and at the same time it will leave some room for customizations when needed. The source property will return a custom BitmapImage based on the ImageContents. You might ask - why can't I simply have a BitmapImage-based property so that I don't have an extra property? The reason is simple - when uploading the image, I will have to pass a byte array and I cannot obtain it from BitmapImage. While at the same time, I cannot set an image source locally to be a byte array.

For the record, this class resides in a custom sub-namespace called Domain (defined by a custom folder of course):

Therefore, in your main window class make sure there are the following additonal using statements present in the class header:

using System.Collections.ObjectModel;
using SevenDrops.Domain;

The first one is for ObservableCollection and the second one is for the ImageUnit class. The reason I am using ObservableCollection instead of any other generic list is because of the very good auto-binding capabilities. Every bound object will be automatically updated when the list is updated (thanks for INotifyPropertyChanged).

Now you can see how the DataTemplate for the list item template is tied to the ImageUnit instances stored in the Images collection. The problem is - I don't have any images there. To correct this, you can see that there is a Button_Click event handler declared for the buttom that handles image addition. Here is what I have in the code-behind:

private void Button_Click(object sender, RoutedEventArgs e)
{
PhotoChooserTask task = new PhotoChooserTask();
task.Completed += new EventHandler<PhotoResult>(task_Completed);
task.Show();
}

This will simply trigger the photo chooser. Don't forget to add the proper reference:

using Microsoft.Phone.Tasks;

And the task_Completed event handler looks like this:

void task_Completed(object sender, PhotoResult e)
{
    ImageUnit unit = new ImageUnit();
    unit.ImageName = Guid.NewGuid().ToString() + ".jpg";
    byte[] b = new byte[e.ChosenPhoto.Length];
    e.ChosenPhoto.Read(b, 0, Convert.ToInt32(e.ChosenPhoto.Length));
    unit.ImageContents = b;

    Images.Add(unit);
}

So a new ImageUnit instance is created per selected image. Then, the obtained image stream is converted to a byte array and stored. Also, a new GUID is generated to represent the unique name of the image (used for uploading it). Later on I can add custom naming, but to show the idea of the application, an automatically assigned name is fine.

Once I run the application, I can add a couple of images and here is what I get:

 

Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

Topics:

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}