2012-02-25 10 views
8

Preguntaimpresión BlockUIContainer a XpsDocument/FixedDocument

  1. ¿Cómo se imprime un FlowDocument que tienen BlockUIContainer?
  2. ¿Cómo puedo forzar una Medida/Actualización/coloque en un FlowDocument?

Antecedentes

He generado un FlowDocument con párrafos de texto con algunos elementos Rectangle llenos DrawingBrushes de un diccionario de recursos y BlockUIContainer con controles personalizados.

El documento hace correctamente cuando se ve en cualquiera de los FlowDocument * Control Sin embargo cuando el documento se convierte a un FixedDocument/XpsDocument, ninguno de los Rectangle o BlockUIContainer elementos render.

Estoy casi seguro de que es porque el control no ha sido medido/arreglado, sin embargo no puedo encontrar la manera de forzarlo antes de convertirlo al XpsDocument.

  • ando la LogicalTree de forma recursiva y hecho lo siguiente,

    UIElement element = (UIElement)d; 
    element.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); 
    element.Arrange(new Rect(element.DesiredSize)); 
    element.UpdateLayout(); 
    

    donde d es una DependencyObject. Puedo ver que esto establece los ActualWidth y ActualHeight propiedades cuando señaló ruptura en el depurador.

  • He intentado forzar el Dispatcher para representarlo como lo sugiere Will ♦.

código utilizado para imprimir el XpsDocument

public class XpsDocumentConverter 
{ 

    public static XpsDocumentReference CreateXpsDocument(FlowDocument document) 
    { 
     // Need to clone the document so that the paginator can work 
     FlowDocument clonedDocument = DocumentHelper.Clone<FlowDocument>(document); 

     Uri uri = new Uri(String.Format("pack://temp_{0}.xps/", Guid.NewGuid().ToString("N"))); 
     MemoryStream ms = new MemoryStream(); 

     Package pkg = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite); 
     PackageStore.AddPackage(uri, pkg); 
     XpsDocument xpsDocument = new XpsDocument(pkg, CompressionOption.Normal, uri.AbsoluteUri); 

     XpsSerializationManager rsm = new XpsSerializationManager(new XpsPackagingPolicy(xpsDocument), false); 
     DocumentPaginator paginator = new FixedDocumentPaginator(clonedDocument, A4PageDefinition.Default); 
     rsm.SaveAsXaml(paginator); 

     return new XpsDocumentReference(ms, xpsDocument); 
    } 

} 

Como se puede ver también estoy usando una costumbre DocumentPaginator llamado 'FixedDocumentPaginator'; Sin embargo no voy a publicar ese código como dudo que el problema está allí como en el momento en que comience a paginar el documento en GetPage(int pageNumber) todo lo que ya se ha convertido un Visual y es demasiado tarde para la disposición.


Editar

Hmm. Como he escrito esto, un pensamiento me acaba de ocurrir que el documento clonado no puede haber tenido un Measure/Arrange/UpdateLayout hecho.

Pregunta: ¿Cómo puedo forzar una Measure/Update/Arrange en un FlowDocument?

Una posible truco que podía trabajar sería mostrar el documento clonado en uno de los FlowDocumentViewers (tal vez fuera de la pantalla).

Otra posible solución que acabo de aprender acerca y no lo he probado sería llamar: ContextLayoutManager.From(Dispatcher.CurrentDispatcher).UpdateLayout();

ContextLayoutManager paseos del árbol lógico para usted y actualiza los datos de diseño.

Código utilizado para clonar el documento

public static FlowDocument Clone(FlowDocument originalDocument) 
{ 
    FlowDocument clonedDocument = new FlowDocument(); 
    TextRange sourceDocument = new TextRange(originalDocument.ContentStart, originalDocument.ContentEnd); 
    TextRange clonedDocumentRange = new TextRange(clonedDocument.ContentStart, clonedDocument.ContentEnd); 
    try 
    { 
     using (MemoryStream ms = new MemoryStream()) 
     { 
      sourceDocument.Save(ms, DataFormats.XamlPackage); 
      clonedDocumentRange.Load(ms, DataFormats.XamlPackage); 
     } 

     clonedDocument.ColumnWidth = originalDocument.ColumnWidth; 
     clonedDocument.PageWidth = originalDocument.PageWidth; 
     clonedDocument.PageHeight = originalDocument.PageHeight; 
     clonedDocument.PagePadding = originalDocument.PagePadding; 
     clonedDocument.LineStackingStrategy = clonedDocument.LineStackingStrategy; 

     return clonedDocument; 
    } 
    catch (Exception) 
    {    
    } 

    return null; 
} 

Respuesta

9

publicar esto como futura referencia para otros que están teniendo problemas de representación similares con FlowDocument/FixedDocument/XpsDocument.

Un par de cosas a tener en cuenta:

  • BlockUIContainers no se clonan cuando se utiliza el método anterior. Esto no fue inmediatamente obvio hasta que imprimí el árbol lógico de la ventana de depuración usando algunos métodos de ayuda (estos métodos se publican a continuación, son increíblemente útiles).
  • Debe mostrar el documento en un visor y mostrarlo brevemente en la pantalla. A continuación se muestra el método de ayuda que escribí para hacer esto por mí.

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(); 
    } 
} 

Editar: Acabo de añadir window.ShowInTaskbar = false al método como si estuviera rápida se podía ver la ventana aparece en la barra de tareas.

El usuario nunca "verá" la ventana, ya que se encuentra fuera de la pantalla en Int32.MaxValue - un truco que era común en el día con la autoría multimedia temprana (por ejemplo, Macromedia/Adobe Director).

Para las personas que buscan y encuentran esta pregunta, puedo decirles que no hay otra forma de para obligar al documento a representar.

visual y lógico Árbol Ayudantes

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); 
    } 

} 

Uso

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

Muchas gracias por esto, simplemente pasado por este infierno a mí mismo, que ayudó mucho =) – JMK

+0

¿Qué está pasando en 'XpsDocumentReference'? Sigo tratando de imprimir desde un 'PrintDialog' tirando del' DocumentPaginator' del documento XPS a través de 'GetFixedDocumentSequence(). DocumentPaginator'. Obtengo una excepción de análisis Xaml resultante de una excepción InvalidURI. Aparentemente, el URI del documento fijo está mutilado de alguna manera. –

+1

@AustinMullins eche un vistazo a mi respuesta en http://stackoverflow.com/questions/9647401/documentviewer-to-richtextbox-binding-error Muestra la implementación de 'XpsDocumentReference'. Han pasado algunos años desde que miré XPS. Feliz de visitar y ayudar.Sería una buena oportunidad para crear una biblioteca de código abierto. – Dennis

4

encontré esta solución here, y me ayudó a conseguir la impresión de la FlowDocment sin tener que hacerla fuera pantalla ... ¡Así que espero que pueda ayudarte!

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

Me encontré con un bucle de recursión infinito cuando incrustó un 'ItemsControl' en un' BlockUIElement' durante 'XamlWriter.Save'. Creo que el problema es que alguna propiedad de ItemsControl hace referencia a su contenedor, y la función 'MarkupWriter.RecordNamespaces' navega recursivamente por cada propiedad del elemento para guardar. –