Jennifer Marsman

Windows Development

Developing a Windows 8 Metro App Part 4: My “Reveal a Picture” Algorithm and Basic Code

Developing a Windows 8 Metro App Part 4: My “Reveal a Picture” Algorithm and Basic Code

Rate This
  • Comments 7

So now we are up to the point where we can start coding! 

First, let me explain my app idea.  I am building a children’s game called “Reveal a Picture” where you touch the screen or move the mouse on a colored screen to reveal a hidden picture underneath.  This is a very simple concept, but for children ages 2-4, this is fascinating.  This application will select a picture from the user’s Pictures Library to hide, so the child will be delighted to discover a picture of herself or her family.  I also like this game idea because if the child plays it with a mouse, it teaches basic mouse manipulation skills (the hand/eye coordination necessary to move the mouse and correlate that action to the mouse pointer moving on the screen). 

Note that I’m using Visual Studio Express 2012 RC for Windows 8 for development (so if you are coming from the future, things may look slightly different). 

I started with the Blank XAML template in C#.  (NOTE: If you’re just getting started with Metro, using the Grid template or the Split template is a good idea.  It takes care of a lot of the navigation, animations, and snap screens for you.)  But this app is so simple that Blank made sense. 

I’m assuming that you’ve already downloaded Visual Studio Express 2012 RC for Windows 8 (if not, check out the Getting Started post).  In Visual Studio, select “File” and “New Project” from the menu bar.  You will see a dialog box like the below.  In the left navigation pane, expand “Installed”, then “Templates”, and then “Visual C#”.  Select “Windows Metro style”.  Then in the center pane, select “Blank App (XAML)”.  Give it a name and click “OK”. 

New Project

Now, I need to build the functionality that shows the picture and then hides it with lots of little rectangles, which can then be individually touched or moused over to make them disappear and reveal the image. 

In MainPage.xaml inside of the Grid, let’s add an image for the picture that will be hidden.  In addition, let’s give the Grid a name (I’ll use the super-creative name of “LayoutRoot”) so that we can access it programmatically. 

<Grid x:Name="LayoutRoot" Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <Image Name="hiddenImage"></Image>
</Grid>

Next, we need some code-behind in MainPage.xaml.cs that implements the basic functionality of the game.  At a high level, we are displaying an image from the Pictures Library and then covering it with a series of small rectangles.  When each rectangle is touched or moused over, it will disappear and reveal the picture underneath.  To implement this, we will need to initialize the LayoutRoot grid using InitializeGrid.  The method SetRandomPictureAsync will get the pictures from the Pictures Library and randomly choose one, and then call SetNewHiddenPictureAsync to read in and display that picture.  Next, HidePicture will cover the picture with small rectangles of a color defined by GetBackgroundColor.  The event handler Rectangle_PointerEntered will make a given rectangle disappear when it is touched or moused over.  Finally, Rehide will make all of the rectangles appear again; we will use this functionality in the next post when we expand the game to allow the user to uncover additional pictures. 

#region Member variables

private const int numRows = 25;         
private const int numColumns = 25;

private string hiddenPicturePath;
public string HiddenPicturePath
{
    get { return hiddenPicturePath; }
    set { hiddenPicturePath = value; }
}

#endregion Member variables

#region Basic Functionality

/// <summary>
/// Set the number of columns and rows in the grid
/// </summary>
private void InitializeGrid()
{
    for (int i = 0; i < numColumns; i++)
    {
        this.LayoutRoot.ColumnDefinitions.Add(new ColumnDefinition());
    }

    for (int i = 0; i < numRows; i++)
    {
        this.LayoutRoot.RowDefinitions.Add(new RowDefinition());
    }

    // Set hiddenImage to reach across all rows and columns
    Grid.SetRowSpan(hiddenImage, numRows);
    Grid.SetColumnSpan(hiddenImage, numColumns);
}

/// <summary>
/// Set the hidden image to a random picture from the Picture Library
/// </summary>
private async void SetRandomPictureAsync()
{
    // Get the pictures from the Pictures Library
    var files = await KnownFolders.PicturesLibrary.GetFilesAsync();

    if (files.Count == 0)
    {
        // There are no pictures in the Pictures Library - show a dialog box to the user.  
        MessageDialog dialog = new MessageDialog("To play this game, you should have pictures in your Pictures Library, so you have something fun to uncover.");
        await dialog.ShowAsync();
    }
    else
    {
        Random rand = new Random();
        var file = files[rand.Next(0, files.Count)];
        await SetNewHiddenPictureAsync(file);
    }
}

/// <summary>
/// Sets the hidden picture to be a given file
/// </summary>
/// <param name="file">The path to the picture to hide</param>
/// <returns></returns>
private async Task SetNewHiddenPictureAsync(StorageFile file)
{
    hiddenPicturePath = file.Path;
    
    // Read in the picture file and display it as an image
    var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
    BitmapImage img = new BitmapImage();
    img.SetSource(fileStream);
    hiddenImage.Source = img;
}

/// <summary>
/// Make a series of small rectangles to cover the picture
/// </summary>
private void HidePicture()
{
    // Cover the picture with color
    for (int row = 0; row < numRows; row++)
    {
        for (int col = 0; col < numColumns; col++)
        {
            Rectangle littleRect = new Rectangle();

            littleRect.Width = LayoutRoot.Width / numColumns;
            littleRect.Height = LayoutRoot.Height / numRows;
            Grid.SetRow(littleRect, row);
            Grid.SetColumn(littleRect, col);

            littleRect.Fill = new SolidColorBrush(GetBackgroundColor());
            littleRect.Visibility = Windows.UI.Xaml.Visibility.Visible;
            littleRect.PointerEntered += new Windows.UI.Xaml.Input.PointerEventHandler(Rectangle_PointerEntered);
            
            this.LayoutRoot.Children.Add(littleRect);
        }
    }
}

/// <summary>
/// Gets color with which to hide the picture
/// </summary>
/// <returns></returns>
private static Color GetBackgroundColor()
{
    return Colors.Cyan;
}

/// <summary>
/// When each little rectangle covering the picture is pressed, it will be made
/// invisible and the part of the picture underneath will be revealed.  
/// </summary>
private void Rectangle_PointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
    Rectangle rect = sender as Rectangle;

    rect.Visibility = Visibility.Collapsed;
}

/// <summary>
/// Cover the picture again by making all of the already-existing rectangles visible.  
/// </summary>
private void Rehide()
{
    foreach (Rectangle rect in LayoutRoot.Children.OfType<Rectangle>())
    {
        rect.Visibility = Visibility.Visible;
    }
}

#endregion Basic Functionality

Now we need to call these functions!  First, modify the constructor to call InitializeGrid().

public MainPage()
{
    this.InitializeComponent();
    InitializeGrid();
}

Then, add logic to OnNavigatedTo to set the hidden image and hide it.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    SetRandomPictureAsync();
    HidePicture();
}

Plus, don’t forget to add these namespaces at the top of MainPage.xaml.cs:

using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI;
using Windows.UI.Popups;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Shapes;

Now, there is one more thing that we need to do.  Since we are accessing the Pictures library, we need to declare that the app has this capability in the app manifest.  Capabilities create a climate of trust – you as the developer state upfront certain things that your app will try to do (like accessing the user’s file libraries or webcam).  When an end user sees your app in the Windows Store, they can very clearly see all of these things that your app might do and can make an informed decision on whether they want to download it.  For example, if I’m downloading an eReader app to read digital books and I see that it will try to access my webcam, I might wonder about that and not download it, because I don’t see a clear reason why an eReader should need my webcam for me to read books.  Which brings up another good point – if you are a developer with a valid reason to access the webcam (or whatever capability) that is not immediately clear (maybe an eReader would use the webcam to scan ISBN numbers and find the corresponding digital book?), it would behoove you to explain what the app is doing with the webcam in your app listing page in the Windows Store, so you don’t scare off cautious/paranoid users like me.  For more information on capabilities, see this MSDN article on "App capability declarations"

Note that if we don’t specify the “Pictures Library” capability, we will get an error at runtime:

Capability Error

In the Solution Explorer, double-click on the Package.appxmanifest file to bring up the Visual Studio editor for the manifest.  Click the “Capabilities” tab, and check the “Pictures Library Access” box. 

Picture Library Capability

 

At this point, our app will run.  It will grab a random picture from the Pictures library and cover it with the color cyan (a light blue/green color).  The user can then touch or mouse over the picture to remove the color and reveal the picture underneath. 

Running App

In the next post, we will add an application bar to the app to allow the user to get a new picture when they are done uncovering the first one. 

 

Other blog posts in this series:

Part 1: Why Would You Want to Develop a Metro Application for Windows 8?

Part 2: Getting Started

Part 3: Metro Design

Part 4: My "Reveal a Picture" Algorithm and Basic Code

 

  • Loading...
Leave a Comment
  • Please add 6 and 5 and type the answer here:
  • Post