В книге Чарльза Петцольда "Программирование для Microsoft Windows на Microsoft Visual Basic .NET" прочитал следующее
учёт заданных пользователем полей - это проблема программирования печати из приложений Windows Forms.
Предлагается формула приблизительного расчёта этих границ
- Код: Выделить всё
Dim rectf As New RectangleF(ppea.MarginBounds.Left - (ppea.PageBounds.Width - grfx.VisibleClipBounds.Width) / 2, _
ppea.MarginBounds.Top - (ppea.PageBounds.Height - grfx.VisibleClipBounds.Height) / 2, _
ppea.MarginBounds.Width, ppea.MarginBounds.Height)
grfx.DrawRectangle(Pens.Black, rectf.X, rectf.Y, rectf.Width, rectf.Height)
В случаи если данные расчёты не устраивают, то предложено обратится к Win32 - вызвать GetDeviceCaps с параметрами PHYSICALOFFSETX/PHYSICALOFFSETY. Как я понимаю это будет самая верхняя левая точка (расчёт заданных пользователем полей пока не берём к вниманию).
А какая будет самая нижняя правая точка? VisibleClipBounds.Width/VisibleClipBounds.Height ?
Вот программа-пример
- Код: Выделить всё
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Drawing.Printing
Imports System.Runtime.InteropServices
Class PrintPreviewSample
Inherits Form
Declare Function GetDeviceCaps_OldSchoolDeclaration Lib "gdi32.dll" Alias "GetDeviceCaps" (ByVal hDC As IntPtr, ByVal nIndex As Integer) As Integer
<DllImport("gdi32.dll")> Public Shared Function GetDeviceCaps(ByVal hDC As IntPtr, ByVal nIndex As Integer) As Integer
End Function
Const PHYSICALOFFSETX As Integer = 112
Const PHYSICALOFFSETY As Integer = 113
'Dim ppc As New PreviewPrintController()
Dim ppc As PreviewPrintController
Dim prndoc As New PrintDocument()
Dim bAllowDrawPreview As Boolean = False
Dim prseldlg As New PrinterSelectionDialog()
Shared Sub Main()
Application.Run(New PrintPreviewSample())
End Sub
Sub New()
Text = "Print Preview Sample"
Menu = New MainMenu()
Menu.MenuItems.Add("&Print and draw preview...", AddressOf MenuPrintAndDrawPreviewOnClick)
Menu.MenuItems.Add("&Select printer...", AddressOf MenuPrintSelectOnClick)
prndoc.DocumentName = Text
'prndoc.PrintController = ppc
AddHandler prndoc.PrintPage, AddressOf OnPrintPage
prseldlg.PrinterName = prndoc.PrinterSettings.PrinterName
End Sub
Private Sub MenuPrintAndDrawPreviewOnClick(ByVal obj As Object, ByVal ea As EventArgs)
ppc = New PreviewPrintController()
prndoc.PrintController = ppc
prndoc.Print()
bAllowDrawPreview = True
Invalidate()
End Sub
Private Sub MenuPrintSelectOnClick(ByVal obj As Object, ByVal ea As EventArgs)
prseldlg.ShowDialog()
prndoc.PrinterSettings.PrinterName = prseldlg.PrinterName
End Sub
Private Sub OnPrintPage(ByVal obj As Object, ByVal ppea As PrintPageEventArgs)
Dim grfx As Graphics = ppea.Graphics()
Dim fnt As New Font("Times New Roman", 120)
Dim sPrinter As String = prndoc.PrinterSettings.PrinterName
Dim szf As SizeF = grfx.MeasureString(sPrinter, fnt)
Dim hDc As IntPtr = grfx.GetHdc()
Dim x1 As Integer = GetDeviceCaps_OldSchoolDeclaration(hDc, PHYSICALOFFSETX)
Dim y1 As Integer = GetDeviceCaps_OldSchoolDeclaration(hDc, PHYSICALOFFSETY)
Dim x2 As Integer = GetDeviceCaps(hDc, PHYSICALOFFSETX)
Dim y2 As Integer = GetDeviceCaps(hDc, PHYSICALOFFSETY)
grfx.ReleaseHdc(hDc)
grfx.Clear(Color.White)
Dim rectf As New RectangleF(ppea.MarginBounds.Left - (ppea.PageBounds.Width - grfx.VisibleClipBounds.Width) / 2, _
ppea.MarginBounds.Top - (ppea.PageBounds.Height - grfx.VisibleClipBounds.Height) / 2, _
ppea.MarginBounds.Width, ppea.MarginBounds.Height)
grfx.DrawRectangle(Pens.Black, rectf.X, rectf.Y, rectf.Width, rectf.Height)
grfx.DrawRectangle(Pens.Blue, x1, y1, grfx.VisibleClipBounds.Width, grfx.VisibleClipBounds.Height)
grfx.DrawRectangle(Pens.Brown, x2, y2, grfx.VisibleClipBounds.Width, grfx.VisibleClipBounds.Height)
grfx.DrawString(sPrinter, fnt, Brushes.Black, (grfx.VisibleClipBounds.Width - szf.Width) / 2, (grfx.VisibleClipBounds.Height - szf.Height) / 2)
ppea.HasMorePages = False
End Sub
'Private Sub ScaleImageIsotropically - From ImagePrint by Charles Petzold
Private Sub ScaleImageIsotropically(ByVal grfx As Graphics, ByVal img As Image, ByVal rectf As RectangleF)
Dim szf As New SizeF(img.Width / img.HorizontalResolution, img.Height / img.VerticalResolution)
Dim fScale As Single = Math.Min(rectf.Width / szf.Width, rectf.Height / szf.Height)
szf.Width *= fScale
szf.Height *= fScale
grfx.DrawImage(img, rectf.X + (rectf.Width - szf.Width) / 2, rectf.Y + (rectf.Height - szf.Height) / 2, szf.Width, szf.Height)
End Sub
Protected Overrides Sub OnPaint(ByVal pea As PaintEventArgs)
If bAllowDrawPreview Then ScaleImageIsotropically(pea.Graphics, ppc.GetPreviewPageInfo()(0).Image, ClientRectangle)
bAllowDrawPreview = False
End Sub
End Class
'PrinterSelectionDialog.vb by Charles Petzold
'Imports System
'Imports System.Drawing
'Imports System.Drawing.Printing
'Imports System.Windows.Forms
Class PrinterSelectionDialog
Inherits Form
Private combo As ComboBox
Sub New()
Text = "Select Printer"
FormBorderStyle = Windows.Forms.FormBorderStyle.FixedDialog
ControlBox = False
MaximizeBox = False
MinimizeBox = False
ShowInTaskbar = False
StartPosition = FormStartPosition.Manual
Dim lbl As New Label()
lbl.Parent = Me
lbl.Text = "Printer"
lbl.Location = New Point(8, 8)
lbl.Size = New Size(40, Font.Height)
combo = New ComboBox()
combo.Parent = Me
combo.DropDownStyle = ComboBoxStyle.DropDownList
combo.Location = New Point(48, 8)
combo.Size = New Size(140, Font.Height)
For Each str As String In PrinterSettings.InstalledPrinters
combo.Items.Add(str)
Next str
Dim btn As New Button()
btn.Parent = Me
btn.Text = "OK"
btn.Location = New Point(40, 32)
btn.Size = New Size(40, 16)
btn.DialogResult = Windows.Forms.DialogResult.OK
AcceptButton = btn
ClientSize = New Size(200, 56)
AutoScaleBaseSize = New Size(4, 8)
End Sub
Protected Overrides Sub OnLoad(ByVal ea As EventArgs)
Location = Point.op_Addition(ActiveForm.Location, Size.op_Addition(SystemInformation.CaptionButtonSize, SystemInformation.FrameBorderSize))
End Sub
Property PrinterName() As String
Get
Return combo.SelectedItem.ToString()
End Get
Set(ByVal value As String)
combo.SelectedItem = value
End Set
End Property
End Class
p.s.
Предварительный просмотр создаётся не самым лучшим образом, но к сожалению при раскомментированном варианте картинка ppc.GetPreviewPageInfo()(0).Image как будто "цементируется", а последующие просто "складируются" что ли.
p.s.s.
В продолжении темы Declare Or DllImport? поставлен эксперимент, и на драйверах принтеров (а не PDF\XPS, для этих всегда 0) были получены разные числовые значения (в одном из запусков и только при одном оставленном объявлении функции). Всего лишь +/- единица, но всё же.