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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2322064/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 13:58:18  来源:igfitidea点击:

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

.netwpfprintingflowdocumentflowdocumentscrollviewer

提问by Cheeso

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

我的各种 WPF 应用程序显示 FlowDocument 的。我可以使用打印 WPF FlowDocument的答案中描述的方法打印它们。

Now I'd like to add a "print preview" capability. In the normal case, I am printing the FlowDocument that is displayed in the Window, and so I wouldn't need a Print Preview then. But in some cases the FlowDocument to print is constructed on-the-fly in memory. And in these cases I'd like to display it before printing.

现在我想添加一个“打印预览”功能。在正常情况下,我正在打印显示在窗口中的 FlowDocument,因此我不需要打印预览。但在某些情况下,要打印的 FlowDocument 是在内存中即时构建的。在这些情况下,我想在打印之前显示它。

Now, I can certainly pop a new window and display the FlowDocument, but

现在,我当然可以弹出一个新窗口并显示 FlowDocument,但是

  1. I want the preview to really feellike it is part of the printing operation, and not just another Window in the app.

  2. I don't want a normal FlowDocument in a FlowDocumentScrollViewer. Rather than being "any size" it needs to be constrained to the size of the paper, a specific HxW ratio, and paginated.

  1. 我希望预览真的感觉像是打印操作的一部分,而不仅仅是应用程序中的另一个窗口。

  2. 我不想要 FlowDocumentScrollViewer 中的普通 FlowDocument。它不是“任何尺寸”,而是需要受限于纸张的尺寸、特定的 HxW 比率和分页。

Suggestions?

建议?

  • should I just use a standard Window, and in that case, how to I ensure the FlowDocument is at the proper ratio?

  • is there a more "integrated" way to do the preview within the scope of the PrintDialog UI that is part of Windows?

  • 我应该只使用标准窗口,在这种情况下,如何确保 FlowDocument 处于正确的比例?

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

Thanks

谢谢

回答by Cheeso

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.

它的作用:它实际上将视觉内容打印到 XPS 文档中。然后它加载“打印的”XPS 文档并将其显示在一个非常简单的 XAML 文件中,该文件存储为一个字符串,而不是一个单独的模块,并在运行时动态加载。生成的窗口具有 DocumentViewer 按钮:打印、调整到最大页面宽度等。

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.

我还添加了一些代码来隐藏搜索框。请参阅WPF 的答案:How can I remove the searchbox in a DocumentViewer? 我是怎么做到的。

The effect is like this:

效果是这样的:

alt text

替代文字

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

XpsDocument 可以在 ReachFramework dll 中找到,XpsDocumentWriter 可以在 System.Printing dll 中找到,两者都必须添加为对项目的引用

回答by Daniel Pratt

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

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

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.

您可以将此类控件放在哪里当然取决于您(和您的应用程序),但我们的应用程序与典型的 Office 应用程序具有相似的行为,您可以直接打印或预览(显示上述界面)并从那里打印.