Draw a simple labeled histogram in C#

[histogram]

The example Make a simple histogram in C# shows how to draw a simple histogram on a PictureBox. This example adds Label controls above the histogram’s bars.

When the program loads, it uses the following code to generate some random data and make its Label controls. This code is similar to the code used by the previous example. The new code is highlighted in blue.

private const int MIN_VALUE = 0;
private const int MAX_VALUE = 100;

private float[] DataValues = new float[10];
private Label[] Labels = new Label[10];

// Make some random data.
private void Form1_Load(object sender, EventArgs e)
{
    Random rnd = new Random();

    // Create data.
    for (int i = 0; i < DataValues.Length; i++)
    {
        DataValues[i] = rnd.Next(MIN_VALUE + 5, MAX_VALUE - 5);

        // Make a label.
        Labels[i] = new Label();
        Labels[i].Parent = picHisto;
        Labels[i].Text = DataValues[i].ToString();
        Labels[i].ForeColor = Color.Black;
        Labels[i].BackColor = Color.Transparent;
        Labels[i].AutoSize = true;
    }
}

The new code declares an array to hold the new Label controls. The form’s Load event handler creates the random data and then makes a Label to display the data. It sets the new Label control’s parent to the picHisto PictureBox control. It also sets the control’s text and colors, and sets its AutoSize property to true so the control resizes itself to fit its contents.

In many programs, this code would also position the Label and then the code would not need to move the Label later. Unfortunately, this example redraws its histogram when you resize the form, so it also needs to move the labels so they sit over their data. The following code shows the DrawHistogram method that draws the histogram and positions the Label controls. The new code that positions the labels is highlighted in blue.

// Draw a histogram.
private void DrawHistogram(Graphics gr, Color back_color,
    float[] values, int width, int height)
{
    Color[] Colors = new Color[] {
        Color.Red, Color.LightGreen, Color.Blue,
        Color.Pink, Color.Green, Color.LightBlue,
        Color.Orange, Color.Yellow, Color.Purple
    };

    gr.Clear(back_color);

    // Make a transformation to the PictureBox.
    RectangleF data_bounds =
        new RectangleF(0, 0, values.Length, MAX_VALUE);
    PointF[] points =
    {
        new PointF(0, height),
        new PointF(width, height),
        new PointF(0, 0)
    };
    Matrix transformation = new Matrix(data_bounds, points);
    gr.Transform = transformation;

    // Draw the histogram.
    using (Pen thin_pen = new Pen(Color.Black, 0))
    {
        for (int i = 0; i < values.Length; i++)
        {
            RectangleF rect = new RectangleF(i, 0, 1, values[i]);
            using (Brush the_brush =
                 new SolidBrush(Colors[i % Colors.Length]))
            {
                gr.FillRectangle(the_brush, rect);
                gr.DrawRectangle(thin_pen, rect.X, rect.Y,
                    rect.Width, rect.Height);
            }

            // Position the value's label.
            PointF[] point =
            {
                new PointF(rect.Left + rect.Width / 2f, rect.Bottom),
            };
            transformation.TransformPoints(point);
            Labels[i].Location = new Point(
                (int)point[0].X - Labels[i].Width / 2,
                (int)point[0].Y - Labels[i].Height);
        }
    }

    gr.ResetTransform();
    gr.DrawRectangle(Pens.Black, 0, 0, width - 1, height - 1);
}

The code creates a transformation to let the program draw the histogram’s bars in a natural coordinate system. The transformation then maps those coordinates onto the PictureBox.

However, the transformation does not affect the Label controls’ positions, so the program must calculate their positions explicitly. Those positions move when the form is resized, so the code must use the transformation to translate the Label controls’ positions into their locations on the form in normal form coordinates.

To position a Label, the code creates an array of PointF holding a single point. That point gives the position of the bottom center of the histogram bar’s rectangle. It uses the bottom because Y coordinates increase downward in C#. Basically that means the bottom of the rectangle is actually above its top when drawn.

Next, the program uses the transformation to transform the point array. That maps the point from natural coordinate system to the PictureBox control’s surface so point[0] now tells us where the upper middle point on the histogram is drawn on the PictureBox. The program calculates the upper left corner where it needs to position the Label to place it centered above the bar and sets the control’s Location to that position.

See the previous example and download this example to see additional details.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in drawing, graphics, mathematics and tagged , , , , , , , , , , . Bookmark the permalink.

One Response to Draw a simple labeled histogram in C#

  1. Pingback: Use label controls to draw a simple labeled histogram in C# - C# HelperC# Helper

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.