Turn the RSS FEED OFF
I want to get progress event when printing using WPF's PrintDialog .
I have a DocumentViewer with a FixedDocument.
And I use System.Windows.Controls.PrintDialog to print the document.
pd = PrintDialog Object
pd. Print()
or
pd.PrintDocument(DocumentPaginator, "")
It proceeds in two forms.
At this time I want to monitor the progress in PrintQueue .
I want to keep showing the currently ongoing Page out of the entire page on the UI thread.
However, no event can be referenced.
Is there any better way?
2 answers
Sort by: Most helpful
-
-
Hui Liu-MSFT 38,251 Reputation points Microsoft Vendor
2023-02-08T08:25:24.2133333+00:00 The PrintDialog is not using any async API. So printing is synchronous. This means you can't update the UI in realtime.
As a solution you would have to print the document manually. This allows to use asynchronous APIs. In addition, you get more control over the process (for example you can pick a printer or configure the print job explicitly).
To allow the user to pick a printer, you would have to create your own custom small dialog.
A custom DocumentPaginator implementation (RangeDocumentPaginator) is used to support printing page ranges.
MainWindow.xaml.cs:
private CancellationTokenSource CancellationTokenSource { get; set; } private async Task OnPrintButtonClickedAsync(object sender, EventArgs e) { DocumentPaginator documentPaginator = ((IDocumentPaginatorSource)this.Document).DocumentPaginator; var progressReporter = new Progress<(int PageNumber, int Percentage)>(ReportProgress); using var printer = new MyDocumentPrinter(); this.ProgressBar.Maximum = await printer.GetPageCountAsync(documentPaginator); this.CancellationTokenSource = new CancellationTokenSource(); try { // Print complete document await printer.PrintFullDocumentAsync(documentPaginator, progressReporter, this.CancellationTokenSource.Token); // Print document pages 1 to 10. // The page numbers must be converted to indices 0 to 9. // The method takes a 'Range' as argument. // 'Range' is defined as inclusive start index and exclusive end index. // Therefore the 'Range' for the pages 1 to 10 is '0..10'. await printer.PrintDocumentPageRangeAsync(documentPaginator, 0..10, progressReporter, this.CancellationTokenSource.Token); } catch (OperationCanceledException) { } finally { this.CancellationTokenSource.Dispose(); this.CancellationTokenSource = null; } } private void OnCancelButtonClicked(object sender, RoutedEventArgs e) => this.CancellationTokenSource?.Cancel(); private void ReportProgress((int PageCount, int Percentage) progress) => this.ProgressBar.Value = progress.PageCount;
MyDocumentPrinter.cs
public class MyDocumentPrinter : IDisposable { private TaskCompletionSource PrintTaskCompletionSource { get; set; } private TaskCompletionSource<int> ComputePagesTaskCompletionSource { get; set; } private PrintServer PrintServer { get; } private IProgress<(int PageNumber, int Percentage)> CurrentProgressReporter { get; set; } private bool IsPrintJobPending { get; set; } private CancellationToken CancellationToken { get; set; } public MyDocumentPrinter() => this.PrintServer = new LocalPrintServer(); public Task<int> GetPageCountAsync(DocumentPaginator documentPaginator) { this.CancellationToken = CancellationToken.None; if (!documentPaginator.IsPageCountValid) { this.ComputePagesTaskCompletionSource = new TaskCompletionSource<int>(TaskCreationOptions.None); documentPaginator.ComputePageCountCompleted += OnCountingPagesCompleted; documentPaginator.ComputePageCountAsync(); return this.ComputePagesTaskCompletionSource.Task; } return Task.FromResult(documentPaginator.PageCount); } public async Task PrintFullDocumentAsync(DocumentPaginator documentPaginator, IProgress<(int PageNumber, int Percentage)> progressReporter, CancellationToken cancellationToken) => await PrintDocumentPageRangeAsync(documentPaginator, .., progressReporter, cancellationToken); public Task PrintDocumentPageRangeAsync(DocumentPaginator documentPaginator, Range pageIndexRange, IProgress<(int PageNumber, int Percentage)> progressReporter, CancellationToken cancellationToke) { this.CancellationToken = cancellationToken; this.CancellationToken.ThrowIfCancellationRequested(); this.IsPrintJobPending = true; this.CurrentProgressReporter = progressReporter; this.PrintTaskCompletionSource = new TaskCompletionSource(TaskCreationOptions.None); var rangeDocumentPaginator = new MyRangeDocumentPaginator(documentPaginator, pageIndexRange); if (!rangeDocumentPaginator.IsPageCountValid) { this.ComputePagesTaskCompletionSource = new TaskCompletionSource<int>(TaskCreationOptions.None); rangeDocumentPaginator.ComputePageCountCompleted += OnCountingPagesCompleted; rangeDocumentPaginator.ComputePageCountAsync(); return this.PrintTaskCompletionSource.Task; } StartPrintJob(rangeDocumentPaginator); this.IsPrintJobPending = false; return this.PrintTaskCompletionSource.Task; } private void StartPrintJob(DocumentPaginator documentPaginator) { this.CancellationToken.ThrowIfCancellationRequested(); /* Select the destination printer */ // Optionally show a custom printer picker dialog PrintQueue destinationPrinter = GetPrintQueueFromUser(printServer); // Alternatively use the default printer PrintQueue destinationPrinter = printServer.DefaultPrintQueue; // Alternatively, pick a particular printer explicitly e.g., native PDF printer PrintQueue destinationPrinter = this.PrintServer.GetPrintQueue("Microsoft Print to PDF"); /* Start the printing */ // Create a XpsDocumentWriter that writes to the printer queue XpsDocumentWriter documentWriter = PrintQueue.CreateXpsDocumentWriter(destinationPrinter); documentWriter.WritingProgressChanged += OnPrintProgressChanged; documentWriter.WritingCompleted += OnPrintingCompleted; documentWriter.WriteAsync(documentPaginator); } private PrintQueue GetPrintQueueFromUser(PrintServer printServer) { PrintQueueCollection printers = printServer.GetPrintQueues(); // TODO::Implement MyPrinterPickerDialog (extend Window) var myPrinterPickerDialog = new MyPrinterPickerDialog(printers); myPrinterPickerDialog.ShowDialog(); return myPrinterPickerDialog.SelectedPrintQueue; } private void OnCountingPagesCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) { var documentPaginator = sender as DocumentPaginator; documentPaginator.ComputePageCountCompleted -= OnCountingPagesCompleted; if (this.CancellationToken.IsCancellationRequested) { this.ComputePagesTaskCompletionSource.TrySetCanceled(this.CancellationToken); this.CancellationToken.ThrowIfCancellationRequested(); } else { _ = this.ComputePagesTaskCompletionSource.TrySetResult(documentPaginator.PageCount); } if (this.IsPrintJobPending) { StartPrintJob(documentPaginator); } } private void OnPrintProgressChanged(object sender, WritingProgressChangedEventArgs e) { var documentPrinter = sender as XpsDocumentWriter; this.CurrentProgressReporter.Report((e.Number, e.ProgressPercentage)); if (this.CancellationToken.IsCancellationRequested) { documentPrinter.WritingCancelled += OnPrintingCancelled; documentPrinter.CancelAsync(); } } private void OnPrintingCancelled(object sender, WritingCancelledEventArgs e) { var documentPrinter = sender as XpsDocumentWriter; documentPrinter.WritingCancelled -= OnPrintingCancelled; this.PrintTaskCompletionSource.TrySetCanceled(this.CancellationToken); this.CancellationToken.ThrowIfCancellationRequested(); } private void OnPrintingCompleted(object sender, WritingCompletedEventArgs e) { // TODO::Handle errors by checking event args documentWriter.WritingCompleted -= OnPrintingCompleted; documentWriter.WritingProgressChanged -= OnPrintProgressChanged; _ = this.PrintTaskCompletionSource.TrySetResult(); } private bool disposedValue; protected virtual void Dispose(bool disposing) { if (!this.disposedValue) { if (disposing) { this.PrintServer.Dispose(); } // TODO: free unmanaged resources (unmanaged objects) and override finalizer // TODO: set large fields to null this.disposedValue = true; } } // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources // ~MyDocumentPrinter() // { // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method // Dispose(disposing: false); // } public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } }
MyRangeDocumentPaginator.cs:
internal class MyRangeDocumentPaginator : DocumentPaginator { protected MyRangeDocumentPaginator() : base() { } public MyRangeDocumentPaginator(DocumentPaginator documentPaginator, Range pageNumberRange) { this.DocumentPaginator = documentPaginator; this.PageIndexRange = pageNumberRange; if (!this.DocumentPaginator.IsPageCountValid) { this.DocumentPaginator.ComputePageCountCompleted += OnPageCountCompleted; this.DocumentPaginator.ComputePageCountAsync(); } else { ThrowIfPageRangeIsInvalid(); } } private void ThrowIfPageRangeIsInvalid() { if (this.PageIndexRange.Start.Value < 0 || this.PageIndexRange.End.Value > this.DocumentPaginator.PageCount) { throw new IndexOutOfRangeException(); } } private void OnPageCountCompleted(object sender, AsyncCompletedEventArgs e) => ThrowIfPageRangeIsInvalid(); public override DocumentPage GetPage(int pageIndex) { // XpsDocumentWriter will always start with page index 0. // We have to use the start page index as offset // to return the correct pages of the range from the underlying document. // XpsDocumentWriter will use the PageCount property to know how many pages it can fetch. // We have manipulated the PageCount property to return the count of the pages contained within the range. int pageOffset = this.PageIndexRange.Start.Value; pageIndex += pageOffset; return pageIndex < this.PageIndexRange.Start.Value || (pageIndex >= this.PageIndexRange.End.Value && !this.IsRangeFullDocument) ? DocumentPage.Missing : this.DocumentPaginator.GetPage(pageIndex); } public override bool IsPageCountValid => this.DocumentPaginator.IsPageCountValid; public override int PageCount { get { int range = this.PageIndexRange.End.Value - this.PageIndexRange.Start.Value; return this.IsRangeFullDocument ? this.DocumentPaginator.PageCount : range; } } public override Size PageSize { get => this.DocumentPaginator.PageSize; set => this.DocumentPaginator.PageSize = value; } public override IDocumentPaginatorSource Source => this.DocumentPaginator.Source; public Range PageIndexRange { get; set; } public bool IsRangeFullDocument => this.PageIndexRange.Equals(Range.All); private DocumentPaginator DocumentPaginator { get; } }
If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.