Часть 1. Основы Visual Basiс
На главную самоучителя
29.01.2005
Глава 13.
Вывод на печать (работа с принтером). Объект Printer.
Его свойства и методы. Функция InStr.
Скачать исходник примера "Printing"

Ну, друзья мои, мы дошли до 13-й чертовой главы. Не будем суеверными и попробуем разобраться, как вывести на печать результаты нашей программной деятельности. Не хочу тебя огорчать, но для этого нужен принтер, который подключен к твоему компьютеру и готов к работе. Если его нет, эту главу не читай.

Традиционно во всех учебных изданиях разбирается пример, как вывести на печать всю форму целиком. На фига нужно выводить всю форму, какая от этого нам польза, остается для меня загадкой на протяжении долгого времени и гнетет мое сознание. Однако, отдавая дань традициям, все-таки напомню, что форма выводится на печать с помощью метода PrintForm. То бишь, ты берешь любую готовую программу, добавляешь кнопку Command, называешь ее "Печать", и в код ее процедуры гордо пишешь:

Form1.PrintForm

Запускаешь программу, нажимаешь эту созданную кнопку и у тебя на печать выходит Form1 со всеми элементами-объектами, видимыми на момент печати. Ты некоторое время смотришь на это безумие, потом перед командой PrintForm делаешь невидимыми кнопки и в надежде на чудо, снова повторяшь процесс, но чуда не произошло. Кнопок теперь нет, но распечатка красивше от этого не стала. И понося на чем свет VB, Microsoft и его хозяина, выключаешь все и идешь пить пиво.

Вот про печать и все.

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

А теперь скажу, что подготовка для печати красивой страницы - достаточно кропотливый процесс. То есть, надо программно расчитывать месторасположение всех элементов страницы, причем в зависимости от формата листа и размера шрифта и всего остального. Зато тщательно подготовленная страница выглядит, как настоящий документ, очень достойно.

Основной объект, с которым мы будем работать - Printer. Несмотря на то, что это объект, ни самого объекта в окне инструментов или на форме, ни свойств в окне свойств мы не увидим. Он невидим. Поэтому все надо делать программно. Но этот объект имеет очень много свойств и методов, большинство из которых нам нужны. Постараемся разобраться подробнее. Хочу заметить, что не все принтеры поддерживают все свойства объекта Printer. В этом случае разные свойства могут вызывать одинаковое действие, не вызвать никакого действия или же вообще привести к ошибке. Я использовал довольно-таки старый лазерный черно-белый принтер HP LaserJet 5L, и основываясь на его возможностях и готовил пример. Можно было взять цветной струйник, но струйники - дороги в эксплаутации и, как правило, документы печатаются на лазерных принтерах.
Итак, начнем со свойств, список важнейших из них приведен в таблице ниже:

Свойство
Характеристика
Синтаксис
.Copies определяет число печатаемых копий Printer.Copies=1 (одна копия)
.ColorMode определяет цветную или монохромную (оттенки серого) печать vbPRCMMonochrome=1 (черно-белая) vbPRCMColor =2 (цветная). Монохромные принтеры это свойство игнорируют. Printer.ColorMode=1 или
Printer.ColorMode=vbPRCMMonochrome
.CurrentX и .CurrentY устанавливают горизонтальную (CurrentX) или вертикальную (CurrentY) координаты для начала (продолжения) печати. Определяется по-умолчанию в твипах, либо в текущих единицах измерения Printer.CurrentX=500
Printer.CurrentY=1000
.Duplex возвращает или устанавливает значение, которое определяет, будет ли страница печататься с двух сторон, константы: vbPRDPSimplex =1, vbPRDPHorizontal= 2, vbPRDPVertical=3.

Printer.Duplex=1 (Одностороння печать с текущей ориентацией листа)
Printer.Duplex=2 (Двухсторонняя печать горизонтальной страницы)
Printer.Duplex=3 (Двухсторонняя печать вертикальной страницы)

.Font определяет "жирность" текста, подчеркивание, перечеркивание, курсив и т.п. Printer.Font.Bold = True (жирный)
Printer.Font.Underline = True (подчеркивание)
.FontCount возвращает число доступных шрифтов Text1.Text=Printer.FontCount
.FontName возвращает или устанавливает имя шрифта Printer.FontName="Arial" (устанавливает шрифт Arial)
.Fonts возвращает имена всех доступных шрифтов Dim I As Long
For I = 0 To Printer.FontCount -1
List1.AddItem Printer.Fonts (I)
Next I
.FontSize возвращает или устанавливает размер шрифта в пунктах (максимальное значение 2160). Задается после задания свойства FontName. При использовании шрифтов TrueType менее 8 пунктов, надо сначала задать свойство FontSize, затем FontName, а потом снова FontSize Printer.FontSize=18
.Height и
.Width
устанавливают физические размеры листа бумаги. Используются вместо установок PiperSize. Для Printer задается только в твипах. Printer.Height=5000 (Высота)
Printer.Widtht=3000 (Ширина)
.Orientation возвращает или устанавливает значение, указывающее, в вертикальном или горизонтальном расположении печатаются документы. Принимает значение двух констант: vbPRORPortrait =1 (вертикаль), vbPRORLandscape=2 (горизонт) Printer.Orientation=1 или
Printer.Orientation=vbPRORPortrait
.Page возвращает текущий номер страницы, начиная с единицы и шагом единица Printer.Print Printer.Page (вывод на печать текущей страницы)
.PaperBin возвращает или устанавливает значение, указывающее для принтера установленный по умолчанию лоток для подачи бумаги. Константы перечислены в библиотеке объектов Visual Basic (VB) в Object Browser.

Printer.PaperBin=1 (верхний лоток)
Printer.PaperBin=2 (нижний лоток)
Printer.PaperBin=3 (средний лоток)
Printer.PaperBin=4 (ручная подача)
Printer.PaperBin=7 (лоток по умолчанию)
и т.д.

.PaperSize возвращает или устанавливает значение, указывающее размер бумаги для текущего принтера.
vbPRPSA4= 9 для листа A4, 210 x 297 мм
vbPRPSUser=256 для пользовательского
Константы перечислены в библиотеке объектов Visual Basic (VB) в Object Browser.
Printer.PaperSize=9 или
Printer.PaperSize=vbPRPSA4
(установка листа A4)
.PrintQuality возвращает или устанавливает значение, указывающее разрешающую способность принтера (отрицательная константа).
Можно также задавать положительные значения в точках на дюйм (dpi).
Printer.PrintQuality=-1 (черновой- draft)
Printer.PrintQuality=-2 (низкая- Low)
Printer.PrintQuality=-3 (средняя- Medium)
Printer.PrintQuality=-4 (высокая- High)
Printer.PrintQuality=300 (разрешение 300 dpi)
.Zoom возвращает или устанавливает процентное соотношение, по которому увеличивается или уменьшается масштаб вывода на печать. Число обозначает процент и по умолчанию равно 0, что соответствует стандартному размеру. Printer.Zoom=50 (лист с уменьшением 1:2)

Теперь, как ни печально, надо переходить к методам, но их не так много. На мой взгляд важные приведены в таблице ниже:

Метод
Характеристика
Синтаксис
.EndDoc завершает формирование листа для печати и начинает печать Printer.EndDoc
.KillDoc немедленно завершает печать сформированной страницы Printer.KillDoc
.NewPage завершает текущую страницу на объекте Printer и переходит на следующую Printer.NewPage
.TextHeight и .TextWidth возвращают высоту (TextHeight ) и длину (TextWidth) текстовой строки, которая получится при ее выводе на печать или на форму с использованием текущего шрифта. В возвращаемое значение включаются и интервалы. Text1.Text=Printer.TextHeight("vbzero")
Text2.Text=Printer.TextWidth("vbzero")

Tеперь попробуем разобраться, как все это использовать на примере программки Printing. Чтобы у нас было, что выводить на печать, я набросал подобие программки, представляющую собой рабочее места продавца мобильных телефонов. Советую вам скачать ее вверху страницы, чтобы иметь перед глазами, а потом продолжать читать главу.
Создадим новый exe-проект. Первая ее часть представляет собой интерфейс для работы с базой мобильных телефонов: вывод на экран изображения и технических характеристик телефона, две кнопки навигации - вперед и назад и кнопка Печать. Изображение и теххарактеристики грузятся из файлов, имеющих имена 1.jpg и 1.txt, 2.jpg и 2.txt, и так далее, т.е., аналогично программке Tester, которую мы разбирали в Главе 8.

Итак стандартное начало:

Option Explicit
Option Base 1

Объявляем статический одномерный массив для двенадцати строк технических характеристик телефона:

Dim MassiveData(12) As String

Затем несколько необходимых переменных

Dim FileName As String 'для имени загружаемых файлов
Dim F As Long 'для номера свободного файла
Dim X As Long 'для переменной цикло For...Next
Dim QuantityFonts As Long 'для числа найденых шрифтов

Затем процедура загрузки формы. Здесь мы просто изначально устанавливаем имя загружаемого файла - "1" ("1.jpg" и "1.txt") и переходим к созданной нами процедуре загрузки этих файлов DataLoading().

Private Sub Form_Load()
FileName = "1"
DataLoading
End Sub

Private Sub DataLoading()

Загружаем изображение телефона в Image1

Image1.Picture = LoadPicture(App.Path & "\" & FileName & ".jpg")

Восстанавливаем положение Image1 от левой и верхней границы экрана соответственно

Image1.Left = 24
Image1.Top = 128


Центрируем положение картинки в зависимости от того, вертикальная она (Image1.Height > Image1.Width - высота больше ширины) или горизонтальная (Image1.Width > Image1.Height - ширина больше высоты)

If Image1.Width > Image1.Height Then Image1.Top = Image1.Top + (250 - Image1.Height) / 2
If Image1.Height > Image1.Width Then Image1.Left = Image1.Left + (250 - Image1.Width) / 2


Открываем текстовой файл с тем же именем, что и картинка и грузим 12 строк текста в 12 элементов массива

F = FreeFile
Open App.Path & "\" & FileName & ".txt" For Input As #F
For X = 1 To 12
Line Input #F, MassiveData(X)


Тут же выводим содержание массива в Label9. Обратите внимание, что Label9 представляет собой Control Array (по сути - это одномерный массив объекта), т.е. группу лейблов с именем Label9(индекс). По этому индексу мы можем перебирать Label9 в цикле. Делается это так: мы кладем на форму Label9, затем копируем его. VB спрашивает "Создавать Control Array?", ты отвечаешь "Да", после чего на форме появляется копия Label9 с именем Label9(1). Наш же просто лейбл Label9 становится Label9(0). Я про это писал, но повторюсь. Продолжаем этот процесс до получения нужного количества лейблов в Control Array. А потом, ненужные элементы надо уничтожить. У нас, поскольку мы используем оператор Option Base 1, и элементы массива начинаются с единицы - это Label9(0). Аналогично создается и Control Array и для Label8, куда присваиваются наименования технических характеристик телефона. Для облегчения кода я сделал это присвоение в окне свойств Label8.

Label9(X).Caption = MassiveData(X)
Next X
Close #F

End Sub

В кнопках Command1 и Command2 мы пишем код для смены имени загружаемого файла. Все это опять-таки похоже на программку Tester из Главы 8, и на этом я подробно останавливаться не буду.

Private Sub Command1_Click()
If Val(FileName) > 1 Then
FileName = Str(Val(FileName) - 1)
FileName = Right(FileName, Len(FileName) - 1)
DataLoading
End If
End Sub

Private Sub Command2_Click()
FileName = Str(Val(FileName) + 1)
FileName = Right(FileName, Len(FileName) - 1)
If Dir(App.Path & "\" & FileName & ".txt") <> "" Or Dir(App.Path & "\" & FileName & ".jpg") <> "" Then
DataLoading
Else
FileName = Str(Val(FileName) - 1)
FileName = Right(FileName, Len(FileName) - 1)
DataLoading
End If
End Sub

А вот сейчас мы переходм к тому, ради чего мы все это затеяли. Попробуем подготовить для печать документ. В процедуре кнопки Command3 мы начнем формировать страницу для печати. Сперва объявим кое-какие переменные

Private Sub Command3_Click()
Dim OrientTelefona As Single ' меняет отступ от картинки в зависимости горизонтальная она или вертикальная
Dim Fonts() As String 'одномерный динамический массив для хранения существующих шрифтов
Dim VertCoord As Single 'накапливает сдвиг по вертикали по мере создания строк в документе
Dim NumberFont As Long ' номер шрифта

обнуляем переменную, содержащую количество шрифтов на всякий случай

QuantityFonts = 0

В смысле определения шрифтов на текущем принтере я пошел по пути наименьшего сопротивления. Исходя из того, что на любом компьютере установлен хотя бы один из четырех общеупотребимых шрифтов, я проверяю их наличие и имена найденых шрифтов загружаю в динамический массив Fonts(). Для этого мы организуем цикл, где перебираем все существующие на текущем компьютере шрифты (их количество нам возвращает свойство .FontCount), и грузим их имена в массив Fonts(). При этом счетчик QuantityFonts отражает, сколько из четырех шрифтов найдено.

For NumberFont = 0 To Printer.FontCount - 1
If Printer.Fonts(NumberFont) = "Arial Cyr" Or Printer.Fonts(NumberFont) = "Times New Roman" _
Or Printer.Fonts(NumberFont) = "Courier New" Or Printer.Fonts(NumberFont) = "MS Sans Serif" Then
QuantityFonts = QuantityFonts + 1
ReDim Preserve Fonts(QuantityFonts)
Fonts(QuantityFonts) = Printer.Fonts(NumberFont)
End If
Next
NumberFont

Затем, если хоть один шрифт найден, мы устанавливаем его из первого элемента массива

If QuantityFonts > 0 Then Printer.FontName = Fonts(1)

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


Printer.ScaleMode = vbCentimeters

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

Printer.FontSize = 12

'устанавливаем подчеркивание
Printer.Font.Underline = True
'устананавливаем качество печати - среднее

Printer.PrintQuality = 3

Прижимаем вправо первую строчку с названием фирмы. Для установки положения начала вывода по-горизонтали используется свойство .CurrentX. Чтобы строчка прижалась вправо мы из общей ширины нашего документа Printer.ScaleWidth вычтем длину нашей строчки Printer.TextWidth(Label1.Caption), которую она займет при печати объявленным нами шрифтом:

Printer.CurrentX = Printer.ScaleWidth - Printer.TextWidth(Label1.Caption)

В переменной VertCoord мы будем накапливать сдвиг по вертикали. Сначала запишем в нее, сколько высоты заняла первая строка (вместе с отступами).

VertCoord = Printer.TextHeight(Label1.Caption)

Теперь встраиваем в страницу первую строчку. Никакой печати при этом не происходит. Собственно принтер начинает работать только тогда, когда VB посчитает, что формирование документа полностью закончено. Это произойдет, если будет написан оператор Printer.EndDoc, либо если ты выйдешь из программы совсем.

Printer.Print Label1.Caption

'устанавливаем большой размер шрифта для названия модели телефона
Printer.FontSize = 20
'отменяем подчеркивание
Printer.Font.Underline = False
'зато ставим жирный
Printer.Font.Bold = True
'делаем отступ вниз на 1,5 см от первой строчки, меняя вертикальную координату
Printer.CurrentY = VertCoord + 1.5
'выравниваем вторую строчку по-середине
Printer.CurrentX = (Printer.ScaleWidth - Printer.TextWidth(Label9(1).Caption & "Лидер продаж - телефон ")) / 2
'добавляем, сколько высоты мы использовали на вторую строчку вместе с отступами
VertCoord = VertCoord + Printer.TextHeight(Label9(1).Caption) + 1.5
'встраиваем в страницу вторую строчку
Printer.Print "Лидер продаж - телефон " & Label9(1).Caption
'устанавливаем размер шрифта поменьше для заглавия технических характеристик телефона
Printer.FontSize = 14
'добавляем курсив
Printer.Font.Italic = True
'отступаем вниз еще на 2 см
Printer.CurrentY = VertCoord + 2
'добавляем высоту третьей строчки вместе с отступами
VertCoord = VertCoord + Printer.TextHeight(Label4.Caption) + 2
'оставляем 8 см слева под картинку, а остальное справа - под текст
'строчку центрируем на ее Printer.ScaleWidth = 8 см

Printer.CurrentX = (Printer.ScaleWidth - 8 - Printer.TextWidth(Label4.Caption)) / 2
'встраиваем в страницу третью строчку
Printer.Print Label4.Caption

Для печати изображения телефона берем текущую координату по вертикали VertCoord.
Печатаем картинку, центрируя ее на своей площади и меняя пустое расстояние перед ней, в зависимости от того, горизонтальная она (OrientTelefona = 0.5) или вертикальная (OrientTelefona = 2.25). Тогда картинка будет располагаться красиво, по центру выделенной ей площади.


If Image1.Height > Image1.Width Then
OrientTelefona = 2.25
Else
OrientTelefona = 0.5
End If

Встраиваем картинку в наш документ

Printer.PaintPicture Image1.Picture, Printer.ScaleWidth - ScaleX(Image1.Width, vbPixels, vbCentimeters) - OrientTelefona, VertCoord

'отменяем курсив и жирность
Printer.Font.Italic = False
Printer.Font.Bold = False
'устанавливаем размер шрифта еще меньше
Printer.FontSize = 12
'отступаем вниз от 3-ей строки на 1 см
Printer.CurrentY = VertCoord + 1
VertCoord = VertCoord + 1


Теперь, остальные строки - технические данные( с 2-ой по 11-ую) мы встроим в цикле (всего 10 строк) не меняя размеров, жирности и т.п. шрифта.
Обратите внимание, что наименование характеристик в Label8(X) и сами характеристики в Label9(X) прописаны отдельно друг от друга и перед каждым установлены свои координаты начала печати Printer.CurrentX и Printer.CurrentY. Тогда печать осуществится красиво, двумя столбиками.

For X = 2 To 11
'отступаем слева по сантиметру
Printer.CurrentX = 1
Printer.CurrentY = VertCoord
Printer.Print Label8(X).Caption

Printer.CurrentX = 5
Printer.CurrentY = VertCoord
Printer.Print Label9(X).Caption
VertCoord = VertCoord + 0.6 'увеличение вертикальной координаты с каждой новой строчкой
Next X


Теперь для акцента на цену телефона, выведем на печать прямоугольник через все страницу. Подробно о рисовании простых графических фигур программным методом мы поговорим в следующей главе. Здесь мы используем метод Line.

VertCoord = VertCoord + 1
Printer.Line (1, VertCoord)-(17.5, VertCoord + 0.7), vbBlack, BF

Printer.CurrentY = VertCoord + 0.1
Printer.CurrentX = 6
'теперь пишем цену
Printer.Font.Bold = True
Printer.Print "Самая лучшая цена: " & Label9(12).Caption & " руб."
Printer.Font.Bold = False
А вот теперь даем команду о завершении формирования страницы и принтер начинает печать:
Printer.EndDoc
End Sub

Этот пример имеет некоторую недоработочку. Строка, которая выводит особенности телефона (Label9(8)) длинная и не входит в рамки страницы, а при печати все, что не влезает в страницу безжалостно отсекается и на печать не выводится. Та же проблема возникает при печати неформатированного текста из текстового файла. Попробуй решить эту проблему самостоятельно. Возможно позже я по этому поводу чего-нибудь напишу, но не здесь, а в Части 3 - "Релизация некоторых задач".

Разбивать текст можно по пробелам. Возможно, ты захочешь использовать функцию InStr.

Функция InStr.

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

Числовая переменная=InStr(номер символа, строка поиска, критерий поиска, способ сравнения)

номер символа - необязательный аргумент определяет с какого символа в строке (третьего, десятого и т.п.) надо начинать поиск. Если не задан, поиск начинается с первого символа.
строка поиска - строка, в которой ведется поиск
критерий поиска - искомое строковое выражение
способ сравнения - необязательно. Указывает способ сравнения строк (0-двоичное сравнение, 1 -посимвольное сравнение без учета регистра, 2 - только в Microsoft Access). Если аргумент опущен, способ сранения определяется параметром инструкции Option Compare.

Вот пример поиска фамилии Иванов по по критерию из четырех начальных букв "иван" в массиве A(4) без учета регистра.

Dim A(4) As String
Dim X As Long
A(1) = "Петров"
A(2) = "Сидоров"
A(3) = "Иванов"
A(4) = "Глюкоза"
For X = 1 To 4
If InStr(1, A(X), "иван", 1) > 0 Then
MsgBox "Найден " & A(X) & " в строке " & X
End If
Next
X

Удачи!


Copyright © 2005 4us


Сайт создан в системе uCoz