I am printing the CHART control on button click:
chart1.SaveImage(ms, ChartImageFormat.Bmp);
Bitmap bm = new Bitmap(ms);
PrintDocument doc = new PrintDocument();
doc.PrintPage += (s, ev) =>
{
ev.Graphics.DrawImage(bm,开发者_JS百科 Point.Empty); // adjust this to put the image elsewhere
ev.HasMorePages = false;
};
doc.DefaultPageSettings.Landscape = true;
doc.Print();
How do I force it to print the control so that it fits to the size of the page (preserving the aspect ratio)?
There are at least two different ways to do it, both include scaling the image to be printed to fit the page size of the selected printer:
1) using .NET facilities (haven't tested it myself, lifted from this post):
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Image i = pictureBox1.Image;
float newWidth = i.Width * 100 / i.HorizontalResolution;
float newHeight = i.Height * 100 / i.VerticalResolution;
float widthFactor = newWidth / e.MarginBounds.Width;
float heightFactor = newHeight / e.MarginBounds.Height;
if(widthFactor>1 | heightFactor > 1)
{
if(widthFactor > heightFactor)
{
newWidth = newWidth / widthFactor;
newHeight = newHeight / widthFactor;
}
else
{
newWidth = newWidth / heightFactor;
newHeight = newHeight / heightFactor;
}
}
e.Graphics.DrawImage(i, 0, 0, (int)newWidth, (int)newHeight);
}
}
2) P/Invoke'ing Windows API calls from the GDI and flat GDI (this is much more complex but faster and you can pass a plain byte array of a bitmap file (read file as byte[]), provide an email to me if need this code):
private static extern bool ClosePrinter(IntPtr hPrinter);
private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
private static extern int SetJob(IntPtr hPrinter, int JobId, int Level, ref byte pJob, int Command_Renamed);
private static extern int GdiplusStartup(out IntPtr token, ref StartupInput input, out StartupOutput output);
private static extern int GdiplusShutdown(IntPtr token);
internal static extern int GdipLoadImageFromStream([In, MarshalAs(UnmanagedType.Interface)]IStream stream, out IntPtr image);
internal static extern int GdipDisposeImage(IntPtr image);
static internal extern int GdipCreateFromHDC2(IntPtr hDC, IntPtr hDevice, out IntPtr graphics);
static internal extern int GdipDeleteGraphics(IntPtr graphics);
static internal extern int GdipReleaseDC(IntPtr graphics, IntPtr hdc);
internal static extern int GdipGetImageDimension(IntPtr image, out float width, out float height);
internal static extern int GdipGetDpiX(IntPtr graphics, out float dpi);
internal static extern int GdipGetDpiY(IntPtr graphics, out float dpi);
static internal extern int GdipDrawImageRectI(IntPtr graphics, IntPtr image, int x, int y, int width, int height);
private static extern IntPtr CreateDC([MarshalAs(UnmanagedType.LPStr)] string lpszDriver, [MarshalAs(UnmanagedType.LPStr)] string lpszDevice, [MarshalAs(UnmanagedType.LPStr)] string lpszOutput, IntPtr lpInitData);
private static extern bool DeleteDC(IntPtr hdc);
private static extern int StartDoc(IntPtr hdc, DOCINFO lpdi);
private static extern int EndDoc(IntPtr hdc);
private static extern int StartPage(IntPtr hdc);
private static extern int EndPage(IntPtr hdc);
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
Here's what I did to get this working. What's annoying is that the chart control will only draw its contents as the same size that it is sized to on the screen. The only way I found to get around this is to manually resize it. And then, since it may not like being resized when it's on a form, this also involved temporarily removing it from the form.
So with all that, I end up with something like this:
Chart ChartBox { get; private set; }
...
var oldParent = ChartBox.Parent;
var oldSize = ChartBox.Size;
ChartBox.Parent = null;
ChartBox.Size = new Size(width, height);
ChartBox.Printing.Print();
ChartBox.Parent = oldParent;
ChartBox.Size = oldSize;
Depending on your form, you may need to save and restore other properties of the chart control as well.
精彩评论