Windows* Style app development: Using Window Runtime from C#

Download Article

Download Windows 8* Metro Style App Development: Using WinRT from C# [PDF 241KB]

 

Objective

With the introduction of WinRT, a native set of APIs for Windows 8* Metro style app development, you might not consider choosing C# given the powerful option of native C++ or the flexible option of web development with HTML5 and JavaScript*. Microsoft has done a great job providing a first-rate and familiar experience for C# developers building Metro style applications and is a great language to use for your next Windows 8 application.

The CLR and .Net* Framework do not go away in Windows 8 Metro style application development, but the APIs available to you have changed. Existing .Net framework applications will not run as is. However, getting your .Net applications running quickly should be an easy task. To help you, in this article I point out some of the differences I came across while creating a simple puzzle application.

 

C# API Summary

With Windows 8, Microsoft introduced a set of APIs called .NET APIs for Metro style apps. The new Windows namespaces and existing .NET namespaces, provide all the functionality you need to get started. The major differences in the .NET APIs for Metro style apps include the removal of mostly redundant, obsolete, and legacy APIs. The sections below talk about some of the specific APIs you might have used before and examples of how to do the same thing with .NET APIs for Metro style apps.

WinRT is written with COM in C++, but using WinRT from .Net really does feel natural and just like consuming any other managed object. You still have properties, events, and statics, and you can pass .Net framework primitives directly to WinRT calls. The new keyword await and async APIs work seamlessly as well. The end result is that while writing C# code you won’t even be able to tell what is different about using a WinRT API.

 

WinRT Component: File Picker

WinRT provides a unique experience for choosing, opening, and saving files and replaces .Net file choosing classes such as FileDialog and FolderBrowserDialog.

FileOpenPicker and FolderPicker allow a user to not only select files and folders from their device but can open and save files to other applications installed on the system by using the new to Windows 8 contract.

Focusing on just presenting the user with a way to choose a folder, the traditional C# code might look something like this:

            FolderBrowserDialog folderChooser = new FolderBrowserDialog();
            folderChooser.SelectedPath = textBoxDirectory.Text;
            folderChooser.ShowDialog();
            String pathChosen = folderChooser.SelectedPath;

Using the WinRT FolderPicker the C# code looks something like this:

            FolderPicker folderPicker = new FolderPicker();
            folderPicker.FileTypeFilter.Add(".zip");
            folderPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
            StorageFolder folder = await folderPicker.PickSingleFolderAsync();

In both cases, only a small amount of code is needed. However with the new Picker classes in Windows 8, file saves and opens can come from any application on the system that implements the provider interface.

 

Asynchronous File Access and Streams

One difference in the WinRT that has a large impact on applications is the change to file and stream handling.

Traditional C# code to open and set a stream as a source of a BitmapImage look something like the following:

            FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read);
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = fs;
            bitmapImage.EndInit();

The disadvantage with this code is that opening a file could take time, and ideally, this code should be written so as to not block the UI thread.

In WinRT, the file namespaces have changed, and, under the hood, these are all native WinRT classes but the usage is very similar:

            StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);
            IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read);
            BitmapImage gridImage = new BitmapImage();
            gridImage.SetSource(stream);

New to WinRT is the use of an interface for stream access, but still very similar syntax to how you would have written the code before. This is easy enough to get around and stream.AsStream() and AsInputStream are available to get to the stream class. It is the use of await, along with the calls to GetFileFromApplicationUriAsync and OpenAsync, that something unique is happening.

This new async keyword signals that a method can be invoked asynchronously, a separate task will be created, and execution will immediately return so as to not pause the UI. The await keyword will stop further execution past the asynchronous call until the completion of the task. When complete, execution continues on the next line. So with a few lines of code to open and read from a file we have created a complex asynchronous task that will provide a fast and fluid UI experience. On top of that, this method is very straightforward and much easier than using the current Asynchronous Programming Model in .Net.

 

Image Processing and Redundant APIs

Manipulating image data comes in handy when cropping or splitting up a larger image. Here is some sample code showing how in a Metro application you might go about splitting up an image into multiple pieces.

Metro Implementation

First, get the BitmapFrame you want to modify:

        IRandomAccessStream readStream = await file.OpenAsync(FileAccessMode.Read);
        BitmapDecoder bmpDecoder = await BitmapDecoder.CreateAsync(readStream);
        BitmapFrame frame = await bmpDecoder.GetFrameAsync(0);

Set up a transformation to apply to the larger images for each column and row:

		uint stepHoriz = WIDTH/cols;
        uint stepVert = HEIGHT/rows;

        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                BitmapTransform bmpTrans = new BitmapTransform();
                bmpTrans.InterpolationMode = BitmapInterpolationMode.Cubic;
                BitmapBounds bounds = new BitmapBounds();
                bounds.X = (uint)(stepHoriz * j);
                bounds.Y = (uint)(stepVert * i);
                bounds.Width = stepHoriz;
                bounds.Height = stepVert;
                bmpTrans.Bounds = bounds;
                …

Now apply the transformation and retrieve the raw pixels with GetPixelDataAsync:

		PixelDataProvider pixelDataProvider = await frame.GetPixelDataAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Ignore, bmpTrans, ExifOrientationMode.RespectExifOrientation,	ColorManagementMode.ColorManageToSRgb);
		byte[] pixelData = pixelDataProvider.DetachPixelData();

Pixels could now be modified directly, or in this case, we are just going to save them right back out to a BitmapImage since the transform did all the work already. Using a memory stream object and a bitmap encoder, encode the raw pixel data into the byte stream.

		InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
		BitmapEncoder enc = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, ras);                            
		// write the pixel data to our stream
       enc.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Ignore, stepHoriz, stepVert, bmpDecoder.DpiX, bmpDecoder.DpiY, pixelData);
       await enc.FlushAsync();

An interesting issue appeared as this code was tested on different preview versions of Windows 8. This code had to have the seek call shown below or the BitmapImage was not created correctly. Seek back to the beginning of the stream and set the source of a BitmapImage to the memory stream. Without a stream, BitmapImage requires a URI to load an image.

       // this is critical and below does not work without it!
       ras.Seek(0);

       // Set to the image
       BitmapImage gridImage = new BitmapImage();
       gridImage.SetSource(ras);

The bitmap image can then be assigned as the source of a XAML Image control.

Overall, the approach is fairly straightforward and very similar to approaches that you might have done before. The great part is that most of the classes used to do this are in the Windows.Graphics and Windows.StorageStream namespaces and execute as native WinRT code.

.Net 4.5 Implementation

Here is one commonly used method to slice up the image outside of Metro. It uses GDI+ and the System.Drawing namespace to perform the same task.

The GDI+ way of doing it with C#:

// Clone a portion of the image
System.Drawing.Rectangle cloneRect = new System.Drawing.Rectangle( (int)(stepHoriz * j), (int)(stepVert * i), (int)stepHoriz, (int)stepVert);
System.Drawing.Imaging.PixelFormat format = bitmapImage.PixelFormat;
Bitmap clonedBitmap = bitmapImage.Clone(cloneRect, format);

Once you have a Bitmap object, you still need to assign this as a source of the XAML image control.

IntPtr hBitmap = clonedBitmap.GetHbitmap();

System.Windows.Media.Imaging.BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                     hBitmap,
                     IntPtr.Zero,
                     Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
	gridImage.Source = bitmapSource;

This illustrates how cumbersome it is to move images from the GDI world into XAML controls; it’s not as straightforward as you would think and you need to use cumbersome interop methods.

Another way to do this is using just System.Windows.Media.Imaging namespace to do something like:

CroppedBitmap croppedBitmap = new CroppedBitmap(     
          origBitmapImage,
          new Int32Rect(stepHoriz * j, stepVert * i, stepHoriz, stepVert));
gridImage.Source = croppedBitmap;

This works just the same, and you can use WriteableBitmap to get a raw byte stream of the image.

These examples illustrate how before Metro there always seemed to be more than one API available to use in a solution. The .Net APIs for Metro style apps does not include System.Drawing or System.Windows.Media. This functionality is replaced by Windows.Graphics. This is a great example of how WinRT has consolidated and created a concise set of APIs for application developers.

 

Conclusion

These are just some of the key differences I came across when creating a simple image puzzle game and hope that some of these examples point you in the right direction.

For More Information:

- Async in 4.5: Worth the Await. http://blogs.msdn.com/b/dotnet/archive/2012/04/03/async-in-4-5-worth-the-await.aspx

- BUILD conference videos:

- An overview of .NET for Metro style apps. http://msdn.microsoft.com/en-us/library/windows/apps/br230302.aspx

- The complete list of .NET APIs for Metro style apps. http://msdn.microsoft.com/en-us/library/windows/apps/br230232(v=vs.110).aspx


About the Author

Nathan Totura is an application engineer in Intel's Software and Services Group. Currently working on the Atom enabling team, he helps connect software developers with Intel technology and resources. Primarily these technologies include tablets and handsets on the Android, Windows 8 and Tizen platforms.




*Other names and brands may be claimed as the property of others.

**This sample source code is released under the Intel Sample Source Code License Agreement

Pour de plus amples informations sur les optimisations de compilation, consultez notre Avertissement concernant les optimisations.
Étiquettes: