Пример: вывод всех шрифтов в системе

Для демонстрации вывода текста была написана программа, которая воспроизводит в графическом поле все установленные шрифты с указанием имен (попутно мы столкнулись с проблемой, описанной в конце раздела). Программа состоит из нестандартного элемента и формы с прокруткой (рис. 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. Для получения этого объекта можно применить один из следующих способов:

  • Воспользоваться элементом Pri ntDocument на панели элементов и положиться на автоматически сгенерированный код или воспользоваться оператором New в конструкции вида
    Dim aPrintDocument As New PrintDocument()
  • Присвоить значение свойства Document экземпляра класса объекту, объявленному с типом Pri ntDocument.
    При использовании панели элементов на форме размещается элемент Pri ntDocument, не обладающий визуальным интерфейсом. При этом генерируется фрагмент следующего вида:


    Friend WithEvents PrintDocumentl As System.Drawing.Printing.PrintDocument


    Непосредственное создание экземпляра происходит в следующей строке, включенной в процедуру

    InitializeComponent: Me.PrintDocumentl = New

    System.Drawing.Printing.PrintDocument()

  • Объявление объекта PrintDocument с ключевым словом WithEvents играет важней^ шую роль для понимания автоматически сгенерированного кода печати. Дело в том, что при вызове метода Print для экземпляра класса Pri ntDocument .NET инициирует по крайней мере три события:

  • BeginPrint;
  • PrintPage (при печати нескольких страниц может инициироваться многократно);
  • EndPrint.
  • Минимальная поддержка печати в программе требует программирования как минимум со бытия PrintPage.

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

    Во втором параметре события Pri ntPage передается объект PagePri ntEventArgs. В этом объекте хранится много полезных данных, в том числе:

  • Графический объект, определяемый значением свойства Graphi cs. С этим объектом выполняются все операции вывода, и его содержимое будет в итоге напечатано на принтере.
  • Объект PageSetti ngs содержит инструкции, относящиеся к печати страниц. Среди свойств этого объекта — признак печати в альбомной (landscape) ориентации, разрешение принтера, размеры полей и т. д.
  • В следующем простом примере при нажатии кнопки вызывается метод 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 сообщает об отсутствии дальнейших страниц для печати.