A lightweight alternative to DropShadowBitmapEffect

This blog post reviews how to create drop shadows around WPF elements in a way which does not have a noticeable impact on an application’s UI performance.

One of the biggest letdowns about the first version of WPF is the fact that bitmap effects are so terribly slow.  The most commonly used bitmap effect is the one which creates a drop shadow around an element; the DropShadowBitmapEffect.  If you start using that class to create drop shadows you will soon find your UI performance suffering immensely.  This is a shame considering how visually appealing a well-placed drop shadow can be.

Here is a screenshot which shows a ListBox with a drop shadow.  The drop shadow was created by adding a DropShadowBitmapEffect to the ListBox’s BitmapEffects collection.

Lightweight DropShadows (using DropShadowBitmapEffect)

If you were to repeatedly resize that Window, you would find that the rendering of the ListBox flickers and is slow.  Why?  The problem is that bitmap effects are not hardware accelerated, and they can execute quite often.  WPF’s support for bitmap effects requires them to be created in unmanaged code (usually C++).  There is currently no way to hardware accelerate those unmanaged routines.  I suppose it is possible to write your own bitmap effects which run themselves on a graphics card’s GPU, but that is way beyond the scope of this post!!

Fortunately there is an easy way to create drop shadows around WPF elements which does not incur any noticeable performance overhead.  The trick is to decorate an element with a SystemDropShadowChrome element.  SystemDropShadowChrome does not have nearly as many settings that you can adjust, but it provides a decent drop shadow for many situations.  Here’s a screenshot of a ListBox which is decorated with that element:

Lightweight DropShadows (usign SystemDropShadowChrome)

SystemDropShadowChrome lives in an obscure assembly and namespace.  If you want to use it, you must add a reference to one of the PresentationFramework.* assemblies (there are currently .Aero, .Luna, .Royale, and .Classic).  You will find the class in the Microsoft.Windows.Themes namespace. 

Each of the PresentationFramework.* assemblies has its own version of the SystemDropShadowChrome class, but after checking them out in Reflector I found out that they are all exactly the same.  Someone must have written the class once, and then pasted it into each of those assemblies.  So, there is no need to worry about choosing the “right” class to use at runtime, based on the user’s current operating system theme settings.  Any one of them will do.

In this post’s demo application I chose to reference the PresentationFramework.Aero assembly, as seen below:

Lightweight DropShadows (assembly references)

Once you have the assembly reference in place, you need to map the Microsoft.Windows.Themes namespace in your XAML file, like this:

xmlns:aero=”clr-namespace:Microsoft.Windows.Themes; assembly=PresentationFramework.Aero”

At that point, it’s simply a matter of wrapping an element in a SystemDropShadowChrome element, and perhaps setting its Color and/or CornerRadius properties.  Here is the XAML which does this in the demo project:

Lightweight DropShadows (XAML)

To read another perspective on the SystemDropShadowChrome class, read here.

Download the demo project here: Lightweight DropShadows (demo project)  Be sure to change the file extension from .DOC to .ZIP and then decompress it.

7 Responses to A lightweight alternative to DropShadowBitmapEffect

  1. Karl Shifflett says:

    Josh,

    You are the Man!

    Karl

  2. I never really liked the idea of referencing one of the many PresentationFramework.* assemblies. As you noted, the code is the same in all cases, but what about future versions or themes? It is one of those times in coding that I’m not really sure why it would break in the future, but it just doesn’t feel right.

    My solution was to write my own ShadowChrome class that would do a similar job, and provided all I needed for the most common cases. I wrote a blog post about it here,

    http://andyonwpf.blogspot.com/2006/11/from-shadows.html

  3. Josh Smith says:

    Andrew,

    I know what you mean when you mention that “it just doesn’t feel right” to hard-wire a dependency to an arbitrary version of the standard theme assemblies. I like your approach, and think it provides a nice workaround for this awkward issue. I have been toying with the idea of creating a “SystemDropShadowChromeWrapper” element, which would choose the right theme assembly from which to load the SystemDropShadowChrome to wrap around an element. But, since the implementations are all currently the same across the board, I can’t justify spending the time to do it yet! 😀

    Thanks,
    Josh

  4. […] details the 15 cool features of WPF 3.5.  Shame they didn’t fix BitmapEffect Personally, I think they should have added a few more developers to the WPF team and pushed more […]

  5. A.V.Ebrahimi says:

    Thanks JOSH

    It took an hour for me to find why my list is SO slow in refresh.

    But how to modify size of shadow?

    Thanks

  6. Josh Smith says:

    I don’t think you can when using this technique.

  7. Matt Stein says:

    Josh,

    When I follow these steps I *only* get the version of the BulletChrome (the one I am struggling with) that exists in the PresentationFramework assembly I choose – even when on another OS.

    Example:

    If I link to PresentationFramework.Aero and I run the project on XP I still get the Aero look and feel.

    How can I get it to resolve correctly based on the OS?

    Thanks,
    Matt