Draw aligned columns of data in C#


[aligned columns]

This example draws a series of rows with aligned columns either left or right justified. The program uses the following code to define the data it will draw and the row and column alignments.

// The values.
private string[][] Values =
    {
        new string[] { "The C# Helper Top 100", "http://www.csharphelper.com/top100.htm", "380", "2017", "9/4/2017" },
        new string[] { "Interview Puzzles Dissected", "http://www.csharphelper.com/puzzles.htm", "300", "2016", "11/10/2016" },
        ...
    };

// The column alignments.
private StringAlignment[] VertAlignments =
{
    StringAlignment.Center,
    StringAlignment.Center,
    ...
};
private StringAlignment[] HorzAlignments =
{
    StringAlignment.Near,
    StringAlignment.Near,
    StringAlignment.Far,
    StringAlignment.Far,
    StringAlignment.Far,
};

The data that will be drawn is an array of arrays of strings. In other words, each row is represented by an array of string values.

The VertAlignments array holds the vertical alignments that should be used to draw each row. The HorzAlignments array holds the horizontal alignments for the data’s columns.

The most interesting part of the program involves two methods: GetRowColumnSizes and DrawTextRowsColumns. The following code shows the GetRowColumnSizes method, which returns the data’s maximum row height and the widths of the data’s columns.

// Return the items' sizes.
private void GetRowColumnSizes(Graphics gr, Font font,
    string[][] values, out float max_height, out float[] col_widths)
{
    // Make room for the column sizes.
    int num_cols = values[0].Length;
    col_widths = new float[num_cols];

    // Examine each row.
    max_height = 0;
    foreach (string[] row in values)
    {
        // Measure the row's columns.
        for (int col_num = 0; col_num < num_cols; col_num++)
        {
            SizeF col_size = gr.MeasureString(row[col_num], font);
            if (col_widths[col_num] < col_size.Width)
                col_widths[col_num] = col_size.Width;
            if (max_height < col_size.Height)
                max_height = col_size.Height;
        }
    }
}

This method takes as parameters a Graphics object similar to the one that the program will later use to draw the data and the font that it will use. The code loops through the rows of data. For each row, it loops through the row’s columns. For each column entry, the code uses the Graphics object’s MeasureString method to see how big that entry will be when displayed and it updates the maximum row height and that column’s width.

The max_height and col_widths parameters are output parameters, so the calling code learns the maximum row height and the column widths.

The following code shows the DrawTextRowsColumns method.

// Draw the items in columns.
private void DrawTextRowsColumns(Graphics gr, Font font,
    Brush brush, Pen box_pen, float x0, float y0,
    float row_height, float[] col_widths,
    StringAlignment[] vert_alignments,
    StringAlignment[] horz_alignments, string[][] values,
    bool draw_box)
{
    // Create a rectangle in which to draw.
    RectangleF rect = new RectangleF();
    rect.Height = row_height;

    using (StringFormat sf = new StringFormat())
    {
        foreach (string[] row in values)
        {
            float x = x0;
            for (int col_num = 0; col_num < row.Length; col_num++)
            {
                // Prepare the StringFormat and drawing rectangle.
                sf.Alignment = horz_alignments[col_num];
                sf.LineAlignment = vert_alignments[col_num];
                rect.X = x;
                rect.Y = y0;
                rect.Width = col_widths[col_num];

                // Draw.
                gr.DrawString(row[col_num], font, brush, rect, sf);

                // Draw the box if desired.
                if (draw_box) gr.DrawRectangle(box_pen,
                    rect.X, rect.Y, rect.Width, rect.Height);

                // Move to the next column.
                x += col_widths[col_num];
            }

            // Move to the next line.
            y0 += row_height;
        }
    }
}

This method loops through the rows in the data and the columns in each row. For each data value, the code creates a RectangleF that is the maximum row height tall and that column’s width wide. The code uses a StringFormat object to draw aligned columns. It sets the StringFormat object’s Alignment and LineAlignment values to properly position the data and then draws it. If the draw_box parameter is true, the code also draws a box around the entry.

After drawing each entry in a row, the code advances the variable x to the position where it should draw the next entry. After it finishes a row, the code advances variable y0 so it is ready to draw the next row.

The final piece to the example is the PictureBox control’s Paint event handler shown in the following code.

// Display the data aligned in columns.
private void picColumns_Paint(object sender, PaintEventArgs e)
{
    using (Font font = new Font("Times New Roman", 12))
    {
        // Get the row and column sizes.
        float row_height;
        float[] col_widths;
        GetRowColumnSizes(e.Graphics, font, Values,
            out row_height, out col_widths);

        // Add column margins.
        const float margin = 10;
        for (int i = 0; i < col_widths.Length; i++)
            col_widths[i] += margin;

        // Draw.
        e.Graphics.Clear(Color.White);
        e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
        DrawTextRowsColumns(e.Graphics, font, Brushes.Black, Pens.Blue,
            margin / 2, margin / 2, row_height, col_widths,
            VertAlignments, HorzAlignments, Values, true);
    }
}

This code calls the GetRowColumnSizes method to get the maximum row height and the column widths. It adds a margin to each column width so the results aren’t too crowded.

Finally the code clears the PictureBox control’s background and calls the DrawTextRowsColumns method to draw the data.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in fonts, graphics, lists and tagged , , , , , , , , , , , , , , , . Bookmark the permalink.

One Response to Draw aligned columns of data in C#

  1. Pingback: Make an owner-drawn ListBox that justifies columns 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.