- A reference to a PrintManager instance for each view that you want users to be able to print.
- Implement a PrintTask instance representing the actual printing operation.
- Create a PrintDocument instance to hold a reference to the content that you want to print and handle the events raised during the printing process.
- Calculate how many pages you need and distribute the content among them
- Render a preview
- Print your pages
- Finally you have to deference all of the events you made before navigating away from the page.
To get started let's create a simple UI
<Page
x:Class="pc.print.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:pc.print"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid x:Name="MainLayout"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Margin="0 100">
<TextBox x:Name="Data_TextBox"
Height="500" TextWrapping="Wrap"
Text="Lorem
ipsum dolor sit amet, consectetur adipiscing elit. Fusce in metus dui, a scelerisque
neque. Morbi eget sapien lectus, hendrerit semper orci. Donec elit sem,
pharetra in ornare ac, dictum sed massa. Donec rhoncus consequat urna. Sed non
enim ut quam aliquet adipiscing. Ut a enim a sem blandit lobortis. Donec non
volutpat orci. In at massa nunc, vel lobortis leo. Fusce vel erat sit amet
justo sollicitudin varius" />
<Button Content="Click" Click="Print_BTN_Click" />
</StackPanel>
</Grid>
</Page>
With that done lets take a look at our codebehind, I've gone ahead and added some of the initital steps
using System;
using Windows.Graphics.Printing;
using Windows.UI.Xaml.Controls;
namespace pc.print
{
public sealed partial class MainPage : Page
{
PrintManager _printManager;
PrintDocument _printDocument;
PrintManager _printManager;
PrintDocument _printDocument;
public MainPage() {
this.InitializeComponent();
_printDocument = new PrintDocument();
_printManager = PrintManager.GetForCurrentView();
_printManager.PrintTaskRequested +=
PrintManager_PrintTaskRequested;
}
private void PrintManager_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args) {
throw new NotImplementedException();
}
//Show
printUI
async void Print_BTN_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
await PrintManager.ShowPrintUIAsync();
}
}
}
Above we referenced the print manager and created a PrinterTask, now to test this we run our app and from the charms bar we selected Devices->Print; now this will fire our PrinteTaskRequested event which we subscribted the PrinterManager_PrintTaskRequested event handler, so this block will be the one that fires. Currently it just throws an exception, but let's change that
We create a printer task and grab a deferral, this is because the document to print can only be set on the UI thread, now at this point we don't accomplish very much at all, but we do get the PrintUI to show up.
No preview but at least we're heading in the right direction, so lets get this thing printing, because lets face it a preview is nice, but printing is a tad more important, don't worry we'll go back and set the preview up but for now lets get something on paper.
what we are going to have to do is create a page level element called PrintDocument and attach a handler to deal with passing pages to the printing API, the one restriction is that whatever we send to print has to inherit form FrameworkElement, which is just about every UI Element.
now Lets take a look at our last event handler, the AddPages one, so the print manager does the printing and the print document is what's printed. We have to add pages to our pint document, this event is raised once we hit the print button on the printUI (seen earlier). now if you hit the print button, you'll see a toast notification with your file saved (if your using xps)
if you click on the toast, you'll see you'r file printed (saved in xps viewer)
It looks awful, and it's cut off at the bottom and we have no preview, but at least we're printing.
so lets' try getting our preview to work; it's actually fairly easy first off in our page initialization we have to give the PrintDoucment's event GetPreviewPage a handler
with that done we create the handler
however as you see above we're duplicating code, which is never a good thing, we're basically creating the same TextBlock to preview as we do to print, now we could very well create a page level text block and build it in the preview and then reuse it for the add pages method, which we will, but not till we handle this paging issue first.
before we get stated know up front that this will not be a demonstration in an optimal sizing strategy, this is just to demonstrate creating multiple pages for printing. so firstly let's create a page level list of TextBlocks that we can use for our preview and for our print methods, once that's done lets' add the pagination event to our PrintDocument object.
with that done let's take a look at our event handler.
lets dig into our parse pages function
with that done lets take a look at our renderPreview Method once again
with that complete let's take a look at our actual print method
and that's it, you're not printing a multi page document.
One final thought, if you'r application nagivates away from this page make sure to de-reference all of the event handlers you referenced in the constructor for the PrintDocument and PrintManager.
//print
UI called
void PrintManager_PrintTaskRequested(PrintManager sender,
PrintTaskRequestedEventArgs args)
PrintTaskRequestedEventArgs args)
{
PrintTask pt = args.Request.CreatePrintTask("Test", async taskArgs =>
{
//create
a deferral, because the document to print can only be set on the UI
thread
var deferral = taskArgs.GetDeferral();
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
taskArgs.SetSource(_printDocument.DocumentSource);
deferral.Complete();
});
});
}
No preview but at least we're heading in the right direction, so lets get this thing printing, because lets face it a preview is nice, but printing is a tad more important, don't worry we'll go back and set the preview up but for now lets get something on paper.
what we are going to have to do is create a page level element called PrintDocument and attach a handler to deal with passing pages to the printing API, the one restriction is that whatever we send to print has to inherit form FrameworkElement, which is just about every UI Element.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Windows.Graphics.Printing;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Printing;
namespace pc.print
{
public sealed partial class MainPage : Page
{
PrintManager _printManager;
PrintDocument _printDocument;
public MainPage()
{
this.InitializeComponent();
_printDocument = new PrintDocument();
_printDocument.AddPages +=
_printDocument_AddPages;
_printManager
= PrintManager.GetForCurrentView();
_printManager.PrintTaskRequested +=
PrintManager_PrintTaskRequested;
}
//Show
printUI
async void Print_BTN_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
await PrintManager.ShowPrintUIAsync();
}
//print
UI called
void PrintManager_PrintTaskRequested(PrintManager sender,
PrintTaskRequestedEventArgs args)
{
PrintTask pt = args.Request.CreatePrintTask("Test", async taskArgs =>
{
//create
a deferral, because the document to print can only be set on
//the
UI thread
var deferral = taskArgs.GetDeferral();
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
taskArgs.SetSource(_printDocument.DocumentSource);
deferral.Complete();
});
});
}
//print
command fired
void _printDocument_AddPages(object sender, AddPagesEventArgs e)
{
//add
the pages that will be printed, in this case a TextBlock that contains our Data
_printDocument.AddPage(new TextBlock
{
Foreground = new SolidColorBrush(Colors.Black),
Text = this.Data_TextBox.Text,
FontSize = 75,
TextWrapping = TextWrapping.Wrap
});
_printDocument.AddPagesComplete();
}
}
}
if you click on the toast, you'll see you'r file printed (saved in xps viewer)
It looks awful, and it's cut off at the bottom and we have no preview, but at least we're printing.
so lets' try getting our preview to work; it's actually fairly easy first off in our page initialization we have to give the PrintDoucment's event GetPreviewPage a handler
public MainPage()
{
this.InitializeComponent();
_printDocument = new PrintDocument();
//Added
Preview Event Handler
_printDocument.GetPreviewPage += _printDocument_GetPreviewPage;
_printDocument.AddPages += _printDocument_AddPages;
_printManager = PrintManager.GetForCurrentView();
_printManager.PrintTaskRequested +=
PrintManager_PrintTaskRequested;
}
//generate
preview for print
void _printDocument_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
//set
the preview element
_printDocument.SetPreviewPage(e.PageNumber,
new TextBlock
{
Foreground = new SolidColorBrush(Colors.Black),
Text = this.Data_TextBox.Text,
FontSize = 75,
TextWrapping = TextWrapping.Wrap
});
}
now when we select our device from the print UI, we get an actual previewhowever as you see above we're duplicating code, which is never a good thing, we're basically creating the same TextBlock to preview as we do to print, now we could very well create a page level text block and build it in the preview and then reuse it for the add pages method, which we will, but not till we handle this paging issue first.
before we get stated know up front that this will not be a demonstration in an optimal sizing strategy, this is just to demonstrate creating multiple pages for printing. so firstly let's create a page level list of TextBlocks that we can use for our preview and for our print methods, once that's done lets' add the pagination event to our PrintDocument object.
PrintManager _printManager;
PrintDocument _printDocument;
List<TextBlock> _tb;
public MainPage()
{
this.InitializeComponent();
_printDocument = new PrintDocument();
_printDocument.Paginate += _printDocument_Paginate;
_printDocument.GetPreviewPage += _printDocument_GetPreviewPage;
_printDocument.AddPages += _printDocument_AddPages;
_printManager = PrintManager.GetForCurrentView();
_printManager.PrintTaskRequested +=
PrintManager_PrintTaskRequested;
}
//Printer
selected
void _printDocument_Paginate(object sender, PaginateEventArgs e)
{
//get
printer details, page widht/height, etc
PrintTaskOptions printingOptions = e.PrintTaskOptions;
PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);
//load
all text data into our virtual textblock to print
var page = new TextBlock
{
Foreground = new SolidColorBrush(Colors.Black),
Text = this.Data_TextBox.Text,
FontSize = 95,
TextWrapping = TextWrapping.Wrap
};
//Create
page boundries
var size = new Size(pageDescription.PageSize.Width,
pageDescription.PageSize.Height);
//recursive
function to spread text data over multiple TextBlocks
_tb = ParsePages(page, new List<TextBlock>(), size);
//set
how many pages in the preview
_printDocument.SetPreviewPageCount(_tb.Count, PreviewPageCountType.Intermediate);
}
List<TextBlock> ParsePages(TextBlock current, List<TextBlock> result, Size pageSize)
{
//allow
us to get our currnet textblocks actualHeight with text filled in
current.Measure(pageSize);
//check
if there's an overflow
if (current.ActualHeight - pageSize.Height <= 0)
{
result.Add(current);
return result;
}
//push
the overflow to another textblock
var overflow = new TextBlock
{
Foreground = new SolidColorBrush(Colors.Black),
FontSize = 95,
TextWrapping = TextWrapping.Wrap,
Text = current.Text.Substring(100)
};
//restrict
the current page to the first 100 chars
current.Text = current.Text.Substring(0,
100);
//add
the current page to the result set
result.Add(current);
//call
the function again
return ParsePages(overflow, result, pageSize);
}
with that done lets take a look at our renderPreview Method once again
//generate
preview for print
void _printDocument_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
//set
the preview element
var count = e.PageNumber;
foreach (var p in _tb)
_printDocument.SetPreviewPage(count++,
p);
}
with that complete let's take a look at our actual print method
//print
command fired
void _printDocument_AddPages(object sender, AddPagesEventArgs e)
{
//add
the pages that will be printed
foreach (var p in _tb)
{
var txt = p.Text;
_printDocument.AddPage(p);
}
_printDocument.AddPagesComplete();
}
One final thought, if you'r application nagivates away from this page make sure to de-reference all of the event handlers you referenced in the constructor for the PrintDocument and PrintManager.