Printing BlockUIContainer to XpsDocument/FixedDocument Printing BlockUIContainer to XpsDocument/FixedDocument wpf wpf

Printing BlockUIContainer to XpsDocument/FixedDocument


Posting this as future reference for others that are having similar rendering issues with FlowDocument/FixedDocument/XpsDocument.

A few things to note:

  • BlockUIContainers are not cloned when you use the above method. This was not immediately obvious until I printed the logical tree out the debug window using some helper methods (these methods are posted below - they are incredibly useful).
  • You need to display the document in a viewer and briefly show it on screen. Below is the helper method that I wrote to do this for me.

ForceRenderFlowDocument

private static string ForceRenderFlowDocumentXaml = @"<Window xmlns=""http://schemas.microsoft.com/netfx/2007/xaml/presentation""          xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">       <FlowDocumentScrollViewer Name=""viewer""/>  </Window>";public static void ForceRenderFlowDocument(FlowDocument document){    using (var reader = new XmlTextReader(new StringReader(ForceRenderFlowDocumentXaml)))    {        Window window = XamlReader.Load(reader) as Window;        FlowDocumentScrollViewer viewer = LogicalTreeHelper.FindLogicalNode(window, "viewer") as FlowDocumentScrollViewer;        viewer.Document = document;        // Show the window way off-screen        window.WindowStartupLocation = WindowStartupLocation.Manual;        window.Top = Int32.MaxValue;        window.Left = Int32.MaxValue;        window.ShowInTaskbar = false;        window.Show();        // Ensure that dispatcher has done the layout and render passes        Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => {}));        viewer.Document = null;        window.Close();    }}

Edit: I just added window.ShowInTaskbar = false to the method as if you were quick you could see the window appear in the taskbar.

The user will never "see" the window as it is positioned way off-screen at Int32.MaxValue - a trick that was common back in the day with early multimedia authoring (e.g. Macromedia/Adobe Director).

For people searching and finding this question, I can tell you that there is no other way to force the document to render.

Visual and Logical Tree Helpers

public static string WriteVisualTree(DependencyObject parent){    if (parent == null)        return "No Visual Tree Available. DependencyObject is null.";    using (var stringWriter = new StringWriter())    using (var indentedTextWriter = new IndentedTextWriter(stringWriter, "  "))    {                       WriteVisualTreeRecursive(indentedTextWriter, parent, 0);        return stringWriter.ToString();    }}private static void WriteVisualTreeRecursive(IndentedTextWriter writer, DependencyObject parent, int indentLevel){    if (parent == null)        return;    int childCount = VisualTreeHelper.GetChildrenCount(parent);    string typeName = parent.GetType().Name;    string objName = parent.GetValue(FrameworkElement.NameProperty) as string;    writer.Indent = indentLevel;    writer.WriteLine(String.Format("[{0:000}] {1} ({2}) {3}", indentLevel,                                                               String.IsNullOrEmpty(objName) ? typeName : objName,                                                               typeName, childCount)                    );    for (int childIndex = 0; childIndex < childCount; ++childIndex)        WriteVisualTreeRecursive(writer, VisualTreeHelper.GetChild(parent, childIndex), indentLevel + 1);}public static string WriteLogicalTree(DependencyObject parent){    if (parent == null)        return "No Logical Tree Available. DependencyObject is null.";    using (var stringWriter = new StringWriter())    using (var indentedTextWriter = new IndentedTextWriter(stringWriter, "  "))    {        WriteLogicalTreeRecursive(indentedTextWriter, parent, 0);        return stringWriter.ToString();    }}private static void WriteLogicalTreeRecursive(IndentedTextWriter writer, DependencyObject parent, int indentLevel){    if (parent == null)        return;    var children = LogicalTreeHelper.GetChildren(parent).OfType<DependencyObject>();    int childCount = children.Count();    string typeName = parent.GetType().Name;    string objName = parent.GetValue(FrameworkElement.NameProperty) as string;    double actualWidth = (parent.GetValue(FrameworkElement.ActualWidthProperty) as double?).GetValueOrDefault();    double actualHeight = (parent.GetValue(FrameworkElement.ActualHeightProperty) as double?).GetValueOrDefault();    writer.Indent = indentLevel;    writer.WriteLine(String.Format("[{0:000}] {1} ({2}) {3}", indentLevel,                                                              String.IsNullOrEmpty(objName) ? typeName : objName,                                                              typeName,                                                               childCount)                    );    foreach (object child in LogicalTreeHelper.GetChildren(parent))    {        if (child is DependencyObject)            WriteLogicalTreeRecursive(writer, (DependencyObject)child, indentLevel + 1);    }}

Usage

#if DEBUG    Debug.WriteLine("--- Start -------");    Debug.WriteLine(VisualAndLogicalTreeHelper.WriteLogicalTree(document));    Debug.WriteLine("--- End -------");#endif


I found this solution here, and it helped me get the printing of the FlowDocment without having to render it off screen...So I hope it can help you!!

String copyString = XamlWriter.Save(flowDocViewer.Document);FlowDocument copy = XamlReader.Parse(copyString) as FlowDocument;