I want to keep showing the currently ongoing Page out of the entire page on the UI thread.
However, no event can be referenced.
I've seen these links:
WPF - How to update ProgressBar based on the progress of a method from a different class?
https://learn.microsoft.com/en-us/answers/questions/1165557/wpf-download-file-with-progress-bar
I have the following code now, but I don't know how to do it.
Is there any better way?
<StackPanel Orientation="Horizontal" >
<Button HorizontalAlignment="Center" Width="80" Click="PrintButtonClick">print</Button>
<ProgressBar x:Name="progressbar" Margin="10,0" Maximum="100" Width="140" Height="20" Value="{Binding Progress}"/>
<TextBox IsReadOnly="True" Width="50" Text="{Binding ProgressValue}"/>
</StackPanel>
<DocumentViewer Name="viewer" Grid.Row="1">
<FixedDocument x:Name="fd">
</FixedDocument>
</DocumentViewer>
</StackPanel>
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Xps.Packaging;
using Window = System.Windows.Window;
using System.Windows.Markup;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using PrintDialog = System.Windows.Controls.PrintDialog;
namespace DocumentViewerPrintProgressbar
{
public partial class MainWindow : Window, ICommand, INotifyPropertyChanged
{
TextBlock page1Text = new TextBlock();
public MainWindow()
{
InitializeComponent();
PrintDialog pd = new PrintDialog();
fd.DocumentPaginator.PageSize = new Size(pd.PrintableAreaWidth, pd.PrintableAreaHeight);
for (int i = 0; i <= 5; i++)
{
FixedPage page1 = new FixedPage();
page1.Width = fd.DocumentPaginator.PageSize.Width;
page1.Height = fd.DocumentPaginator.PageSize.Height;
UIElement page1Text = pages();
page1.Children.Add(page1Text);
PageContent page1Content = new PageContent();
((IAddChild)page1Content).AddChild(page1);
fd.Pages.Add(page1Content);
}
}
private UIElement pages()
{
Canvas pcan = new Canvas();
TextBlock page1Text = new TextBlock();
page1Text.TextWrapping = TextWrapping.Wrap;
for (int i = 1; i < 1200; i++)
{
page1Text.Text += i.ToString() + "This is a testssssssssssssssssssssssssssssssssssssssssssss";
}
page1Text.FontSize = 40;
page1Text.Margin = new Thickness(96);
pcan.Children.Add(page1Text);
return pcan;
}
void PrintButtonClick(object sender, RoutedEventArgs e)
{
var dlg = new PrintDialog();
// Allow the user to select a PageRange
dlg.UserPageRangeEnabled = true;
if (dlg.ShowDialog() == true)
{
DocumentPaginator paginator = fd.DocumentPaginator;
dlg.PrintDocument(paginator, "Just a test");
}
}
public string ProgressValue { get; set; }
public int Status { get; set; }
public double Progress { get; set; }
public void Execute(object parameter)
{
Progress = 0;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => true;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
public static bool PrintWholeDocument(string xpsFilePath, bool hidePrintDialog = false)
{
PrintDialog printDialog = new PrintDialog();
if (!hidePrintDialog)
{
bool? isPrinted = printDialog.ShowDialog();
if (isPrinted != true)
return false;
}
try
{
XpsDocument xpsDocument = new XpsDocument(xpsFilePath, FileAccess.Read);
FixedDocumentSequence fixedDocSeq = xpsDocument.GetFixedDocumentSequence();
DocumentPaginator docPaginator = fixedDocSeq.DocumentPaginator;
printDialog.PrintDocument(docPaginator, $"Printing {System.IO.Path.GetFileName(xpsFilePath)}");
return true;
}
catch (Exception e)
{
System.Windows.MessageBox.Show(e.Message);
return false;
}
}
}
}
Update:
private void btnPrint_Click(object sender, RoutedEventArgs e)
{
this.Topmost = false;
PrintDialog printDialog = new PrintDialog();
CancellationTokenSource printServerWatchTaskCancellationTokenSource = new CancellationTokenSource();
PrintServer printServer = new PrintServer(PrintSystemDesiredAccess.AdministrateServer);
if (printDialog.ShowDialog() == true)
{
Task printServerTask = new Task(async () =>
{
await this.Dispatcher.InvokeAsync(async () =>
{
PrintQueue _queue = new PrintQueue(printServer, printDialog.PrintQueue.FullName);
StringBuilder sb = new StringBuilder();
PrintSystemJobInfo job;
while (true)
{
_queue.Refresh();
if (_queue.NumberOfJobs > 0)
{
sb.Clear();
job = _queue.GetPrintJobInfoCollection().Where(x => x.Name.Equals("AbleSoft PostDocument2.0")).SingleOrDefault();
if (job != null)
{
switch (job.JobStatus)
{
case PrintJobStatus.Spooling:
sb.AppendLine($"Spooling");
sb.AppendLine($"{job.NumberOfPages} / {PrintTargetFileInfo.Count}");
break;
case PrintJobStatus.Printing:
case (PrintJobStatus.Printing | PrintJobStatus.Retained):
sb.AppendLine($"Printing");
sb.AppendLine($"{job.NumberOfPagesPrinted} / {PrintTargetFileInfo.Count}");
break;
}
ProgressText.Dispatcher.Invoke(() =>
{
ProgressText.Text = sb.ToString();
});
}
}
await Task.Delay(1);
}
}, System.Windows.Threading.DispatcherPriority.Background);
await Task.Delay(1);
}, printServerWatchTaskCancellationTokenSource.Token);
printServerTask.Start();
printDialog.PrintDocument(documentViewer.Document.DocumentPaginator, "AbleSoft PostDocument2.0");
printServerWatchTaskCancellationTokenSource.Cancel();
}
}
The
PrintDialogis not using anyasyncAPI. 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
DocumentPaginatorimplementation (RangeDocumentPaginator) is used to support printing page ranges.MainWindow.xaml.cs
DocumentPrinter.cs
RangeDocumentPaginator.cs