Drawing, images and printing with GDI+

Graphics, Pen and Brush basics, drawing to a control versus a bitmap, DPI-aware scaling, and printing through PrintDocument with a preview.

The drawing model

protected override void OnPaint(PaintEventArgs e)
{
    var g = e.Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.PixelOffsetMode = PixelOffsetMode.HighQuality;

    using var pen = new Pen(Color.FromArgb(200, 60, 60), 2f);
    using var fill = new SolidBrush(Color.FromArgb(40, 200, 60, 60));

    var rect = new Rectangle(10, 10, 200, 120);
    g.FillRectangle(fill, rect);
    g.DrawRectangle(pen, rect);
    g.DrawLine(pen, rect.Left, rect.Bottom, rect.Right, rect.Top);
    g.DrawString("Sample", Font, Brushes.Black, rect.Location);
}
  • Everything created with new - pens, brushes, fonts, bitmaps, graphics contexts on images - must be disposed. Wrap them in using.
  • Static Brushes and Pens entries are shared and must not be disposed.
  • DrawString renders with GDI+ text layout; TextRenderer.DrawText uses GDI and matches the rest of the UI. Prefer TextRenderer for labels, DrawString for rotated or transformed text.
  • Coordinates are in device pixels; at 150 percent scaling a 200 px rectangle is physically smaller than the designer suggested.

Drawing to a bitmap

static Bitmap RenderChart(IReadOnlyList<double> values, int width, int height)
{
    var bmp = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
    using var g = Graphics.FromImage(bmp);
    g.Clear(Color.White);
    g.SmoothingMode = SmoothingMode.AntiAlias;

    var max = values.Max();
    using var pen = new Pen(Color.SteelBlue, 2f);
    for (var i = 1; i < values.Count; i++)
    {
        var x1 = (i - 1) * width / (float)values.Count;
        var x2 = i * width / (float)values.Count;
        g.DrawLine(pen, x1, height - (float)(values[i - 1] / max * height),
                        x2, height - (float)(values[i] / max * height));
    }
    return bmp;
}

// on a control: a 500 x 300 bitmap shown in a 250 x 150 box
pictureBox.Image?.Dispose();
pictureBox.Image = RenderChart(values, 500, 300);
pictureBox.SizeMode = PictureBoxSizeMode.Zoom;
TargetCreate withRemember
A controle.Graphics in OnPaintNever store it; it is only valid during the paint
An imageGraphics.FromImage(bmp)Dispose the Graphics, keep the Bitmap
The printere.Graphics in PrintPageUnits are hundredths of an inch by default
A screen captureGraphics.CopyFromScreenMulti-monitor coordinates can be negative

Render behind-the-scenes images at the pixel size they will be displayed, multiplied by the current DPI scale, then draw them with InterpolationMode.HighQualityBicubic. Scaling up a low-resolution bitmap in the paint handler is the usual cause of blurry charts.

Printing and print preview

var doc = new PrintDocument { DocumentName = "Invoice 1042" };
doc.DefaultPageSettings.Landscape = false;
doc.DefaultPageSettings.Margins = new Margins(60, 60, 60, 60);

var page = 0;
doc.PrintPage += (s, e) =>
{
    var g = e.Graphics;
    g.DrawString($"Invoice 1042 - page {page + 1}", Font, Brushes.Black, 60, 60);

    // print the same visual the screen shows, scaled to the printable area
    var area = e.MarginBounds;
    var chart = RenderChart(values, area.Width, area.Height / 2);
    g.DrawImage(chart, area.Left, area.Top + 40, area.Width, area.Height / 2);
    chart.Dispose();

    page++;
    e.HasMorePages = page * 2 < values.Count;   // decide inside the handler
};

using var preview = new PrintPreviewDialog { Document = doc, Width = 1000, Height = 700 };
preview.ShowDialog(this);
⚠️
Print output is in hundredths of an inch, not pixels, and the printable area is smaller than the page because of unprintable margins. Always lay out from e.MarginBounds and test on a real printer - a preview can look correct while the physical page clips the last column.

FAQ

Why is my drawing blurry at 150 percent scaling?
Either the bitmap was rendered at 100 percent sizes and then stretched, or AutoScaleMode is wrong for the container. Render at device size and let the DPI scaling handle the rest.
Print to PDF without a printer driver?
Use the Microsoft Print to PDF queue, or generate the document with a PDF library and skip PrintDocument entirely when you need precise pagination.

Custom controls and owner-drawn rendering High DPI, accessibility and theming

Last refreshed 2026-09-18.