31 Days of Windows 8 | Day #23: The Compass

This article is Day #23 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample

Today, we’re going to talk about the compass.  If you’ve done any Windows Phone development previously, you might have read my article on the Windows Phone compass, which provides magnetic and true north headings, as well as X, Y, and Z rotation data.

From what I have found so far, the compass in Windows 8 provides significantly less data from the sensor.  In fact, we only get the values related to our heading, with no consideration for rotation data.  In addition, you’re going to find that since we’re building apps for Windows 8, there’s going to be a wide variety of sensors we’ll encounter.  Here’s a quick video of the 4 different machines that I use regularly, and how they each respond differently to the same code:

 

Writing Some Code to Access the Compass

The compass, like many of the sensors we will cover in the next few days, is actually very accessible and easy to use.  With a few lines of code, and one event handler, we can gather rich data from our user’s device very quickly.  Here’s my entire MainPage.xaml.cs file for my simple compass app:

using System;
using Windows.Devices.Sensors;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Day23_Compass
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        Compass c;

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            c = Compass.GetDefault();
            if (c != null)
            {
                c.ReadingChanged += c_ReadingChanged;
                Data.Visibility = Visibility.Visible;
            }
            else NoSensorMessage.Visibility = Visibility.Visible;
        }

        protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
        {
            c.ReadingChanged -= c_ReadingChanged;
        }

        async void c_ReadingChanged(Compass sender, CompassReadingChangedEventArgs args)
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                MagneticNorth.Text = args.Reading.HeadingMagneticNorth.ToString();
                if (args.Reading.HeadingTrueNorth != null)
                {
                    TrueNorth.Text = args.Reading.HeadingTrueNorth.ToString();
                }
                TimeStamp.Text = args.Reading.Timestamp.ToString();
            });

        }
    }
}

To explain the code above, in my OnNavigatedTo method, I try to initialize my Compass object, and if it’s not null, I create an event handler for each time that the reading of the compass changes.  (Unless the user’s device is sitting completely still, it’s likely you’ll consistently get new readings.)  In the ReadingChanged event handler, we use a Dispatcher to get back to the UI thread, where we then assign our three values, HeadingMagneticNorth, HeadingTrueNorth, and Timestamp to TextBlock values I’ve placed in my XAML file.

That’s it.  As you probably saw, however, most of the sensors you’re going to encounter will not provide a HeadingTrueNorth value, so relying on the HeadingMagneticNorth is going to be a more reliable value for you.

Finally, both of the Heading values are measured in degrees relative to their associated heading.  This means that you’ll see a range of values from 0 to 360, with zero being the value headed directly north.

Summary

This was a quick but exhaustive look at the Compass in Windows 8.  We saw that you can easily access the Compass data by using an event handler, but that we receive a limited amount of data from this sensor, and your mileage will vary from device to device.

If you would like to download the sample app featured in this article, click the icon below:

downloadXAML

Tomorrow, we’re going to look at the light sensor, and how we can manipulate our application to take advantage of knowing if the user is sitting in a light or dark room.

downloadTheTools

31 Days of Windows 8 | Day #22: Using Play To

This article is Day #22 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample

Today, we get to talk about another really cool feature to Windows 8: Play To.  At its core, this is the ability to share content from your computer to a television, another computer, or an Xbox 360.  Imagine finding a cool video on YouTube that you want to share with the other people in the room.  Play To would allow you share that content to your television so that everyone can see it without having to huddle around your tablet.

When I first started investigating this technology, I was concerned that it would be difficult to test effectively.  While I have a couple of nice TVs in my house, they’re just old enough to not have any idea what Play To is.  (If you’d like to check your devices, check out Microsoft’s Compatibility Center.)  Anyways, what I’ve discovered is that you really only need a second machine running Windows 8 on it, or an Xbox 360 to test this out.

In my setup, I’ve deployed my application to my Samsung Series 7 Slate running Windows 8 Pro, and I am able to use Play To on my home network to share content with Windows Media Player on a second PC that I have on my desk.  I’ve also tested it on my Xbox 360, and it works marvelously.

Before we get into the coding, I want to make sure you have a way to test this functionality.  In Windows 8, open the Windows Media Player app.  (You know, the app that you haven’t opened on a Windows machine in 10 years?  Yeah, that one.)

22-XAML-WindowsMediaPlayer

Select the “Allow remote control of my Player…” option.  You’ll be asked to confirm your choice, just to make sure this is actually what you want to do.

22-XAML-AllowRemoteControl

Once you’ve confirmed this choice, your machine will be registered on your network as a device available for Play To content.  OK, let’s get to coding, so that we can see what this really means.

Making Your App a Play To Source

This is the easy half of this article, but the second half is just more involved, not necessarily difficult.  When I first ventured out to learn how to do this, I fully expected a trip to the package.appxmanifest file, where we would simply treat this experience much like we did with Sharing on Day #7, where we have to do most of the work in configuration.

I was wrong.  It’s WAY easier.

First, we need to create a PlayToManager object, which will activate our ability to share our content via Play To.  We create an event handler which handles when the user connects their device to a target, and finally, we set the source of the content we want to send.  The entire operation looks like this:

PlayToManager manager = null;
CoreDispatcher dispatcher = null;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    dispatcher = Window.Current.CoreWindow.Dispatcher;
    manager = PlayToManager.GetForCurrentView();
    manager.SourceRequested += manager_SourceRequested;
}

void manager_SourceRequested(PlayToManager sender, PlayToSourceRequestedEventArgs args)
{
    var deferral = args.SourceRequest.GetDeferral();
    var handler = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        args.SourceRequest.SetSource(MusicSource.PlayToSource);
        deferral.Complete();
    });
}

protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
    manager.SourceRequested -= manager_SourceRequested;
}

That’s seriously ALL the code you need to make this happen.  Remember Day #20 when we talked about printing?  It took over 400 lines of code to get a printer to put ink on paper.  Getting a song to stream wirelessly over a network and play on another device without any need for credentials?  We can do that in 10 lines.  Here’s a little video to illustrate:

The only real “magic” that is happening in the code above is when we set our source.  I am grabbing the Source of a MediaElement I have on my page, but this seems perfectly reasonable.  The standard use case for Play To is that the user will start watching the content on their tablet or personal device, and then want to share it to another Play To device, so you’ll already have a MediaElement in use.

The next step is to make our app capable of being the source as well.

Making Your Windows 8 App a Play To Target

In this example, we’re going to add functionality to our app so that we can be on the receving end of any Play To content that might be flying around our local network.  To do this, we will start with a PlayToReceiver object, and a giant pile of REQUIRED event handlers to go with it.  I’ve modified my OnNavigatedTo method from earlier to accomodate this new code:

PlayToManager manager = null;
CoreDispatcher dispatcher = null;
PlayToReceiver receiver = null;

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    dispatcher = Window.Current.CoreWindow.Dispatcher;
    manager = PlayToManager.GetForCurrentView();
    manager.SourceRequested += manager_SourceRequested;

    receiver = new PlayToReceiver();
    receiver.PlaybackRateChangeRequested += receiver_PlaybackRateChangeRequested;
    receiver.PlayRequested += receiver_PlayRequested;
    receiver.PauseRequested += receiver_PauseRequested;
    receiver.StopRequested += receiver_StopRequested;
    receiver.MuteChangeRequested += receiver_MuteChangeRequested;
    receiver.VolumeChangeRequested += receiver_VolumeChangeRequested;
    receiver.TimeUpdateRequested += receiver_TimeUpdateRequested;
    receiver.CurrentTimeChangeRequested += receiver_CurrentTimeChangeRequested;
    receiver.SourceChangeRequested += receiver_SourceChangeRequested;
    receiver.SupportsAudio = true;
    receiver.SupportsVideo = true;
    receiver.SupportsImage = true;

    receiver.FriendlyName = "Day #22 - Play To";

    await receiver.StartAsync();
}

As you can see, there are NINE event handlers that need to be implemented.  I’m stressing this fact because if you don’t implement every single one of them, you will not be able to call the StartAsync() method on your PlayToReceiver object.  (I just spent the last 30 minutes discovering that cold, hard fact.)

Once you’ve done it, however, and have your nine new methods, it’s up to you which ones you actually implement (though it’s recommended that you implement all of them.)  Here are each of my nine event handlers, and the code that goes with each one:

SourceChangeRequested

In this method (probably the most important of the nine), we detect which type of content is being sent our way, grab it, and send it to the appropriate XAML control in our UI.  The ShowSelectedPanel() method is one I built to manage the switching of visibilities and stop/start methods for the MediaElements.  You can certainly choose to disregard those in your app.

async void receiver_SourceChangeRequested(PlayToReceiver sender, SourceChangeRequestedEventArgs args)
{
    if (args.Stream != null)
    {
        if (args.Stream.ContentType.Contains("image"))
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
            {
                BitmapImage bmp = new BitmapImage();
                bmp.SetSource(args.Stream);
                PhotoSource.Source = bmp;
                ShowSelectedPanel(1);
            });
        }
        else if (args.Stream.ContentType.Contains("video"))
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
            {
                VideoSource.SetSource(args.Stream, args.Stream.ContentType);
                ShowSelectedPanel(3);
            });
        }
        else if (args.Stream.ContentType.Contains("audio"))
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
            {
                MusicSource.SetSource(args.Stream, args.Stream.ContentType);
                ShowSelectedPanel(2);
                MusicSource.Play();
            });
        }
    }
}

PlayRequested

This, and all of the subsequent event handlers simply make a call of the appropriate type (this example is obviously “Play”), and then notify our source that the call has been made.

async void receiver_PlayRequested(PlayToReceiver sender, object args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        MusicSource.Play();
        VideoSource.Play();
        receiver.NotifyPlaying();
    });
}

 

PauseRequested

Practically identical to the PlayRequested method, this one implements Pause.

async void receiver_PauseRequested(PlayToReceiver sender, object args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        MusicSource.Pause();
        VideoSource.Pause();
        receiver.NotifyPaused();
    });
}

The Rest

As you can see below, the last 5 event handlers follow the exact same format for their properties.

private async void receiver_PlaybackRateChangeRequested(PlayToReceiver sender, PlaybackRateChangeRequestedEventArgs args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        VideoSource.PlaybackRate = args.Rate;
    });
}

private async void receiver_CurrentTimeChangeRequested(PlayToReceiver sender, CurrentTimeChangeRequestedEventArgs args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        if (VideoSource.CanSeek)
        {
            {
                VideoSource.Position = args.Time;
                receiver.NotifySeeking();
            }
        }
    });
}

private async void receiver_TimeUpdateRequested(PlayToReceiver sender, object args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        receiver.NotifyTimeUpdate(VideoSource.Position);
    });
}

private async void receiver_VolumeChangeRequested(PlayToReceiver sender, VolumeChangeRequestedEventArgs args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        VideoSource.Volume = args.Volume;
    });
}

private async void receiver_MuteChangeRequested(PlayToReceiver sender, MuteChangeRequestedEventArgs args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        VideoSource.IsMuted = args.Mute;
    });
}

 

Finally, we’ve got a working application that both send and receive content via Play To.  Here’s a video of the final product in action:

 

Summary

Today, we dove in deep to the Play To protocol, which allows us to send and receive media files from another app on another device.  I think we’ve done a pretty good job of giving you the fine-grained control necessary to make this a success in your applications.

If you’d like to download the working sample code from this article, click the icon below:

downloadXAML

Tomorrow, we’re going to dive into using another sensor: the Compass.  Navigate your way back here tomorrow to learn more about it.  See you then!

downloadTheTools

31 Days of Windows 8 | Day #21: Camera Capture

This article is Day #21 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample43

Today, we’re going to kick off a long line of article on the sensors that Windows 8 has access to.  We are starting with capturing data from the camera.  This can be both photos and video, and we’ll take a look at both in this article.

Updating our Manifest

As we have had to do for many of our topics in this series, we need to start by updating our package.appxmanifest file to enable the use of the user’s webcam and microphone.  You do that by checking two boxes in the Capabilities section, like this:

21-XAML-Manifest

Without these, you won’t be able to use these technologies, so make sure you’ve handled this first.

Capturing a Still Image

One of the great things about capturing media in Windows 8 is that practically all of the work is done for you.  That being said, there are some things to consider.  First, your app will prompt the user for permission to use your camera and microphone when you try to access that hardware.

21-XAML-Permission

Much like Lock Screen access, you only get one shot at this.  Thankfully, if the user clicks “Block” for whatever reason (maybe because it’s the highlighted option?), the camera “display” dialog will prompt the user to change their settings, like this:

21-XAML-NoPermission

And if the user opens their Settings charm, they’ll find a “Permissions” option, like this:

21-XAML-SettingsPermissions

Clicking the “Permissions” link will bring up this menu, which is generated based on the choices we made in our appxmanifest file.

21-XAML-SettingsPermissionsMenu

The user will always have the ability to turn this on and off, but since the Camera Dialog handles this case for us, we only need to make sure we actually had an image object returned to us.  Here’s the three lines of code that are required to capture an image from the camera, and return it as a StorageFile object:

CameraCaptureUI camera = new CameraCaptureUI();
camera.PhotoSettings.CroppedAspectRatio = new Size(16, 9);
StorageFile photo = await camera.CaptureFileAsync(CameraCaptureUIMode.Photo);

Technically, only two of those lines are required.  The 2nd line, which sets the CroppedAspectRatio is not required, but highly recommended.  When the Camera Dialog opens, the user is presented with a full-screen view of their webcam.

21-XAML-InitialCameraScreen

They have a couple of options (like resolution and using a timer), and they can tap on the screen (or click) to initiate the camera capture.  If we specify a CroppedAspectRatio, this is what the user will see (feel free to comment on the beard, the EMPTY Red Bull fridge, or the overall messiness of my home office):

21-XAML-AspectRatio

You can see that there are 4 white circles on the screen which indicate the cropping region.  The user has the ability to change the size and position of this box, but by specifying a CroppedAspectRatio, we can force them to choose an image that is the shape we’re expecting.  I used 16:9 in my example, but you could also do 1:1 to require a square image.  Without this option, the user is not forced to crop, but has the option to crop it any size, from the bottom menu options:

21-XAML-CropMenu

Which, if they choose Crop, will allow them to select any portion of the captured image:

21-XAML-NoRatio

Ultimately, we’re waiting for the user to click the OK button, which will return the image data to our application, letting us then use this image however we’d like.  Here’s a quick video of that entire process:

 

And here’s the code I’m using to make all of this happen:

CameraCaptureUI camera = new CameraCaptureUI();
camera.PhotoSettings.CroppedAspectRatio = new Size(16, 9);
StorageFile photo = await camera.CaptureFileAsync(CameraCaptureUIMode.Photo);

if (photo != null)
{
    BitmapImage bmp = new BitmapImage();
    IRandomAccessStream stream = await photo.OpenAsync(FileAccessMode.Read);
    bmp.SetSource(stream);
    ImageSource.Source = bmp;
    ImageSource.Visibility = Visibility.Visible;
}

As you can see, our first three lines are there, and then I check to see if we had actual data returned to us.  If we do, we read the data into a BitmapImage object, and set that as the source of an Image control I have on my page named ImageSource.  Finally, I turn the visibility of that Image control on, so that the user can see the image.  That’s pretty much all you need to do to capture an image in Windows 8 using C# and XAML.  Next, we’ll go though the same process for video.

Capturing a Video

Thankfully, video is practically identical to video.  There are a few differences related to how we interact with the data once it’s been returned, but in general, the process is very similar.

When we were talking about capturing photos, I didn’t dive too deep into the additional options that were available because they were limited (you can decide between JPG or PNG files, for example).  With video, however, I thought I’d mention a few of them.  So, here’s my code for capturing video, with a few additional options sprinked in:

CameraCaptureUI videocamera = new CameraCaptureUI();
videocamera.VideoSettings.Format = CameraCaptureUIVideoFormat.Mp4;
videocamera.VideoSettings.AllowTrimming = true;
videocamera.VideoSettings.MaxDurationInSeconds = 30;
videocamera.VideoSettings.MaxResolution = CameraCaptureUIMaxVideoResolution.HighestAvailable;

StorageFile video = await videocamera.CaptureFileAsync(CameraCaptureUIMode.Video);

if (video != null)
{
    IRandomAccessStream stream = await video.OpenAsync(FileAccessMode.Read);
    VideoSource.SetSource(stream, "video/mp4");
    VideoSource.Visibility = Visibility.Visible;
}

Let’s talk about a few of these things.  First, we can specify between MP4 video or WMV.  You won’t see a difference between the two formats until you try to share that video with other systems and platforms.  MP4 is far more widely used than WMV, but it’s also a larger format, so that’s worth considering.

Second, I’ve turned on “AllowTrimming” which allows the user to select the segment of their recorded video that they would like to use.  They’ll get a user interface similar to this, where they can drag either end of the video using the white crop circles:

21-XAML-VideoCrop

Third, I’ve specified a MaxDurationInSeconds, which limits the maximum length of video a user can provide.  If they record more than that duration, they’ll be able to select which 30 seconds (or the duration you specify) that they’d like to use.  I can see this being useful for businesses like Match.com where people make profile videos, but they don’t want them to run on forever.

Finally, I’ve also specified a MaxResolution.  This allows us to manage the resolution that the user can record with, which can also help with the final file size we end up with.  You have four choices:

21-XAML-ResolutionList

Otherwise, we end up with a very similar result to our camera efforts.  I set the source of my MediaElement, VideoSource to our new data stream, and turn the visibility of that MediaElement to Visible.

Summary

In short, capturing photos and videos from your user’s camera is very easy, as well as customizable.  Most of the complicated UI is handled directly by Windows 8, so we don’t have to worry about most of the interaction with the user.  I’ve provided screenshots and a video in this article, but the best way to get a feel for this process is to work with a real application.

To download a working solution that uses all of the code from this article, click the icon below:

downloadXAML

Tomorrow, we’re going to play with a very cool technology called Play To.  It allows us to stream media from a device to televisions, Xbox 360 devices, and more.  See you then!

downloadTheTools

31 Days of Windows 8 | Day #20: Printing

This article is Day #20 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample4

Today, we’re going to talk about printing from your Windows 8 app.  In my past as a software developer, the only time I’ve ever been concerned with printing was from a web page.  It was a simple call: window.print() and several things happened:

  • A print dialog box was opened with options for the user.
  • A print preview was created.
  • Pagination options were available based on the actual “length” of my page.

Despite all of my experience with XAML over the years, printing is not something I have had to do.  As such, I was surprised at how much code is necessary to print a simple page, but I certainly understand why it happens.  We will have to create all of those things from code, and for good reason.

Our pages are not laid out for the size or shape of paper.

Therefore, we are going to need to supplement a “printable” version of our page behind the scenes to make this easier for ourselves.  For the example today, I’m using a simple page with some RichTextBlock controls, but your page can be as complex as you’d like.  Just optimize it for the shape of a sheet of paper.

Additionally, writing this article brought me to the realization that printing from XAML/C# apps in Windows 8 is just hard.  Not only will we be handling all of the steps required to generate print previews and the like, we also have to manually handle all of the pagination required to make our document print appropriately.  This is definitely a situation where the HTML5/JS crew has an advantage over us, and it made me physically angry about how convoluted this process really is.

Making Printing Available From Your App

If you’ve tried to print already, you’ve probably discovered that it’s not possible from your base Windows 8 application.  We have to create a PrintManager object on our page first.  This is not something we can do at the application level, so each page that you might want to print from will have to repeat this code.  (I’m putting this inside my OnNavigatedTo and OnNavigatingFrom events…you can put this logic behind a click if you’d prefer.)

PrintManager manager = PrintManager.GetForCurrentView();
manager.PrintTaskRequested += manager_PrintTaskRequested;
 
 

By adding this code (and the subsequent event method, PrintTastRequested) to our page, it takes our user’s device charm menu from this:

20-XAML-Unprintable

To this:

20-XAML-Printable

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

I’m currently writing this article from a location where I don’t have any printers available to me, but the same print options are available to us when “printing” to OneNote or the Microsoft XPS Document Writer.  Using those is a perfectly acceptable test, and it’s environmentally friendly.  When working on printing for your app, please use one of these options over printing many, many pieces of paper.  It’s just wasteful, and the electronic documents will look exactly the same.

Making Printing Happen in 27 Easy Steps

If you added the two lines of code above and attempted to print, you were probably disappointed.  First, you couldn’t run the app.  We didn’t build the event handler yet.  Second, if you did create the event handler, you got an error the moment you tapped a printer to use. 

First, we have a couple of global variables we’ll be using:

PrintDocument document = null;
IPrintDocumentSource source = null;
List<UIElement> pages = null;
FrameworkElement page1;
protected event EventHandler pagesCreated;
protected const double left = 0.075;
protected const double top = 0.03;
 

Let’s explain what each of these are for now, as we’ll see them in much of the code we have left to write.

  • PrintDocument – this is the actual document object we’ll be adding our content to.
  • IPrintDocumentSource – this is the source of our PrintDocument object.
  • List<UIElement> – this will be where we store the individual pages of our printable document after we have determined the pagination.
  • FrameworkElement – this will hold our first page of our document.
  • protected event – this will be used to determine if we have actually created any pages in this process.
  • left, top – these constants will be used to define a margin in our page, so the content doesn’t run right to the edge of our page.

Let’s first look at my OnNavigatedTo event handler, where I get everything set up:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    document = new PrintDocument();
    source = document.DocumentSource;
 
    document.Paginate += printDocument_Paginate;
    document.GetPreviewPage += printDocument_GetPreviewPage;
    document.AddPages += printDocument_AddPages;
 
    PrintManager manager = PrintManager.GetForCurrentView();
    manager.PrintTaskRequested += manager_PrintTaskRequested;
 
    pages = new List<UIElement>();
 
    PrepareContent();
}
 
 

First, we instantiate our document and source objects, and then create three new event handlers for our document, Paginate, GetPreviewPage, and AddPages.  Finally, we create our PrintManager (the snippet from earlier), and as two last steps, we create our pages List<> and call a method PrepareContent().  We will revisit PrepareContent a little later.  For now, we have to look at first event handler.

void manager_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
    PrintTask task = null;
    task = args.Request.CreatePrintTask("Simple Print Job", sourceRequested =>
    {
        sourceRequested.SetSource(source);
    });
}
 

When the user selects a printer, the method above will be fired.  Inside, we create a PrintTask object, where we assign it a name, and set its source to our source object that we created earlier.  The name is the actual value that will appear when you user looks in their print queue, like this:

20-XAML-PrinterQueue

Once a printer has been selected, they will see a new window that looks like this:

20-XAML-PrintPreviewMissing

You should notice a couple of things:

  1. We haven’t even specified what to print yet.
  2. The Print Preview box seems to be struggling to show something.

Let’s fix the first problem, which is a lack of content.  Here’s my PrepareContent() method from earlier:

private void PrepareContent()
{
    if (page1 == null)
    {
        page1 = new PageForPrinting();
        StackPanel header = (StackPanel)page1.FindName("header");
        header.Visibility = Windows.UI.Xaml.Visibility.Visible;
    }
 
    PrintContainer.Children.Add(page1);
    PrintContainer.InvalidateMeasure();
    PrintContainer.UpdateLayout();
}
 

There are several important things happening in this method.  First, we’re assigning a new object, PageForPrinting to our page1 element.  PageForPrinting is actually a completely seperate XAML file in our application that is a formatted page for printing.  Second, you’ll see that we’re actually reaching into PageForPrinting, and looking for an element named “header.” (This is entirely optional, but it’s a useful illustration.) This is a StackPanel in our formatted page that is only displayed when the page is printed.  This is a great way to add custom headers and footers to pages, especially when you don’t want them shown when the page is displayed on-screen.  Finally, you can see that we’re adding our page1 to an element named PrintContainer.  We actually want to commit our page to the existing visual tree to force it to go through layout so that all of the containers distribute the content within them.

Here’s what our sample PageForPrinting.xaml file looks like:

<Page
    x:Class="Day20_Printing.PageForPrinting"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Day20_Printing"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
 
    <Grid x:Name="printableArea">
 
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="3*"/>
            <RowDefinition Height="3*"/>
            <RowDefinition Height="4*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="6*" />
            <ColumnDefinition Width="4*"/>
        </Grid.ColumnDefinitions>
 
        <StackPanel x:Name="header" Grid.Row="0" Grid.ColumnSpan="2" Height="60"  Visibility="Collapsed">
            <StackPanel Orientation="Horizontal" >
                <Image Source="ms-appx:///Assets/StoreLogo.png" HorizontalAlignment="Left" Stretch="None"/>
                <RichTextBlock Foreground="Black" FontSize="20" TextAlignment="Left" FontFamily="Segoe UI">
                    <Paragraph>Day #20 - Printing</Paragraph>
                </RichTextBlock>
            </StackPanel>
        </StackPanel>
 
        <RichTextBlock Foreground="Black" x:Name="textContent" FontSize="18" Grid.Row="1"  Grid.ColumnSpan="2" OverflowContentTarget="{Binding ElementName=firstLinkedContainer}" 
                       IsTextSelectionEnabled="True" TextAlignment="Left" FontFamily="Segoe UI" VerticalAlignment="Top" HorizontalAlignment="Left">
            <Paragraph FontSize="32">Lorem ipsum dolor sit amet, consectetur</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph >Sed convallis ornare velit et interdum. Donec sapien neque, aliquet consequat convallis at, interdum et enim. Donec iaculis, lectus vel pulvinar cursus, lectus diam interdum ante, a rhoncus tortor quam porta metus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam pulvinar fringilla vestibulum. Pellentesque pharetra nunc in turpis tempus sed faucibus ligula sagittis. Praesent hendrerit est vitae lorem mattis in porttitor urna vestibulum. Phasellus adipiscing aliquam libero ac adipiscing. In a erat sit amet erat sollicitudin bibendum id vitae dui. Vestibulum non consequat nisl. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris elit nisi, blandit et porttitor quis, malesuada nec mi.</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph >Aliquam erat volutpat. In non urna ut libero ultricies fringilla. Proin tellus neque, aliquam lacinia consequat at, vulputate et arcu. Maecenas odio nunc, lobortis sit amet pulvinar sit amet, accumsan et leo. Suspendisse erat lectus, commodo ac auctor eget, rutrum in mi. Suspendisse potenti. Proin ac elit non lacus rutrum mollis. Vivamus venenatis, tellus vel placerat lacinia, arcu ligula dignissim orci, consectetur consectetur eros massa vel nulla. Quisque malesuada iaculis ornare. Nullam tincidunt accumsan egestas. Mauris sit amet scelerisque arcu. Proin euismod sodales magna faucibus commodo. Nam in fringilla orci. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph >Sed eget nunc quis tellus interdum aliquet. Suspendisse rhoncus malesuada nisi a imperdiet. Suspendisse ullamcorper mi sed purus tristique interdum. Mauris lobortis, ante ultrices varius consequat, eros ante hendrerit enim, vulputate convallis dui ligula eget velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec eget lectus fermentum nisi consequat dictum. Sed volutpat justo non purus semper vel pretium enim molestie. Nam consectetur, lectus quis feugiat malesuada, neque nunc faucibus velit, nec vehicula risus est id sapien. Vestibulum ut metus massa, ut placerat lacus. Fusce condimentum vehicula tortor, nec vestibulum ligula iaculis ut. Nulla facilisi. Phasellus tincidunt scelerisque erat, ut fermentum urna pretium eu. Donec ut nibh orci. Curabitur sodales metus dictum mauris varius vitae mollis tellus pulvinar. Quisque facilisis ligula sed risus laoreet non lacinia odio luctus. Nam lobortis rhoncus felis vitae ultrices.</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph>Aliquam erat volutpat. In non urna ut libero ultricies fringilla. Proin tellus neque, aliquam lacinia consequat at, vulputate et arcu. Maecenas odio nunc, lobortis sit amet pulvinar sit amet, accumsan et leo. Suspendisse erat lectus, commodo ac auctor eget, rutrum in mi. Suspendisse potenti. Proin ac elit non lacus rutrum mollis. Vivamus venenatis, tellus vel placerat lacinia, arcu ligula dignissim orci, consectetur consectetur eros massa vel nulla. Quisque malesuada iaculis ornare. Nullam tincidunt accumsan egestas. Mauris sit amet scelerisque arcu. Proin euismod sodales magna faucibus commodo. Nam in fringilla orci. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph >Sed eget nunc quis tellus interdum aliquet. Suspendisse rhoncus malesuada nisi a imperdiet. Suspendisse ullamcorper mi sed purus tristique interdum. Mauris lobortis, ante ultrices varius consequat, eros ante hendrerit enim, vulputate convallis dui ligula eget velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec eget lectus fermentum nisi consequat dictum. Sed volutpat justo non purus semper vel pretium enim molestie. Nam consectetur, lectus quis feugiat malesuada, neque nunc faucibus velit, nec vehicula risus est id sapien. Vestibulum ut metus massa, ut placerat lacus. Fusce condimentum vehicula tortor, nec vestibulum ligula iaculis ut. Nulla facilisi. Phasellus tincidunt scelerisque erat, ut fermentum urna pretium eu. Donec ut nibh orci. Curabitur sodales metus dictum mauris varius vitae mollis tellus pulvinar. Quisque facilisis ligula sed  risus laoreet non lacinia odio luctus. Nam lobortis rhoncus felis vitae ultrices.</Paragraph>
        </RichTextBlock>
        <RichTextBlockOverflow x:Name="firstLinkedContainer" OverflowContentTarget="{Binding ElementName=continuationPageLinkedContainer}" Grid.Row="2" Grid.Column="0"/>
        <RichTextBlockOverflow x:Name="continuationPageLinkedContainer" Grid.Row="3" Grid.ColumnSpan="2"/>
        <Image Source="ms-appx:///Assets/Logo.png" x:Name="scenarioImage"  HorizontalAlignment="Right" Grid.Row="2" Grid.Column="1" Margin="10"/>
 
        <StackPanel x:Name="footer"  Grid.Row="4" Grid.Column="0" VerticalAlignment="Top" Visibility="Collapsed">
            <Image Source="ms-appx:///Assets/StoreLogo.png" HorizontalAlignment="Left" Stretch="None"/>
            <RichTextBlock Foreground="Black" FontSize="16" TextAlignment="Left" FontFamily="Segoe UI">
                <Paragraph>Copyright © 31 Days of Windows 8.  But please reuse this.</Paragraph>
            </RichTextBlock>
        </StackPanel>
    </Grid>
</Page>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

We will be using the RichTextBlockOverflow elements link page breaks.  There is nothing changed in the code behind file for this page, but if you were loading this content dynamically, there certainly would be.

At this point, we have handled the user’s choice of printer, and we’ve pulled in a formatted page for printing.  Our next step is to handle the other three event handlers that we’ve created, starting with the Paginate one:

void printDocument_Paginate(object sender, PaginateEventArgs e)
{
    pages.Clear();
    PrintContainer.Children.Clear();
 
    RichTextBlockOverflow lastRTBOOnPage;
    PrintTaskOptions printingOptions = ((PrintTaskOptions)e.PrintTaskOptions);
    PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);
 
    lastRTBOOnPage = AddOnePrintPreviewPage(null, pageDescription);
 
    while (lastRTBOOnPage.HasOverflowContent && lastRTBOOnPage.Visibility == Windows.UI.Xaml.Visibility.Visible)
    {
        lastRTBOOnPage = AddOnePrintPreviewPage(lastRTBOOnPage, pageDescription);
    }
 
    if (pagesCreated != null)
    {
        pagesCreated.Invoke(pages, null);
    }
 
    PrintDocument printDoc = (PrintDocument)sender;
 
    printDoc.SetPreviewPageCount(pages.Count, PreviewPageCountType.Intermediate);
}

 

Each time that this method is called, we want to clear our “pages” list, as well as anything we may have committed to the PrintContainer object that is on our MainPage.  (It’s just a canvas with an Opacity set to zero, so that we can add content to it, but the user doesn’t see it.)

We’re also creating a RichTextBlockOverflow object to keep track of which page we’re currently on.  Finally, we’re calling a method called AddOnePrintPreviewPage.  This method does all of the heavy lifting for us, calculating the size of the page, setting margins, and saving them to the pages List<>.  You’ll also see that we activate the footer in our page as well.

private RichTextBlockOverflow AddOnePrintPreviewPage(RichTextBlockOverflow lastRTBOAdded, PrintPageDescription printPageDescription)
{
    FrameworkElement page;
    RichTextBlockOverflow link;
 
    if (lastRTBOAdded == null)
    {
        page = page1;
        StackPanel footer = (StackPanel)page.FindName("footer");
        footer.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    }
    else
    {
        page = new ContinuationPage(lastRTBOAdded);
    }
 
    page.Width = printPageDescription.PageSize.Width;
    page.Height = printPageDescription.PageSize.Height;
 
    Grid printableArea = (Grid)page.FindName("printableArea");
 
    double marginWidth = Math.Max(printPageDescription.PageSize.Width - printPageDescription.ImageableRect.Width, printPageDescription.PageSize.Width * left * 2);
    double marginHeight = Math.Max(printPageDescription.PageSize.Height - printPageDescription.ImageableRect.Height, printPageDescription.PageSize.Height * top * 2);
 
    printableArea.Width = page1.Width - marginWidth;
    printableArea.Height = page1.Height - marginHeight;
          
    PrintContainer.Children.Add(page);
    PrintContainer.InvalidateMeasure();
    PrintContainer.UpdateLayout();
 
    // Find the last text container and see if the content is overflowing
    link = (RichTextBlockOverflow)page.FindName("continuationPageLinkedContainer");
 
    // Check if this is the last page
    if (!link.HasOverflowContent && link.Visibility == Windows.UI.Xaml.Visibility.Visible)
    {
        StackPanel footer = (StackPanel)page.FindName("footer");
        footer.Visibility = Windows.UI.Xaml.Visibility.Visible;
    }
 
    // Add the page to the page preview collection
    pages.Add(page);
 
    return link;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

If another RichTextBlockOverflow element is found, it is returned to our calling method, and we loop through this process until we’ve added all of the pages to our List<>.

Next, we need to add those pages to our original PrintDocument object, and we do this with the AddPages event we registered at the beginning.  We simply loop through all of the pages that were created in our Paginate method.

void printDocument_AddPages(object sender, AddPagesEventArgs e)
{
    for (int i = 0; i < pages.Count; i++)
    {
        document.AddPage(pages[i]);
    }
 
    PrintDocument printDoc = (PrintDocument)sender;
    printDoc.AddPagesComplete();
}
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

When that Print Preview box loads on the user’s screen, the GetPreviewPage is fired.  This allows us to provide the first page of the set to the Print Preview window:

void printDocument_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
    PrintDocument printDoc = (PrintDocument)sender;
    printDoc.SetPreviewPage(e.PageNumber, pages[e.PageNumber - 1]);
}
 

This is the last step.  Once we provide our preview pages, we’re done.  Your user should now see a menu like this:

20-XAML-PrintPreviewWorking

And if they click Print when using the Microsoft XPS Document Writer, they end up with a file in their Documents folder:

20-XAML-DocumentsFolder

Finally, when we open that file, we can see that our formatted content has been spread over two pages in this new document, just as it would have been on a printer.

 20-XAML-FinalProduct

Summary

In short, this is a really messy, difficult process that seems like it HAS to be simpler than it is.  Sadly, I’ve made this one about as simple as it gets for now.  I think I am going to revisit this problem in the future, and build out a PrintHelper class that just allows you to build a page, formatted with a few simple rules, and call a method to make all of this magic happen.  Very few people will build apps that print if it isn’t made to be easier.

If you’d like to download a working sample of the code shown in this article, click the icon below (I would HIGHLY recommend it for this topic):

downloadXAML

Tomorrow, we’re going to touch on a more useful (and far simpler to implement) subject: capturing data from the camera.  See you then!

downloadTheTools

31 Days of Windows 8 | Day #19: File Picker

This article is Day #19 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample

Today, we are going to look at another invaluable tool in Windows 8 development, the File Picker.  There are millions of ways to use the File Picker, but when you boil it down, you’re going to need to get files from the user’s computer for something.  This article will show you how to do it.

Updating Your Package.Appxmanifest

As with all of the different mechanisms we’ve used to interact with the user’s system, we need to start with updating our package.appxmanifest file.  Since we are going to be both opening files AND saving files in this article, we need to add both of those Declarations to our file.

For the File Open Picker, you’ll start with a form that looks like this:

19-XAML-FileOpenManifestBroken

You’re expected to make one of two choices:

  • Supports any file type.  By checking this box, you are declaring that you could access any file type from your application.
  • Supported file type.  You can also specify which file types you want to open, so if your app only opens Excel files, for example, you could list “.xls” as your file type.

For this demo, you can just select “Supports any file type.”  The same process can be done for the File Save Picker, which offers you the same choices, like this:

19-XAML-FileSaveManifestBroken

Getting a File from Your User’s Machine

This article is going to build a progressively more complex app for selecting files from a user’s machine.  We’ll start getting just one file, then multiple files, and then multiple files of only a specific file type (.png).  Most of the code for each example is identical, so we’ll focus on what is the “new” code in each step.

For selecting a single file from the user’s hard drive, we are going to start with the FileOpenPicker object.  This is the object that will open the File Picker dialog for the user, help them select one or many files, and return them to us.  It has many options, and we’ll work our way through all of them.  For selecting a single file, of any type, the code starts quite simply:

FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".xls");
StorageFile file = await picker.PickSingleFileAsync();

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

You’ll notice that we have to set a FileTypeFilter when we create a FileOpenPicker.  You are not allowed to provide a wildcard, or just ignore the filter.  You have to provide at least one, but you can also provide several, which means you can list 8-10 common file types (especially when you’re looking for images on the user’s device, because they could be PNG, JPG, JPEG, GIF, BMP, etc.) When you run this, your user will see an interface that looks like this:

19-XAML-FilePicker

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

This is the File Picker interface, and once the user selects a file, it will be returned to your application as a StorageFile, which we have worked with in several of the previous articles in this series.

Next, we might have a good idea as to where the user should start looking for the files they would want to open in your app.  Maybe it’s their Photos collection, or Music.  We can specify one of 8 locations on their computer:

19-XAML-PickerLocationId

So, to get the File Picker to open in one of these locations, you need to add one line of code to your method, so that it looks like this (you can see I’ve added a few new filters as well):

FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".png");
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".gif");
picker.FileTypeFilter.Add(".bmp");
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
StorageFile file = await picker.PickSingleFileAsync();
 

This time the File Picker will launch directly to the location you’ve specified (in my example, it is the Pictures Library):

19-XAML-FilePicker-Location

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

The last option we can manipulate relates to how the File Picker will display the files.  Simply use the ViewMode property of the FileOpenPicker object, like this:

picker.ViewMode = PickerViewMode.List;
 

Your options are limited to List or Thumbnail, but they look distinctly different.  Thumbnail ONLY shows a square image representing the file.  List shows an icon as well, but also shows some of the metadata for the files.  Here’s a look at the same folder with each view (click to enlarge):

List

19-XAML-List

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Thumbnail

19-XAML-Thumbnail

Retrieving Multiple Files From Your User’s Computer

Sometimes, we want to grab more than one file at a time.  In this case, we can use a different method call on our FileOpenPicker object.  This time, we’ll use the PickMultipleFilesAsync() method.

FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".png");
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".bmp");
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
picker.ViewMode = PickerViewMode.Thumbnail;
IReadOnlyList<StorageFile> files = await picker.PickMultipleFilesAsync();
 

So, more or less, there’s very little difference between selecting one or multiple files from a code perspective.  We do receive a collection of StorageFile objects rather than just one, but that should be expected when we’re enabling multiple files to be selected.

What is different, at least from what I can tell, is HOW the user can select multiple files.

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

19-XAML-MultipleFileSelection

As you can see from the image above (click to enlarge), not only can we select multiple files, but we can also see that there is a “basket” of sorts at the bottom of the File Picker interface.  This shows a list of the files that the user has selected.  What it also enables, however, is a way for a user to select multiple files at once, from completely different folders.  Your users will be able to select a few files from one folder, and a few files from another, and they’ll all be provided to your application at once, without your user having to make multiple trips to those folders.  This is an absolutely awesome feature that should not be overlooked.

In our final section of this article, let’s look at how we can use the File Picker to also save files to a user’s machine.

Saving a File To Your User’s Hard Drive

One of the related operations we may want to use is to save a file to your user’s machine.  This is different from what we talked about on Day #8 of this series, where we were saving files internal to our application.  In this instance, we are saving a permanent file that will continue to live on the user’s machine, even if our app is uninstalled.  If your files are unusable by other apps, this is likely NOT the way you’d want to store your files.  I generally recommend saving files like this for file types that could be opened by a variety of applications.  To do this, it looks very similar to our FileOpenPicker, but it’s now a FileSavePicker.

The other diffference with this situation is that we are writing data to the user’s hard drive.  This means we have all of the other hassles that come with it.  Files can be open already, they can be edited by other apps, or even deleted as we’re working with them.  So we will first launch the File Save Picker to determine where the user wants to save their file (as well as what file type and name they want to give it.  This looks like this:

19-XAML-FileSave

And here’s what the code looks like to make this entire process happen:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

FileSavePicker saver = new FileSavePicker();
saver.SuggestedStartLocation = PickerLocationId.Desktop;
saver.FileTypeChoices.Add("Text File", new List<string>() { ".txt" });
saver.FileTypeChoices.Add("Microsoft Excel", new List<string>() { ".xls" });
saver.FileTypeChoices.Add("Image", new List<string>() { ".png", ".jpg", ".bmp" });
saver.SuggestedFileName = "PeriodicTableOfTheElements";
StorageFile file = await saver.PickSaveFileAsync();
 
if (file != null)
{
    CachedFileManager.DeferUpdates(file);
    await FileIO.WriteTextAsync(file, "This is a link to the Periodic Table of the Elements.  http://www.ptable.com/  You didn't expect to find all of the contents here, did you?");
    FileUpdateStatus status = await CachedFileManager.CompleteUpdatesAsync(file);
}
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

In this case, you can see that the beginning of our code sample doesn’t look that different.  Instead of filters, we can add FileTypeChoices which function as the different formats our user might want to save their file.  The choices I’ve included don’t really make much sense together, but it helps to illustrate that you can specify any file types (or types) in this list.  It’s at your full control.

In addition, once the user has selected a location and name for their file, we’re not done.  If they actually returned those values to use, our next step is to actually write the file to the drive itself.  Inside our if statement, we use the DeferUpdates() method to prevent any additional edits to the file that we’re working with.  They will be “deferred” until after our operation is complete.  Next, we write the contents of our file.  Finally, we commit those changes using the CompleteUpdatesAsync() method.  This is where all of the work is actually completed.

You’ll find that if the file already exists, you’ll also get prompted with a “Replace the existing file?” dialog box.

19-XAML-ReplaceDialog

Ultimately, that’s all it takes to save a simple file to the user’s system.  For more complex operations and file types, you’re going to want to explore the FileIO class.  You can read more about that on MSDN.

Selecting a Folder on the User’s Machine

The last topic I want to cover in this article is also very related to the others: selecting a folder from the user’s machine instead of a specific file.  This is great for allowing the user to choose a default save location for the future, or perhaps a place for you to look for files in the future.  What’s great about this process is that once the user has selected a folder, we can save it as our default folder, and we are granted explicit rights to read and write to/from this folder in the future without having to request future access.  Here’s how we do it.

FolderPicker picker = new FolderPicker();
picker.FileTypeFilter.Add(".xls");
StorageFolder folder = await picker.PickSingleFolderAsync();
if (folder != null)
{
    StorageApplicationPermissions.FutureAccessList.AddOrReplace("DefaultFolder", folder);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

As you can see, our first three lines of code look very familiar.  However, if we confirm that an actual folder was returned to us, we can save this to the StorageApplicationPermissions.FutureAccessList as a place we have been granted permission to use.  Saving it this way, and referring to it in the future will limit the number of times you have to ask your user for permission to save a file to their system.

Summary

Today, we looked at several different ways to help our users interact with the files that live on their machine.  We saw how to open single and multiple files, save files back to the hard drive, and even select default save locations for future use.  You’ll likely find yourself using these methods over and over in your apps, so this is an important lesson to learn.

If you would like to download the sample code that was discussed in this article, click the icon below:

downloadXAML

Tomorrow, we’re going to take a look at another common feature you’re likely to run into: Printing.  We’ll look at how to communicate with a user’s printers, as well as how we register with the system to make printing even easier.  See you then!

downloadTheTools

31 Days of Windows 8 | Day #18: File Associations & App Contracts

This article is Day #18 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample

Today, we are going to look at a couple of cool features in Windows 8: File Associations and App Contracts.  With file associations, we’ll look at both sides of this Windows 8 feature:

  • Registering our app with Windows 8 as an app that opens files of a certain type, like .png, or a custom extension, .31days.
  • Empowering our app to suggest a list of compatible applications when our user tries to open a file that our app doesn’t support, like an .xls file.

Finally, we’ll also discuss some of the other ways our application can register with the user’s system, including things like AutoPlay.

Registering to Open Certain File Types

There’s going to be times that we want to have our user open files of a certain type in our application.  Maybe we are creating an image editor, so no matter what type of image format the user’s files are in, if they’re a common image type, we should be able to open it.  Perhaps we’re building an XML editor that consumes .xml files.  In either case, we want to register our app with the user’s system, so that when they get a prompt like this:

18-XAML-OpenDialog

We want to make sure that our app is in this list when a user opens files appropriate for our application.  To do this, it’s as simple as making some simple changes to our package.appxmanifest file.  Open the file, and choose the declarations tab.  From the “Available Declarations” selection list, choose “File Type Associations,” and click Add.

You will see that it fills the screen with a new form for specifying the file associations you would like to register your app with.  By default, it has a few red Xs that you will need to address.

18-XAML-DeclarationsBroken

For this sample, I am going to pick two different file types.  The first will be a custom extension that files created by my app will use: “31days".  The other will be to register with the “png” extension, so we can try to open images with this application as well.

18-XAML-DeclarationsFixed

The first value, Name, is just a name for the set of extensions you are listing here.  The reason that we can have different sets of extensions is because you might want your app to act differently depending on the file type.  At the bottom of the form, you can specify the entry point for your application for this set of files.  If it’s an XML file, you might want to take them directly to your text editor page.  If it’s an image, perhaps you’d rather take them to an image editing page, or just attach it to an email.  That’s up to you.

Once you’ve done that, you’re basically done.  Run the application once, to install it on your system, and then try to do an “Open With” for the specific file types you used.  The easiest way to test this is to jump to your Desktop, and create a new Text File.  I named mine Blankenburg.31days as the file name.  First, you’ll get the standard warning for renaming files:

18-XAML-FileRenameWarning

Then, you’ll get an icon that looks like all of the default icons that come with a default project.  The only problem is, I don’t have an image that looks like that in my project anymore, so I’m not sure where this one comes from.  Here’s my file sitting on the desktop:

18-XAML-FileNoLogo

That’s not going to work.  We want our logo or icon to appear when we are the default application for a file type.  Just as Excel files have an Excel icon, so too should .31days files have a 31 Days icon.  To do this, jump back into your package.appxmanifest file, and specify a logo in the section where you specified name, like this:

18-XAML-DeclarationsLogo

You can use any icon you’d like, but they do have a square aspect ratio, so I chose my Logo.png file which has the same ratio.  You’ll end up with files that look like this (make sure to run your app again to update your system):

18-XAML-FileLogo

In addition, when we right-click on this file and choose “Open With…” we get this menu now:

 18-XAML-31DaysExtensionDialog

You can see that we’ve found yet another location where our SmallLogo.png file is being used by default, with no place to change it.  Finally, since we also registered our app for “.png” files, let’s take a look at what opening one of those looks like:

18-XAML-PNGAssociation

So, for step one of our article, we’ve demonstrated how to set file associations for your application in Windows 8.  In the next section, we’re going to look at how we prompt the user to open a file from our app to another app.

Opening a File In Another App

Sometimes, your application just can’t open every single file type.  For example, Microsoft Outlook is really good at accepting images, text, and files, but if you’ve ever tried to open an Excel file attached to an email, you’ve probably noticed (and come to expect) that the file gets opened in Excel, not Outlook.  We want to provide that same functionality to our users.  To do this, we are going to explore the Windows.System.Launcher class.

In our first simple example, we’re just going to launch our file (the Excel file from Day #17) without any options or settings.  The system will use the default app to launch our file, without any choice or warning.

Uri uri = new Uri("ms-appx:///Assets/PeriodicTable.xls");
StorageFile sf = await StorageFile.GetFileFromApplicationUriAsync(uri);
await Launcher.LaunchFileAsync(sf);
 

As you can see in this first example, it’s pretty simple.  We call Launcher.LaunchFileAsync(), and pass it a StorageFile object.  Sometimes, however, you might want to warn your user that you are going to be launching another piece of software, so that they get to decide if that’s really what they want to do.  In this case, we can create a LauncherOptions object, and provde that as part of our LaunchFileAsync() call.  Like this:

Uri uri = new Uri("ms-appx:///Assets/PeriodicTable.xls");
StorageFile sf = await StorageFile.GetFileFromApplicationUriAsync(uri);
 
LauncherOptions options = new LauncherOptions();
options.TreatAsUntrusted = true;
 
bool action = await Launcher.LaunchFileAsync(sf, options);
 

When we do this, an alert pops up on the screen letting the user know that another application is being opened.

18-XAML-SwitchingAppsAlert

Finally, there may be a time where you want your user to be able to select the app that they will open a file with.  In this case, we want to bring up the same box that appears when a user selected “Open With…” in the previous section.  To do this, we just set another option: DisplayApplicationPicker.  If you want to modify the position of this box, there’s a little more work, but it’s very similar to what we did on Day #16 with context menus: we need to determine where on the screen to show the box.

If you don’t specify a location, however, it will launch the dialog box centered on the user’s screen.  Personally, I prefer how this looks to having it closer to the control the user clicked, but I understand what the Windows team is trying to do.  Here’s screenshots of the difference (click to enlarge):

Open With Dialog Centered Open With Dialog Positioned
18-XAML-OpenWithCentered 18-XAML-OpenWithPositioned

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }  And here’s the code to make it happen.  (If you don’t want your box to be positioned, which is actually my recommendation, just chop out the pieces that are related to positioning.)

private async void OpenExcelButtonWithOpenWithMenu_Click(object sender, RoutedEventArgs e)
{
    Uri uri = new Uri("ms-appx:///Assets/PeriodicTable.xls");
    StorageFile sf = await StorageFile.GetFileFromApplicationUriAsync(uri);
 
    LauncherOptions options = new LauncherOptions();
    options.DisplayApplicationPicker = true;
    options.UI.InvocationPoint = GetPosition(sender as FrameworkElement);
    options.UI.PreferredPlacement = Placement.Below;
 
    bool action = await Launcher.LaunchFileAsync(sf, options);
}
 
private Point GetPosition(FrameworkElement sender)
{
    GeneralTransform transform = sender.TransformToVisual(null);
    Point location = transform.TransformPoint(new Point());
    location.Y = location.Y + sender.ActualHeight;
 
    return location;
}
 

So there you have it.  Three different ways to launch a file in a separate application from your own.  In the final section of this article, we’re going to look at App Contracts, and how we can make our apps available in other lists for our users.

Using App Contracts for Good

When reading this article, some developers will think that it’s a good idea to register their app for every possible file extension, because it will keep their app and logo in front of their users as often as possible.

PLEASE DON’T DO THAT.

That rule applies to App Contracts as well.  Don’t abuse them, especially if you aren’t actually providing the functionality required.  This will be reviewed when you submit your application to the store, so it will be discovered.

Account Picture Provider

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

You can declare this functionality if you can provide the ability for the user to create a new Account Picture in Windows 8.  You get to the menu on the Start Screen, like this:

18-XAML-AccountPicture

And when your app is registered, the options will look like this, with your app listed:

18-XAML-PersonalizeMenu
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

To do this, just add the Account Picture Provider to your declarations tab in your package.appxmanifest file, like this:

18-XAML-AccountPictureProvider

By default, it will just launch your app, but you can also provide custom entry points, so that the user ends up on the right section of your app automatically.

AutoPlay and Protocols

There are three other App Contracts we can subscribe to, and each of them only really requires another entry in the Declarations.

“AutoPlay Content” recognizes when new media is inserted into the device, like a USB drive or DVD.  If the content type you register for is present, your app will appear in the AutoPlay list that is displayed.  There is an excellent article on MSDN about AutoPlay, and the different values you can use.

“AutoPlay Device” is basically the same idea, but you’re registering for specific types of devices, like cameras, printers, or USB drives.  The same article above applies to this as well.

“Protocol” is a little different.  This allows us to register for URI schemes, like “mailto:” or even use our own custom URI schemes like “31days:.”  It’s another way to register our application with the system to be used at the appropriate times.  Microsoft has an excellent article about protocols as well.

Summary

Today, we looked at some of the extensible points we can access to make our app more prominent and useful to our users.  If they expect to be able to open images with our application, then we should show up in their list of apps when they try to open an image.

If you would like to see the full working solution for the code covered in this article, click the download icon below:

downloadXAML 

Tomorrow, we’re going to look at another powerful mechanism in Windows 8: the File Picker.  This empowers our user to grab files from their hard drive, and provide them to our applications.  See you then!

downloadTheTools

31 Days of Windows 8 | Day #17: The Clipboard

This article is Day #17 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample

Today, we’re going to focus on the Windows 8 clipboard exclusively. More specifically, we’re going to look at how we can save and retrieve data from this system-wide and heavily used mechanism.  There are generally four types of data that we are going to see a user want to copy and paste:

  • Text
  • HTML
  • Images
  • Files

We’ll write some code in this article that shows not only how to copy and paste these values, but also how to detect which type of data the clipboard currently contains.

Saving Data to the Clipboard

As we saw briefly in yesterday’s article on context menus, we took a very quick look at saving some text to the clipboard.  This time, we’ll cover that in more depth, and include saving images and files to the clipboard as well.  To get started, we should talk about the DataPackage object.

A DataPackage is what we are going to use to transport our data.  We will create a DataPackage when we save data to the clipboard, and we will use the Clipboard.GetContent() method to retrieve the data when we’re pasting it.

In my sample project (which you can download from GitHub), you will see that I’ve created a button with an event handler for each data type.  We’ll spend the rest of this section looking at how we store these different types of data to the Clipboard.

It is important to note that the clipboard is actually divided into several sections, one for each data type.  You can save data to one or many of these sections, and the target for pasting will make the determination as to what type of data it will display when pasted.  This might sound very similar to how the Share Contract works, and for the most part, it is.

It’s also likely that you’ve encountered this in the past: you copy content from one app, and try to paste it in another, only to have it paste something else.  This is a direct artifact of the “sections” of the clipboard.  Let’s take a look at how to save each data type:

Text

Certainly the simplest example, text can be stored to the Clipboard in three lines of code:

DataPackage d = new DataPackage();
d.SetText(TextBoxValue.Text);
Clipboard.SetContent(d);
 

You will see a pattern form as we move through the other examples, but this is about as basic as they come.  We create a new DataPackage object, call the SetText() method with the text we want to save, and then set the content of the Clipboard to that DataPackage.

HTML

HTML, while similar to text, has its own quirks and formatting, and because of this, requires slightly different treatment when adding it to a DataPackage.  In this example, I am grabbing the HTML source from a WebView control (HTMLSource), and using that as my DataPackage contents.  However, I am also setting the SetText() value of this DataPackage, so that you can see how multiple values can be set at the same time.

DataPackage d = new DataPackage();
            
string s = HtmlFormatHelper.CreateHtmlFormat(HTMLSource.InvokeScript("eval", new string[] { "document.documentElement.outerHTML;" }));
d.SetHtmlFormat(s);
 
string t = HtmlUtilities.ConvertToText(s);
d.SetText(t);
 
Clipboard.SetContent(d);

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

As you can see above, we can set multiple values at the same time, and as you get deeper into this example, you’ll start to notice that most of your favorite apps store data in multiple formats “just in case.”

Images

Our next example is images, and I want to be really clear on this one.  We’re not talking about a file that happens to be an image.  We’re talking about copying an image from a website, for example, and then pasting it into a OneNote file or Windows Live Writer.  In my example, I am allowing the user to copy an image from my application to their clipboard.

DataPackage d = new DataPackage();
Uri uri = new Uri("ms-appx:///Assets/WideLogo.png");
StorageFile sf = await StorageFile.GetFileFromApplicationUriAsync(uri);
d.SetBitmap(RandomAccessStreamReference.CreateFromFile(sf));
Clipboard.SetContent(d);
 

In the image example, we are grabbing the image from its location in our project, creating a StorageFile from it, and then setting the SetBitmap() content of the DataPackage with it.  As with all of the examples in this article, we end our code with Clipboard.SetContent() to actually complete the action.

Files

Files are treated very similarly to images, but there’s one major difference: we can copy multiple files to the clipboard at once.  You’ve done this before, highlight and grab some of the files cluttering up your desktop before dragging them as a group to the Recycle Bin.  Instead of throwing them away, you could cut/copy them, and paste them to another location.  Files are always passed as a collection of files, even if there is only one.

DataPackage d = new DataPackage();
Uri uri = new Uri("ms-appx:///Assets/PeriodicTable.xls");
List<StorageFile> files = new List<StorageFile>();
StorageFile sf = await StorageFile.GetFileFromApplicationUriAsync(uri);
files.Add(sf);
d.SetStorageItems(files);
Clipboard.SetContent(d);
 

As you can see, there’s very little difference between the code for images and files.  One important note, as you work through this in your own project, relates to files you might add to your project.  When I added the PeriodicTable.xls file to my project, I initially forgot to set the “Build Action” on that file in my project, and by default, the Build Action is “None.”  If you also forget, using the code above will result in a “FileNotFoundException.”

Simply set the Build Action of your file to “Content” by looking in the Properties pane of Visual Studio 2012.

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }17-XAML-BuildAction

So, as far as saving content to the Clipboard, that’s just about it!  Our next step is going to be retrieving it from the Clipboard when the user chooses to paste it.

Detecting the Contents of the Clipboard

When it comes to “getting pasted” as I’ve started saying, I tend to do this all in one method that gets the content, determines which data type is available, and displays the content correctly based on that determination.

I had hoped, when writing this article, that there was going to be some amazing event handler, like OnPastedTo() or something, that would recognize when the user is attempting to paste some content to the app, so that we can take action on it.  (I personally live and die with Ctrl + X, Ctrl + C, and Ctrl + V).  I have not found that event yet, but if/when I do, I will update this page.  For now, I’ve created my own event handler that fires when you click a button.

You’ll see, in the example below, that I call the GetContent() method on the Clipboard, and then use a series of IF statements to act on the content appropriately.

private async void PasteButton_Click(object sender, RoutedEventArgs e)
{
    var DataPackage = Clipboard.GetContent();
 
    if (DataPackage.Contains(StandardDataFormats.Text))
    {
        TextBoxPaste.Text = await DataPackage.GetTextAsync();
    }
 
    if (DataPackage.Contains(StandardDataFormats.Bitmap))
    {
        RandomAccessStreamReference image = await DataPackage.GetBitmapAsync();
        var imageStream = await image.OpenReadAsync();
        BitmapImage bmp = new BitmapImage();
        bmp.SetSource(imageStream);
        ImagePaste.Source = bmp;
    }
 
    if (DataPackage.Contains(StandardDataFormats.StorageItems))
    {
        var storageFiles = await DataPackage.GetStorageItemsAsync();
 
        foreach (var file in storageFiles)
        {
            var currentFile = file as StorageFile;
            await currentFile.CopyAsync(ApplicationData.Current.LocalFolder, currentFile.Name, NameCollisionOption.ReplaceExisting);
            MessageText.Text = currentFile.Name + " has been saved to " + ApplicationData.Current.LocalFolder.Path.ToString() + "/" + currentFile.Name;
        }
    }
 
    if (DataPackage.Contains(StandardDataFormats.Html))
    {
        string html = await DataPackage.GetHtmlFormatAsync();
        HTMLPaste.NavigateToString(html);
    }
}

 

Let me quickly discuss each of the IF statements.  For text, I simply grab the Text data and place it in a TextBlock control on my page.  For an image, I need to convert the Image data to a BitmapImage object before I can set it as the source of an Image control.  For files, I loop through the collection of files, saving each of them as local files in my application, and then write the name of the file and the location of the folder to a TextBlock.  Finally, for HTML, I grab the HTML source, and, using a WebView control, navigate the control to the HTML source that I have received.

In all it’s pretty simple, but I was delighted the first time that I copied content from a webpage directly into my app, and it just worked!  The same is true for images, text, and files.  I highly recommend grabbing the source of this project and playing with it.  It will really help you to understand exactly how the clipboard is being used by your other applications.

Summary

Today, we took a deep look at the Clipboard, and how we can save and retrieve data as needed.  It supports several different file types, and I think you’ll be surprised just how redundant most of your favorite applications are when they save data to the Clipboard.

To download the entire code solution for the project discussed in this article, click the icon below:

downloadXAML

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Tomorrow, we’re going to add another useful tool to our Windows 8 development tool belt: the FilePicker.  We’ll look at retrieving files from the user’s device, and even filtering them so that we only get our preferred file types.  See you then!

downloadTheTools

31 Days of Windows 8 | Day #16: Context Menus

This article is Day #16 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample

Today, our focus is on context menus.  These are those small little popup commands that appear from time to time in your application when you right click on something. Microsoft offers some very specific guidance on when to use these context menus vs. when to use the AppBar control instead, so we will be following those rules in this article. 

What Is a Context Menu?

If you’ve used Windows 8 at all, you’ve likely encountered these before.  Often they result from a right-click on something you couldn’t select, or on text you wanted to interact with.  Here’s a quick sample of a context menu:

An image of the context menu for text shown over editable text while there is no text in the clipboard to paste.

(image from http://msdn.microsoft.com/library/windows/apps/Hh465308)

You could also launch a context menu from an element on your page that is unselectable, like this image in my sample app:

16-XAML-ContextMenuExample

Right-clicking the image launches the context menu to the right.  (I will show you how to make this happen next.)  Each of the command items will have an action assigned to it, which is executed when the item is clicked.

Determining The Location of an Element

You may have noticed that context menus appear directly adjacent to the element that has been selected.  This doesn’t happen by magic.  We’ll actually have to determine the position of the clicked element by ourselves (as well as its size), and pass that to our Popup menu control when we create it in the form of a Rect object.  Here’s the method I’ve created to determine this location:

private Rect GetRect(object sender)
{
    FrameworkElement element = sender as FrameworkElement;
    GeneralTransform elementTransform = element.TransformToVisual(null);
    Point point = elementTransform.TransformPoint(new Point());
    return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
}
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

As you can see, I will be able to pass my “sender” object into this method, and return a Rect object with a point (the location of the top left corner of our element) and size (the dimensions of our calling object.)

Creating a Context Menu

Once I have this GetRect() method in place, creating a context menu around any of my controls is simple.  In this example, I have added a RightTapped event to my image, which calls this method:

private async void Element_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
    PopupMenu p = new PopupMenu();
    p.Commands.Add(new UICommand("31 Days of Windows 8", (command) =>
        { ((Grid)Logo.Parent).Background = new SolidColorBrush(Colors.Orange); }));
    await p.ShowForSelectionAsync(GetRect(sender), Placement.Right);
}
 

As you can see above, this simply creates a new PopupMenu control, adds a command to it (with the executing code specified in the lambda expression), and calls the ShowForSelectionAsync() method to show it.  This method takes two parameters:

  • The first is the Rect value that we are retrieving from our GetRect() method.  Pass the sender in, get the appropriate Rect in return.
  • The second is the placement of the context menu.  In my example, I set Placement.Right as my value.  You have four options, but don’t go crazy with this.  Microsoft’s guidance on context menus is that “they should appear above the object they are acting upon, unless it obscures other UI elements, in which case, moving it to the side or below the object is acceptable.”  Keep the experience in Windows 8 consistent for your users unless you have a good reason to do otherwise.

Running this code, as shown above, will result in a menu that looks like this:

16-XAML-ContextMenuRight

And when I execute that option, the background of the page turns orange.  Like this:

16-XAML-ContextMenuRightLight

So, that’s how we pop a context menu.  But how many commands can we have at once?  The answer is SIX.  Adding more than 6 elements to your PopupMenu control will result in an error when you try to create it.  I showed it earlier, but here’s an example of a 6 element menu:

16-XAML-ContextMenuExample

Finally, you also have the option to create a UICommandSeparator(), which is simply a horizontal line instead of a command.  Separators still take up one of your 6 available slots, however, so keep this in mind when using it.  It looks like this:

16-XAML-ContentSeparator

To do this, your command creation statement would look like this:

p.Commands.Add(new UICommand("31 Days of Windows 8", (command) => { ((Grid)Logo.Parent).Background = new SolidColorBrush(Colors.Orange); }));
p.Commands.Add(new UICommandSeparator());
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

That’s about it!  You should now be able to add contextual menus to your applications when AppBars and other UI don’t make sense.  But what about interacting with just some of the text in a TextBox?

Launching a Context Menu From a TextBox

Actually, nearly everything about this process is the same, except for the math on where to pop the box from.  Initially when setting out to solve this problem, I was hoping to add on to the existing context menu that already appears when you right-click on text:

16-XAML-TextMenuDefault

As it turns out, you can’t.  So, for our example, let’s say that we want to keep all of those options, but also add a new one titled “Delete” at the bottom.  To do this, we first need to override the default context menu by creating an event handler to cancel the default one, and then recreate all of the functionality of the old one.

To do this, we create a new event handler like this:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    Lipsum.ContextMenuOpening += Lipsum_ContextMenuOpening;
}
 
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
    Lipsum.ContextMenuOpening -= Lipsum_ContextMenuOpening;
}
 

I also created a statement to remove this event handler when we leave the page.

In the method that this event handler calls, we will cancel the initial call with Handled = true, and then create our own context menu that still makes all of the appropriate calls to the clipboard.  We are only going to briefly cover using the Clipboard in this article, but we will spend the entire article tomorrow on the different ways to use this great tool.

In addition, finding the right place to pop a context menu inside of a TextBox is different than just determining the position of a control on the page.  Here’s my GetTextBoxRect() method:

private Rect GetTextBoxRect(TextBox t)
{
    Rect temp = t.GetRectFromCharacterIndex(t.SelectionStart, false);
 
    GeneralTransform transform = t.TransformToVisual(null);
    Point point = transform.TransformPoint(new Point());
    point.X = point.X + temp.X;
    point.Y = point.Y + temp.Y;
 
    return new Rect(point, new Size(temp.Width, temp.Height));
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And here’s the method for creating our PopupMenu control (you’ll notice that I used a secondary method for determining which command is tapped that doesn’t use lambdas this time.  It makes for easier readability, but you should also notice the method is much, much longer.)

async void Lipsum_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            e.Handled = true;
            TextBox t = (TextBox)sender;
 
            PopupMenu p = new PopupMenu();
            p.Commands.Add(new UICommand("Cut", null, 0));
            p.Commands.Add(new UICommand("Copy", null, 1));
            p.Commands.Add(new UICommand("Paste", null, 2));
            p.Commands.Add(new UICommand("Select All", null, 3));
            p.Commands.Add(new UICommandSeparator());
            p.Commands.Add(new UICommand("Delete", null, 4));
 
            var selectedCommand = await p.ShowForSelectionAsync(GetTextBoxRect(t));
 
            if (selectedCommand != null)
            {
                String text;
                DataPackage d;
                
                switch ((int)selectedCommand.Id)
                {
                    case 0: //CUT
                        text = t.SelectedText;
                        t.SelectedText = "";
                        d = new DataPackage();
                        d.SetText(text);
                        Clipboard.SetContent(d);
                        break;
                    case 1: //COPY
                        text = t.SelectedText;
                        d = new DataPackage();
                        d.SetText(text);
                        Clipboard.SetContent(d);
                        break;
                    case 2: //PASTE
                        text = await Clipboard.GetContent().GetTextAsync();
                        t.SelectedText = text;
                        break;
                    case 3: //SELECT ALL
                        t.SelectAll();
                        break;
                    case 4: //DELETE
                        t.SelectedText = "";
                        break;
                }
            }
        }
 

If you’re following along in your own project, you should now be able to highlight some text in your TextBox, and get a menu that looks like this one:

16-XAML-FinalTextBox

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Summary

Today we looked at the art of creating inline context menus for our users.  They are an excellent way to provide interactions with elements that aren’t selectable, or for commands that make more sense directly adjacent to the element being interacted with.

If you would like to see the entire code sample from this article, click the icon below:

downloadXAML

Tomorrow, as promised, we will venture further into the Clipboard functionality available to us in Windows 8 development.  See you then!

downloadTheTools

31 Days of Windows 8 | Day #15: The On-Screen Keyboard

This article is Day #15 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample

Today we are going to take a look at the on-screen keyboard in Windows 8.  Microsoft does not seem to make a distinction in naming between the keyboard that appears when you tap your finger on a TextBox control and the one that can be found in the Ease of Access Center.  We are going to focus today on the keyboard that looks like this:

15-XAML-NormalKeyboard

The Ease of Access On-Screen Keyboard, on the other hand, is a tool designed for making a computer easier to use for those that may not be able to use a keyboard at all.  It can be found by opening the Ease of Access center on your machine…

15-XAML-EaseOfAccessCenter

And clicking the “Start On-Screen Keyboard” option.  You will discover a keyboard that looks like this, instead:

15-XAML-EaseOfAccessKeyboard

The primary focus of this keyboard is to allow a user to completely use Windows without having a keyboard attached to their computer.  It’s not customizable, and doesn’t respond to any of the code we are going to write in this article.  This keyboard is also one of the only windows that can be placed over the Start Screen.  Check this out:

15-XAML-OnScreenKeyboardStartScreen

 

OK, so I’ve spent the first few paragraphs of this article talking about a keyboard that is NOT the focus of this article.  Why?  There are two reasons:

  1. If you are using a non-touch device when you work through this article (or on your own app and want to use the features of the regular touch keyboard), you’ll find that mouse clicks don’t launch the touch keyboard on your machine.  So you’ll search the web looking for a solution to make it show up.
  2. As you search around the web looking for more information about manipulating the on-screen keyboard in Windows 8, you’re going to get plenty of articles on the Ease of Access one, which is likely not what you wanted. If you do find an appropriate article about how to turn on the touch keyboard, it’s likely this one, because I wasn’t able to find any way to make this work.

The primary reason for this is because this is one of the few times that Windows 8 makes a distinction between a mouse click and and finger tap.  If you mouse click on a TextBox, Windows 8 assumes you are using a real keyboard, even if you’re using a touch-enabled machine.  A finger-tap, however, will produce that keyboard we’re going to talk about today.

To save you some frustration, when developing your application that will take advantage of the on-screen keyboard, use the simulator if you’re not working on a touch machine.  It allows you to simulate “taps” with your mouse.  Here’s where you set it:

15-XAML-SimulatorTapButton

OK, now that we’ve gotten all that out of the way, let’s get this actual article started.

Using the On-Screen Keyboard

First, let’s take a look at the default states of the on-screen keyboard that the user can navigate to any time the keyboard is open.  We saw the standard QWERTY view earlier:

15-XAML-NormalKeyboard

But there are several more.  When we build an application, our primary focus, above all else, should be to make the tasks a user needs to accomplish as easy as possible.  (That IS on the top of your mind, right?)  To that end, the on-screen keyboard can be manipulated to make that happen.  There is a property that can be set on TextBox controls called InputScope that allows us to pop the appropriate keyboard for the task at hand.  If an InputScope is not specified, the normal keyboard will be displayed.  Here’s what the code looks like:

<TextBox InputScope="EmailSmtpAddress" />
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

You will find, as you start playing with InputScope, that there are actually 18 different InputScope values that can be used (but for English-speaking users, there’s really only three different keyboards at this time).  They are (in alphabetical order):

  • AlphanumericFullWidth
  • AlphaNumericHalfWidth
  • ChineseFullWidth
  • ChineseHalfWidth
  • Default
  • EmailSmtpAddress
  • HangulFullWidth
  • HangulHalfWidth
  • Hanja
  • Hiragana
  • KatakanaFullWidth
  • KatakanaHalfWidth
  • NativeScript
  • Number
  • NumberFullWidth
  • Search
  • TelephoneNumber
  • Url

The only input scope values that respond to computers set to display English are: Default, EmailSmtpAddress, Number, Search, TelephoneNumber, and Url.  Here’s what they look like:

EmailSmtpAddress (added “@” and “.com buttons

15-XAML-EmailKeyboard

Url (added “/” and “.com” buttons, smaller space bar, Go key)

15-XAML-URL

Number and TelephoneNumber (shows the default numeric keypad)

15-XAML-Number

Search (same a default keyboard, but Enter key replaced with Search key)

15-XAML-Search

The rest of the InputScope values are generally focused on East Asian languages, and while I mean them no disrespect, I don’t know enough about those character sets to have an intelligent discussion about the subtle differences between FullWidth and HalfWidth characters.

In addition to our InputScope values, it’s important to understand the other options our users can navigate to at any time when the keyboard is on the user’s screen.  Here they are:

Capital Letters

15-XAML-CAPS

Emoji (there’s actually 30 PAGES of emoji, click here to see all of them)

15-XAML-Emoji

Symbols (a second set of symbols after the set shown with the Number keyboard)

15-XAML-Symbols2

Split Keyboard (a user choice, this keyboard is optimized for thumb typing)

15-XAML-SplitKeyboard

Inking Keyboard (this keyboard does handwriting recognition)

15-XAML-InkingKeyboard

Obviously, this is a huge set of input points for us as developers, and by providing the right keyboard for the job will make your app more useful to your users.  There’s also one more keyboard layout, and that’s the one for a PasswordBox control.  A PasswordBox control shows symbols instead of the actual characters that were typed, and the keyboard looks like this:

 15-XAML-PasswordBox

You’ll notice that it also has a “Hide Keypress” button.  There are times when people can see your machine (especially if you’re projecting it to an audience) and you don’t want them to be able to see your keystrokes for your password.  This button doesn’t highlight the buttons when they are tapped.  (You’ll also notice that the Emoji button is disabled, as those characters should not be used for a password.)

In the past, I have written articles about TextBoxes and virtual keyboards, and went on and one about the use cases where you want to pop-up the keyboard without showing the user their input.  A great example of this might be the game HANGMAN.  You want the user to be able to choose a letter, but then you want to dismiss the keyboard so that the user can see their progress.

In Windows 8, this is no longer possible.  You can’t launch the on-screen keyboard via code at all.  In fact, setting the focus on a TextBox control won’t do anything but make the cursor blink.  It specifically requires a tap event (not a mouse click) to occur on the TextBox before the on-screen keyboard will appear.  (This is the same reason why you should use the Simulator when debugging this type of functionality in your apps.)

Summary

Today, we’ve covered off on the variety of keyboards that are available to our users.  We can leverage InputScope values to show the right keyboard at the right time.  In addition, I learned that there are 30 entire pages of Emoji characters to use as well.  (If it’s not obvious, I benefit greatly from writing these articles as well!)

If you would like to see a working application that uses every one of the InputScope values, click the download icon below:

downloadXAML

Tomorrow, we are going to dive into using Context Menus to offer our user contextual commands directly on the app surface.  See you then!

downloadTheTools

Here’s that huge list of Emoji, in case you forgot to click on it:

Emoji

31 Days of Windows 8 | Day #14: Geolocation

HTMLCallout

This article is Day #14 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample4

Today, we get to talk about one of my favorite features of any development platform: geolocation.  Having knowledge about the user’s location (or more specifically, the device) makes every app better.  Here’s some examples of how:

Line of Business Apps

Know which plant your field manager is visiting today, so that he has all of the appropriate documentation for the machinery in that facility.  In addition, your app could surface the photos and names of the employees in that facility, so that they are more prepared and personable when they arrive.

Games

Identify other nearby players, and make an opportunity to match them up in game.  It will not only potentially improve network latency for them, but you might also form a guild out of nothing.  Create a global game of tag, based solely on proximity, not touching.

Maps

Duh.  We’ll do this as part of the sample code today.

Travel

Recognize the user’s distance and potential travel time from the airport, and alert them to the impending urgency to leave before missing their flight.  In addition, once you’ve established they can’t make it on time, help them find the next available flight without the user having to find it on their own.

Exercise

There are tons of apps available to track your exercise route.  Think running, cycling, etc.  The obvious application of geolocation to these apps is the ability to map the route you took during your travels.  A more important one, however, and I think this responsibility probably falls to the mobile OS manufacturers themselves, is the ability to recognize an accident.  On average, 4500 cyclists a year are victims of hit-and-run situations, where they are left injured (or worse) on the side of the road.  In today’s world, nearly everyone has their phone with them, even when running or cycling.  Imagine that your phone is capable of recognizing a large impact (using accelerometer or gyroscope data), followed by no movement at all.  After 30 seconds of no movement, your phone prompts you to call 911 and send text messages to a pre-selected list of contacts with an SOS message and your location.  After 30 more seconds, it immediately sends that information and dials 911.  This functionality could save lives, as many of these cyclists die needlessly only because nobody knows where they are.  Sorry for being so morbid, but it’s an important thing to think about.

As you can see there is always a way to take location data and make your app even better.  In addition, if you love my ideas above, you are completely welcome to steal them outright.  I will likely never build any of those apps, specifically.  There needs to be 48 hours in every day to make that happen.

Updating our Manifest

Thankfully, getting our geolocation data is pretty easy, and as usual, we have to declare that Location is one of our capabilities in order to use it.  To do this, open the Capabilities section of your package.appxmanifest file, and check the Location box.

14-XAML-Manifest

If you skip this step, and just try implementing the code in the rest of the article, you won’t get any errors, but you also won’t get any data.

Getting our Geolocation Data

In this example, I have constructed an application that constantly tracks the device’s location.  The first thing that got me confused in this process was how to ask the user for permission to start tracking their location in the app.  If you’ll recall, in Day #11 Lock Screen Apps, we had to explicitly prompt the user for permission to access their lock screen.  With Location, however, we don’t.  The simple act of trying to access the data will prompt the user on your behalf.  To make this incredibly easy, here’s ALL of the code you’ll need to access a device’s location from your app.  (Keep in mind that I am sending all of my values to TextBlocks on my MainPage.xaml that display the data.  They are all named like [Datatype]Value to make it obvious.)

Geolocator location;


protected override void OnNavigatedTo(NavigationEventArgs e)

{

    location = new Geolocator();

    location.PositionChanged += location_PositionChanged;

    location.StatusChanged += location_StatusChanged;

}


protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)

{

    location.PositionChanged -= location_PositionChanged;

    location.StatusChanged -= location_StatusChanged;

}


async void location_PositionChanged(Geolocator sender, PositionChangedEventArgs args)

{

    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>

    {

        Geoposition position = args.Position;


        LatitudeValue.Text = position.Coordinate.Latitude.ToString();

        LongitudeValue.Text = position.Coordinate.Longitude.ToString();

        AccuracyValue.Text = position.Coordinate.Accuracy.ToString();


        TimestampValue.Text = position.Coordinate.Timestamp.ToString();


        if (position.Coordinate.Altitude != null)

            AltitudeValue.Text = position.Coordinate.Altitude.ToString()

                                    + "(+- " + position.Coordinate.AltitudeAccuracy.ToString() + ")";

        if (position.Coordinate.Heading != null)

            HeadingValue.Text = position.Coordinate.Heading.ToString();

        if (position.Coordinate.Speed != null)

            SpeedValue.Text = position.Coordinate.Speed.ToString();

    });

}


async void location_StatusChanged(Geolocator sender, StatusChangedEventArgs args)

{

    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>

    {

        StatusValue.Text = args.Status.ToString();

    });

}

As you might have guessed, this is an event-driven operation.  When my page loads, I create a new Geolocator object, and add two events: PositionChanged and StatusChanged.

PositionChanged is the important one.  This is where we get all of the useful data like Longitude, Latitude, and Accuracy.  All of these values come from the Geocoordinate class, and as you can see from the code above, there’s several more.

  • Accuracy is the measurement, in meters, of the radius of a circle that has our latitude and longitude value as the centerpoint.  Like this:

14-XAML-Accuracy

  • Timestamp is the actual time that the Location value was read.  This includes timezone information, so you’ll have to convert the times to store everything in UTC.
  • Altitude is the elevation of the device, in meters.  This is likely only to be found in devices with a barometer (we will cover sensors later in this series), but it’s a valuable piece of information if you can get it.
  • AltitudeAccuracy is just like our other Accuracy value, except that it is simple the margin of error for the Altitude value.  It means you are + or – this many meters.
  • Heading will also only be displayed with the appropriate sensor, the compass.  This is measures in degrees relative to true north.
  • Speed can technically be calculated by tracking the lat/long values over time, but they’ve made it incredibly easy for us.  It is measured in meters/second, but is an optional value that the device’s GPS sensor can provide.  This means that not all devices will return a value.

The other code, from the sample above, that I’d like to talk about is the async/await stuff.  You should have noticed that both of our event handler methods have async at the beginning, and then we have something like this:

await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {};

Because the acquisition of our Location data happens on a background thread, we need to use a Dispatcher to get us back to the primary UI thread in order to write our values to the screen.  Without this code, we will get an error the moment we try to manipulate the UI layer, because background threads do not have access to this directly.  Here’s what my basic UI looks like (no, that is not where I live, but you’d know that if you check out that location):

14-XAML-UI

So we’ve gathered our location data, but there’s still that “StatusChanged” event handler we created earlier.  This is not something we would generally give our user direct access to, as it’s more administrative data for us, but it’s an interesting set of data nonetheless.  It can have six different values:

  • notInitialized is returned when your application has not yet requested permission to access the location data.
  • disabled is the value you will get when the user declines your request to access their location information.
  • notAvailable is returned if the user’s device does not support location data.
  • noData is returned if the user’s device supports location, but can’t acquire any.  This could happen in a scenario where the device is not connected to wi-fi, and is inside a buiding, for example.
  • initializing is the status value you’ll receive between the time the user grants access to location data, and the time you actually start receiving that data.  In future uses of your app, this will also be sent when your app is waiting to access location data for the first time.
  • ready is the golden response, and lets you know that data is available for your use.

So the example I’ve provided you in this article allows you to recognize every time your user’s location data changes, and one that has millions of use cases for.  If you only need this data ONCE, say to tag an image that the user has just taken, you just make a request to the Geolocator object without having to create all of the events I’ve outlined above.  In the method below, we can make one simple call to the GetGeopositionAsync() method instead.

async private void GetLocationDataOnce()

{

    Geoposition position = await location.GetGeopositionAsync().AsTask();


    LatitudeValue.Text = position.Coordinate.Latitude.ToString();

    LongitudeValue.Text = position.Coordinate.Longitude.ToString();

    AccuracyValue.Text = position.Coordinate.Accuracy.ToString();

}


 

In this case, we just need one simple value, one time.  Do not create a loop that makes this call over and over, it’s much slower, and you’ll save tons of machine resources by following the earlier example that registered event handlers.

Using the Simulator to Fake It

If you’re using a traditional desktop PC, you have probably noticed that your machine doesn’t do a great job of determining your location.  Network location can be decent, but it’s unlikely your lat/long coordinates are very accurate.  Enter the simulator.  You can set a very specific location in the simulator without actually having to get on an airplane.  To do this, just open the Location Settings in the simulator:

14-XAML-SimulatorOption

At this point, you can provide latitude, longitude, altitude, and accuracy values:

14-XAML-SimulatorSettings

So there you have it!  You can now write an application that can determine where the user’s device is, and while you’re building it, you can pretend to be anywhere on Earth.  Not a bad way to travel…at least the costs are low. 🙂

Summary

Today, we talked about Geolocation data, and how important it can be to any application in the market.  We looked at tracking a user’s location, as well as getting their location once, for applications that just need the specific location “right now,” but not all the time.

If you would like to download a sample application that includes all of the code from this article, click the icon below:

downloadXAML

Tomorrow, we are going to look at the on-screen keyboard, InputScopes, and TextBoxes.  See you then!

downloadTheTools