Click here to Skip to main content
15,861,168 members
Articles / Desktop Programming / WPF

WPF Basic Print Rendering

Rate me:
Please Sign up or sign in to vote.
4.76/5 (9 votes)
19 Jun 2013MIT2 min read 32.4K   32   3
Render a control and print it

Introduction

The standard way to design printable documents in WPF is to use FlowDocuments or FixedDocuments. Unfortunately, making detailed layouts with either can be a bit frustrating. If you've ever used them, then you know what I mean. On the surface, they are fairly easy to grasp, for the most part it's like the rest of WPF. However, shifting things, using bindings, or setting up fairly complicated UIElement layouts can be tricky and may not work quite right.

Background

The basic idea here is that you use a UserControl, very friendly to use (unlike FlowDocuments), and design them in such a manner that they look like paper. The size of the UserControl should be 816px X 1056px (width X height), which gives you your common 8.5 X 11 piece of computer paper. So you have to carefully choose how you implement ListViews, TreeViews, and the like. Getting into multipage printing can still be managed this way, albeit with a little work. Rather than "stacking" UserControls representing each page, you can make one long UserControl and let it get cut into 1056px vertical chunks visually.

Expanding Usage

This class is very basic and is intended to be used as a starting point for you to customize your own printing solution. It is designed to intentionally be simplistic. All the stuff you can find online can be rather mind numbing to search through. Even when you do find something, a vast majority doesn't work or is unnecessarily complicated.

The methods have been made extension methods for a FrameworkElement. If you desire a lesser derived type, like UIElement, then be sure to remove the references to the FrameworkElement.Name property. This class does not implement page numbers, intelligent page breaks or header/footer support. You will have to add that stuff yourself. The page numbers shouldn't be too hard, but the intelligent page breaks may be a bit more difficult. There is an article here that you can glean some hints from (the rowPixelDeviation method in particular) if you wish to take a stab at it.

Using the Code

Below is the class you can use to start from:

C#
/// <summary>Provides printing capabilities.</summary>
public static class Printer
{
    /// <summary>Prints the FrameworkElement.</summary>
    /// <param name="fe">The FrameworkElement.</param>
    public static void Print(this FrameworkElement fe)
    {
        PrintDialog pd = new PrintDialog();

        bool? result = pd.ShowDialog();

        if (!result.HasValue || !result.Value) return;

        fe.Dispatcher.Invoke(new Action(() =>
        {
            fe.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
            fe.Arrange(new Rect(fe.DesiredSize));
            fe.UpdateLayout();
        }), System.Windows.Threading.DispatcherPriority.Render);

        int height = (int)pd.PrintableAreaHeight;
        int width = (int)pd.PrintableAreaWidth;
        int pages = (int)Math.Ceiling((fe.ActualHeight / height));

        FixedDocument document = new FixedDocument();

        for (int i = 0; i < pages; i++)
        {
            FixedPage page = new FixedPage();
            page.Height = height;
            page.Width = width;

            PageContent content = new PageContent();
            content.Child = page;

            document.Pages.Add(content);

            VisualBrush vb = new VisualBrush(fe);
            vb.AlignmentX = AlignmentX.Left;
            vb.AlignmentY = AlignmentY.Top;
            vb.Stretch = Stretch.None;
            vb.TileMode = TileMode.None;
            vb.Viewbox = new Rect(0, i * height, width, (i + 1) * height);
            vb.ViewboxUnits = BrushMappingMode.Absolute;

            RenderOptions.SetBitmapScalingMode(vb, BitmapScalingMode.HighQuality);

            Canvas canvas = new Canvas();
            canvas.Background = vb;
            canvas.Height = height;
            canvas.Width = width;
                
            FixedPage.SetLeft(canvas, 0);
            FixedPage.SetTop(canvas, 0);

            page.Children.Add(canvas);
        }

        pd.PrintDocument(document.DocumentPaginator, 
        ((String.IsNullOrWhiteSpace(fe.Name) ? "Temp" : fe.Name) + " PRINT"));
    }

    /// <summary>Prints the FrameworkElement as a 
    /// continuous (no page breaks) print.</summary>
    /// <param name="fe">The FrameworkElement.</param>
    public static void PrintContinuous(this FrameworkElement fe)
    {
        PrintDialog pd = new PrintDialog();

        bool? result = pd.ShowDialog();

        if (!result.HasValue || !result.Value) return; 

        fe.Dispatcher.Invoke(new Action(() =>
        {
            fe.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
            fe.Arrange(new Rect(fe.DesiredSize));
            fe.UpdateLayout();
        }), System.Windows.Threading.DispatcherPriority.Render);

        pd.PrintVisual(fe, ((String.IsNullOrWhiteSpace
        (fe.Name) ? "Temp" : fe.Name) + " PRINT"));
    }

    /// <summary>Takes a one page snapshot 
    /// the FrameworkElement.</summary>
    /// <param name="fe">The FrameworkElement.</param>
    public static void Snapshot(this FrameworkElement fe)
    {
        PrintDialog pd = new PrintDialog();

        bool? result = pd.ShowDialog();

        if (!result.HasValue || !result.Value) return; 

        pd.PrintVisual(fe, ((String.IsNullOrWhiteSpace(fe.Name) ? 
        	"Temp" : fe.Name) + " PRINT"));
    }
}

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Architect SL-X
United States United States
Developed various languages over the years. Found C# and fell in love with it. Been doing it ever since. Love WPF or ASP.Net MVC with the Razor view engine as front-end technologies. As a developer I want to make development easier for all developers. I like designing and providing tools for other programmers to use.

Comments and Discussions

 
QuestionToo much memory Pin
Member 116053137-Apr-18 17:16
Member 116053137-Apr-18 17:16 
GeneralMy vote of 5 Pin
Cornelius Henning18-Jun-13 0:47
professionalCornelius Henning18-Jun-13 0:47 
GeneralMy vote of 5 Pin
fredatcodeproject17-Jun-13 22:56
professionalfredatcodeproject17-Jun-13 22:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.