Exported DrawingVisual quality when using VisualBrush

Either sooner or later, as a WPF developer, you will need to write code to generate a DrawingVisual for exporting image or printing purposes using a VisualBrush generated from a content element in your user interface like this:

private DrawingVisual GetDrawingVisual(FrameworkElement contentElement)
{
    DrawingVisual drawingVisual = new DrawingVisual();
    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        VisualBrush contentBrush = new VisualBrush(contentElement) {
            Stretch = Stretch.None,
            AlignmentX = AlignmentX.Left, AlignmentY = AlignmentY.Top };
        drawingContext.DrawRectangle(contentBrush, null, new Rect(0, 0,
            contentElement.ActualWidth, contentElement.ActualHeight));
    }
    return drawingVisual;
}

The code above may be called in a DocumentPaginator.GetPage method override passing the output Visual as a parameter to a DocumentPage instance constructor, in order to implement custom printing behavior in your application.

Alternatively, you may pass the same DrawingVisual as parameter of the RenderTargetBitmap.Render method, in order to export an image reflecting the user interface content of your source element.

Everything seems great, but in any of the cases, if the actual size of your contentElement is huge (such as if you try to print a large chart or another type of generated image), the output will be (sometimes extremely) blurry when some context conditions are applied.

Furthermore, if you create an XPS document using the WPF XpsDocumentWriter class using your custom DocumentPaginator instance that eventually refers the original DrawingVisual describe above, you may notice that the XPS Viewer components from Windows Vista’s Internet Explorer would display it perfectly, while the XPS Viewer application from Windows 7 may display it blurry, although faster. Strangely, it doesn’t matter on which operating system you create the XPS file, but on which you view it. (It seems Microsoft has optimized the viewer components in Windows 7 in relation to VisualBrush usage inside XPS documents.)

What can you do to resolve the problem? A possible workaround could be to use multiple VisualBrush “tiles” in your output DrawingVisual: split the output as multiple drawn rectangles, each of them having a VisualBrush bound to a corresponding view box referring to the original contentElement (if contentElement is presented inside a ScrollViewer, its left and top scrolling offset values should be zero, so that the view boxes coordinates would not be affected):

private DrawingVisual GetDrawingVisual(FrameworkElement contentElement)
{
    DrawingVisual drawingVisual = new DrawingVisual();
    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        DrawContentTiles(contentElement, drawingContext, 768, 640);
    }
    return drawingVisual;
}

private void DrawContentTiles(FrameworkElement contentElement,
    DrawingContext drawingContext,
    double tileWidth, double tileHeight)
{
    double contentWidth = contentElement.ActualWidth,
           contentHeight = contentElement.ActualHeight;
    for (int i = 0; i <= contentHeight / tileHeight; i++)
    {
        for (int j = 0; j <= contentWidth / tileWidth; j++)
        {
            double width = tileWidth, height = tileHeight;
            if ((j + 1) * tileWidth > contentWidth)
                width = contentWidth – j * tileWidth;
            if ((i + 1) * tileHeight > contentHeight)
                height = contentHeight – i * tileHeight;
            double x = j * tileWidth, y = i * tileHeight;
            VisualBrush contentBrush = new VisualBrush(contentElement) {
                Stretch = Stretch.None,
                AlignmentX = AlignmentX.Left, AlignmentY = AlignmentY.Top,
                Viewbox = new Rect(x, y, width, height),
                ViewboxUnits = BrushMappingMode.Absolute
};
            drawingContext.DrawRectangle(contentBrush, null,
                new Rect(x, y, width, height));
        }
    }
}

Note that this solution will obviously increase the runtime memory usage for your application’s export and/or print features, so you need to test it thoroughly in the conditions of the maximum expected size for contentElement in order to avoid any out of memory exceptions occurring after your product deployment.

About Sorin Dolha

My passion is software development, but I also like physics.
This entry was posted in Computers and Internet and tagged , , , , , , , , , . Bookmark the permalink.

2 Responses to Exported DrawingVisual quality when using VisualBrush

  1. Wayne says:

    Thanks for your solution, it helps me a lot.

  2. Monsignor says:

    Thanks a lot, a brilliant solution!

Add a reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s