Implementing A4 Page Output in WPF Applications

Pixel Dimensions and Resolution

The fidelity of printed output is dictated by the target device's dots per inch (DPI). For commercial-grade materials like coated paper, a density of 300 DPI is generally considered optimal. Lower resolutions may result in blurry text, whereas excessively high resolutions consume significant memory and processing resources during rendering operations. An effective balance ensures that the input bitmap matches the printer's capabilities.

An A4 sheet measures approximately 21 cm × 29.7 cm physically. Converting these physicall measurements to screen pixels requires applying the standard scaling factor based on the system's current resolution. Below is a reference for common resolutions:

DPI Width (Pixels) Height (Pixels)
72 595 842
96 794 1123
120 995 1405
150 1240 1754
300 2480 3508

In most standard desktop environments, the baseline resolution defaults to 96 DPI. Consequently, an A4 page displayed at this scale translates to roughly 794 pixels wide and 1123 pixels high. Calculations follow this formula:

Width_px = (Width_cm / 2.54) * System_DPI
Height_px = (Height_cm / 2.54) * System_DPI

Configuring the Printable Area

To ensure accurate representation during the print job, the root UI element destined for output must have its width and height explicitly matched to the calculated pixel values derived from the 96 DPI standard. Once the layout is established and data bindings are active, this specific visual element can be passed directly to the PrintDialog.PrintVisual API.

Handling Complex Layouts

Scenarios involving extensive data often require multiple pages. Wrapping print-ready controls within containers like StackPanel managed by a ScrollViewer creates challenges during preview and print operations. Directly passing a container containing hidden items to PrintVisual frequently results in blank pages starting from the second sheet. This typically stems from asynchronous rendering pipelines where child elements are not yet fully arranged or rendered before the print queue processes them.

While converting the entire visual tree into a rasterized image object (DrawingVisual) guarantees visibility, it compromises sharpness as the original vector shapes become bitmaps. A superior approach involves traversing the visual children, forcing immediate layout updates, and printing individual elements.

The following snippet demonstrates how to instantiate a DrawingVisual from a framework element by capturing a render target bitmap:

sourceElement.Measure(intendedSize);
sourceElement.Arrange(new Rect(Point.Empty, intendedSize));

// Create off-screen rendering surface
RenderTargetBitmap cacheMap = new RenderTargetBitmap(
    (int) intendedSize.Width,
    (int) intendedSize.Height,
    outputDpi,
    outputDpi,
    PixelFormats.Pbgra32);

cacheMap.Render(sourceElement);

// Generate DrawingVisual using captured bitmap
DrawingVisual visualContainer = new DrawingVisual();
using (RenderingContext ctx = visualContainer.RenderOpen())
{
    ctx.DrawImage(cacheMap.Clone(), new Rect(0, 0, intendedSize.Width, intendedSize.Height));
}

return visualContainer;

}


</div>Note that the rasterization method mentioned above effectively treats the control as a static image, which reduces crispness on high-resolution prints. To maintain vector integrity, iterate through the container's children, enforce their measurement state, and invoke the print handler individually.

<div>```
// Locate specific printable items within the panel
void ProcessPageItems(StackPanel mainPanel, Func<FrameworkElement, bool> actionHandler)
{
    for (int index = mainPanel.Children.Count - 1; index >= 0; index--)
    {
        BasePrintControl item = mainPanel.Children[index] as BasePrintControl;
        if (item != null)
        {
            // Temporarily detach to prevent re-flow interference
            mainPanel.Children.RemoveAt(index);

            // Force immediate layout calculation
            Size requiredSpace = new Size(item.ActualWidth, item.ActualHeight);
            if (item.ActualWidth == 0) requiredSpace = new Size(item.Width, item.Height);
            
            item.Measure(requiredSpace);
            item.Arrange(new Rect(Point.Empty, requiredSpace));

            // Trigger custom print logic for this specific view
            if (actionHandler != null) actionHandler(item);
        }
    }
}
if (queue.IsInError)
{
    throw new InvalidOperationException("Selected printer is currently unavailable or in error state.");
}

PrintDialog dialog = new PrintDialog
{
    PrintQueue = queue,
    PrintTicket = 
    {
        CopyCount = quantity,
        PageMediaSize = new PageMediaSize(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight)
    }
};

dialog.PrintVisual(targetVisual, printName);

}


</div></div>

Tags: WPF C# PrintDialog rendering DPI

Posted on Tue, 25 Aug 2026 16:06:03 +0000 by brattt