Пример:
вывод всех шрифтов в системе
Для демонстрации
вывода текста была написана программа, которая воспроизводит в графическом поле
все установленные шрифты с указанием имен (попутно мы столкнулись с проблемой,
описанной в конце раздела). Программа состоит из нестандартного элемента и формы
с прокруткой (рис. 8.24).
Рис.
8.24. Вывод списка установленных шрифтов в форме с прокруткой
Список системных
шрифтов возвращается методом InstalledFontCollection пространства имен System.Drawing.Text.
Этот метод возвращает объект класса Sys-tem.Drawing.Text.FontCollection. Объекты,
входящие в коллекцию FontCollection, представляют не шрифты, а семейства
шрифтов (такие, как Arial или Courier). Вы должны выбрать из семейства нужный
шрифт. Некоторые шрифты не имеют обычного начертания, поскольку их семейство
состоит только из курсивных, узких, тонких и других модифицированных начертаний.
Следовательно, в программе должен присутствовать фрагмент вида
For Each aFontFamily
In TheFonts.Families
If aFontFamily.IsStyleAvailable(FontStyle.Regular)
Then
Мы решили
оформить программу в виде специализированного графического поля (элемент PictureBox),
которое размещается на форме при загрузке. При этом на форме также включаются
обе полосы прокрутки:
Private Sub Form1_Load(ByVal sender As System.Object._
ByVal e As System.
EventArgs)Hand.les MyBase.Load
Me.VScroll =
True
Me.HScroll =
True
Me.AutoScroll
= True
FontPictureBoxl.Left
= 0
FontPictureBoxl.Top = 0
End Sub
Ниже приведен
полный код специализированного графического поля:
1 Public Class
FontPictureBox
2 Inherits System.Windows.Forms.PictureBox
3 Protected Overrides Sub OnPaint(ByVal pe As _
System.Windows.Forms.PaintEventArgs)
4 ' Всегда вызывайте
Mybase.OnPaint!
5 MyBase.OnPaint(pe)
6 DisplayFonts(pe.Graphics)
7 End Sub
8 Private Sub
OisplayFonts(ByVal g As Graphics)
9 ' HE РАБОТАЕТ:
Dim g As Graphics = Me.CreateGraphics()
10 Dim aFontFamily
As FontFamily
11 Dim curx.curY
As Single
12 Dim TheFonts
As System.Drawing.Text.FontCollection
13 Dim tempFont
As Font
14 Dim spacing
As Integer = 2 ' Вывод с интервалом 2 пиксела
15 TheFonts
=New System.Drawing.Text.InstalledFontCollection()
16 For Each
aFontFamily In TheFonts.Families
17 Me.Height
+= 2
18 If aFontFamily.IsStyleAvailable(FontStyle.Regular)
Then
19 tempFont
=New Font(aFontFamily. 14. FontStyle.Regular)
20 Elself aFontFamily.IsStyleAvailable(FontStyle.Bold)
Then
21 tempFont
=New Font(aFontFamily, 14. FontStyle.Bold)
22 Elself aFontFamily.IsStyleAvailable(FontStyle.Italic)
Then
23 tempFont
=New Font(aFontFamily, 14. FontStyle.Italic)
24 End If
25 g.DrawString("Thls
is displayed in " & aFontFamily.Name._
26 tempFont.
Brushes.Black, curx, curY)
27 DimtheSizeAs
SizeF = g.MeasureString("This text is displayed in "_
28 & aFontFamily.Name.
tempFont)
29 curY = curY
+ theSize.Height + spacing
30 Me.Height
= Me.Height + CIntCtheSize.Height) + spacing
31 Me.Width
= Math.Max(CInt(theSize.Width). Me.Width)
32 Next
33 End Sub
34 End Class
Обратите
внимание: в строке 6 функции Displayfonts, определяемой в строках 8-33, передается
текущий графический контекст — вместо того, чтобы создавать новый графический
контекст вызовом Me.CreateGraphics(). В исходной версии эта вспомогательная
процедура получала собственный контекст вместо использования контекста ре.Graphics,
переданного в объекте PaintEventArgs. По каким-то загадочным причинам такое
решение не работало. Закомментированный вызов Me. CreateGraphics0 остался в
строке 9; при желании снимите комментарий и посмотрите, к чему это приведет.
При выводе
текста необходимо знать высоту каждой строки. Высота вычисляется
в строках 27 и 28 очень полезной функцией MeasureString:
Public Function MeasureString(String.Font) As SizeF
Функция возвращает объект класса Si zeF — разновидность структуры Si ze, в которой
вместо типа Integer используется тип Single. Поскольку класс SizeF содержит
вещественные числа, в строках 30 и 31 преобразование осуществляется функцией
CInt. В строке 30 происходит наращивание высоты графического поля, а строка
31 гарантирует, что ширина поля позволяет вместить самую длинную из выводимых
строк. Проверка осуществляется методом Мах класса Math.
Печать в .NET
— дело непростое, однако богатство возможностей окупает все хлопоты. В этом
разделе мы подробно опишем процесс печати одной страницы (в основном на примере
автоматически сгенерированного кода), а затем покажем, как напечатать несколько
страниц. Также будет показано, как запрограммировать собственную процедуру печати
с использованием делегата. Начнем с печати изображения, хранящегося в графическом
поле.
Чтобы
обойти некоторые ограничения, действующие в GDI+, мы будем предполагать, что
изображение задается свойством Image, а не прямым копированием в графическое
поле.
Печать в конечном счете сводится к выводу информации в графическом контексте,
но вместо экранного контекста используется контекст, ассоциированный с принтером
или окном предварительного просмотра печати.
Как при выводе на принтер, так и при использовании поддержки предварительного
просмотра (Print Preview) в .NET работа всегда начинается с создания объекта
класса System. Drawl ng. Pri nti ng. Pri ntDocument. Для получения этого объекта
можно применить один из следующих способов:
Dim aPrintDocument As New PrintDocument()
При использовании панели элементов на форме размещается элемент Pri ntDocument, не обладающий визуальным интерфейсом. При этом генерируется фрагмент следующего вида:
Friend WithEvents PrintDocumentl
As System.Drawing.Printing.PrintDocument
Непосредственное создание экземпляра происходит в следующей строке, включенной
в процедуру
InitializeComponent: Me.PrintDocumentl = New
System.Drawing.Printing.PrintDocument()
Объявление
объекта PrintDocument с ключевым словом WithEvents играет важней^ шую роль для
понимания автоматически сгенерированного кода печати. Дело в том, что при вызове
метода Print для экземпляра класса Pri ntDocument .NET инициирует по крайней
мере три события:
Минимальная
поддержка печати в программе требует программирования как минимум со бытия
PrintPage.
При
необходимости можно запрограммировать обработчики и для двух других событий,
но фактическая печать выполняется именно в обработчике PrintPage. Два других
события обычно используются для оповещения о начале и завершении печати.
Во втором
параметре события Pri ntPage передается объект PagePri ntEventArgs. В этом объекте
хранится много полезных данных, в том числе:
В следующем
простом примере при нажатии кнопки вызывается метод Print класса PrintDocument:
Private Sub Buttonl_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs)
Handles Buttonl.Click
PrintDocumentl.Print()
End Sub
Метод Print
вызывает событие PrintPage, поэтому на следующем этапе следует запрограммировать
обработчик события PrintDocumentl_PrintPage, в котором и происходит непосредственная
печать. Если обработчик был сгенерирован с помощью дизайнера, в заголовок автоматически
включается соответствующая секция Handles:
1 Private Sub Pri ntDocumentl_PrintPage(
ByVal sender As System.Object. ByVal e As
System.Drawing.Printing.PrintPageEventArgs)
Handles PrintDocument1.PrintPage
2 Dim g As Graphics
3 g = e.Graphics
4 g.DrawImageCPictureBoxl.Image.
0. 0)
5 g.Dispose()
6 e.HasMorePages
= False
7 End Sub
При выполнении
этого фрагмента изображение печатается на принтере по умолчанию (о том, как
сменить принтер, будет рассказано в следующем разделе). Хотя наш пример относительно
прост, каждая строка в нем играет важную роль и заслуживает объяснения. В строке
1 событие связывается с процедурой, имеющей правильную сигнатуру, не требуя
от вас прямого использования делегата (см. главу 6).
В строке
3 мы получаем объект Graphics, представляющий поверхность вывода текущего принтера.
Строка 4 выводит изображение начиная с левого верхнего угла. Вывод происходит
на принтере, с которым связан графический контекст. Присутствие вызова D1 spose
в строке 5 связано с тем, что графические контексты (как было сказано выше)
не освобождаются сборщиком мусора. Строка 6 сообщает об отсутствии дальнейших
страниц для печати.