How can I produce a “print preview” of a FlowDocument in a WPF application?

后端 未结 2 1118
暖寄归人
暖寄归人 2020-11-29 19:40

Various WPF applications of mine display FlowDocument\'s. I\'m able to print them, using the approach described in the answer to Printing a WPF FlowDocument.

Now

相关标签:
2条回答
  • 2020-11-29 19:57

    Taking the hint from the comment added to my question, I did this:

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

    What it does: it actually prints the content of a visual into an XPS document. Then it loads the "printed" XPS document and displays it in a very simple XAML file that is stored as a string, rather than as a separate module, and loaded dynamically at runtime. The resulting Window has the DocumentViewer buttons: print, adjust-to-max-page-width, and so on.

    I also added some code to hide the Search box. See this answer to WPF: How can I remove the searchbox in a DocumentViewer? for how I did that.

    The effect is like this:

    The XpsDocument can be found in the ReachFramework dll and the XpsDocumentWriter can be found in the System.Printing dll both of which must be added as references to the project

    0 讨论(0)
  • 2020-11-29 20:02

    The "FlowDocumentPageViewer" control is the basis for the "preview" control used in one of our projects. Here is the XAML of the "DocumentPreviewer" control (apologies for the length -- XAML is not succinct):

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

    Where you might put such a control is up to you (and your app) of course, but our app has a similar behavior to the typical Office app where you can either print directly or preview (which shows the above interface) and print from there.

    0 讨论(0)
提交回复
热议问题