|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIn this article, I present a very useful class that works around a serious limitation in the .NET printing classes. The class
BackgroundWhen you want to print a document using .NET, you create a
private void printDoc_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
Rectangle r = e.MarginBounds;
e.Graphics.DrawRectangle(Pens.Black , r.Left , r.Top , 200 , 200);
e.HasMorePages = false;
}
Looks easy, right? But there's a serious problem when you look at the printed output (it looks fine in a print preview). You will notice that the rectangle is not at 1 inch from the left and top edge, but maybe 1.2 inches. The reason is simple: The The ImplementationTo determine the printer's physical margins, we need to call a Win32 function from gdi32.dll :GetDeviceCaps()
[DllImport("gdi32.dll")] private static extern Int32
GetDeviceCaps(IntPtr hdc, Int32 capindex);
We can then use it like this:
HardMarginLeft = GetDeviceCaps(hDC , PHYSICALOFFSETX);
HardMarginTop = GetDeviceCaps(hDC , PHYSICALOFFSETY);
Of course, we need the handle to the device context of the printer, which is not too hard, since we have a reference to the
IntPtr hDC = e.Graphics.GetHdc(); // Get the device context handle
HardMarginLeft = GetDeviceCaps(hDC , PHYSICALOFFSETX);
HardMarginTop = GetDeviceCaps(hDC , PHYSICALOFFSETY);
e.Graphics.ReleaseHdc(hDC); // Don't forget to release it again
Now we have the margins in device units. To convert these to printer units (1/100 inch), we have to get the DPI of the printer, which is available in the Remember I mentioned that the
public class PrinterBounds
{
[DllImport("gdi32.dll")] private static extern Int32
GetDeviceCaps(IntPtr hdc, Int32 capindex);
private const int PHYSICALOFFSETX = 112;
private const int PHYSICALOFFSETY = 113;
public readonly Rectangle Bounds;
public readonly int HardMarginLeft;
public readonly int HardMarginTop;
public PrinterBounds(PrintPageEventArgs e)
{
IntPtr hDC = e.Graphics.GetHdc();
HardMarginLeft = GetDeviceCaps(hDC , PHYSICALOFFSETX);
HardMarginTop = GetDeviceCaps(hDC , PHYSICALOFFSETY);
e.Graphics.ReleaseHdc(hDC);
HardMarginLeft = (int)(HardMarginLeft * 100.0 / e.Graphics.DpiX);
HardMarginTop = (int)(HardMarginTop * 100.0 / e.Graphics.DpiY);
Bounds = e.MarginBounds;
Bounds.Offset(-HardMarginLeft , -HardMarginTop);
}
}
Using the codeUsing the class is very simple. In your
private void printDoc_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
PrinterBounds objBounds = new PrinterBounds(e);
Rectangle r = objBounds.Bounds; // Get the REAL Margin Bounds !
e.Graphics.DrawRectangle(Pens.Black , r.Left , r.Top , 200 , 200);
e.HasMorePages = false;
}
Points of InterestMicrosoft knew about this problem long before v1.1 of the .NET Framework was released, but they didn't fix this bug. I suppose they didn't want to break existing code. I haven't checked the v2.0 beta of the Framework, but I suspect they left it like this.
|
||||||||||||||||||||||