In questa lezione esamineremo quali sono le modalità d'accesso al sistema di stampa di Windows da una Windows Store App scritta in XAML e C#. È possibile anche leggere come ottenere gli stessi risultati in HTML5/JavaScript cliccando qui.
Una volta lanciata l'applicazione (ed eventualmente navigato fino al contenuto che desideriamo stampare), per accedere al sistema di stampa da una Windows Store app è sufficiente aprire la Charms Bar e scegliere Devices, come illustrato nell'immagine successiva.
In alternativa, è anche possibile attivare il Devices Charm da programma, tramite il metodo ShowPrintUIAsync
. Ecco un esempio dell'uso di questo metodo all'interno dell'handler dell'evento click
di un ipotetico pulsante:
private async void Button_Click(object sender, RoutedEventArgs e)
{
await PrintManager.ShowPrintUIAsync();
}
Microsoft, nella documentazione ufficiale, sconsiglia di usare questo metodo in applicazioni generiche: è sempre preferibile che sia l'utente a decidere di attivare il processo di stampa tramite l'apposito charm, in modo da garantire l'uniformità della user experience. Solo in scenari particolari, in cui la stampa rappresenti un momento fondamentale del flusso applicativo (ad esempio, durante l'acquisto di un biglietto), l'uso del metodo ShowPrintUIAsync
può risultare utile per guidare l'utente lungo il processo.
Perché un'app possa effettivamente stampare, è tuttavia necessario aver prima implementato il relativo contratto di stampa. In caso contrario, l'unica cosa che l'utente vedrebbe sarebbe il messaggio "This app can't send to other devices right now", mostrato nella prossima immagine:
Nel caso di app in XAML/C#, i passi necessari all'implementazione del contratto sono decisamente più complessi rispetto a un'app in HTML5/JavaScript. Nel primo caso, infatti, è necessario procedere all'impaginazione del contenuto da mostrare nella preview direttamente via codice, mentre nel secondo caso le operazioni di impaginazione e di preview sono portate avanti direttamente dal sistema di stampa di WinRT, semplificando notevolmente l'implementazione del relativo contratto.
Questa maggiore complessità è però compensata dal fatto che, in un'applicazione Windows Store in XAML/C# lo sviluppatore ha un notevole controllo sull'intero processo (ad esempio, come avremo modo di vedere più avanti, in un'applicazione JavaScript non è possibile modificare l'impaginazione e/o il contenuto in base alle scelte effettuate dall'utente nel pannello di preview, né creare opzioni di stampa custom).
In estrema sintesi, in un'applicazione Windows Store sviluppata in XAML/C#, L'implementazione del Print contract richiede tre passaggi fondamentali:
- Ottenere una reference a un oggetto di tipo PrintManager, la classe che orchestra l'intero flusso di stampa, e abbonarsi al relativo evento
PrintTaskRequested
. - Creare un oggetto di tipo PrintTask, che rappresenta una specifica operazione di stampa.
- Gestire gli eventi esposti dalla classe PrintDocument.
Come vedremo, è possibile poi specificare altri parametri, come decidere quali opzioni di stampa mostrare all'utente, incluse eventuali opzioni di stampa custom.
Recuperare una reference ad un oggetto PrintManager
Cominciamo con il creare un nuovo progetto Windows Store app usando il template XAML Blank App messo a disposizione da Visual Studio, come mostrato nella prossima figura:
Il primo passo consiste nel recuperare una reference a un'istanza della classe Windows.Graphics.Printing.PrintManager
, ossia la classe responsabile della gestione dell'intero processo di stampa. È importante notare come la stessa istanza non possa essere condivisa tra più pagine, ma debba essere specifica per ciascuna delle viste che vogliamo stampare.
Per ottenere una reference all'istanza per la vista corrente, basta semplicemente utilizzare il metodo statico PrintManager.GetForCurrentView
in ciascuna delle pagine XAML che devono essere stampate dall'utente. Il metodo OnNavigatedTo
è un ottimo candidato per questo compito.
Il passo successivo consiste nel sottoscrivere l'evento PrintManager.PrintTaskRequested
, il quale viene sollevato ogniqualvolta l'utente attiva il Devices Charm.
Il seguente codice illustra entrambi questi passaggi:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
PrintManager pm = PrintManager.GetForCurrentView();
pm.PrintTaskRequested += PrintManager_PrintTaskRequested;
}
È importante ricordarsi di rimuovere l'handler dall'evento PrintTaskRequested
prima di lasciare la pagina, come mostrato nel seguente codice:
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
PrintManager pm = PrintManager.GetForCurrentView();
pm.PrintTaskRequested -= PrintManager_PrintTaskRequested;
}
Creare un oggetto di tipo PrintTask e gestire i relativi eventi
Una volta agganciato l'evento di richiesta di stampa, è necessario implementare l'operazione di stampa vera e propria creando un oggetto di tipo PrintTask
. La classe PrintTask
rappresenta infatti una specifica operazione di stampa e include tanto il contenuto da stampare (nella forma di un oggetto di tipo PrintDocument
, su cui torneremo fra breve), quanto una serie di opzioni relative alle modalità di stampa (come colore, qualità della stampa, formato e dimensioni della pagina, ecc.).
Per creare un l'oggetto di tipo PrintTask
che rappresenti l'operazione corrente è necessario invocare il metodo CreatePrintTask
esposto, tramite la proprietà Request
(che a sua volta è di tipo PrintTaskRequest
e, come il nome suggerisce, rappresenta la richiesta di stampa attivata dall'utente tramite il Devices Charm), dalla classe PrintTaskRequestedEventArgs
ricevuta come parametro dall'handler dell'evento PrintTaskRequested
.
private void PrintManager_PrintTaskRequested(PrintManager sender,
PrintTaskRequestedEventArgs args)
{
PrintTask printTask = null;
printTask = args.Request.CreatePrintTask("Html.it Print Sample in C#",
(sourceRequested) =>
{
// codice omesso
});
}
Come si vede, il metodo CreatePrintTask
accetta due parametri: il primo è una stringa che rappresenta il nome da assegnare a quella particolare operazione di stampa, ed è lo stesso nome che contraddistinguerà il nostro documento all'interno del pool di stampa, come mostrato nella prossima immagine:
Il secondo parametro è rappresentato da un delegate di tipo PrintTaskSourceRequestedHandler
. Questo delegate (nel codice sopra riportato presentato sotto forma di lambda expression) referenzia il codice che verrà eseguito nel momento in cui l'utente selezionerà, all'interno dei device disponibili, la periferica da utilizzare per la stampa.
È importante sottolineare come WinRT metta a disposizione un tempo relativamente limitato (200 millisecondi) per il completamento delle operazioni all'interno dell'handler dell'evento PrintTaskRequested
. Per ovviare a questo limite, è necessario utilizzare chiamate asincrone (con il relativo pattern async/await), assieme a un deferral (di tipo PrintTaskRequestedDeferral
). In questo modo, WinRT attenderà il completamento delle operazioni asincrone fino a quando il deferral non sarà marcato come completato tramite una chiamata al metodo Complete, come illustrato nel seguente snippet:
private void PrintManager_PrintTaskRequested(PrintManager sender,
PrintTaskRequestedEventArgs args)
{
PrintTask printTask = null;
var deferral = args.Request.GetDeferral(); // prendiamo tempo
printTask = args.Request.CreatePrintTask("HTML.it Print Sample in C#",
async (sourceRequested) =>
{
// esecuzione di un'operazione asincrona
});
deferral.Complete();
}
La classe PrintTask
espone inoltre quattro eventi che permettono di seguire le diverse fasi delle operazioni:
Campo | Descrizione |
---|---|
Completed |
Viene sollevato al completamento del processo di stampa |
Previewing |
Viene sollevato quando viene inizializzato il pannello dell'anteprima di stampa |
Progressing |
È scatenato ogni volta una nuova pagina è stata inviata al sistema di stampa, in modo da permettere di conoscere, tramite la proprietà DocumentPageCount della classe PrintTaskRequestedEventArgs , lo stato di avanzamento del processo di stampa |
Submitting |
Sollevato quando il contenuto è inviato al sistema di stampa per essere stampato |
In particolare, l'evento Completed
offre importanti indicazioni sul completamento dell'operazione di stampa, contenute nella proprietà Completion
dell'oggetto PrintTaskRequestedEventArgs, che riceve l'handler come argomento. Ecco i possibili valori:
Campo | Descrizione |
---|---|
Abandoned |
L'operazione è stata abbandonata. Questo accade quando, per qualche motivo, l'operazione di stampa si interrompe dopo che il delegate di tipo PrintTaskSourceRequestedHandler è stato eseguito. Da non confondere con il successivo, che invece richiede l'azione dell'utente |
Canceled |
L'operazione è stata annullata dall'utente (in genere chiudendo il Devices Charm prima di aver avviato l'operazione di stampa) |
Submitted |
Il contenuto è stato inviato al sistema di stampa (a seguito della pressione del pulsante Stampa nel pannello di preview) |
Failed |
L'operazione di stampa è fallita |
Il seguente codice mostra, a titolo d'esempio, come gestire l'evento di Completed
.
private void PrintManager_PrintTaskRequested(PrintManager sender,
PrintTaskRequestedEventArgs args)
{
PrintTask printTask = null;
printTask = args.Request.CreatePrintTask(
"HTML.it Print Sample in C#",
(sourceRequested) =>
{
// aggiunto l'handlers
printTask.Completed += OnPrintTaskCompleted;
});
}
// ecco il codice dell'handler
private async void OnPrintTaskCompleted(PrintTask sender,
PrintTaskCompletedEventArgs args)
{
if (args.Completion == PrintTaskCompletion.Failed)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
{
// informare l'utente che l'operazione di stampa è fallita
});
}
}
Se a questo punto eseguite l'applicazione e attivate il Devices Charm, questa volta verrà mostrato direttamente l'elenco delle periferiche di stampa disponibili sul sistema. Tuttavia, dato che per adesso non abbiamo ancora fornito alcun contenuto da inviare al sistema di stampa per la preview, selezionando una qualunque delle periferiche incluse nell'elenco, l'unica cosa che otterremmo è il seguente messaggio: "The app didn't provide anything to print
".
Preparare il contenuto da stampare
Aggiungiamo adesso del codice XAML per mostrare a video un testo d'esempio, testo che poi verrà passato al sistema di stampa per la preview. Il seguente listato mostra il codice XAML usato per la pagina MainPage.xaml:
<Page
x:Class="Demo.Html.it.PrintContract.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.PrintContract.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid VerticalAlignment="Center" Width="Auto" Height="Auto" Margin="50">
<StackPanel>
<TextBlock FontSize="22" TextWrapping="Wrap" FontFamily="Segoe UI"
HorizontalAlignment="Left" VerticalAlignment="Top">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce in metus ...
</TextBlock>
<TextBlock FontSize="22" TextWrapping="Wrap" FontFamily="Segoe UI"
HorizontalAlignment="Left" VerticalAlignment="Top">
Ut vitae pretium lorem. Aliquam eleifend leo et mollis commodo. Quisque ac pretium nibh, sit amet suscipit elit...
</TextBlock>
<TextBlock FontSize="22" TextWrapping="Wrap" FontFamily="Segoe UI"
HorizontalAlignment="Left" VerticalAlignment="Top">
Integer id dapibus purus. Integer a elit sed risus rhoncus scelerisque ac vel nisi...
</TextBlock>
</StackPanel>
</Grid>
</Page>
È importante sottolineare che la pagina mostrata a video (MainPage.xaml
) non è la stessa istanza che sarà passata al sistema di anteprima, poiché ogni modifica al contenuto della pagina da stampare (come la modifica delle dimensioni e del colore del font, o una diversa impostazione dei margini) si tradurrebbe in un'analoga modifica del contenuto visualizzato a video.
Ciò che faremo è creare una nuova pagina con lo stesso identico testo ma specificamente formattata per essere stampata (ad esempio, con un font leggermente ridotto, e due controlli Stackpanel
destinati a mostrare, rispettivamente, l'intestazione superiore e inferiore della pagina). Il prossimo listato mostra il codice completo della pagina secondaria, denominata PrintSample.xaml
:
<Page
x:Class="Demo.Html.it.PrintContract.CS.PrintSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.PrintContract.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid VerticalAlignment="Center" x:Name="PrintableArea" Width="Auto" Height="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel x:Name="DocumentHeader" Grid.Row="0" VerticalAlignment="Top" Visibility="Collapsed" Margin="0 20">
<TextBlock Foreground="Black" FontSize="16" TextAlignment="Left" FontFamily="Segoe UI">HTML.it Windows 8 Print Sample in C# Header</TextBlock>
</StackPanel>
<StackPanel x:Name="DocumentBody" Grid.Row="1">
<TextBlock FontSize="20" Foreground="Black" TextWrapping="Wrap" FontFamily="Segoe UI" HorizontalAlignment="Left" VerticalAlignment="Top">
Lorem ipsum dolor sit amet, consectetur adipiscing elit...
</TextBlock>
<TextBlock FontSize="20" Foreground="Black" TextWrapping="Wrap" FontFamily="Segoe UI" HorizontalAlignment="Left" VerticalAlignment="Top">
Ut vitae pretium lorem. Aliquam eleifend leo et mollis commodo...
</TextBlock>
<TextBlock FontSize="20" Foreground="Black" TextWrapping="Wrap" FontFamily="Segoe UI" HorizontalAlignment="Left" VerticalAlignment="Top">
Integer id dapibus purus. Integer a elit sed risus rhoncus scelerisque ac vel nisi...
</TextBlock>
</StackPanel>
<StackPanel x:Name="DocumentFooter" Grid.Row="2" VerticalAlignment="Top" Visibility="Collapsed">
<TextBlock Foreground="Black" FontSize="16" TextAlignment="Left" FontFamily="Segoe UI">HTML.it Windows 8 Print Sample in C# Footer</TextBlock>
</StackPanel>
</Grid>
</Page>
Adesso che abbiamo un contenuto specificamente pensato per essere stampato, possiamo assegnare la nuova pagina a una variabile globale di tipo FrameworkElement
. Più avanti, useremo questa reference per impaginare e formattare il contenuto prima di inviarlo al sistema di preview. Modifichiamo di conseguenza anche il metodo OnNavigatedTo
per chiamare il metodo PreparePrintContent
.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this._printDocument = new PrintDocument();
this._documentSource = this._printDocument.DocumentSource;
PrintManager pm = PrintManager.GetForCurrentView();
pm.PrintTaskRequested += PrintManager_PrintTaskRequested;
this.PreparePrintContent();
}
private FrameworkElement _pageToPrint;
private void PreparePrintContent()
{
if (this._pageToPrint == null)
{
this._pageToPrint = new PrintSample();
this._pageToPrint.InvalidateMeasure();
this._pageToPrint.UpdateLayout();
}
}
Il passo successivo consiste nel passare all'oggetto PrintTask
una reference ad un oggetto di tipo PrintDocument
che, come il nome suggerisce, rappresenterà il "contenuto" vero e proprio da inviare al sistema di preview e, successivamente, alla periferica di stampa per essere stampato.
Per prima cosa, creiamo un nuovo oggetto PrintDocument
e otteniamo una reference alla proprietà DocumentSource
(di tipo IPrintDocumentSource
; per il momento si tratta di un'interfaccia vuota, aperta tuttavia a future implementazioni). Dopodiché, nell'implementazione dell'oggetto PrintTask
, passiamo questa reference al metodo SetSource
della classe PrintTaskRequestedEventArgs
.
In questo modo, abbiamo creato "un'associazione" tra l'oggetto PrintDocument
e la specifica operazione di stampa rappresentata dall'oggetto PrintTask
. Più avanti utilizzeremo la reference alla classe PrintDocument
(o meglio, gli eventi esposti da questa classe) per impaginare il contenuto da stampare e passarlo al sistema di preview. Il seguente codice illustra i vari passaggi:
private PrintDocument _printDocument;
private IPrintDocumentSource _documentSource;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this._printDocument = new PrintDocument();
this._documentSource = this._printDocument.DocumentSource;
PrintManager pm = PrintManager.GetForCurrentView();
pm.PrintTaskRequested += PrintManager_PrintTaskRequested;
}
private void PrintManager_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
PrintTask printTask = null;
printTask = args.Request.CreatePrintTask("Html.it Print Sample in C#", (sourceRequested) =>
{
printTask.Completed += OnPrintTaskCompleted;
sourceRequested.SetSource(this._documentSource);
});
}
Eseguendo adesso l'applicazione, la finestra di preview cercherà di recuperare l'anteprima del documento da stampare ma, dal momento che ancora non abbiamo fornito alcun contenuto, l'unica cosa che vedremo al termine delle operazioni sarà una pagina bianca, come mostrato nella prossima immagine.
Impaginare il documento per la preview di stampa
La parte centrale del processo di stampa è rappresentata dall'impaginazione del contenuto per la finestra di preview e nell'invio del documento finale alla stampante. Per gestire questa fase cruciale, la classe PrintDocument
espone tre eventi, che rappresentano altrettante fasi del complessivo flusso di stampa:
Evento | Quando si verifica |
---|---|
Paginate |
Nel momento in cui l'utente attiva il Devices charm e seleziona una periferica di stampa dall'elenco dei device disponibili sul sistema. È questo il momento in cui il documento deve essere impaginato |
GetPreviewPage |
Ogniqualvolta l'utente naviga da una pagina all'altra all'interno della finestra di preview |
AddPages |
Quando l'utente clicca sul pulsante Print nella finestra di preview. In questo caso, occorre fornire al sistema di stampa il set finale delle pagine da inviare alla stampante |
Riprendiamo dunque il nostro metodo OnNavigatedTo
e abboniamoci ai tre eventi come segue:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this._printDocument = new PrintDocument();
this._documentSource = this._printDocument.DocumentSource;
this._printDocument.Paginate += PrintDocument_Paginate;
this._printDocument.GetPreviewPage += PrintDocument_GetPreviewPage;
this._printDocument.AddPages += PrintDocument_AddPages;
PrintManager pm = PrintManager.GetForCurrentView();
pm.PrintTaskRequested += PrintManager_PrintTaskRequested;
this.PreparePrintContent();
}
Come già accennato, l'evento Paginate
viene sollevato da WinRT nel momento in cui l'utente seleziona una periferica di stampa nel Devices Charm. Il seguente listato mostra il metodo completo:
private List<UIElement> _pageList = new List<UIElement>();
private void PrintDocument_Paginate(object sender, PaginateEventArgs e)
{
var marginLeft = 0.2;
var marginTop = 0.3;
this._pageList.Clear();
PrintTaskOptions printingOptions = ((PrintTaskOptions)e.PrintTaskOptions);
PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);
this._pageToPrint.Width = pageDescription.ImageableRect.Width;
this._pageToPrint.Height = pageDescription.ImageableRect.Height;
Grid printableArea = (Grid)this._pageToPrint.FindName("PrintableArea");
double marginWidth = Math.Max(pageDescription.PageSize.Width - pageDescription.ImageableRect.Width,
pageDescription.PageSize.Width * marginLeft);
double marginHeight = Math.Max(pageDescription.PageSize.Height - pageDescription.ImageableRect.Height,
pageDescription.PageSize.Height * marginTop);
printableArea.Width = this._pageToPrint.Width - marginWidth;
printableArea.Height = this._pageToPrint.Height - marginHeight;
this._pageList.Add(this._pageToPrint);
this._printDocument.SetPreviewPageCount(this._pageList.Count, PreviewPageCountType.Intermediate);
}
Vediamo di analizzare i singoli passaggi un po' più nel dettaglio.
Per prima cosa, il codice provvede ad adattare le dimensioni della pagina da stampare (referenziata dalla variabile _pageToPrint
) al formato della carta utilizzata, accessibile tramite la proprietà ImageableRect
della classe PrintPageDescription
. Per recuperare queste informazioni, viene utilizzato il metodo GetPageDescription
della classe PrintTaskOptions
(su cui torneremo nei prossimi paragrafi):
PrintTaskOptions printingOptions = ((PrintTaskOptions)e.PrintTaskOptions);
PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);
this._pageToPrint.Width = pageDescription.ImageableRect.Width;
this._pageToPrint.Height = pageDescription.ImageableRect.Height;
Il passo successivo consiste nel ridurre le dimensioni del controllo Grid
che contiene il testo da stampare in modo da lasciare un po' di margini tra i bordi della pagina e il testo medesimo.
Grid printableArea = (Grid)this._pageToPrint.FindName("PrintableArea");
double marginWidth = Math.Max(pageDescription.PageSize.Width -
pageDescription.ImageableRect.Width,
pageDescription.PageSize.Width * marginLeft);
double marginHeight = Math.Max(pageDescription.PageSize.Height -
pageDescription.ImageableRect.Height,
pageDescription.PageSize.Height * marginTop);
printableArea.Width = this._pageToPrint.Width - marginWidth;
printableArea.Height = this._pageToPrint.Height - marginHeight;
A questo punto, possiamo aggiungere la nostra pagina – formattata e impaginata per la stampa – alla collezione di UIElement
che rappresenta l'insieme delle pagine da stampare (nel nostro caso limitata a una per semplicità) e impostare il numero totali di pagine da stampare tramite il metodo SetPreviewPageCount
della classe PrintDocument
:
this._pageList.Add(this._pageToPrint);
this._printDocument.SetPreviewPageCount(this._pageList.Count,
PreviewPageCountType.Intermediate);
Una volta impaginato il contenuto (nel nostro caso, vale la pena ripeterlo, limitato a una sola pagina per motivi di brevità espositiva), il passo successivo consiste nell'implementare l'evento GetPreviewPage
, che come si è accennato viene sollevato ogni volta che l'utente passa da una pagina all'altra nella finestra di preview.
Quello che dobbiamo fare nel relativo event handler è semplicemente passare al sistema la pagina di volta in volta richiesta per la preview. Per far questo, è sufficiente invocare il metodo SetPreviewPage
della classe PrintDocument
, passando come parametri il numero della pagina corrente e l'UIElement
che contiene la vista corrispondente, come mostrato nel prossimo snippet:
private void PrintDocument_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
this._printDocument.SetPreviewPage(1, this._pageList[e.PageNumber - 1]);
}
Da notare come la classe GetPreviewPageEventArgs
ricevuta come parametro espone una proprietà PageNumber
che indica quale pagina è stata richiesta dal sistema di stampa per la preview.
L'ultimo passaggio consiste nell'implementare il terzo evento descritto, ovvero AddPages
. Questo evento viene sollevato allorché l'utente clicca sul pulsante Print nella finestra di preview e richiede di inviare il documento, precedentemente impaginato e formattato, al sistema di stampa per essere effettivamente stampato. Per compiere questa operazione, è sufficiente aggiungere le singole pagine che compongono il documento finale all'oggetto PrintDocument
, tramite il metodo AddPage
. Una volta che tutte le pagine sono state aggiunte, è necessario invocare il metodo AddPagesComplete
per segnalare il completamento dell'operazione.
private void PrintDocument_AddPages(object sender, AddPagesEventArgs e)
{
for (int i = 0; i < this._pageList.Count; i++)
this._printDocument.AddPage(this._pageList[i]);
this._printDocument.AddPagesComplete();
}
Finalmente la nostra app è in grado di stampare il contenuto mostrato a video. Se adesso premete F5, dovreste ottenere un risultato simile a quello mostrato nella prossima immagine.
Come abbiamo visto, stampare nativamente da una applicazione Windows Store non è semplicissimo e richiede una serie di passaggi per preparare il contenuto da stampare, renderlo visibile nell'anteprima di stampa, gestire l'interazione con i vari eventi che il Device Charm invia all'applicazione.
Dall'altra parte questo meccanismo consente di spingersi ben oltre la semplice stampa di form/documenti, come vedremo anche nel prossimo articolo "Caratteristiche avanzate del Print Contract".