In a brief departure from my usual ramblings about Windows Presentation Foundation, I wanted to write a couple of technical posts about using new Windows Vista shell features from managed code. To start off, I thought I'd talk about how to add glass to an existing WinForms applications.
Firstly, what do I mean by adding glass? As most people know, Windows Vista includes a new Aero theme; one aspect of that theme is the translucent borders that are supplied by the Desktop Window Manager. There's a lot of subtle sophistication in the way window frames are drawn - a drop shadow from the window, glow effects over the maximize / minimize / close buttons, a reflective texture on the window frame itself - so it's far more than just painting with a 50% opacity gray brush.
On suitably equipped machines, every window gets a glass frame (even Command Prompt!). But some applications extend that glass frame into the client area of the window for aesthetic reasons; for example, Internet Explorer extends glass into the address bar, and Windows Media Player uses glass for the playback controls. Your application can also take advantage of the API behind this to extend glass into its own client area. This isn't free - there's quite a hefty tax involved in rendering glass, so it's something to use sparingly rather than as the background for your whole window. Nevertheless, it's a great way to make your application feel like an integral part of the operating system on which it runs.
In this first part, I'll show you how to add glass to a Windows Forms application; in future entries, I'll cover some other related areas such as drawing text onto glass, using glass in a WPF application, and creating blur effects.
The single API call that does most of the dirty-work is the following one:
[DllImport("dwmapi.dll")] public static extern int DwmExtendFrameIntoClientArea( IntPtr hWnd, ref MARGINS pMarInset);
This call takes two parameters - a window handle and a MARGINS struct that contains information on how much extra the DWM should extend the frame on the top, left, right and bottom sides of the screen. Here's the declaration for MARGINS:
[StructLayout(LayoutKind.Sequential)]public struct MARGINS{ public int cxLeftWidth; public int cxRightWidth; public int cyTopHeight; public int cyBottomHeight; }
The one big challenge with glass is getting alpha-blending to work correctly. Without using alpha-blending, then the content that you place on the glass will overwrite the glass itself and make it invisible. This is a problem in GDI, since it has no awareness of an alpha channel, but it's a little easier with GDI+. In your Windows Forms application, you simply need to set the TransparencyKey property to a color that you won't use elsewhere in the application (I use Gainsboro, for reasons that will become apparent later). Then you can create one or more panels that are docked to the margins of your form and set the background color for the panel to the transparency key. Now when you call DwmExtendFrameIntoClientArea, the glass will show within its margins wherever you've set something of the appropriate transparency key.
Here's an example of using the above API call (from a Form_Load event)
MARGINS margins = new MARGINS();margins.cxLeftWidth = 0;margins.cxRightWidth = 0;margins.cyTopHeight = 45;margins.cyBottomHeight = 0;IntPtr hWnd = this.Handle; int result = DwmExtendFrameIntoClientArea(hWnd, ref margins);
So long as you've created the panel appropriately, you should now see glass in your application. You can now draw buttons, labels or other controls onto the surface and so long as you set their background color to be transparent, you'll see them integrating well with glass. Here's an example screenshot that puts it all together:
There's just one caveat, which is that the text smoothing doesn't work out quite right. Since it uses the panel background to determine the color it should smooth against, you'll hit problems if you pick a garish color for the transparency key such as Fuchsia - you'll see that the text renders with a horrid pink glow. That's because we've cheated a little bit with the text rendering. So long as you set the transparency color to something that's close to a typical glass color, this effect is barely noticeable (you can just see a little white fringing around the title text above, if you look really closely). That's why we chose Gainsboro as our color earlier. Fortunately, there's a better way to do it - if a little more convoluted. Win32 actually provides a useful API for this situation called DrawThemeTextEx that renders the text correctly on glass and also provides an appropriate back-glow that helps distinguish the text when it's over a complex background. Next time I cover this topic, we'll look at that API as well as discussing how you can detect whether the DWM is present and enabled or not (which is important if you want your application to run downlevel).
Download the sample application and source code (requires Windows Vista to execute).
Well, ugh - of course you don't - who would? It's a horrible thought...! But perhaps you think you'd be a good fit for the open position we have in our team right now for another Windows Presentation Foundation evangelist, in which case, read on.
We're actively recruiting right now for someone who can help lead the effort to change forever the way that Windows-based applications look. With WPF, we're already starting to see a new wave of applications that redefine user experience. You can see early examples of these by looking at some of the screenshots Karsten posted in his mix06 trip report, but as WPF starts to hit the mainstream, we're going to need to broaden our team to effectively reach and assist consumer and business ISVs, enterprises, and web content publishers.
What does an evangelist actually do? Over the last year or so, I find that the majority of my work falls into four largely discrete buckets:
If I were to sum up the characteristics we'd be looking for from a potential hire, I'd include the following:
As you can probably see, we're looking for an all-rounder. There aren't many jobs like this, and not everyone is suited for it or has the breadth of skills needed. But some people are born evangelists. My gut feeling is that you probably know if you fall into that category already, and if so, you'll love this role.
If you're interested, please feel free to check out the formal job specification; you can contact me via the contact hyperlink on this blog if you want to submit a resume directly. The job is based in Redmond here in the US. Oh, and make sure you read this before you interview...!
XPS is the fixed format document technology that ships in Windows Presentation Foundation. It's a specification for a subset of XAML that can describe the layout and form of one or more printed pages, and is intended for allowing applications to generate content both for printing and sharing in soft copy form.
XPS is included as part of the WinFX Runtime Components, both as a managed API for creating fixed format documents (the System.Windows.Xps namespace) and as a viewer application that enables you to see the generated output. Here's a sample XPS document that you can open if you have the WinFX Runtime Components Feb CTP installed. We also have a printer driver that enables you to take any application and "print to XPS", regardless of whether the application is aware of XPS natively. (The 2007 Office System applications also ave the ability to save to XPS built into the application).
Today we released another way to get hold of XPS beyond the WinFX Runtime Components: the first beta of the XPS Essentials Pack. This provides an alternative unmanaged viewer for XPS documents that works downlevel on machines that aren't able to run the WinFX Runtime Components (e.g. Windows 2000, Windows XP versions prior to Service Pack 2). The package also includes a preview handler for XPS (so you can view documents from within Outlook 2007 and the Windows Vista shell without having to open up a full standalone application) as well as an IFilter add-in (so that client search engines such as Windows Desktop Search can index any XPS documents you have on your system). It's tiny too - small enough to fit on a floppy disk, if you still remember such things!
The XPS Essentials Pack co-exists with the WinFX Runtime Components; in fact, it's worth installing even on a machine with the rest of WinFX just for the preview handler and IFilter stuff. However, unfortunately the current Beta package doesn't support Windows Vista because of interactions between the setup installer and some of the operating system components it installs; we'll make a later build available to support Windows Vista in due course.
A good place to go for more information on XPS is the team blog, where you can also find more information on this essentials pack.
If you walked into my office at a random moment during the day, chances are that you'd find one of my machines in the middle of a Windows Vista installation. As most people know, we produce daily internal test builds (actually, probably more like 20-30 builds when you count all the various private feature branches, but I digress), and I like to keep a couple of my machines up-to-date with a fairly fresh build, to track what's new and whether any bugs I've filed have been fixed. Mostly I install clean each time to prevent any left-over cruft from one daily build causing problems with the next build.
Anyway, there are times when I have a fresh machine and I just want to run some WPF code or a demo. It would be a real pain to have to install Visual Studio, the Windows SDK and the Visual Studio Extensions for WinFX every time just to compile something. Fortunately, you don't have to do that at all.
It's easy to forget that Windows Vista includes almost everything you need out the box to compile a solution from scratch: the C# and VB.NET command-line compilers as well as MSBuild - the command-line version of the build engine that we ship with Visual Studio. They're all in the standard .NET Framework directory: %windir%\Microsoft.NET\Framework\v2.0.50727.
You'll even find the assemblies necessary to compile XAML into BAML, here: %ProgramFiles\Reference Assemblies\Microsoft\WinFx\v3.0
The only thing some projects need is the assembly linker (al.exe) which you can install with just the .NET Framework 2.0 SDK (as opposed to the entire tools package). I've not spent any time to investigate what triggers the need for al.exe - presumably something like compiling satellite resource assemblies.
All you have to do is add the .NET Framework directory above to your path with the following line: set path=%path%;%windir%\Microsoft.NET\Framework\v2.0.50727and then you should be good to go with a command line such as the following: msbuild MediaMania.sln
I like this - it's saved me hours, and hopefully it'll save you some time too!
I've been playing around with some of the accessibility and UI automation features in Windows Vista recently, with the view to writing a short article on the topic. En route, I came across a somewhat remarkable discovery (to me, at least).
I'm sure that most people are familiar with the Desktop Window Manager or DWM in Windows Vista (we're recording an interview for Channel 9 with Greg Schechter, one of the architects, tomorrow). You're probably aware that the role of the DWM is to composite the desktop based on in-memory buffers to which applications render themselves, as opposed to rendering directly to screen in previous Windows releases. Since the DWM controls desktop rendering, it can do clever things like showing those groovy Flip 3D effects when a user hits Win+Tab, automatically scaling windows for high-DPI displays, or displaying live thumbnails when a user hovers the mouse over a minimized window in the taskbar. Not everyone knows that the DWM does its dirty work through the unmanaged lower tiers of WPF, known as the Media Integration Layer or MIL.
Anyway, as I was saying, I was experimenting with some of the accessibility features in Windows Vista, specifically the Magnifier tool, which can be used by partially sighted users to zoom in on the area of the screen that the mouse is hovering over. Using this tool feels a little like viewing VGA 640x480 on a 24" monitor - that is to say, the pixels are up to 16x their normal size. So it's big, but it's also blocky. But I just chanced to hover the mouse over a WPF application that I had running at the time, and... wow!
Here's a screenshot so that you can see the effect (cropped, click the image to see a larger, lossless version).
In the lower window, I'm viewing a XAML sample that was created by converting the Yellowstone Map image from Adobe Illustrator using Mike Swanson's XAML exporter tool for that application. In the upper window, I have Magnifier running at 4x normal resolution. But instead of just scaling each pixel to be a 4x4 pixel square, the DWM works in concert with WPF to actually scale the original canvas to its new size. So rather than simply magnifying the existing rendered bitmap, it actually uses every pixel so that the magnified view is actually four times more detailed than the original.
I showed this to one of my customers that builds large engineering displays, and the product manager was blown away - the ability to zoom in on one part of that display without affecting the overall screen was something he considered a "killer feature" for their application. And of course, you get it free with WPF.
If you want to try this out yourself, simply click the Start menu, type Magnifier in the search box and execute it; open a WPF application, and then hover the mouse over the application. Of course, you'll need a Windows Vista machine with the DWM running (if you can't see the Aero Glass experience, then the DWM isn't running, and it won't work). I should also caution that this feature is pretty buggy in the February CTP release of Windows Vista (the most current external release at the time of writing) - it works, but you probably need to toggle the DWM on and off after enabling Magnifier and before starting the WPF application.
Anyway, I thought this was cool, and nobody I've shown this to was expecting to see this, so I figured I'd share it with you too!