Draw a picture-filled histogram in C#

[example]

The example Make a simple histogram in C# shows how to draw a histogram. This example shows how to fill the histogram’s bars with pictures.

The following TileRectangle method fills a rectangle with an image.

// Tile an area from the bottom left corner up.
private void TileRectangle(Graphics gr, RectangleF rect, Image picture)
{
    using (TextureBrush brush = new TextureBrush(picture))
    {
        // Move the brush so it starts in the
        // recrangle's lower left corner.
        brush.(rect.Left, rect.Bottom);

        // Fill.
        gr.FillRectangle(brush, rect);
    }
}

This code creates a TextureBrush from the image. Normally when you fill a rectangle with a TextureBrush, the brush starts with the image int he area’s upper left corner. Histograms usually start with the image on their lower left, so this method uses the brush’s TranslateTransform method to transform the brush’s origin to the rectangle’s lower left corner.

The method then fills the rectangle with the brush.

The following code calls the TileRectangle method.

// Draw a histogram.
private void DrawHistogram(Graphics gr, Color back_color,
    float[] values, int width, int height,
    float xscale, float yscale)
{
    gr.Clear(back_color);

    // The images we will use to fill the rectangles.
    Image[] images =
    {
        Properties.Resources.apple,
        Properties.Resources.banana,
        Properties.Resources.grapes,
        Properties.Resources.pear,
        Properties.Resources.strawberry,
        Properties.Resources.tomato,
    };

    // Draw the histograms.
    for (int i = 0; i < values.Length; i++)
    {
        // Get the rectangle's bounds in device coordinates.
        float rect_wid = xscale;
        float rect_hgt = yscale * values[i];
        float rect_x = i * xscale;
        float rect_y = height - rect_hgt;

        // Make the rectangle.
        RectangleF rect = new RectangleF(
            rect_x, rect_y, rect_wid, rect_hgt);

        // Fill the rectangle.
        TileRectangle(gr, rect, images[i]);

        // Outline the rectangle.
        gr.DrawRectangle(Pens.Black,
            rect_x, rect_y, rect_wid, rect_hgt);
    }
}

This method clears the Graphics object and then makes an array holding the images that it will use. I added the images to the program at design time by selecting Project > Properties, clicking on the Resources tab, opening the Add Resource dropdown, and picking Add Existing File.

Next the code loops through the histogram’s values. It scales the width and height of each value and uses the results to make a RectangleF to represent the value. It then calls TileRectangle to fill the rectangle with its image and then outlines the rectangle.

The only remaining piece of interesting code is the following.

// Draw the histogram.
private void picHisto_Paint(object sender, PaintEventArgs e)
{
    // Calculate a transformation to map
    // data values onto the PictureBox.
    float xscale = picHisto.ClientSize.Width / (float)DataValues.Length;
    float yscale = picHisto.ClientSize.Height / (float)(MAX_VALUE - MIN_VALUE);

    DrawHistogram(e.Graphics, picHisto.BackColor, DataValues,
        picHisto.ClientSize.Width, picHisto.ClientSize.Height,
        xscale, yscale);
}

This code calculates the scale factors in the X and Y directions to map the range of allowed values onto the PictureBox.

Download the example to see more 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.

Leave a Reply

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