Печать рисунка в WinRT
92Разработка под Windows 10 --- Печать рисунка
Не сомневаюсь, что с самых первых экспериментов с различными программами FingerPaint в предыдущих статьях вы горели желанием распечатать свое творение и вывесить его на дверце холодильника. Конечно, вы всегда можете сделать снимок экрана FingerPaint и распечатать его, но давайте встроим поддержку печати прямо в программу FingerPaint.
Чтобы свести к минимуму изменения готового кода FingerPaint, я решил определить производный от PrintDocument класс с именем BitmapPrintDocument. Ранее я уже упоминал о такой возможности, и хотя полученный класс не имеет переопределяемых методов, он упрощает некоторые ссылки на объекты:
public MainPage()
{
// ...
// Создание экземпляра класса, производного от PrintDocument,
// для процесса печати
new BitmapPrintDocument(() => { return bitmap; });
}
Обратите внимание на странный аргумент конструктора BitmapPrintDocument! Проблема в том, что классу BitmapPrintDocument определенно необходима ссылка на поле WriteableBitmap с именем bitmap, если он собирается выводить на печать это изображение, но его нельзя просто взять и передать конструктору BitmapPrintDocument. Поле bitmap изменяется каждый раз, когда программа загружает изображение из файла или буфера обмена или создает новый «холст». По этой причине я определил конструктор BitmapPrintDocument с параметром типа Func<BitmapSource>, чтобы каждый раз, когда экземпляру BitmapPrintDocument потребуется текущее изображение, он мог просто обратиться с обратным вызовом к MainPage.
BitmapPrintDocument сохраняет этот аргумент в поле и выполняет стандартную инициализацию:
using System;
using Windows.Graphics.Printing;
using Windows.Graphics.Printing.OptionDetails;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Printing;
namespace FingerPaint
{
public class BitmapPrintDocument : PrintDocument
{
Func<BitmapSource> getBitmap;
IPrintDocumentSource printDocumentSource;
// Печатаемый элемент
Border border = new Border
{
Child = new Image()
};
public BitmapPrintDocument(Func<BitmapSource> getBitmap)
{
this.getBitmap = getBitmap;
// Получение объекта IPrintDocumentSource и регистрация
// обработчиков событий
printDocumentSource = this.DocumentSource;
this.Paginate += OnPaginate;
this.GetPreviewPage += OnGetPreviewPage;
this.AddPages += OnAddPages;
// Прикрепить обработчик PrintManager
PrintManager.GetForCurrentView().PrintTaskRequested +=
OnPrintDocumentPrintTaskRequested;
}
// ...
}
}
Обработчик PrintTaskRequested - первое место, в котором возникает необходимость в обращении к изображению, потому что исходная ориентация страницы печати выбирается в соответствии с ориентацией изображения:
// ...
namespace FingerPaint
{
public class BitmapPrintDocument : PrintDocument
{
// ...
private async void OnPrintDocumentPrintTaskRequested(PrintManager sender,
PrintTaskRequestedEventArgs e)
{
PrintTaskRequestedDeferral deferral = e.Request.GetDeferral();
// Получение PrintTask
PrintTask printTask = e.Request.CreatePrintTask("Finger Paint",
OnPrintTaskSourceRequested);
// Возможный выбор альбомной ориентации
PrintTaskOptionDetails optionDetails =
PrintTaskOptionDetails.GetFromPrintTaskOptions(printTask.Options);
PrintOrientationOptionDetails orientation =
optionDetails.Options[StandardPrintTaskOptions.Orientation] as
PrintOrientationOptionDetails;
bool bitmapIsLandscape = false;
await border.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
BitmapSource bitmapSource = getBitmap();
bitmapIsLandscape = bitmapSource.PixelWidth > bitmapSource.PixelHeight;
});
orientation.TrySetValue(bitmapIsLandscape ? PrintOrientation.Landscape :
PrintOrientation.Portrait);
deferral.Complete();
}
// ...
}
}
Обратите внимание на необходимость использования объекта CoreDispatcher для обращения к растровому изображению в потоке пользовательского интерфейса. Вероятно, другие обработчики событий выглядят достаточно знакомо, не считая того, что ссылки на методы PrintDocument стали обычными ссылками на методы this:
// ...
namespace FingerPaint
{
public class BitmapPrintDocument : PrintDocument
{
// ...
private void OnPrintTaskSourceRequested(PrintTaskSourceRequestedArgs e)
{
e.SetSource(printDocumentSource);
}
private void OnPaginate(object sender, PaginateEventArgs e)
{
PrintPageDescription pageDesc = e.PrintTaskOptions.GetPageDescription(0);
// Получение изображения
(border.Child as Image).Source = getBitmap();
// Задать отступы для Border
double left = pageDesc.ImageableRect.Left;
double top = pageDesc.ImageableRect.Top;
double right = pageDesc.PageSize.Width - left - pageDesc.ImageableRect.Width;
double bottom = pageDesc.PageSize.Height - top - pageDesc.ImageableRect.Height;
border.Padding = new Thickness(left, top, right, bottom);
this.SetPreviewPageCount(1, PreviewPageCountType.Final);
}
private void OnGetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
this.SetPreviewPage(e.PageNumber, border);
}
private void OnAddPages(object sender, AddPagesEventArgs e)
{
this.AddPage(border);
this.AddPagesComplete();
}
}
}