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

Building an Imgur Client for Windows Phone - Part 6 - Uploading UI

02.27.2013
| 2453 views |
  • submit to reddit

If you missed the previous four parts, you can easily find them here:

In the previous article I talked about building the upload mechanism. Now, I am going to talk about building the UI workflow that is needed to create a user-friendly upload routine.

I create a new upload page (UploadPage.xaml) that allows the user to select the image from the media library, as well as take a new one if necessary. Not only that, but I am also giving the user the possibility to enter an image title and description, even if those are not mandatory.

The complete XAML looks like this:

<phone:PhoneApplicationPage
    x:Class="Imagine.UploadPage"
    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"
    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
    shell:SystemTray.IsVisible="False">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="UPLOAD IMAGE" Style="{StaticResource PhoneTextNormalStyle}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="230"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            
            <Image Stretch="UniformToFill" x:Name="imgPreview" Margin="12,12,12,12"></Image>
            
            <Button x:Name="btnPick" Click="btnPick_Click_1">
                <Image Source="/Images/photocapture.png"></Image>
            </Button>
            
            <StackPanel Grid.Row="1">
                <TextBlock Style="{StaticResource PhoneTextTitle3Style}">Name:</TextBlock>
                <TextBox x:Name="txtName"></TextBox>
                <TextBlock Style="{StaticResource PhoneTextTitle3Style}">Description:</TextBlock>
                <TextBox x:Name="txtDescription" TextWrapping="Wrap" AcceptsReturn="True" 
                         TextOptions.TextHintingMode="Animated" Height="210"></TextBox>
            </StackPanel>
        </Grid>
        
        <Grid Visibility="Collapsed" Grid.RowSpan="2" x:Name="grdUploading">
            <Grid.Background>
                <SolidColorBrush Color="Black"></SolidColorBrush>
            </Grid.Background>
            
            <TextBlock Text="Uploading..." HorizontalAlignment="Center"
                       VerticalAlignment="Center"></TextBlock>

            <toolkit:PerformanceProgressBar IsIndeterminate="True" Margin="0,80,0,0"></toolkit:PerformanceProgressBar>
        </Grid>
    </Grid>

    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar>
            <shell:ApplicationBarIconButton x:Name="btnUpload" Click="btnUpload_Click_1" Text="ok" IconUri="/Images/appbar.check.png"></shell:ApplicationBarIconButton>
            <shell:ApplicationBarIconButton x:Name="btnCancel" Click="btnCancel_Click_1" Text="cancel" IconUri="/Images/appbar.cancel.png"></shell:ApplicationBarIconButton>
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>
</phone:PhoneApplicationPage>

To do a quick overview, here is what it looks like as a result:


The main working grid is split in two parts - the image preview component that has a Button control overlaid on top of it, and the meta area that is dedicated to specifying the name and the description of the image to be uploaded. 

The Image control behind the button will display the selected content. At the same time, the main grid background (for LayoutRoot) will also showcase the selected image, although in a much larger format. This is all done with the help of PhotoChooserTask:

PhotoChooserTask task = new PhotoChooserTask() { ShowCamera = true };
task.Completed += (s, d) =>
    {
        if (d.TaskResult == TaskResult.OK)
        {
            byte[] buffer = new byte[16 * 1024];

            using (MemoryStream stream = new MemoryStream())
            {
                int read = 0;

                while ((read = d.ChosenPhoto.Read(buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, read);
                }

                BitmapImage image = new BitmapImage();
                image.SetSource(stream);

                imgPreview.Source = image;

                ImageBrush brush = new ImageBrush();
                brush.ImageSource = image;
                LayoutRoot.Background = brush;
                brush.Opacity = .2;
                brush.Stretch = Stretch.UniformToFill;

                ImageContent = stream.ToArray();
            }
        }
    };
task.Show();

ImageContent is a common byte buffer that will be used if the user wants to upload the selected image. Once the selection is done, the UI will look like this:


Here is the code snippet that activates the upload:

grdUploading.Visibility = System.Windows.Visibility.Visible;
this.ApplicationBar.IsVisible = false;

App.ServiceClient.UploadImage(ImageContent, txtName.Text, txtDescription.Text, (n, image) =>
    {
        if (n)
        {
            Dispatcher.BeginInvoke(() =>
            {
                Clipboard.SetText(image.Image.Link);

                MessageBox.Show("Your image was uploaded. The link is placed in the clipboard.",
                    "Image Upload", MessageBoxButton.OK);

                if (NavigationService.CanGoBack)
                    NavigationService.GoBack();
            });
        }
        else
        {
            Dispatcher.BeginInvoke(() =>
                {
                    MessageBox.Show("There was an error uploading the image. Please try again later.",
                        "Image Upload", MessageBoxButton.OK);
                });
        }

        Dispatcher.BeginInvoke(() =>
                {
                    grdUploading.Visibility = System.Windows.Visibility.Collapsed;
                    this.ApplicationBar.IsVisible = true;
                });
    });

When the users accepts the image, an overlay blocks off the entire UI to show that the upload is in progress:


The custom action that is passed to UploadImage will be triggered with two arguments - the boolean flag that shows whether the upload succeeded or not, and the deserialized instance of ImgurAtomicImageData. If the first argument is equal to true, then I am setting the image URL to be in the clipboard, so that the user can share it if necessary. If the upload failed, the user is being informed about this. 

You can download the current source code for the project here. Don't forget to specify your API key and secret.