Как распечатать из visual studio
Тема печати в Windows традиционно относится к одной из самых сложных: наличие огромного количества параметров, свойств и методов делает эту довольно простую вещь сильно запутанной. В этой лекции мы рассмотрим такие несложные задачи, как настройка параметров страницы, предварительный просмотр, печать содержимого элемента RichTextBox , а также преобразование форматов и печать рисунков, помещенных в PictureBox . Элемент PictureBox поддерживает различные режимы просмотра изображений – мы применим их всех и создадим простое приложение , позволяющее просматривать фотографии. Мы также еще раз создадим пользовательский элемент управления с собственными свойствами.
Печать содержимого RichTextBox. Элементы управления PrintDocument, PageSetupDialog, PrintPreviewDialog, PrintDialog
В качестве исходного приложения, в котором будем реализовывать возможность печати, возьмем проект TextEditor, созданный нами во второй лекции. Как мы знаем, максимальный объем элемента TextBox ограничивается 64 Кб, для работы с многостраничными документами этого явно мало. Поэтому удаляем с формы элемент txtBox и на его место помещаем элемент RichTextBox , которому задаем имя rtbText . Свойству Dock устанавливаем значение Fill , кроме того, удаляем значение свойства Text. Изменяем и добавляем следующие пункты главного меню (рис. 6.1):
Этот фрагмент полностью аналогичен добавлению на форму элементов. Мы, однако, будем использовать визуальный режим.
Свойству Document элементов управления PageSetupDialog и PrintPreviewDialog устанавливаем значение свойства Name элемента PrintDocument — printDocument1 . Тем самым мы связываем объект printDocument1 , отвечающий за формирование страниц печати, с диалоговыми окнами. Устнавливаем следующие свойства элемента PrintDialog :
В классе формы объявляем следующие переменные:
Следует обратить внимание на переменную stringPrintText — именно она будет отвечать за передачу текста в объект печати. Поскольку мы определили для нее строковый тип, это означает, что мы не сможем вывести на печать рисунки в тексте — как мы знаем, элемент RichTextBox поддерживает их наличие (в этом можно убедиться, скопировав содержимое документа Microsoft Word в наше приложение). Определение полной печати будет более сложным, что, впрочем, не является существенным для понимания самой концепции печати 1 1 За ответом на этот вопрос я отправляю читателя к библиотеке MSDN. Если вы поймете общую идею, то без труда сможете нарастить дополнительные возможности на приложение для печати. .
В конструкторе форме определяем диапазон страниц для печати:
Максимальное количество страниц в диапазоне — 9999;, очевидно, что его вполне хватит для распечатки любого документа. Основным событием, в обработчике которого практически и будет осуществляться вся печать, будет событие PrintPage элемента printDocument1 . Переключаемся в режим дизайна, выделяем элемент и, переключившись в окне Properties на вкладку событий, дважды щелкаем в поле этого события:
Далее все описываемые фрагменты кода будут относиться к этому обработчику.
Класс Graphics, принадлежащий пространству имен System.Drawing, — один из важнейших классов, отвечающих за рисование графики и вывода текста на форме. При создании новой формы пространство имен System.Drawing по умолчанию включается в шаблон. Для формирования страницы печати создаем экземпляр этого класса:
Создание документа для печати. Элементы управления PrintDocument , PageSetupDialog , PrintDialog , PrintPreviewDialog
Содержание
- Теоретические сведения
- Условие задачи
- Выполнение
- 1. Создать проект
- 2. Разработка формы. Размещение элементов управления на форме
- 3. Настройка базовых визуальных элементов управления на форме
- 4. Настройка элементов управления PageSetupDialog , PrintDialog , PrintPreviewDialog , PrintDocument
- 5. Ввод дополнительных переменных в класс формы
- 6. Программирование обработчиков событий
- 6.1. Основные события, обеспечивающие печать документа
- 6.2. Программирование обработчика события BeginPrint компонента printDocument1
- 6.3. Программирование обработчика события PrintPage компонента printDocument1
- 6.4. Программирование обработчика события клика на кнопке «Page Setup»
- 6.5. Программирование обработчика события клика на кнопке «Print»
- 6.6. Программирование обработчика события клика на кнопке «PrintPreview»
Поиск на других ресурсах:
Теоретические сведения
- PrintDocument – представляет класс, объект которого посылает данные на принтер;
- PageSetupDialog – представляет диалоговое окно, которое позволяет пользователю изменять настройки страницы для печати (внутренние поля, ориентация печати, и т.п.);
- PrintDialog – представляет диалоговое окно, которое позволяет пользователю выбирать принтер и прочие настройки принтера, такие как количество копий, ориентация страницы и т.п.;
- PrintPreviewDialog – представляет диалоговое окно, которое показывает пользователю предыдущий просмотр документа в том виде как он будет отображен при печати.
Условие задачи
Разработать приложение типа Windows Forms, который распечатывает информацию содержащуюся в элементе управления RichTextBox . Приложение должно содержать команды:
- печать документа;
- предварительный просмотр документа;
- настройка страниц в документе.
В программе использовать элементы управления PrintDocument , PageSetupDialog , PrintDialog , PrintPreviewDialog .
Выполнение
1. Создать проект
После создания проекта создается пустая форма. Класс формы называется Form1 .
2. Разработка формы. Размещение элементов управления на форме
Разработать главную форму программы как изображено на рисунке 1.
Рисунок 1. Главная форма программы
На форме нужно разместить следующие элементы управления:
- элемент управления типа Label . Создается экземпляр с именем label1 ;
- три элемента управления типа Button . Будут созданы экземпляры с именами button1 , button2 , button3 ;
- элемент управления типа RichTextBox . Создается экземпляр с именем richTextBox1 ;
- элемент управления типа PrintDocument . Создается экземпляр с именем printDocument1 . Этот элемент управления представляет документ, который будет распечатан;
- элемент управления типа PageSetupDialog . Создается экземпляр с именем pageSetupDialog1 ;
- элемент управления типа PrintDialog . Создается экземпляр с именем printDialog1 ;
- элемент управления типа PrintPreviewDialog . Создается экземпляр с именем printPreviewDialog1 .
Размещение элементов управления PageSetupDialog , PrintDialog , PrintDocument , PrintPreviewDialog изображено на рисунке 2.
Рисунок 2. Элементы управления PageSetupDialog , PrintDialog , PrintDocument , PrintPreviewDialog
3. Настройка базовых визуальных элементов управления на форме
После настройки форма будет иметь вид, как изображено на рисунке 1.
4. Настройка элементов управления PageSetupDialog , PrintDialog , PrintPreviewDialog , PrintDocument
Следующим шагом нужно связать элементы управления pageSetupDialog1 , printDialog1 , printPreviewDialog1 с документом printDocument1 . Для этого используется свойство Document . Настройки выглядят следующим образом:
– pageSetupDialog1.Document = printDocument1 (рисунок 3);
– printDialog1.Document = printDocument1 ;
– printPreviewDialog1.Document = printDocument1 ;
– printDialog1.AllowSomePages = True . Это свойство разрешает или запрещает кнопку настройки страницы в окне печати.Рисунок 3. Элемент управления pageSetupDialog1 . Свойство Document = printDocument1
5. Ввод дополнительных переменных в класс формы
Согласно условию задачи, нужно распечатать текст, который набран в элементе управления richTextBox1 . При печати нужно сохранять номер строки в richTextBox1 , которая на данный момент печатается. Для обеспечения этого в класс формы Form1 вводится дополнительная переменная counter .
Также вводится переменная curPage , определяющая страницу, которая печатается на данный момент.Фрагмент текста класса формы Form1 следующий
6. Программирование обработчиков событий
6.1. Основные события, обеспечивающие печать документа
Если вызывается команда печати на принтер или предварительного просмотра документа, то в работу вступают обработчики событий класса PrintDocument . С помощью событий класса PrintDocument можно руководить печатью документа.
Перечень возможных событий класса PrintDocument следующий:- BeginPrint – это событие возникает перед самым началом печати документа. В обработчик этого события целесообразно вписывать код инициализации внутренних переменных, которые руководят печатью документа;
- PrintPage – возникает один раз при печати каждой страницы. Например, если печатается 5 страниц, то это событие возникнет 5 раз;
- EndPrint – возникает, после печати документа;
- QueryPageSetting – возникает прежде чем каждая страница должна быть напечатана. Перехват этого события полезен в случаях, когда нужно изменить настройки конкретной печатаемой страницы.
6.2. Программирование обработчика события BeginPrint компонента printDocument1
Первым программируется событие BeginPrint элемента управления printDocument1 . Чтобы получить обработчик этого события, нужно выбрать элемент управления printDocument1 и в перечне событий выбрать событие BeginPrint (двойной клик мышкой).
В нашем случае, в обработчике события BeginPrint нужно реализовать инициализацию переменных counter и curPage6.3. Программирование обработчика события PrintPage компонента printDocument1
Основной программный код, выполняющий вывод на принтер, реализован в обработчике события PrintPage . Текст обработчика следующий:
В цикле while фигурирует два счетчика: счетчик страницы i и счетчик сквозной строки вывода counter (см. п. 5). Как только один из счетчиков принимает свое максимальное значение, происходит выход из цикла.
Также, в цикле в переменной yPos формируется координата y вывода текста по формуле. Вывод строки с текстом осуществляется с помощью метода
- e – экземпляр типа PrintPageEventArgs , обеспечивающий необходимые данные для события PrintPage . С помощью этих данных можно управлять печатью документа;
- e.Graphics – класс, который предназначен для рисования на странице. Кроме текста можно выводить любые графические примитивы;
- e.Graphics.DrawString() – метод, который выводит определенную строку ( curLine ) в определенной позиции ( leftMargin , yPos ). Строка выводится шрифтом myFont и цветом Brushes.Blue .
В методе e.Graphics.DrawString() задается необязательный формат вывода типа StringFormat с помощью вызова
После цикла while происходит определение того, нужно ли генерировать следующую страницу
Значение e.HasMorePages отвечает за генерирование следующей страницы. Если e.HasMorePages=true то следующая страница генерируется. В противном случае следующая страница не генерируется, а это значит, что текущая страница есть последней. В программе происходит сравнение значения curPage (текущая страница) с общим количеством страниц nPages . Если значения curPage и nPages равны, то e.HasMorePages становится равным false и печать (просмотр) страниц прекращается.
Вышеприведенный алгоритм обработчика не учитывает вывод текста за границы страницы по ширине. Алгоритм реализован в демонстрационных целях. По желанию можно доделать это свойство.
Непосредственно печать документа осуществляется с помощью метода Print() , который вызывается из экземпляра printDocument1 . Предварительно выводится диалоговое окно печати.
7. Запуск программы на выполнение
После выполненных действий можно запускать программу на выполнение. Для того, чтобы распечатать документ из richTextBox1 нужно чтобы в системе был установлен принтер.
Данная программа есть демонстрационной. По желанию, процесс формирования документа для печати может быть дополнен собственными шрифтами, цветами и т.д.Исходный код к этой статье можно скачать по ссылке printwinforms.exe sample code.
Рис. 1. Табличные отчеты применяются для распечатки списков, например бухгалтерских счетов, заказов или других данных, которые можно представить в виде сетки
Хотя я не рассматриваю другой распространенный тип отчетов - форму-счет (рис. 2), многие принципы создания отчетов, обсуждаемые в этой статье, относятся и к таким отчетам.
Рис. 2. Счета, налоговые декларации и другие документы этого типа часто выводятся в отчетах-формах
Попробуйте сами
Чтобы получить первое впечатление о средствах печати, выполните следующие действия и создайте небольшое тестовое приложение.
Запустите приложение. Если все правильно, при щелчке кнопки откроется диалоговое окно предварительного просмотра, содержащее почти пустую страницу с именем пользователя.
Создание реальных отчетов
Короткого упражнения, приведенного выше, вполне достаточно, чтобы понять, как создавать собственные отчеты; вся остальная работа - это просто рисование средствами GDI+ и решение задач разметки, связанных с границами и другими параметрами. Для создания законченных отчетов, управляемых данными (data-driven reports), достаточно добавить код к обработчику события PrintPage. Конечно, для одного никогда не меняющегося отчета это неплохо, но если вы создаете отчеты постоянно, поддерживать весь код в PrintPage будет трудновато. Вместо этого разбейте печать страницы на отдельные задачи и для каждой задачи напишите функцию, выполняющую свою часть работы. Если вам удастся сделать этот код повторно используемым, вы сможете генерировать любые отчеты без написания нового кода.
В прошлом я пользовался разными инструментами для генерации отчетов и обнаружил, что все они разбивают отчеты на одни и те же общие элементы:
- верхние и нижние колонтитулы страницы;
- верхние и нижние колонтитулы отчета;
- верхние и нижние колонтитулы группы;
- строки с данными.
Чтобы облегчить себе задачу, я решил обойтись без реализации разделов отчета (Report) и группы (Group) и сосредоточиться на верхних и нижних колонтитулах страницы, а также, конечно, на строках с данными. Я начал с новой Windows-формы, добавил PrintDocument точно так же, как в приведенном выше упражнении, а затем создал несколько других функций - PrintPageFooter, PrintPageHeader и PrintDetailRow, - каждая их которых возвращает размер (высоту в пикселах) созданного ею раздела. Каждой из этих функций я предоставил объект PrintPageEventArgs, передаваемый обработчику события PrintPage, чтобы они получили доступ к информации о параметрах принтера и к объекту Graphics, нужному им для отрисовки. Кроме того, я передаю прямоугольник, bounds, описывающий площадь страницы, на которой требуется вывести раздел, и общий аргумент sizeOnly, указывающий, надо ли печатать раздел или достаточно вычислить и вернуть его размер. Функция PrintDetailRow принимает некоторые другие аргументы, которые я опишу, когда мы перейдем к рассмотрению ее кода. После того как функция для каждого отдельного раздела написана, отчет создается быстро. Как я обнаружил, независимо от конкретного раздела приходится решать несколько основных задач, в том числе настройка разрешения и обработка нескольких страниц.
Проблемы разрешения: преобразование дюймов в пикселы
Имея дело с GDI+, вы всегда работаете в каких-то конкретных единицах измерения, т. е. дюймах, пикселах, точках или в чем-то еще, причем на связь между единицами измерения влияет устройство вывода (например, принтер). Я обнаружил, что легче измерять все в аппаратно-независимых дюймах (дюйм - всегда дюйм) и вообще избегать проблем с разрешением. Для этого (поскольку дюйм не является единицей измерения по умолчанию) вам придется установить свойство PageUnit объекта Graphics в начале обработчика события PrintPage.
Единственное неудобство при работе в дюймах - такие параметры Page и Printer, как поля страницы (margins), измеряются в сотых долях дюйма. Конвертировать из одних значений в другие легко, но помните, что всегда следует использовать тип данных Single, чтобы после конвертирования не потерять точность. В начале функции PrintPage, я получаю значения границ и во избежание путаницы немедленно их конвертирую.
Работа с несколькими страницами
Если ваш отчет занимает несколько страниц, нужно следить за номером текущей страницы и текущей строкой данных, так как событие PrintPage вызывается для печати каждой страницы. В любом случае переменная, существующая вне кода обработчика события, позволяет сохранять текущую позицию между вызовами PrintPage.
Не забывайте каждый раз обнулять эти значения перед началом печати, иначе при последующей печати вы начнете не с первой страницы. Это следует делать в обработчике события BeginPrint объекта PrintDocument.
Кроме того, через свойства HasMorePages объекта PrintPageEventArgs следует указывать, закончена печать отчета или еще остались страницы. Перед завершением обработчика события PrintPage установите это свойство в false, если печать закончена, или в true, если напечатаны не все строки. При печати на нескольких страницах нужно знать, когда достигнут конец страницы, поэтому все мои процедуры печати (например, PrintDetailRow) принимают аргумент "sizeOnly". Передавая True в параметре sizeOnly, я выясняю размер строки, которую необходимо напечатать, что позволяет определить, есть ли для этой строки место на странице (конечно, не забывая про нижний колонтитул). Если места больше нет, я не печатаю строку и не увеличиваю значение переменной currentRow; вместо этого я устанавливаю свойство HasMorePages в True и печатаю строку на следующей странице.
Примечание - Если размер строки превышает размер страницы, вы никогда не сможете ее напечатать и войдете в бесконечный цикл, печатая пустые страницы. После того как вы выясните размер раздела, проверьте, разумно ли это значение (меньше ли оно свободного места на пустой странице) до того, как работать с ним. Если оно окажется слишком велико, генерируйте исключение.
Вывод текста
Независимо от того, какой тип имеют данные в полях, текст в каждом столбце, верхнем и нижнем колонтитулах выводится через метод DrawString класса Graphics. При выводе текста в отчете его размер и местоположение зачастую ограничены, и именно поэтому метод DrawString содержит параметр LayoutRectangle. С его помощью текст ограничивается некоей областью, при этом параметр StringFormat обеспечивает управление такими функциями, как автоматический перенос слов.
Сочетание параметров StringFormat с LayoutRectangle
Каждый из этих рисунков - результат вызова DrawString со строкой "The quick brown fox jumped high over the slow and lazy black dog" и разными параметрами формата строки. Ограничивающий прямоугольник обычно не виден, поэтому я выводил его потом вызовом функции DrawRectangle - так легче понять смысл иллюстраций.
Рис. 3. Результат работы с параметрами StringFormat по умолчанию
На первой из этих иллюстраций показано поведение по умолчанию. Текст переносится, видны неполные строки, а все, что находится за границами прямоугольника, отсекается (не рисуется).
Рис. 4. Включение LineLimit предотвращает появление неполных строк
При включенном параметре LineLimit выводятся только полные строки. Отсутствие дополнительной строки слегка меняет перенос слов.
Рис. 5. При NoClip текст может появляться вне ограничивающего прямоугольника
Как показано на рис. 5, при отключении параметра LineLimit и включении NoClip части строк, отсеченные ограничивающим прямоугольником, становятся видны.
Автоматическое добавление многоточий
Если текст обрезается ограничительным прямоугольником, с помощью объекта StringFormat (через свойство Trimming) можно добавлять многоточия взамен последних нескольких символов (EllipseCharacter), за последним полностью видимым словом (EllipseWord) или взамен средней части строки, так чтобы начало и конец строки были видимы (EllipsePath). Все три варианта (слева направо: EllipseCharacter, EllipseWord и EllipsePath) показаны на рис. 6.
Рис. 6. Если строка не помещается полностью в ограничивающий прямоугольник, с помощью свойства Trimming можно автоматически добавлять многоточия
Индикаторы "горячих" клавиш
Еще одно настраиваемое свойство этого класса - HotkeyPrefix, очень полезное для самостоятельной отрисоввки элементов управления Windows Forms. Оно влияет на то, как DrawString обрабатывает символы &, и может принимать значения Hide, Show или None. Если оно равно Show, символ & перед буквой означает, что буква является "горячей клавишей", и тогда буква подчеркивается. При указании Hide буква не подчеркивается, но и сам символ & не выводится, а в случае None этот символ обрабатывается как обычный текст. На рисунке ниже показаны результаты действия трех значений (Show, Hide и None) для строки "&Print".
Рис. 7. Свойство HotkeyPrefix полезно при создании собственных элементов управления
Выравнивание текста
Наконец, класс StringFormat позволяет задавать выравнивание (alignment) с использованием far, near и center вместо left, right и center, так что эти значения будут работать в любом языке независимо от направления чтения текста. В английском языке текст читается слева направо, там эти значения преобразуются в выравнивание по правому (far) и левому (near) краю, а center так и остается выравниванием по центру. Независимо от способа выравнивания оно влияет на положение текста относительно ограничивающего прямоугольника. Если ограничивающий прямоугольник не задан, выравнивание влияет на интерпретацию координат x и y, заданных для вывода строки. Без ограничивающего прямоугольника параметр Alignment = Center центрирует строку относительно координаты x; когда Alignment = Far, координата x считается конечной точкой строки, а при значении Alignment = Near (по умолчанию) строка начинается с позиции x. На рис. 8 показано три варианта этого параметра (near, far и center), при этом для примеров в верхней половине рисунка указан ограничивающий прямоугольник, а в нижней задана только пара координат x и y (верхний левый угол нарисованной линии). Обратите внимание: в системе с направлением текста справа налево результаты были бы другими.
Рис. 8. Эффект от применения свойства Alignment зависит от наличия или отсутствия ограничивающего прямоугольника
Все примеры DrawString из этого раздела включены во второй проект, PlayingWithDrawString (его можно скачать вместе с исходным кодом, прилагаемым к этой статье). Фактически я использовал этот проект и для создания всех изображений, представленных в данном разделе, поэтому то, что вы видите, действительно соответствует тому, что вы получаете!
Обработка столбцов
Во многих отчетах, особенно в табличных, данные состоят из столбцов. Для каждого столбца необходимо определить ряд параметров, в том числе источник данных столбца (скажем, поле в источнике данных), ширину столбца на странице, шрифт, выравнивание содержимого столбца и т. д. Так как основным содержанием моего табличного отчета являются столбцы, я решил создать специальный класс ColumnInformation, а затем пользоваться набором (collection) объектов этого типа, чтобы определить, какие столбы (точнее - поля) должны быть в каждой строке данных. Этот класс содержит всю информацию, необходимую для правильного отображения всех столбцов, и даже два свойства (HeaderFont и HeaderText), зарезервированные на случай, если в будущем мне захочется добавить к коду отчета строку заголовка. Для облегчения восприятия листинга я не привожу закрытые члены свойств и сам код процедур. Полный исходный код см. в материалах, которые можно скачать для этой статьи.
Кроме информации, описывающей столбцы, я создал событие FormatColumn, позволяющее писать собственный форматирующий код для преобразования значений из базы данных в строки. Ниже приведен пример обработчика, преобразующего числовое поле базы данных в денежный формат (currency).
Прежде чем выводить отчет, я заполняю ArrayList объектами ColumnInformation, при необходимости присоединяя обработчики FormatColumn.
Для каждой строки в источнике данных код PrintDetailRow перебирает объекты в ArrayList и выводит содержимое каждого столбца. Код этой функции см. в материалах для скачивания; загляните в него, чтобы понять, как там используются возможности, описанные в разделе Вывод текста, для формирования столбцов.
Класс TabularReport
До сих пор я рассматривал отдельные части задачи создания отчета, но в конечном итоге нужно собрать все эти части воедино и создать компонент, позволяющий легко генерировать табличные отчеты (рис. 1). Я создал новый класс, наследующий от PrintDocument, так что его можно использовать со всеми существующими средствами печати типа PrintDialog, PrintPreviewDialog и PrintPreviewControl. Полный исходный код достаточно длинный, так что я расскажу об этапах его разработки, а затем поясню, как применять такой класс в ваших приложениях.
Добавление свойств
Чтобы вы могли настраивать табличный отчет, мне пришлось добавить к классу массу свойств, позволяющих настраивать столбцы, указывать источник данных и изменять общий вид всего отчета.
Настройка столбцов
В дополнение к классу ColumnInformation я мог бы создать строго типизированный класс набора для хранения нескольких экземпляров ColumnInformation, что сравнительно несложно сделать с помощью Strongly Typed Collection Generator с Web-сайта GotDotNet. Я не стал двигаться в этом направлении, так как мне хотелось, чтобы доступ ко всем столбцам осуществлялся через мой класс; вместо этого я решил просто воспользоваться ArrayList и создать в классе отчета несколько методов для строго типизированного доступа к этому списку.
Настройка внешнего вида отчета
Мой класс отчета довольно гибок, так как его пользователи могут настроить все шрифты и кисти, а также высоту каждого раздела отчета. Для этого я предусмотрел открытые процедуры-свойства и внутренние переменные, инициализируемые значениями по умолчанию. Я также создал свойства DefaultReportFont и DefaultReportBrush, позволяющие указывать объекты Font и Brush, применяемые по умолчанию ко всему отчету, если не заданы более специфичные свойства. Код первого из них приведен ниже, а второго - опущен, поскольку он делает практически те же, что и первый (только с объектом Brush).
Кроме объектов Font и Brush, я реализовал свойства, позволяющие управлять высотой разделов отчета (устанавливая минимальную и максимальную высоту для раздела Detail).
Настройка источника данных
Чтобы создать отчет, нужен доступ к данным, поэтому я добавил свойство, принимающее экземпляр DataView, строки которого перебираются в перегруженной версии OnPrintPage.
Печать отдельных разделов отчета
Одних только свойств недостаточно, в конце концов отчет нужно напечатать. Но я постоянно обращаюсь ко всем этим свойствам, проверяя, что результат печати совпадает с указанными пользователем параметрами. Печать отчета контролируется процедурой OnPrintPage базового класса (PrintDocument), перегруженной в моем классе-потомке; именно оттуда вызываются процедуры печати отдельных разделов (PrintPageHeader, PrintPageFooter и PrintDetailRow). Как и в ранее приведенных примерах, эти процедуры достаточно велики и в силу этого их код не приводится в статье. Вместо этого предлагаю скачать код и запустить тестовое приложение.
Применение класса TabularReport
После того как я закончил класс TabularReport, для создания отчета-примера понадобилось всего несколько шагов.
Шаг 1: создание экземпляра TabularReport
Сначала создайте экземпляр моего класса. Вообще-то это можно сделать и прямо в коде, но, так как этот класс - наследник PrintDocument, вы можете добавить мой компонент в окно инструментария (toolbox) и перетащить его на разрабатываемую Windows-форму.
Шаг 2: получение данных
Чтобы получить данные для отчета, вам потребуется экземпляр DataView, который вы можете получить, заполнив DataTable результатами, возвращенными хранимой процедурой или SQL-запросом.
В этом примере я получаю данные от базы данных Access, поэтому я применяю классы OleDB, но вы можете использовать любую базу по своему выбору: все, что нужно моему отчету, - результирующий DataView.
Шаг 3: настройка столбцов отчета
На этом шаге надо создать объекты ColumnInformation для всех столбцов отчета и добавить их в набор столбцов TabularReport. Я поместил этот код в процедуру SetupReport, настраивающую столбцы и другие детали внешнего вида отчета. Я уже рассматривал объекты ColumnInformation в разделе Обработка столбцов, а здесь покажу, как они настраиваются в приложении-примере.
При настройке трех столбцов - orderDate, shippedDate и total - для каждого из них вызывается AddHandler, связывающий столбцы с процедурами, форматирующими их данные в виде денежного значения или даты.
Что бы вы ни писали в обработчике события FormatColumn, помните, что он должен работать максимально быстро, так как для каждого столбца, к которому прикреплен такой обработчик, он вызывается при печати одной строки дважды. В этой ситуации заметна даже минимальная задержка. Такая реализация форматирования столбцов имеет свои плюсы и минусы. Плюс заключается в том, что вы можете реализовать сколь угодно сложное форматирование и отчет получается более гибким, а минус - довольно трудно реализовать даже простое форматирование. С другой стороны, вы можете создать класс ColumnInformation таким, чтобы простые форматы вроде денежного конвертировались с помощью свойства. Если встроить в класс поддержку форматирования на простом уровне, обработчик события FormatColumn потребуется только в сложных случаях и общая производительность увеличится.
Шаг 4: печать или предварительный просмотр документа
Теперь вызывая метод Print класса TabularReport или один из элементов управления Windows Forms (а именно PrintDialog, PrintPreviewDialog или PrintPreviewControl), способных взаимодействовать с объектом PrintDocument, вы можете печатать или просматривать настроенный отчет. Любой элемент управления, содержащий свойство типа PrintDocument, прекрасно работает с экземпляром TabularReport, так как наследует от класса PrintDocument.
Если вы хотите распечатать PDF-файл из приложения Winforms без использования платного API, мы покажем вам 2 обходных пути, которые помогут вам легко распечатать PDF-файл.
А. Использование Adobe Acrobat
Первый способ требует, чтобы у вас был установлен Adobe Acrobat. Обычно на каждом компьютере есть программа для чтения PDF-файлов, а именно Acrobat Reader, поэтому убедитесь, что у вас установлен этот PDF-ридер.
Этот метод рекомендуется, поскольку он позволяет пользователю выбирать, какие страницы печатать, какой принтер использовать и другие настройки с помощью собственного диалогового окна печати:
Этот код запускает команду печати в скрытой командной строке и отображает диалоговое окно печати системы:
В некоторых версиях Windows Acrobat Reader может запуститься на секунду, но он будет автоматически закрыт после нажатия кнопки «ОК» и файл будет напечатан.
Б. Использование пакета RawPrint
Чтобы установить эту библиотеку в свой проект, вы можете использовать менеджер пакетов NuGet в Visual Studio. Перейдите в обозреватель решений проекта в Visual Studio и щелкните правой кнопкой мыши свой проект, в списке выберите опцию Manage NuGet Packages:
Как только появится менеджер, найдите библиотеку RawPrint, выберите (вероятно) первый вариант Тони Эджкомба и нажмите «Установить»:
После установки вы сможете использовать класс RawPrint в своем проекте. Для получения дополнительной информации об этой библиотеке, пожалуйста, посетите официальный репозиторий на Github здесь.
1. Перечислите имена принтеров
Рекомендуется перечислить все доступные принтеры в каком-либо поле со списком, поэтому пользователю нужно только выбрать нужный принтер, а затем распечатать его. Вы можете перечислить все доступные принтеры, используя Свойство InstalledPrinters в типе PrinterSettings:
2. Печать PDF
Если вы выполнили предыдущий код и файл существует, принтер теперь должен выполнить свою работу.
Читайте также: