31 Days of Windows 8 | Day #26: Gyrometer

This article is Day #26 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 continues our adventure through the world of sensors, and today we are looking at the Gyrometer.  The Gyrometer, or gyroscope, measures rotational velocity.  So, while the Accelerometer measures the acceleration around three axes, the Gyrometer measures the velocity of rotation around those axes.  Like this:

220px-Gyroscope_operation[1] Now, I’m not suggesting that there’s a little spinning widget inside your Windows 8 device.  Generally, mobile devices and tablets use a MEMS gyroscope, which uses vibrations or resonance to determine their data.

The gyroscope is also another one of those sensors that you’re going to get “sometimes.”  As you can see in the video below, some devices have one, and some don’t.  This is going to be a common theme with any sensor in Windows 8, because it is so terribly backwards compatible.

 

When Windows Phone 7 came out, every device was new, and there were stringent requirements about the sensors that had to be included in every device.  When it comes to the PC world, however, Windows 8 can be installed on practically any computer that was created in the last 5 years.  This makes it hard to get a consistent hardware set across devices, but doesn’t mean you shouldn’t support these sensors.  Many of the new machines that are now being offered DO include these sensors, which means as time moves forward, a higher percentage of computers will support your efforts.

In addition, if you’re attempting to do any kind of augmented reality in your app (where you look through the device like a window on the real world, with additional data like store ratings or popularity added to the display), the gyrometer will be exceptionally helpful in keeping you oriented.

Getting Data From the Gyrometer

In the past few articles, we’ve seen that getting data from a sensor follows a familiar pattern.  We create a new sensor object, we create an event handler, and then we grab the resulting data from the event handler’s method.  Today is no different, but I’ve added a little “pizazz” by visualizing the data as well.  In my XAML file, I’ve included three Line controls inside a Grid.

<Grid x:Name="ContentPanel" Margin="429,78,12,0">
    <Line x:Name="xLine" X1="240" Y1="350" X2="340" Y2="350" Stroke="Red" StrokeThickness="4"></Line>
    <Line x:Name="yLine" X1="240" Y1="350" X2="240" Y2="270" Stroke="Yellow" StrokeThickness="4"></Line>
    <Line x:Name="zLine" X1="240" Y1="350" X2="190" Y2="400" Stroke="Blue" StrokeThickness="4"></Line>
</Grid>

These lines will extend in three different directions, driven by the Gyrometer data.  Here’s the entire contents of my MainPage.xaml.cs file:

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 Day26_Gyrometer
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }
 
        Gyrometer gyrometer;
 
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            gyrometer = Gyrometer.GetDefault();
            if (gyrometer != null)
            {
                gyrometer.ReadingChanged += gyrometer_ReadingChanged;
                Data.Visibility = Visibility.Visible;
            }
            else
            {
                NoSensorMessage.Visibility = Visibility.Visible;
            }
        }
 
        async void gyrometer_ReadingChanged(Gyrometer sender, GyrometerReadingChangedEventArgs args)
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                XValue.Text = args.Reading.AngularVelocityX.ToString();
                YValue.Text = args.Reading.AngularVelocityY.ToString();
                ZValue.Text = args.Reading.AngularVelocityZ.ToString();
                TimeStamp.Text = args.Reading.Timestamp.ToString();
 
                xLine.X2 = xLine.X1 + args.Reading.AngularVelocityX * 200;
                yLine.Y2 = yLine.Y1 - args.Reading.AngularVelocityY * 200;
                zLine.X2 = zLine.X1 - args.Reading.AngularVelocityZ * 100;
                zLine.Y2 = zLine.Y1 + args.Reading.AngularVelocityZ * 100;
            });
        }
    }
}

.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 example, you can see that I’m doing some simple math to change the X and Y values of the Line controls, so that they reflect the Gyrometer data that is being captured.  In case you’re curious, the reason the zLine has both its X and Y values changed is because it is set at an angle, which requires manipulation of both coordinates of one end of the line.

Ultimately, you’re going to find far more interesting ways to use this data than I have, so I’m looking forward to hearing what you’ve created!  Leave a comment on this post when you’ve got something.

If for nothing else, if you’re building a game that might have achievements or badges, I think it’s always cool to add ridiculous awards for hitting a certain number on a sensor.  Have some fun with it.  It’s certainly easy enough, right?

Summary

Today, we took a look at the Gyrometer (gyroscope) and how we can read the data from this sensor in Windows 8 app development.  Another sensor available on Windows 8 devices, it’s going to give you smoother, more accurate data about the movement of the device than the Accelerometer, but they both are excellent tools for the job.

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

downloadXAML

Tomorrow, we’re going to cover the last sensor in this series, the Inclinometer.  Much like the Motion class from Windows Phone, this sensor aggregates some of the other data we’ve talked about to generate Pitch, Yaw, and Roll values from our device.  See you then!

downloadTheTools

31 Days of Windows 8 | Day #25: Accelerometer

This article is Day #25 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 looking at yet another sensor available in Windows 8 devices, the Accelerometer.  It measure the acceleration of the device on 3 axes, X, Y, and Z.

The X axis runs horizontally across the device.  The Y axis runs vertically across the device.  The Z axis runs immediately through the device, from front to back.  Here’s an image to help illustrate, using a Windows Phone as the example (courtesy of http://www.andybeaulieu.com/):

25-Accelerometer

In simpler terms, we’re measuring the g-forces applied to those three axes.  Because of this, when we lay a Windows 8 tablet flat on a table, we get a Z axis value of –1, because there is one “g” (one unit of gravity) exerting itself on the negative size of the Z axis.  In the same way, if we propped our device up (similar to the phone in the photo) so that the bottom of the device is sitting on the table, we would get a Y axis value of –1.

So how do we gather these values?  Very similarly to how we have interacted with the past few sensors.  We create an Accelerometer object, we add an event handler, and we read the data when it changes.  Here’s my entire MainPage.xaml.cs file:

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 Day25_Accerometer
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }
 
        Accelerometer accelerometer;
        int shakes;
 
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            accelerometer = Accelerometer.GetDefault();
            if (accelerometer != null)
            {
                accelerometer.ReadingChanged += accelerometer_ReadingChanged;
                accelerometer.Shaken += accelerometer_Shaken;
                Data.Visibility = Visibility.Visible;
            }
            else
            {
                NoSensorMessage.Visibility = Visibility.Visible;
            }
        }
 
        async void accelerometer_ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args)
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                XValue.Text = args.Reading.AccelerationX.ToString();
                YValue.Text = args.Reading.AccelerationY.ToString();
                ZValue.Text = args.Reading.AccelerationZ.ToString();
                TimeStamp.Text = args.Reading.Timestamp.ToString();
            });
        }
 
        async void accelerometer_Shaken(Accelerometer sender, AccelerometerShakenEventArgs args)
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                shakes++;
                ShakeCount.Text = shakes.ToString();
            });
        }
    }
}

.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 you have been following along with this series, this should look really familiar.  If you haven’t, you can see that we’re using the ReadingChanged event handler to grab our data values from the Accelerometer object, and then using the Dispatcher to write those values to our UI thread.

The one major difference you should notice is the new Shaken event handler.  Some accelerometers support this function, and others don’t, but we just don’t get any data when it’s not supported (it’s basically just a meta-calculation that approximates when the user’s device has been shaken similarly to how you might shake an Etch-A-Sketch to erase the screen.)  If you’re going to use this event, be sure that the user is using a device that supports it, like the Microsoft Surface.

You can certainly use the X, Y, and Z values to determine a shake on your own, so the Shaken event will likely not be of huge value to you until it is more widely supported.  By using these values, however, you can do some pretty impressive math.  Think of games like Labyrinth, and how you can use the angle of the device to determine where the objects on the screen should move.  I think there are tons of applications for this, and I’m looking forward to seeing what you create.

Summary

Today we took a look at the Accelerometer sensor, and how we can use it to grab data about the orientation and acceleration of the device on three different axis directions.

If you would like to download the sample application that uses the code from this article, click the icon below:

downloadXAML

Tomorrow, we’ll continue diving into sensors with the Inclinometer.  It allows us to measure the angle of incline for a Windows 8 device on the same three axes.  See you then!

downloadTheTools

31 Days of Windows 8 | Day #24: Light Sensor

This article is Day #24 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 taking a look another one of the sensors we might find in a Windows 8 device: the Light Sensor.  With the Light Sensor, we can determine the brightness of the light around the user’s machine, and help to accommodate things like contrast, brightness, and other values that would make our app easier to read in high and low light.

I’ve put together yet another horribly produced video to show you how a light sensor works, and what kinds of values we can expect:

 

To make this application work, we’re going to follow a very similar pattern to the one we used yesterday for the Compass sensor. 

  • Initialize the Light Sensor.
  • If it’s available, create a ReadingChanged event handler.
  • In the event handler, grab the data from the sensor and write it to the screen.

Here’s my entire MainPage.xaml.cs file:

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 Day24_LightSensor
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }
 
        LightSensor sensor;
 
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            sensor = LightSensor.GetDefault();
            if (sensor != null)
            {
                sensor.ReadingChanged += sensor_ReadingChanged;
                Data.Visibility = Visibility.Visible;
            }
            else
            {
                NoSensorMessage.Visibility = Visibility.Visible;
            }
        }
 
        async void sensor_ReadingChanged(LightSensor sender, LightSensorReadingChangedEventArgs args)
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                Lux.Text = args.Reading.IlluminanceInLux.ToString();
                TimeStamp.Text = args.Reading.Timestamp.ToString();
            });
 
        }
    }
}

.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; }

 

There’s nothing surprising about getting this data, but I was surprised to see how diverse the values on different machines (that were sitting next to each other) could be.  For instance, my Qualcomm ARM device (the one featured in the video above) generally rated the room I’m currently sitting in around 59 lux.  My Samsung tablet, however, which is a Windows 8 Pro device, rates this room around 42 lux.  Finally, my Surface RT device says this place is about 115 lux.

This is likely due to the accuracy and quality of the light sensor in each device, but in general, they’re really not that far apart on the scale of lux values.  Here’s an example from the Wikipedia article on Lux.

24-XAML-LuxChart

As you can see, even 100 lux is still a pretty dim value.  Just flipping the lightswitch on in my office jumped my sensor values up closer to 175.  Using the chart above, however, you should be able to create “ranges” of values that behave differently depending on the brightness of the light available.

For example, if you recognize that the user is in a low-light environment, you might switch their display to show a dark background and white text, because that is easier to read in that kind of light.  In a bright room, you might want to switch to black text on a white background.

In either case, you now know how to recognize the data from the light sensor, and use it effectively in your app.

Summary

Today we talked about the Light Sensor, and how it can be used to alter a user’s interface to make it more readable.  Ultimately, there are dozens of creative ways to leverage the lux data in your applications, and I’m looking forward to hearing how you’ll use it in your app.

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

downloadXAML

Tomorrow, we’re going to get involved with a more robust sensor, the Accelerometer.  We can use this data to determine the rotation of the user’s device.  See you then!

downloadTheTools

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