Native Tooltips in WPF (Part 1)

View source on GitHub.

<preamble>

One of my hobbies is getting WPF controls to look more like their native counterparts. I’ve been shoehorning the UxTheme APIs (and older equivalents when theming is unavailable) into something that can be used by WPF over the past year or more, and I’ll write about this process at some point in the future (I’ve implemented native-looking push buttons, radio buttons, checkboxes, scrollbars, list views/list view items, text boxes, group boxes and read-only combo boxes). These efforts pay off the most in Windows 8, since WPF’s theme for that OS leaves much to be desired (just look at the push buttons!). This isn’t entirely surprising given that the theme wasn’t finalised until post-RC, leaving the WPF team without much time to do a good job.

</preamble>

In this post I’ll compare WPF tooltips with Win32 tooltips, and in part 2 I’ll demonstrate how to use Win32 tooltips in a WPF application and post some sample code.

Tooltips in WPF look more or less like traditional Windows tooltip controls. Here are some advantages and disadvantages of each:

WPF Tooltips

Advantages:

  • Can host any content, not just text.
  • Style them like any WPF control.
  • Drop shadow fades in and out.

Disadvantages:

  • Colours aren’t quite the same as native tooltips.
  • Weird bottom margin/padding with Aero theme.
  • Drop shadows don’t look exactly like Win32 tooltip shadows.
  • Drop shadows are missing in the Windows 8 theme, regardless of system settings (can be fixed by modifying the control template, which is missing SystemDropShadowChrome, meaning the ‘HasDropShadow’ property does nothing).
  • No ClearType without RenderOptions.ClearTypeHint.

Win32 Tooltips

Advantages:

  • Consistent with tooltips in native applications.

Disadvantages:

  • Can only display text (displaying other things is non-trivial).
  • Can’t be styled.
  • Drop shadows don’t fade in or out.
  • Animations sometimes don’t fire (particularly the fade-in animation).
  • Updating the text of an open tooltip can cause redraw flicker.
  • Custom positioning can involve a lot of work.

There are some other very minor differences:

  • The algorithm for finding the position under the mouse is different in WPF, even though it’s based on the ‘_GetHcursorPdy3’ function from tooltips.cpp, according to the reference source.
  • When the content of a native tooltip changes, the tooltip will be repositioned beneath the cursor. WPF tooltips remain fixed in place.

WPF ToolTip Drop Shadows in Windows 8

The standard ToolTip control template for the Windows 8 theme in WPF (‘Aero2’) has no drop shadow, whether or not the HasDropShadow property is set. The background colour and margins are also slightly off compared to the native style.

The following template produces a result that’s closer to the real thing:

You’ll need to add a reference to PresentationFramework.Aero in your project (note the ‘themes’ namespace used in the control template).

Screenshot of Windows 8 Tooltips

The top image shows a standard Windows 8 tooltip, the middle shows the default tooltip appearance for WPF applications running in Windows 8, and the bottom shows a WPF tooltip using the above template. Note that the Win32 and WPF drop shadows are slightly different in appearance. Additionally, the WPF shadow fades in and out with the tooltip, while the Win32 shadow just pops in and out.

Now, you’ll only want to use this template when the user is running Windows 8, and if the user changes themes you’ll no longer have tooltips that match the native style.

Retaining DPI/PPI Information with PNGGauntlet

I’ve mentioned PNGGauntlet many times on this blog – it’s a great tool for squeezing the best compression ratios out of the PNG format. It combines the tools PNGOUT, OptiPNG and DeflOpt in a nice WinForms GUI.

As part of the compression process, most chunks are removed by default. This includes the pHYs chunk, which defines the DPI/PPI of the image – an important property when dealing with WPF. To make sure this chunk isn’t deleted, add ‘pHYs’ under ‘Chunks to keep’ (PNGOUT) and check the ‘Preserve PNG metadata’ box for DeflOpt.

Image Background Remover Tool

View source on GitHub.

Inspired by Window Clippings, my preferred screenshot tool (which unfortunately crashes a lot under Windows 8 RTM), I wrote a small program for making image backgrounds transparent.

Screenshot of Background Remover

It comes with a command-line interface and a basic GUI (pictured above). The GUI supports dragging-and-dropping images, and you can switch the black and white images by right-clicking on either. The source code is included, along with a sample set of images.

The command-line syntax is: BackgroundRemover.exe imageout imageblack imagewhite

Thanks are owed to Ian Griffiths for the equations used to calculate transparency.

BackgroundRemover.7z
478,473 bytes; SHA-1: 002CF9C89B918951BBC7DECB9F9C1A8D53008E1F

Windows Update Notification Tool for Windows 8

I was apparently part of the 5.82% of Windows 7 users who preferred to be notified about updates rather than have them installed automatically. Annoyingly, Windows 8 no longer displays the familiar balloon tip and notify icon when new updates are available, instead placing a notice on the lock screen (which I almost never have occasion to see on my desktop). I found this frustrating enough that I decided to write a small unmanaged program to mimic the basic functionality of the old notify icon. It also supports automatic installation of specified updates (identified by their Knowledge Base ID – I use this to install Windows Defender definition updates). You can find out more and download the tool here:

Windows Update Notification Tool

Pixel-perfect Multi-DPI Images in WPF (Part 3)

View source on GitHub.

In Part 1 of this series, I discussed the problem of displaying different bitmap images at different DPIs in WPF. In Part 2, I proposed a solution using multi-frame TIFFs and two simple markup extensions. In this final post I will present a basic program that takes multiple images (PNG recommended), gives the option to specify the DPI of each and generates a multi-frame TIFF file accordingly.

Screenshot of PNG to Multi-DPI TIFF Program

I think the UI is self-explanatory. The program attempts to provide an output filename based on the pattern of the names of the input files. Alternatively, you can specify an absolute or relative file URI of your own.

Add files to the list by dragging and dropping them onto the window or click ‘Add Files’. Hovering over an image will display the unscaled image (to a point) in a tooltip, as shown above.

As mentioned in the previous post, avoid using PNGOUT or the like to compress your images – the Windows TIFF decoder has some issues displaying TIFF files made from highly-compressed PNGs.

Apologies to Microsoft for nicking an icon from imageres.dll.

The download includes the program binary (signed) and source (which isn’t particularly well structured or documented).

PNGToMultiDPITIFF.7z
156,593 bytes; SHA-1: 4CD2D2A7F7CE124A95D7900CBE89CAA1E0310790

Pixel-perfect Multi-DPI Images in WPF (Part 2)

View source on GitHub.

See also: Part 1 and Part 3.

In Part 1 of this series, I explored the issue of displaying pixel-perfect bitmap images in the Windows Presentation Foundation. In this article, I’ll describe a method of displaying different images depending on the system DPI setting using a custom Markup Extension and multi-image TIFF files.

Tagged Image File Format (TIFF) files may contain multiple images, and WPF contains support for this format out of the box. You can use the TiffBitmapEncoder class to combine multiple images into one TIFF – I made a tool called PNGToMultiDPITIFF that does just this, but I’ll leave that to Part 3.

To pick the best-matching image from a multi-frame TIFF, I created two Markup Extensions – one for creating an ImageSource, and one for setting the image’s BitmapScalingMode. If the TIFF contains an exact match for the current DPI, the BitmapScalingMode can be set to NearestNeighbour (as there should be no scaling). If not, it will be set to ‘Unspecified’ (which means ‘Linear’ in WPF 4 or newer) so it looks better.

MultiDPIImage.7z
1,229 bytes; SHA-1: BB0B8867C48ECEADD7655E792DAA780A30299747

You can download the code for the Markup Extensions above.

As discussed in the previous post, remember to set UseLayoutRounding and SnapToDevicePixels to true on your Windows.

Code Discussion

The code for the markup extension is quite simple. To get the image frames, we use the TiffBitmapDecoder class:

We loop through the frames to see if any match the system DPI. If there is no exact match, the first frame with a DPI above the system DPI is selected. If there is no such frame, we just pick the frame with the highest DPI.

The code for choosing the BitmapScalingMode is similar – instead of returning an image, we return ‘NearestNeighbour’ if there is an exact DPI match or ‘Unspecified’ otherwise. If you want to use a different BitmapScalingMode fallback, you can specify an optional second paramater in MultiDPIImageScalingMode:

Drawbacks

  • Images set with the markup extension will not be visible in the WPF designer (Visual Studio or Blend). I’d welcome any suggestions on how to fix this.
  • Avoid using PNGOUT or PNGGauntlet on PNG images before putting them into multi-frame TIFF files. The Windows TIFF decoder has some issues with compressed PNGs.

Pixel-perfect Multi-DPI Images in WPF (Part 1)

View source on GitHub.

See also: Part 2 and Part 3.

I’ve written previously about DPI-awareness in the Windows Presentation Foundation and how to specify measurements in pixels rather than Device Independent Units (DIUs). Something else to consider is image scaling – unlike the Windows Ribbon control or WinRT, WPF has no in-built mechanism for displaying different images according to the system’s DPI setting. This is a nuisance.

To illustrate the problem, I created 5 images. Left-to-right, the image DPIs (vertical and horizontal) are: 72, 96, 120, 144 and 192. The image dimensions are: 32x32px, 32x32px, 40x40px, 48x48px and 64x64px. I purposely used single-pixel-wide lines to make any stretching obvious.

Original Images

Read on to see what WPF does with these images.

Continue reading “Pixel-perfect Multi-DPI Images in WPF (Part 1)”

P/Invoke and ChooseFont (Comdlg32.dll)

Mister Goodcat wrote a good article a little while ago about using P/Invoke in Silverlight 5 to display the native font chooser dialog with the ChooseFont function. I couldn’t get the code working for a 64-bit executable, however, as the definition of the CHOOSEFONT structure on MSDN he used as a basis for the managed implementation doesn’t match with what’s in the Windows SDK. I’ve created a page on PInvoke.net for the function that includes a definition of CHOOSEFONT that works in both 32-bit and 64-bit versions of Windows.

Screenshot of Font Chooser Dialog

Retrieving Windows 8 Theme Colours

See also: Windows 8 Theme Colours Reference.

Windows 8 doesn’t offer developers access to system theme colours, unlike its cousin, Windows Phone. Even for a version 1 product, this seems like a strange omission. Then again, we still don’t have a working public API for retrieving the Aero glass colour (or whatever we call it now that Aero’s gone) 6 years after the release of Windows Vista.

The functions that the system uses to retrieve colours are defined in UxTheme.dll. In particular, we’re interested in GetImmersiveColorSetCount (export #94), GetImmersiveColorFromColorSetEx (export #95), GetImmersiveColorTypeFromName (export #96), GetImmersiveUserColorSetPreference (export #98) and GetImmersiveColorNamedTypeByIndex (export #100). Relying on undocumented functions is a bad idea, and will cause your program to fail certification, so you won’t be able to use them in apps distributed through the Windows Store anyway.

For desktop developers who still want to use these functions, read on. Just assume that they’ll break in future versions of Windows (or even with patches to Windows 8).

Screenshot of Windows 8 Colours

Continue reading “Retrieving Windows 8 Theme Colours”