Impression de BlockUIContainer sur XpsDocument / FixedDocument

Question

  1. Comment pouvez-vous imprimer un FlowDocument qui a BlockUIContainer ?
  2. Comment puis-je forcer une mesure / mise à jour / organiser sur un FlowDocument?

Contexte

J’ai un FlowDocument généré avec des paragraphes de texte avec quelques éléments Rectangle remplis DrawingBrushes partir d’un dictionnaire de ressources et BlockUIContainer avec des contrôles personnalisés.

Le rendu du document est correct lorsqu’il est affiché dans l’un des contrôles FlowDocument *, TOUJOURS lorsque le document est converti en un document FixedDocument / XpsDocument, aucun des éléments Rectangle ou BlockUIContainer .

Je suis presque certain que c’est parce que le contrôle n’a pas été mesuré / organisé , mais ne peut pas comprendre comment le forcer avant qu’il ne soit converti en XpsDocument.

  • J’ai parcouru le LogicalTree récursivement et fait ce qui suit,

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

    d est un DependencyObject . Je peux voir que cela définit les propriétés ActualWidth et ActualHeight lors d’une rupture dans le débogueur.

  • J’ai essayé de forcer le Dispatcher à effectuer le rendu comme suggéré par Will ♦ .

Code utilisé pour imprimer le 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(document); Uri uri = new Uri(Ssortingng.Format("pack://temp_{0}.xps/", Guid.NewGuid().ToSsortingng("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); } } 

Comme vous pouvez le constater, j’utilise également un DocumentPaginator personnalisé nommé ‘FixedDocumentPaginator’; cependant, je ne publierai pas ce code car je doute que le problème soit là car, au moment où il commence à GetPage(int pageNumber) le document dans GetPage(int pageNumber) tout a déjà été converti en Visual et il est trop tard pour le mettre en page.


modifier

Hmm. Tandis que je tapais ceci, une idée me Measure/Arrange/UpdateLayout que le document cloné n’avait peut-être pas fait l’object Measure/Arrange/UpdateLayout .

Question: Comment puis-je forcer une mesure / mise à jour / organiser sur un FlowDocument?

Un hack possible que je pourrais travailler serait de montrer le document cloné dans l’un des FlowDocumentViewers (peut-être hors écran).

Une autre solution possible que je viens d’apprendre et que je n’ai pas essayée serait d’appeler: ContextLayoutManager.From(Dispatcher.CurrentDispatcher).UpdateLayout();

ContextLayoutManager parcourt l’arborescence logique pour vous et met à jour la présentation.

Code utilisé pour le clonage du document

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

En publiant cela comme référence future pour les autres personnes rencontrant des problèmes de rendu similaires avec FlowDocument / FixedDocument / XpsDocument.

Quelques points à noter:

  • BlockUIContainers ne sont pas clonés lorsque vous utilisez la méthode ci-dessus. Cela n’était pas évident dès que j’ai imprimé l’arborescence logique dans la fenêtre de débogage à l’aide de méthodes d’assistance (ces méthodes sont décrites ci-dessous – elles sont incroyablement utiles).
  • Vous devez afficher le document dans une visionneuse et l’afficher brièvement à l’écran. Ci-dessous, la méthode d’assistance que j’ai écrite pour le faire pour moi.

ForceRenderFlowDocument

 private static ssortingng ForceRenderFlowDocumentXaml = @"  "; public static void ForceRenderFlowDocument(FlowDocument document) { using (var reader = new XmlTextReader(new SsortingngReader(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: je viens d’append window.ShowInTaskbar = false pour la méthode comme si vous étiez rapide, vous pouviez voir la fenêtre apparaître dans la barre des tâches.

L’utilisateur ne “verra” jamais la fenêtre car celle-ci est positionnée hors écran à Int32.MaxValue – une astuce qui était habituelle dans la journée avec la création multimédia au début (par exemple Macromedia / Adobe Director).

Pour les personnes cherchant et trouvant cette question, je peux vous dire qu’il n’y a pas d’autre moyen de forcer le rendu du document.

Aides visuelles et logiques

 public static ssortingng WriteVisualTree(DependencyObject parent) { if (parent == null) return "No Visual Tree Available. DependencyObject is null."; using (var ssortingngWriter = new SsortingngWriter()) using (var indentedTextWriter = new IndentedTextWriter(ssortingngWriter, " ")) { WriteVisualTreeRecursive(indentedTextWriter, parent, 0); return ssortingngWriter.ToSsortingng(); } } private static void WriteVisualTreeRecursive(IndentedTextWriter writer, DependencyObject parent, int indentLevel) { if (parent == null) return; int childCount = VisualTreeHelper.GetChildrenCount(parent); ssortingng typeName = parent.GetType().Name; ssortingng objName = parent.GetValue(FrameworkElement.NameProperty) as ssortingng; writer.Indent = indentLevel; writer.WriteLine(Ssortingng.Format("[{0:000}] {1} ({2}) {3}", indentLevel, Ssortingng.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(); int childCount = children.Count(); ssortingng typeName = parent.GetType().Name; ssortingng objName = parent.GetValue(FrameworkElement.NameProperty) as ssortingng; double actualWidth = (parent.GetValue(FrameworkElement.ActualWidthProperty) as double?).GetValueOrDefault(); double actualHeight = (parent.GetValue(FrameworkElement.ActualHeightProperty) as double?).GetValueOrDefault(); writer.Indent = indentLevel; writer.WriteLine(Ssortingng.Format("[{0:000}] {1} ({2}) {3}", indentLevel, Ssortingng.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 

J’ai trouvé cette solution ici , et cela m’a aidé à obtenir l’impression du FlowDocment sans avoir à le rendre en dehors de l’écran … J’espère donc que cela pourra vous aider!

 Ssortingng copySsortingng = XamlWriter.Save(flowDocViewer.Document); FlowDocument copy = XamlReader.Parse(copySsortingng) as FlowDocument;