With that done lets take a look at our codebehind, I've gone ahead and added some of the initital steps
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
//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();
});
});
}
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.
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();
}
}
}
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
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;
}
with that done we create the handler
//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 preview
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.
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;
}
with that done let's take a look at our event handler.
//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);
}
lets dig into our parse pages function
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();
}
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.