2012-02-25 19 views
8

Domandastampa BlockUIContainer a XpsDocument/FixedDocument

  1. Come si fa a stampare un FlowDocument che hanno BlockUIContainer?
  2. Come posso forzare un Measure/Update/Arrange su un FlowDocument?

Sfondo

ho un generato FlowDocument con paragrafi di testo con pochi Rectangle elementi pieni DrawingBrushes da un dizionario di risorse e BlockUIContainer con controlli personalizzati.

Il documento rende correttamente quando visti in qualche FlowDocument * controlla Tuttavia quando il documento viene convertito in un FixedDocument/XpsDocument, nessuno dei Rectangle o BlockUIContainer elementi rendering.

Sono quasi certo che sia perché il controllo è non è stata misurata/arrangiate, tuttavia, non può capire come forza che accada prima di essere convertito nel XpsDocument.

  • ho percorso il LogicalTree in modo ricorsivo e fatto quanto segue,

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

    dove d è un DependencyObject. Riesco a vedere che questo imposta le proprietà ActualWidth e ActualHeight quando viene interrotto nel debugger.

  • Ho provato costringendo il Dispatcher di rendere come suggerito da Will ♦.

codice utilizzato per stampare il 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); 
    } 

} 

Come potete vedere sto utilizzando anche un costume DocumentPaginator chiamato 'FixedDocumentPaginator'; tuttavia, non inserirò il codice perché dubito che il problema sia presente al momento in cui inizia a impaginare il documento in GetPage(int pageNumber) è già stato convertito un Visual ed è troppo tardi per il layout.


Modifica

Hmm. Durante la digitazione, un pensiero mi è venuto in mente che il documento clonato potrebbe non aver avuto un Measure/Arrange/UpdateLayout terminato.

Domanda: In che modo è possibile forzare una misura/aggiornamento/disposizione su un documento di flusso?

Una possibile modifica che potrei risolvere sarebbe quella di mostrare il documento clonato in uno dei FlowDocumentViewers (forse fuori dallo schermo).

Un'altra possibile soluzione che ho appena imparato a conoscere e non ho provato sarebbe quello di chiamare: ContextLayoutManager.From(Dispatcher.CurrentDispatcher).UpdateLayout();

ContextLayoutManager passeggiate l'albero logico per voi e aggiorna il layout.

codice utilizzato per la clonazione del 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; 
} 

risposta

9

postando questo come futuro riferimento per gli altri che stanno avendo problemi di rendering simili con FlowDocument/FixedDocument/XpsDocument.

Un paio di cose da notare:

  • BlockUIContainers non vengono clonati quando si utilizza il metodo di cui sopra. Questo non è stato immediatamente evidente fino a quando non ho stampato l'albero logico nella finestra di debug usando alcuni metodi di supporto (questi metodi sono pubblicati qui sotto - sono incredibilmente utili).
  • È necessario visualizzare il documento in un visualizzatore e mostrarlo brevemente sullo schermo. Di seguito è il metodo di supporto che ho scritto per fare questo per 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: ho solo aggiunto window.ShowInTaskbar = false al metodo come se foste rapido si poteva vedere la finestra viene visualizzata nella barra delle applicazioni.

L'utente non "vedrà" mai la finestra poichè è posizionata fuori dallo schermo a Int32.MaxValue - un trucco che era comune nel corso della giornata con l'authoring multimediale iniziale (ad esempio Macromedia/Adobe Director).

Per le persone alla ricerca e alla ricerca di questa domanda, posso dirvi che non c'è altro modo che forzare il rendering del documento.

visivo e logico Albero aiutanti

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

Grazie molto per questo, appena attraversato questo inferno me, hai aiutato un sacco =) – JMK

+0

Cosa sta succedendo in 'XpsDocumentReference'? Continuo a provare a stampare da un 'PrintDialog' estraendo' DocumentPaginator' dal documento XPS tramite 'GetFixedDocumentSequence(). DocumentPaginator'. Ottengo un'eccezione Xaml di analisi risultante da un'eccezione InvalidURI. Apparentemente l'URI del documento fisso è stato alterato in qualche modo. –

+1

@AustinMullins dai un'occhiata alla mia risposta all'indirizzo http://stackoverflow.com/questions/9647401/documentviewer-to-richtextbox-binding-error Mostra l'implementazione di 'XpsDocumentReference'. Sono passati alcuni anni da quando ho guardato XPS. Felice di rivisitare e aiutare.Sarebbe una buona opportunità per creare una libreria open source. – Dennis

4

ho trovato questa soluzione here, e mi ha aiutato a ottenere la stampa del FlowDocment senza dover rendere fuori schermo ... Quindi spero che possa aiutarti !!

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

Ho eseguito un ciclo di ricorsione infinito quando incorporavo un 'ItemsControl' in un' BlockUIElement' durante 'XamlWriter.Save'. Il problema è che alcune proprietà dell'oggetto ItemsControl fanno riferimento al suo contenitore e la funzione 'MarkupWriter.RecordNamespaces' scorre in modo ricorsivo ogni proprietà dell'oggetto da salvare. –