WPF DocumentViewer override Print button

2020-06-17 13:59发布

问题:

I have implemented print preview functionality within my application using a custom DocumentViewer (shown below). I call PrintDialog.ShowDialog() before showing the preview so as to correctly create the document based on paper orientation.

The DocumentViewer print button however calls the PrintDialog.ShowDialog() prompting the user to chose printer and options once again (which they already did prior to the preview window opening).

Is there a way to have the DocumentViewer print button simply print without calling PrintDialog.ShowDialog()?

Here are my method calls:

ReportViewModel.cs

    public void PrintButtonClick(DataGrid dataGrid)
    {
        PrintDialog printDialog = new PrintDialog();
        if (printDialog.ShowDialog() == false)
            return;

        // Get page size based on print dialog printable area (orientation)
        Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);

        // create new paginator for datagrid
        DataGridDocumentPaginator paginator = new DataGridDocumentPaginator(dataGrid as DataGrid, "Employer Match Report", pageSize, new Thickness(30, 20, 30, 20));
        ...
    }

I am doing it this way so that I can correctly generate the paginator with either Portrait or Landscape size values. Without this the preview document within the DocumentViewer might not display correctly based on orientation chosen.

PrintDocumentViewer : DocumentViewer

   protected override void OnPrintCommand()
   {
       PrintDialog printDialog = new PrintDialog();
       printDialog.PrintQueue = LocalPrintServer.GetDefaultPrintQueue();
       printDialog.PrintTicket = printDialog.PrintQueue.DefaultPrintTicket;

       printDialog.PrintTicket.PageOrientation = PageOrientation;
       // Code assumes this.Document will either by a FixedDocument or a FixedDocumentSequence
       FixedDocument fixedDocument = this.Document as FixedDocument;
       FixedDocumentSequence fixedDocumentSequence = this.Document as FixedDocumentSequence;

       if (fixedDocument != null)
           fixedDocument.PrintTicket = printDialog.PrintTicket;

       if (fixedDocumentSequence != null)
           fixedDocumentSequence.PrintTicket = printDialog.PrintTicket;

       XpsDocumentWriter writer = PrintQueue.CreateXpsDocumentWriter(printDialog.PrintQueue);

       if (fixedDocument != null)
           writer.WriteAsync(fixedDocument, printDialog.PrintTicket);

       if (fixedDocumentSequence != null)
           writer.WriteAsync(fixedDocumentSequence, printDialog.PrintTicket);

       // Create Preview Window and show preview
       string s = _previewWindowXaml;
       using (var reader = new System.Xml.XmlTextReader(new StringReader(s)))
       {
           Window preview = System.Windows.Markup.XamlReader.Load(reader) as Window;

           DocumentViewer _docViewer = LogicalTreeHelper.FindLogicalNode(preview, "PrintDocumentViewer") as DocumentViewer;
           _docViewer.Document = (fixedDocument != null) ? fixedDocument as IDocumentPaginatorSource : fixedDocumentSequence as IDocumentPaginatorSource;

           // hide the search bar in the PrintPreview dialog
           ContentControl cc = _docViewer.Template.FindName("PART_FindToolBarHost", _docViewer) as ContentControl;
           cc.Visibility = Visibility.Collapsed;

           preview.ShowDialog();
       }
   }

回答1:

You may fill printer and paper size properties, then dialog is not shown.

var pd = new PrintDialog();
pd.PrintTicket.PageMediaSize = new PageMediaSize(PageMediaSizeName.NorthAmericaLetter, 816.0, 1056.0);
pd.PrintQueue = new LocalPrintServer().GetPrintQueue("Microsoft Print to PDF");

To override Print command:

<DocumentViewer>
    <DocumentViewer.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Print" Executed="Print_Executed" />
    </DocumentViewer.CommandBindings>
</DocumentViewer>


回答2:

The print button allone can not easily be overridden but I found a way to do it. Even if it may be considered a dirty hack, what counts in the end is the result.

I just overlayed the print button (which is the first button anyway) with a non-visible button that redirects clicks to my own print function. In this example code the search toolbar is also hidden and Ctrl+P is disabled:

<Grid>
    <DocumentViewer x:Name="DocumentViewer">
        <DocumentViewer.Resources>
            <Style TargetType="ContentControl">
                <Style.Triggers>
                    <Trigger Property="Name" Value="PART_FindToolBarHost">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </DocumentViewer.Resources>
        <DocumentViewer.InputBindings>
            <KeyBinding Key="P" Modifiers="Control" Command="ApplicationCommands.NotACommand" />
        </DocumentViewer.InputBindings>
    </DocumentViewer>
    <Button Width="32" Height="32" VerticalAlignment="Top" HorizontalAlignment="Left" Opacity="0.01" Click="OnPrint" ></Button>
</Grid>


回答3:

I hope you have resolved this by now, but you may be able to use this question as a base for modifying the template for the DocumentViewer control:

How do you hide a WPF DocumentViewer's menu bars?

There is a link to an MSDN article that should show you how to modify the template so that the print button does what you want instead of the default Print action.



回答4:

I think you can try this: https://github.com/Jet20070731/PrintDialog

It is a custom Print Dialog with preview in real time. And it's strong enough, you can select printer and set copies count, orientation, color, quality, zoom, paper size, paper type, paper source, etc. And it is beautiful, too.