如何在WPF应用程序中生成FlowDocument的“打印预览”?

16
我的一些 WPF 应用程序显示 FlowDocument。我可以使用描述在 回答中的 Printing a WPF FlowDocument 中的方法进行打印。
现在,我想添加“打印预览”功能。在正常情况下,我正在打印在窗口中显示的 FlowDocument,因此那时不需要打印预览。但在某些情况下,要打印的 FlowDocument 是在内存中即时构建的。在这些情况下,我想在打印之前显示它。
我当然可以弹出新窗口并显示 FlowDocument,但是
  1. 我希望预览实际上感觉就像是打印操作的一部分,而不仅仅是应用程序中的另一个窗口。

  2. 我不想在 FlowDocumentScrollViewer 中使用普通的 FlowDocument。它需要被限制为纸张大小,具有特定的 HxW 比率和分页而不是“任何大小”。

有什么建议吗?
  • 我应该只是使用标准窗口,在这种情况下,如何确保 FlowDocument 具有正确的比例?

  • 是否有更“集成”的方法在 Windows 的 PrintDialog UI 的范围内进行预览?

谢谢

2
嗨Cheeso,这个答案https://dev59.com/yXRB5IYBdhLWcg3wkH9N#587962建议使用XpsDocument与标准窗口结合使用...不想将其写成答案,因为我担心你已经看过那个链接。以防万一。干杯 :) - Anvaka
2个回答

24

受到我的问题下面的评论启发,我做了这个:

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
            {
            }
        }
    }
} 
它的作用是: 将一个可视化内容打印到 XPS 文档中,然后加载这个“打印”的 XPS 文档并将其显示在一个非常简单的 XAML 文件中,该文件以字符串形式存储,而不是作为一个单独的模块,在运行时动态加载。生成的窗口具有 DocumentViewer 按钮: 打印、调整最大页面宽度等。我还添加了一些代码来隐藏搜索框。详见此回答:WPF: How can I remove the searchbox in a DocumentViewer?
效果如下图所示: alt text XpsDocument 可在 ReachFramework dll 中找到,而 XpsDocumentWriter 可在 System.Printing dll 中找到,这两者都必须作为项目的引用添加。

2
我无法使用你的代码,因为我的项目找不到XpsDocument并且无法解决它。我应该添加哪个引用到我的项目中? - icaptan
1
按照Cheeso的说法,为ReachFramework和System.Printing添加引用,然后编写using System.Windows.Xps; using System.Windows.Xps.Packaging; using System.Printing;进行包含。我的编译没有问题。 - user1618054
6
_parent 是什么?_parent 是指在网页中表示当前页面的父级页面。 - Monzer Yaghi
2
嗯,它提供预览很不错,但它并不是一个完整的打印对话框。窗口中没有太多的打印选项。 - Kyle Delaney

2

"FlowDocumentPageViewer"控件是我们项目中使用的“预览”控件的基础。下面是“DocumentPreviewer”控件的XAML代码(由于XAML不够简洁,敬请谅解):

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

你可以自由选择在哪里放置这样的控件(取决于你的应用程序),但我们的应用程序与典型的Office应用程序具有类似的行为,您可以直接打印或预览(显示上面的界面)并从那里打印。


确实很有趣,但我不想拥有和管理所有的代码!我希望有一种更简单的方法。这是我做的方式:https://dev59.com/IXE95IYBdhLWcg3wadOq#2322751 - Cheeso

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接