.net wpf printing flowdocument flowdocumentscrollviewer

.net - ¿Cómo puedo producir una "vista previa de impresión" de un FlowDocument en una aplicación WPF?



printing flowdocumentscrollviewer (2)

El control "FlowDocumentPageViewer" es la base del control de "vista previa" utilizado en uno de nuestros proyectos. Aquí está el XAML del control "DocumentPreviewer" (disculpas por la longitud - XAML no es sucinto):

<Control xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:l="clr-namespace:Tyler.ComPort.UI" mc:Ignorable="d" x:Class="Tyler.ComPort.UI.DocumentPreviewer" x:Name="UserControl" Background="Gray" d:DesignWidth="640" d:DesignHeight="480"> <Control.Resources> <ObjectDataProvider x:Key="ViewStyles" MethodName="GetValues" ObjectType="{x:Type sys:Enum}" > <ObjectDataProvider.MethodParameters> <x:Type TypeName="l:ViewType" /> </ObjectDataProvider.MethodParameters> </ObjectDataProvider> <l:EnumMatchVisibilityConverter x:Key="EnumVisibilityConverter" /> </Control.Resources> <Control.Template> <ControlTemplate> <ControlTemplate.Triggers> <Trigger Property="l:DocumentPreviewer.ViewType"> <Trigger.Value> <l:ViewType>Actual</l:ViewType> </Trigger.Value> <Trigger.Setters> <Setter TargetName="ScrollViewer" Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" /> <Setter TargetName="ScrollViewer" Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" /> <Setter TargetName="Viewbox" Property="Viewbox.Stretch" Value="None" /> </Trigger.Setters> </Trigger> <Trigger Property="l:DocumentPreviewer.ViewType"> <Trigger.Value> <l:ViewType>Fit</l:ViewType> </Trigger.Value> <Trigger.Setters> <Setter TargetName="ScrollViewer" Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" /> <Setter TargetName="ScrollViewer" Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled" /> <Setter TargetName="Viewbox" Property="Viewbox.Stretch" Value="Uniform" /> </Trigger.Setters> </Trigger> <Trigger Property="l:DocumentPreviewer.ViewType"> <Trigger.Value> <l:ViewType>Wide</l:ViewType> </Trigger.Value> <Trigger.Setters> <Setter TargetName="ScrollViewer" Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" /> <Setter TargetName="ScrollViewer" Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" /> <Setter TargetName="Viewbox" Property="Viewbox.Stretch" Value="UniformToFill" /> </Trigger.Setters> </Trigger> </ControlTemplate.Triggers> <DockPanel> <ToolBar DockPanel.Dock="Top"> <Button Command="{x:Static ApplicationCommands.Print}" CommandTarget="{Binding ElementName=PageViewer}" Content="Print..." /> <Separator /> <Button Command="{x:Static NavigationCommands.PreviousPage}" CommandTarget="{Binding ElementName=PageViewer}" Content="&lt; Previous" /> <Button Command="{x:Static NavigationCommands.NextPage}" CommandTarget="{Binding ElementName=PageViewer}" Content="Next &gt;" /> <Separator /> <l:ToolBarButtonGroup ItemsSource="{Binding Source={StaticResource ViewStyles}}" SelectedItem="{Binding ViewType, ElementName=UserControl}" IsSynchronizedWithCurrentItem="True" > <l:ToolBarButtonGroup.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" ToolTip="{Binding}" SnapsToDevicePixels="True"> <Image Source="../Images/actual.png" Visibility="{Binding Converter={StaticResource EnumVisibilityConverter}, ConverterParameter=Actual}" /> <Image Source="../Images/fit.png" Visibility="{Binding Converter={StaticResource EnumVisibilityConverter}, ConverterParameter=Fit}" /> <Image Source="../Images/wide.png" Visibility="{Binding Converter={StaticResource EnumVisibilityConverter}, ConverterParameter=Wide}" /> </StackPanel> </DataTemplate> </l:ToolBarButtonGroup.ItemTemplate> </l:ToolBarButtonGroup> </ToolBar> <ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled"> <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Center" VerticalAlignment="Top" Background="White" Margin="10"> <Viewbox x:Name="Viewbox" Stretch="Uniform"> <FlowDocumentPageViewer x:Name="PageViewer" Document="{Binding Document, ElementName=UserControl}" Zoom="100" MinZoom="20" MaxZoom="200"> <FlowDocumentPageViewer.Template> <ControlTemplate TargetType="{x:Type FlowDocumentPageViewer}"> <AdornerDecorator> <DocumentPageView FlowDocumentPageViewer.IsMasterPage="True" /> </AdornerDecorator> </ControlTemplate> </FlowDocumentPageViewer.Template> </FlowDocumentPageViewer> </Viewbox> </Border> </ScrollViewer> </DockPanel> </ControlTemplate> </Control.Template> </Control>

Dónde colocar este control depende de usted (y de su aplicación), por supuesto, pero nuestra aplicación tiene un comportamiento similar al de la aplicación típica de Office donde puede imprimir directamente u obtener una vista previa (que muestra la interfaz anterior) e imprimir desde allí .

Varias aplicaciones de WPF mías muestran FlowDocument''s. Puedo imprimirlos, usando el enfoque descrito en la respuesta a Impresión de un FlowDocument de WPF .

Ahora me gustaría agregar una capacidad de "vista previa de impresión". En el caso normal, estoy imprimiendo el FlowDocument que se muestra en la ventana, y entonces no necesitaría una vista previa de impresión. Pero en algunos casos, el FlowDocument para imprimir se construye sobre la marcha en la memoria. Y en estos casos, me gustaría mostrarlo antes de imprimir.

Ahora, ciertamente puedo abrir una nueva ventana y mostrar el FlowDocument, pero

  1. Quiero que la vista previa parezca realmente parte de la operación de impresión, y no solo otra ventana en la aplicación.

  2. No quiero un FlowDocument normal en un FlowDocumentScrollViewer. En lugar de ser "de cualquier tamaño", debe estar limitado al tamaño del papel, a una relación HxW específica y paginado.

Sugerencias?

  • ¿Debería usar una ventana estándar y, en ese caso, cómo asegurarme de que FlowDocument tenga la proporción adecuada?

  • ¿Hay una forma más "integrada" de hacer la vista previa dentro del alcance de la interfaz de usuario de PrintDialog que es parte de Windows?

Gracias


Tomando la pista del comentario agregado a mi pregunta, hice esto:

private string _previewWindowXaml = @"<Window xmlns =''http://schemas.microsoft.com/netfx/2007/xaml/presentation'' xmlns:x =''http://schemas.microsoft.com/winfx/2006/xaml'' Title =''Print Preview - @@TITLE'' Height =''200'' Width =''300'' WindowStartupLocation =''CenterOwner''> <DocumentViewer Name=''dv1''/> </Window>"; internal void DoPreview(string title) { string fileName = System.IO.Path.GetRandomFileName(); FlowDocumentScrollViewer visual = (FlowDocumentScrollViewer)(_parent.FindName("fdsv1")); try { // write the XPS document using (XpsDocument doc = new XpsDocument(fileName, FileAccess.ReadWrite)) { XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc); writer.Write(visual); } // Read the XPS document into a dynamically generated // preview Window using (XpsDocument doc = new XpsDocument(fileName, FileAccess.Read)) { FixedDocumentSequence fds = doc.GetFixedDocumentSequence(); string s = _previewWindowXaml; s = s.Replace("@@TITLE", title.Replace("''", "&apos;")); using (var reader = new System.Xml.XmlTextReader(new StringReader(s))) { Window preview = System.Windows.Markup.XamlReader.Load(reader) as Window; DocumentViewer dv1 = LogicalTreeHelper.FindLogicalNode(preview, "dv1") as DocumentViewer; dv1.Document = fds as IDocumentPaginatorSource; preview.ShowDialog(); } } } finally { if (File.Exists(fileName)) { try { File.Delete(fileName); } catch { } } } }

Lo que hace: en realidad, imprime el contenido de una imagen visual en un documento XPS. A continuación, carga el documento XPS "impreso" y lo muestra en un archivo XAML muy simple que se almacena como una cadena, en lugar de como un módulo separado, y se carga dinámicamente en el tiempo de ejecución. La ventana resultante tiene los botones de DocumentViewer: imprimir, ajustar al máximo de la página, etc.

También agregué un código para ocultar el cuadro de búsqueda. Vea esta respuesta a WPF: ¿Cómo puedo eliminar el cuadro de búsqueda en un DocumentViewer? por cómo lo hice

El efecto es así:

texto alternativo http://i48.tinypic.com/2hzkfat.jpg

El XpsDocument se puede encontrar en el dll ReachFramework y el XpsDocumentWriter se puede encontrar en System.Printing dll, los cuales se deben agregar como referencias al proyecto