Print Word document from WPF with print settings (

2019-05-24 21:59发布

问题:

I've been fighting with a printing issue for a long time now, hopefully someone can help.

Background I'm creating an Aspose.Words-document from a word-template, and mail merging it, and then want to print it directly from the WPF application with a print dialog. When printing i need to be able to select all different printer settings available for my printer (what paper to use, zoom, orientation, color etc.). This last thing seems to be what keeps my Google searches from succeeding, as all examples I find is only about giving the printer name, or how many copies to print.

Test 1 - Aspose's preferred way of printing From their forum

    private void Print(Document document)
    {
        var printDialog = new System.Windows.Forms.PrintDialog
        {
            AllowSomePages = true,
            PrinterSettings = new PrinterSettings
            {
                MinimumPage = 1,
                MaximumPage = document.PageCount,
                FromPage = 1,
                ToPage = document.PageCount
            },
            UseEXDialog = true
        };

        var result = printDialog.ShowDialog();
        if (result.Equals(DialogResult.OK))
            document.Print(printDialog.PrinterSettings);
    }

Now this seems to be perfect! But I get two one issue(s).

  • The text is doubled on the page, as it seems to print with the default font first, and second with my special font second, on top of the first. See screencap: Sorry about this one, it was a hidden Image inside the docx-file, which came to the front when converted somehow (even though hidden in Word).

  • It is dog slow... it takes forever for document.Print, even though it's only 2 pages to print, and no graphics.

Test 2 - Print with process (PDF)

    private void Print(Document document)
    {
        var savePath = String.Format("C:\\temp\\a.pdf");
        document.Save(savePath, SaveFormat.Pdf);

        var myProcess = new Process();
        myProcess.StartInfo.FileName = savePath;
        myProcess.StartInfo.Verb = "Print";
        //myProcess.StartInfo.CreateNoWindow = true;
        myProcess.Start();
        myProcess.WaitForExit();
    }

This would be a nice solution, but it doesn't give me the dialog (I can use word PrintTo and give some arguments, like printer name etc. But not for my special requirements, right?)

Test 3 - Print with Word Automation

    private void Print(Document document)
    {
        object nullobj = Missing.Value;

        var savePath = String.Format("C:\\temp\\a.docx");
        document.Save(savePath, SaveFormat.Docx);

        var wordApp = new Microsoft.Office.Interop.Word.Application();
        wordApp.DisplayAlerts = Microsoft.Office.Interop.Word.WdAlertLevel.wdAlertsNone;
        wordApp.Visible = false;

        Microsoft.Office.Interop.Word.Document doc = null;
        Microsoft.Office.Interop.Word.Documents docs = null;
        Microsoft.Office.Interop.Word.Dialog dialog = null;
        try
        {
            docs = wordApp.Documents;
            doc = docs.Open(savePath);

            doc.Activate();
            dialog = wordApp.Dialogs[Microsoft.Office.Interop.Word.WdWordDialog.wdDialogFilePrint];
            var dialogResult = dialog.Show(ref nullobj);
            if (dialogResult == 1)
            {
                doc.PrintOut(false);
            }
        }catch(Exception)
        {
            throw;
        }finally
        {
            Thread.Sleep(3000);
            if (dialog != null) Marshal.FinalReleaseComObject(dialog);
            if (doc != null) Marshal.FinalReleaseComObject(doc);
            if (docs != null) Marshal.FinalReleaseComObject(docs);

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();

            doc = null;
            wordApp.Quit(false, ref nullobj, ref nullobj);
        }

    }

Ok, so should I use Automation? It prints fine, but when it comes to closing the word-app and document I get trouble. For example I sometimes get the dialog "Margins outside printable area", and woops, the code cannot quit the process and leaves it. You see the Thread.Sleep? If I dont have it there, Word will be quitted before the printing has finished.

You see, I fall short in all my tries. What is the best way to go about this?

Thanks for your time!

回答1:

OK, I found a proper WPF-solution, converting the document to XPS and loading it into a DocumentViewer, from which I can use the native print functionality.

View.xaml

<DocumentViewer Document="{Binding XpsFixedDocumentSequence}"/>

ViewModel.cs

using System.Windows.Xps.Packaging;
...

private void PrepareDocument(Document document)
{
    var xpsDoc = GetDocumentAsXps(document);
    XpsFixedDocumentSequence = xpsDoc.GetFixedDocumentSequence();
}

private XpsDocument GetDocumentAsXps(Document document)
{
    var savePath = "C:\\temp\\doc.xps";
    document.Save(savePath, SaveFormat.Xps);
    var xpsDoc = new XpsDocument(savePath, FileAccess.Read);
    return xpsDoc;
}

/* Property XpsFixedDocumentSequence */
public const string XpsFixedDocumentSequencePropertyName = "XpsFixedDocumentSequence";
private FixedDocumentSequence _xpsFixedDocumentSequence;
public FixedDocumentSequence XpsFixedDocumentSequence
{
    get { return _xpsFixedDocumentSequence; }

    set
    {
        if (_xpsFixedDocumentSequence == value) return;
        _xpsFixedDocumentSequence = value;
        RaisePropertyChanged(XpsFixedDocumentSequencePropertyName);
    }
}

Note to self: reference the ReachFramework dll