Печатаемые и непечатаемые поля в WinRT

51

Даже после предосторожности с назначением черного цвета элемент TextBlock некорректно печатается на моем принтере - и скорее всего, на вашем тоже. Он втискивается в левый верхний угол страницы, а для большинства механизмов печати край листа недоступен, поэтому часть текста пропадает.

Если вы попытаетесь решить проблему, задавая свойствам HorizontalAlignment и VerticalAlignment элемента TextBlock значение Center, выясняется, что эти свойства в данном случае не работают. Режимы выравнивания задаются относительно родительского элемента, а элемент TextBlock не имеет родителя, потому что на печатной странице он является элементом верхнего уровня. Свойство Margin не работает по той же причине. Впрочем, задание свойства Padding элемента TextBlock приведет к желаемому эффекту, потому что его действие относится к самому элементу TextBlock.

Другое, намного более удобное и универсальное решение - создание для каждой страницы печати визуального дерева, начинающегося с объекта Border верхнего уровня. При печати элемент Border будет занимать весь размер листа, но для него можно определить ненулевое свойство Padding, фактически определяющее поля для всей страницы.

Объекты PaginateEventArgs и AddPagesEventArgs включают свойство с именем PrintTaskOptions и типом PrintTaskOptions. Многие свойства этого объекта соответствуют свойствам принтера, которые могут задаваться пользователем вручную: Collation, NumberOfCopies, Orientation, PrintQuality и т.д. Программа может обращаться к этим свойствам для настройки печати, но обычно это не обязательно. Позднее я покажу, как программа может инициализировать эти свойства, а также добавить к ним некоторые пользовательские настройки.

PrintTaskOptions также содержит метод GetPageDescription. Аргумент метода содержит номер страницы (нумерация начинается с нуля); предполагается, что страницы могут иметь разные размеры. Возвращаемая этим методом структура PrintPageDescription содержит свойства DpiX и DpiY, в которых содержится информация о фактическом разрешении принтера (обычно 600 или 1200), а также свойство PageSize типа Size со значениями в 1/96 дюйма. Для стандартного листа Letter 8,5 х 11 дюймов свойства PageSize равны 816 и 1056.

Структура PrintPageDescription также включает свойство ImageableRect типа Rect. Оно обозначает прямоугольную область страницы, внутри которой может осуществляться печать. Для листа размера Letter на моем принтере левый верхний угол этого прямоугольника находится в точке (12.48, 11.35748), а размеры равны (791.04, 988.1575) в единицах 1/96 дюйма. Сравните с размерами PageSize 816 x 1056. После простых операций вычитания можно сделать вывод, что принтер не может печатать в областях 12.48 единицы у левой и правой сторон, 11.3748 у верхней стороны и 56.48502 (более 0,5 дюйма) у нижней стороны. В альбомном режиме значения PageSize и ImageableRect отражают конкретную ориентацию страницы.

Проверим, насколько точны эти числа. Программа PrintPrintableArea определяет в файле XAML элемент TextBlock:

<Grid Background="#FF1D1D1D">
        <TextBlock Text="Область печати"
                   FontSize="24"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center" />
</Grid>

Файл фонового кода имеет такую же структуру, как в программе HelloPrinter из предыдущей статьи, разве что выводимое содержимое более детализировано: оно состоит из элемента Border с красным фоном, вложенного элемента Border с белым фоном и черным контуром и элемента TextBlock, выровненного по центру.

Обратите внимание: вместо отдельного метода обратного вызова, передаваемого методу CreatePrintTask, я определил анонимную лямбда-функцию. Это стандартный прием, но при усложнении логики печати он становится менее удобным:

using Windows.Graphics.Printing;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Printing;

namespace WinRTTestApp
{
    public sealed partial class MainPage : Page
    {
        PrintDocument printDocument;
        IPrintDocumentSource printDocumentSource;

        // Элемент выводимый на печать
        Border border = new Border
        {
            Background = new SolidColorBrush(Colors.Red),

            Child = new Border
            {
                Background = new SolidColorBrush(Colors.White),
                BorderBrush = new SolidColorBrush(Colors.Black),
                BorderThickness = new Thickness(1),
                Child = new TextBlock
                {
                    Text = "Область печати",
                    FontSize = 96,
                    FontFamily = new FontFamily("Times New Roman"),
                    Foreground = new SolidColorBrush(Colors.Black),
                    HorizontalAlignment = HorizontalAlignment.Center,
                    VerticalAlignment = VerticalAlignment.Center
                }
            }
        };

        public MainPage()
        {
            this.InitializeComponent();

            // Создание объекта PrintDocument и назначение обработчиков
            printDocument = new PrintDocument();
            printDocumentSource = printDocument.DocumentSource;
            printDocument.Paginate += OnPrintDocumentPaginate;
            printDocument.GetPreviewPage += OnPrintDocumentGetPreviewPage;
            printDocument.AddPages += OnPrintDocumentAddPages;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // Присоединить обработчик
            PrintManager printManager = PrintManager.GetForCurrentView();
            printManager.PrintTaskRequested += OnPrintManagerPrintTaskRequested;

            base.OnNavigatedTo(e);
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            // Отсоединение обработчика
            PrintManager.GetForCurrentView().PrintTaskRequested 
                -= OnPrintManagerPrintTaskRequested;

            base.OnNavigatedFrom(e);
        }

        private void OnPrintManagerPrintTaskRequested(PrintManager sender, 
            PrintTaskRequestedEventArgs e)
        {
            e.Request.CreatePrintTask("Привет принтер", OnPrintTaskSourceRequested);
        }

        private void OnPrintTaskSourceRequested(PrintTaskSourceRequestedArgs e)
        {
            e.SetSource(printDocumentSource);
        }

        private void OnPrintDocumentPaginate(object sender, 
            PaginateEventArgs e)
        {
            printDocument.SetPreviewPageCount(1, PreviewPageCountType.Final);
        }

        private void OnPrintDocumentGetPreviewPage(object sender, 
            GetPreviewPageEventArgs e)
        {
            printDocument.SetPreviewPage(e.PageNumber, border);
        }

        private void OnPrintDocumentAddPages(object sender, AddPagesEventArgs e)
        {
            printDocument.AddPage(border);
            printDocument.AddPagesComplete();
        }
    }
}

Другое важное отличие - обработчик события Paginate объекта PrintDocument. Этот обработчик получает структуру PrintPageDescription и вычисляет значение Padding, которое применяется к внешнему элементу Border печатаемой страницы:

Поля для печати

Хотя программы печати рисунков и других растровых изображений обычно стараются занять как можно большую часть страницы, многие печатные приложения предпочитают задать увеличенные поля - фиксированного или настраиваемого размера. В таких случаях обращение к свойству ImageableRect становится излишним. В следующей статье мы рассмотрим пример определения размера полей пользователем.

Пройди тесты
Лучший чат для C# программистов