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);
This posting is provided "AS IS" with no warranties, and confers no rights.
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