In WPF Adorners are used to place interactive cues above elements in you interface, think grab handles for resizeing or overlays for error messages. MSDN has an overview of the WPF adorner system. Under the hood adorners track their adorned elements by listening to LayoutUpdated on their adorned element. The LayoutUpdated event fires for all elements in an element tree whenever *any* element in the tree performs layout, this makes LayoutUpdated event handlers a potential performance and scalability problem in large element trees and the adorner system is no exception.
Enter LocalAdorner. If you are willing to give up some of the flexibility of WPF adorners LocalAdorner becomes a more lightweight and scalable alternative. LocalAdorner acts like Decorator, you wrap it around the element you want adorned and set its Adornment property to the element you want to adorn with. Because LocalAdorner is the parent of the adorned element they both layout in the same location without needing LayoutUpdated to keep them synchronized.
The source and a sample application for the decorator can be found on codeplex under,
Projects\Libraries\LocalAdorners for the decorator.
Projects\Applications\LocalAdorner for a sample application.
This posting is provided "AS IS" with no warranties, and confers no rights.
If you dont know what Pack Uris are consider yourself lucky and move along, there's nothing to see here. Otherwise here's the MSDN documentation for the Pack Uri syntax. It comprehensible but defies mastery, after something like six years I still can't build a Pack Uri off the top of my head.
Below is a helper class and sample code I use when I want to work with Pack Uris. One really nice side benefit of using the helper class is that it constructs the Uris with WPF's PackUriHelper which means the pack:// scheme parser is registered for you and you don't run into the "cannot parse pack://" problem that you sometimes see in unit tests.
namespace ConsoleApplication1
{
using System;
using System.Diagnostics;
using System.IO.Packaging;
using System.Reflection;
using System.Text;
class Program
static void Main(string[] args)
Assembly sysXmlAssembly = typeof(System.Xml.XmlNode).Assembly;
Console.WriteLine(WPFPackUriHelper.CreateLocalAssemblyPackUri(@"Resources\LocalAssemblyLogo.png"));
Console.WriteLine(WPFPackUriHelper.CreateReferencedAssemblyPackUri(sysXmlAssembly.GetName(), @"Resources\ReferencedAssemblyLogo.png"));
Console.WriteLine(WPFPackUriHelper.CreateContentFilePackUri(@"Resources\ContentFileLogo.png"));
Console.WriteLine(WPFPackUriHelper.CreateSiteOfOriginPackUri(@"Resources\SiteOfOriginLogo.png"));
Console.WriteLine("Press any key to end");
Console.ReadKey();
}
public static class WPFPackUriHelper
/// <summary>
/// Constructs a pack Uri for a resource in 'local assembly'.
/// </summary>
/// <param name="path">Path to local assembly resource</param>
/// <returns>Pack Uri for a resource in 'local assembly'</returns>
/// <remarks>
/// Based on rules enumerated at http://msdn.microsoft.com/en-us/library/aa970069(VS.85).aspx#Resource_File_Pack_URIs___Local_Assembly
/// </remarks>
public static Uri CreateLocalAssemblyPackUri(string path)
if (path == null)
throw new ArgumentNullException("path");
return Create(_applicationAuthority, path);
/// Constructs a pack Uri for a resource in a referenced assembly.
/// <param name="name">Name of referenced assembly</param>
/// <param name="path">Path to referenced assembly resource</param>
/// <returns>Pack Uri for a resource in referenced assembly</returns>
/// Based on rules enumerated at http://msdn.microsoft.com/en-us/library/aa970069(VS.85).aspx#Resource_File_Pack_URIs___Referenced_Assembly
public static Uri CreateReferencedAssemblyPackUri(AssemblyName name, string path)
if (name == null)
throw new ArgumentNullException("name");
string assemblyShortName = name.Name;
if (string.IsNullOrWhiteSpace(assemblyShortName))
throw new ArgumentException("Argument must not have null or empty name", "name");
const int averageVersionStringLength = 10; // 4 dot separators + 4 digits + 2 extra padding
const int averagePublicKeyTokenLength = 16; // 8 hex digits (2 characters per digit)
int averageAssemblySpecifierLength = assemblyShortName.Length + averageVersionStringLength + averagePublicKeyTokenLength;
StringBuilder assemblySpecifier = new StringBuilder(averageAssemblySpecifierLength);
assemblySpecifier.Append(assemblyShortName);
Version version = name.Version;
if(version != null)
assemblySpecifier.Append(';');
assemblySpecifier.Append(version.ToString());
byte[] publicKeyToken = name.GetPublicKeyToken();
if (publicKeyToken != null)
assemblySpecifier.Append(ToHexString(publicKeyToken, 0, publicKeyToken.Length));
assemblySpecifier.Append(";component");
Uri packageUri = new Uri(_applicationAuthority, assemblySpecifier.ToString());
return Create(packageUri, path);
/// Constructs a pack Uri for a 'content file' resource.
/// <param name="path">Path to content file resource</param>
/// <returns>Pack Uri for a content file resource</returns>
/// Based on rules enumerated at http://msdn.microsoft.com/en-us/library/aa970069(VS.85).aspx#Content_File_Pack_URIs
public static Uri CreateContentFilePackUri(string path)
/// Constructs a pack Uri for a 'site of origin' resource.
/// <param name="path">Path to site of origin resource</param>
/// <returns>Pack Uri for a site of origin resource</returns>
/// Based on rules enumerated at http://msdn.microsoft.com/en-us/library/aa970069(VS.85).aspx#Site_of_Origin_File_Pack_URIs
public static Uri CreateSiteOfOriginPackUri(string path)
return Create(_siteOfOriginAuthority, path);
private static Uri Create(Uri packageUri, string path)
return PackUriHelper.Create(packageUri, PackUriHelper.CreatePartUri(new Uri(path, UriKind.Relative)));
/// Converts a byte array to a hexidecimal string
/// <param name="bytes">input byte sequence</param>
/// <returns>A hexidecimal string</returns>
/// I can't believe I have to roll out my own converter but
/// the closest equivalent 'BitConvert.ToString(byte [])' pads the bytes with dashes
private static string ToHexString(byte[] bytes, int startIndex, int count)
if (bytes == null)
throw new ArgumentNullException("bytes");
if (startIndex < 0)
throw new ArgumentOutOfRangeException("startIndex");
if (startIndex >= bytes.Length)
if (count < 0)
throw new ArgumentOutOfRangeException("count");
int endIndex = startIndex + count;
if (endIndex > bytes.Length)
StringBuilder result = new StringBuilder(count * 2); // two characters per byte
for (int i = startIndex; i < endIndex; i++)
byte b = bytes[i];
result.Append(ToHexDigit(b >> 4));
result.Append(ToHexDigit(b & 0x0F));
return result.ToString();
private static char ToHexDigit(int value)
Debug.Assert(value >= 0);
Debug.Assert(value < 0x10);
return (value < 0x0A) ? ((char)('0' + value)) : ((char)('a' + (value - 0x0A)));
private static readonly Uri _applicationAuthority = new Uri("application:///.", UriKind.Absolute);
private static readonly Uri _siteOfOriginAuthority = new Uri("siteoforigin:///.", UriKind.Absolute);
Sometimes you want to show a Visual in more than one place but VisualBrush isn't quite what you're looking for.
Maybe you're implementing visual feedback for a drag drop operation or you need a static copy of a Visual to do a slide-out\slide-in transition. The normal thing to do in these cases is render to a RenderTargetBitmap and use the bitmap instead. There are a couple of downsides to this.
To get things really right your bitmap should be at the same DPI as the PresentationSource hosting your Visual. Easy enough only if you know where to look. You also need to be careful about the size of the bitmap you create. A 1920x1200 at 32bpp bitmap weighs in at 8.79MB just for a fancy full screen slide transition not to mention blocking the UI thread to render the bitmap.
One alternative to VisualBrush and RenderTargetBitmap is to capture your Visual as a Drawing and play back that Drawing wherever you need to render. There isn't an API in WPF to do this but all the building blocks are there.
The secret lies in the almost symmetrical relationship between VisualTreeHelper and DrawingContext. VisualTreeHelper provides methods for getting the state of a Visual including a drawing of its rendered data. DrawingContext draws given some state. Combine the above with the fact that with a DrawingContext you can either draw into a Visual or a DrawingGroup gives you all you need to copy a Visual's render tree into a drawing. So for example you can
A nice helper class that puts this all together is available on Codeplex along with a sample applicaition that (poorly) implements a slide transition control.
http://ifeanyie.codeplex.com/SourceControl/BrowseLatest
If you watch the task manager while rapidly triggering transitions you can see that the drawing based transitions require far fewer memory and CPU resources than the bitmap based transitions.
The Library is in Libraries\DrawingContextExtensions
The Application is in Applications\DrawVisualSample
Markup Extensions (ME) are the bees knees. Xaml parsers use them to compute the values assigned to properties when constructing object trees. StaticResource for example in an extension that provides an value from a resource dictionary given a key.
There is one downside to MEs, they are evaluated once at parse time (slightly false, but true enough for now). Which means if the computation takes a long time it can hold up constructing your object tree. Not a good thing - especially if it prevents your application from loading quickly. Obviously it would be nice to evaluate the ME out of band and defer providing a value. One really useful scenario for deferred ME's is getting images downloaded, decoded and frozen in a worker thread to prevent stutter in you application UI.
Well there's some hope. Althought not explicitly designed to work this way it is possible to defer ME values in a supported way. The secret is to take advantage of some of the services passed to the markup extension in its ProvideValue callback.
The IProvideValueTarget service contains information about the object and property that will recieve the ME result. You defer ProvideValue by caching this information and using it to apply the value when it's ready.
Code wise this looks something like
public abstract class DeferredMarkupExtension<T> : MarkupExtension
public sealed override object ProvideValue(IServiceProvider serviceProvider)
object result = default(T);
if (serviceProvider != null)
IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (provideValueTarget != null)
object targetObject = provideValueTarget.TargetObject;
object targetProperty = provideValueTarget.TargetProperty;
// Schedule some background worker to set targetObject.targetProperty
// when value is ready
DeferProvideValue(targetObject, targetProperty);
return result;
WPF's Xaml parser usualy passes targetObject, targetProperty pairs of type
The one remaining kink in this whole scheme is Templates. When WPF templates are parsed the template is sealed to prevent modification. This allows WPF to share as much of the template as possible across all expanded instances of the template. Template sealing and sharing has some implications
Remember when I told you I was lying earlier about ME evaluation? The truth is that you can get an ME to evaluate more than once. While template parsing if you return an ME as the result of ProvideValue that ME will be evaluated again every time the template is expanded.
You can deal with the templates corner case with the following heuristic -
If the targetProperty is not a known supported type then you are probably in a Template. Return *this* to cache your ME in the template and later its ProvideValue will be called again during template expansion.
A library with a complete implementation and a full demo application are available at codeplex http://ifeanyie.codeplex.com/SourceControl/BrowseLatest. The application contains an animation and performs a Bing image search of up to 50 image results. The animation stutters when using the built-in WPF Uri to ImageSource converter but is smooth when using the AsyncImageSourceExtension.
The library is in Projects\Libraries\DeferredMarkupExtension
The sample application in is Projects\Applications\DeferredMarkupExtensionSample
A guide I wrote a while back to get a friend of mine started on WPF
WPF for Serhend - Setup
WPF for Serhend - C#
WPF for Serhend - WPF via C#
WPF for Serhend - Enter the XAML
WPF for Serhend - More of that XAML
WPF for Serhend - Custom objects in XAML
WPF for Serhend - Drawing data with templates
WPF for Serhend - Visual Studio 2008