Recently I've been trying to remember the last time I wrote a program that targetted the native Windows API. I think it was 2002, when I used C and the Win32 API to write the last of a series of programs for the PC Magazine Utilities column. But even at that time, I had already moved on. In the summer of 2000 I was introduced to the .NET Framework and the marvelous language C#. I became a strong advocate of managed code, and I really haven't had any reason to return to the world of message loops and wayward memory pointers. I already paid my Windows API dues, and plenty of them.
But the Windows API is now changing. A couple weeks ago I was among the 5,000 attendees at the Microsoft Build conference in Anaheim, where the big focus was on the forthcoming (meaning maybe next year) Windows 8. Speaking as someone who has been using Windows and coding Windows applications since before Windows was released in 1985, I can say without exaggeration that Windows 8 represents the most revolutionary change in the Windows user interface and application programming interface since that time.
The UI style has been dubbed "Metro," so-called because the use of unadorned fonts and clean styling is characteristic of design in metropolitan environments. For users of Microsoft products, Metro was first introduced on Windows Phone 7, and its incorporation into Windows 8 represents a very interesting trend: In years gone by, Microsoft attempted to move the design of the Windows desktop down to smaller devices such as hand-held computers and phones. Now a user-interface design for the phone is being moved up to tablets and the desktop.
I think this is a good trend. Multi-touch is becoming pervasive, and has really changed the relationship between human and computer. In fact, the term "multi-touch" is now outmoded because virtually all new touch devices respond to multiple fingers. The simple word "touch" is now sufficient.
Part of the core .NET Framework has been incorporated into Windows 8, as well as an additional .NET-like framework known as the Windows Runtime or WinRT.
WinRT is very similar to Silverlight. Programmers familiar with both the Windows Presentation Foundation (WPF) and Silverlight (which is roughly a subset of WPF with an emphasis on the roughly) will recognize the kinship with Silverlight immediately: WinRT has the familiar DependencyProperty class, but no way to create a read-only dependency property. WinRT has the familiar UIElement and FrameworkElement hierarchy but there's no virtual protected OnRender method in FrameworkElement. The WinRT version of FrameworkElement has a RenderTransform property but no LayoutTransform. Like Silverlight, there's no Freezable class in WinRT. The multi-touch Manipulation events are at first glance much closer to the Silverlight versions that the WPF versions. (You can explore WinRT yourself at the Windows Runtime reference.)
One of the major cosmetic changes between Silverlight and WinRT are namespace names: rather than System.Windows.Blah.Blah.Blah we now have Windows.UI.Xaml.Blah.Blah.Blah. The core .NET namespaces retain the familiar namespace names such as System and System.Collections and System.IO but there are also other new Windows namespaces, including Windows.ApplicationModel.*, Windows.Devices.*, Windows.Foundation.*, Windows.Media.*, Windows.Storage.*, and a small but surprisingly named Windows.System namespace.
To some observers, technology is a zero-sum game, so any introduction of a new technology necessitates the "death" of some older technology. Consequently, you've probably heard that WPF is dead, or Silverlight is dead, or .NET is dead. (But not, oddly enough, that Windows XP or Windows Vista or Windows 7 is dead!) But it's hard to consider WPF or .NET to be dead when a developer preview of .NET 4.5 has just been released, and it's hard to consider Silverlight to be dead because it's for a different platform — the Web rather than the desktop or tablet — and the basic Silverlight API is now part of Windows 8.
What made it easy for the 5,000 Build attendees to experiment with Windows 8 programming were the free Samsung tablets we were given. (Of course, after I spent nearly $3,000 to attend Build, it wasn't clear to me that the word "free" was entirely appropriate.) These tablets were preloaded with the developer release of Windows 8, a bunch of sample applications, and Visual Studio 11 Express for Windows Developer Preview. (Of course, the developer release of Windows 8 and all the tools are downloadable and installable on other machines.)
The "Build tablet" came with a Bluetooth keyboard and a docking port with an HDMI output for an external secondary monitor. Monitors with HDMI inputs generally have resolutions of 1920 × 1080 and are available for under $200. Add a USB mouse, and you have a nice Windows 8 development setup. The Samsung table has a 10-finger touch screen and a stylus as well, so you can test your programs with the pointing triumvirate: mouse, stylus, and touch.
For my first non-trivial Windows 8 Metro-style application, I decided to port a program called SpinPaint. As I discussed in a May 2010 blog entry, SpinPaint originated as a Microsoft Surface application, which I then ported to Silverlight for Windows Phone (where the performance was unsatisfactory), so I rewrote it for XNA for Windows Phone, where it appeared in my Windows Phone book, and then I spruced it up for my first Windows Phone marketplace submission, as described in a November 2010 blog entry.
SpinPaint simulates a spinning disk that you can paint on with your fingers. The color of the paint cycles through the rainbow every 10 seconds, and what makes the program a little more visually interesting is that the spinning disk is divided into four quadrants, and whatever you paint with your fingers in one quadrant is duplicated in mirror images in the other three quadrants.
The Silverlight version of SpinPaint used a WriteableBitmap to accumulate the "paint," and the first thing I noticed in the WinRT version of WriteableBitmap was the absence of a Render method. The Render method lets you render the visuals of any UIElement derivative on the bitmap, so it's fairly easy to paint polylines on the WriteableBitmap using individual Line elements with rounded ends.
Interestingly, the absence of a Render method in the WinRT WriteableBitmap was not a deal-killer for me. I already had code that could render straight thick lines with rounded ends on a bitmap by calculating the actual pixel bits. I developed that code to draw lines in some of my XNA programs for Windows Phone; in fact, these classes (RoundCappedLine, LineSegment, and ArcSegment) were part of the XNA version of SpinPaint.
The Silverlight version of WriteableBitmap has a Pixels property that's an array of integers that lets you address individual pixels of the bitmap. The corresponding property of the WinRT version of WriteableBitmap is PixelBuffer and it's of type IBuffer, a new WinRT interface defined in the Windows.Storage.Stream. But I had a true "Why The Face?" moment when I discovered that the IBuffer interface is defined with only two properties, both integers: Capacity and Length. Dude, where's my pixels?
Someday I'm sure the other classes in Windows.Storage.Stream will be totally comprehensible to me, but the lack of documentation was a problem, and they seemed to offer no solution. Fortunately a thread in an online MSDN forum provided a couple crucial clues: Wes Haggard and Joe Stegman of Microsoft pointed out the existence of an extension class in the System.Runtime.InteropServices.WindowsRuntime namespace with an AsStream method that lets you access the IBuffer pixel bits as a Stream object. (Thanks, guys!) But the little example using the WriteByte method of Stream should be ignored for any application that requires speed. Use Write instead. (And why is such an important method is an obscure extension class?)
Here's how it's done: After creating a WriteableBitmap, also create a pixels array and get a Stream from the bitmap's PixelBuffer property:
bitmap = new WriteableBitmap(width, height);
pixels = new byte[4 * bitmap.PixelWidth * bitmap.PixelHeight];
pixelStream = bitmap.PixelBuffer.AsStream();
The WriteableBitmap object always has a 32-bit pixel format. The following method sets a pixel to a particular color:
void SetPixel(int x, int y, Color clr)
int index = 4 * (y * bitmap.PixelWidth + x);
pixels[index + 0] = clr.B;
pixels[index + 1] = clr.G;
pixels[index + 2] = clr.R;
pixels[index + 3] = clr.A;
When you need to update the WriteableBitmap from the pixel array, use the following code:
pixelStream.Write(pixels, 0, pixels.Length);
You might be able to reduce the range you're updating by specifying a different starting index and buffer length in the Write call, but the Write call is very fast regardless.
The other big change I needed to make from the Silverlight version of SpinPaint was the input handling. SpinPaint has some special needs: If you just put your finger down and hold it, your finger continues to draw as the disk spins. Yet, the program is getting no touch events during this time because your finger isn't moving.
For this reason, SpinPaint processes touch-down, touch-move, and touch-up events by simply accumulating the current and previous positions of all the touching fingers in a Dictionary<uint, FingerInfo> object, where the key is the ID of the finger, and FingerInfo is a simple internal class with ThisPosition and LastPosition fields of type Point.
The WinRT API does not support the Silverlight Touch.FrameReported event necessary for tracking the motion of individual fingers. Nor does WinRT support the WPF TouchDown, TouchMove, TouchUp, TouchEnter, and TouchLeave events. Nor does WinRT have any mouse events!
In one of the boldest — and to my mind, furthest thinking — decisions, WinRT consolidates mouse, stylus, and touch input in a single set of Pointer events (and corresponding protected methods): These are all defined by UIElement and include PointerPressed, PointerMoved, PointerReleased, PointerEntered, PointerExited, PointerWheelChanged, PointerCaptureLost, and PointerCanceled (which indicates an abnormal condition). In addition, UIElement defines device-independent Tapped, DoubleTapped, RightTapped, and Holding events as well as a set of Manipulation events that consolidate multiple fingers into single manipulations.
In this way, you can code your Pointer handlers for touch, and you get mouse and stylus support for free. (One of the buzz phrases at Build was the idea of a "touch first" environment, where touch is the primary means of input.) That's what SpinPaint does, so you can also use the mouse and stylus for painting. If necessary, a program can determine what type of device the Pointer input is coming from, and obtain lots of detail about touch and stylus pressure and other information if the hardware supports it.
Metro style applications are generally full-screen applications with a very clean uncluttered layout and little "chrome." Here's what SpinPaint looks like running on the 1366 × 768 display of the "Build tablet," reduced to half-size so it's not ginormous:
Actually, that "clear" button shouldn't even be part of the screen. That should actually be in a Windows Phone-like application bar along with a "save" button. Also you should be able to share these images, for example, by sending them to someone via email or posting them to Facebook. Perhaps I'll add those feature at a later time. (Perhaps this afternoon.)
Meanwhile, here's the SpinPaint source code. One warning, though: I haven't been able to run this program more than a couple minutes at a stretch without getting a random crash with a message box in Visual Studio that looks like this:
Do you know what I say when I get a random crash in a developer preview operating system (perhaps a year away from release), particularly one that refers to a COM interop? I say "Not my fault."