Title: Print a grid of values with alternating row colors in C#
The following code shows how the program prints its grid. The complete code isn't too complicated but it's long so here it's broken into several methods.
// Draw the grid.
private void pdocGrid_PrintPage(object sender, PrintPageEventArgs e)
{
// Make some sample data.
string[] headers;
object[][] values;
MakeSampleData(out headers, out values);
// See how wide the columns need to be.
using (Font header_font =
new Font("Times New Roman", 18, FontStyle.Bold))
{
using (Font body_font =
new Font("Times New Roman", 18, FontStyle.Regular))
{
int[] col_wid = FindColumnSizes(e.Graphics,
header_font, body_font, headers, values);
// Define the column alignments.
StringAlignment[] alignments =
FindColumnAlignments(values);
// Get the total width.
int grid_wid = 0;
for (int i = 0; i < col_wid.Length; i++)
{
grid_wid += col_wid[i] + 2 * ColMargin;
}
// Print the data.
Rectangle grid_bounds = new Rectangle(
e.MarginBounds.Left, e.MarginBounds.Top,
grid_wid, e.MarginBounds.Bottom);
PrintGrid(grid_bounds, e.Graphics, col_wid, alignments,
header_font, body_font, headers, values);
}
}
}
This code creates arrays to hold the grid's headers and body data. It then calls MakeSampleData to initialize those arrays. That method is straightforward so it's not shown here.
Next the program creates fonts to use when drawing the grid's header and data. It calls FindColumnSizes to find the necessary column sizes to display the data.
The code then calls FindColumnAlignments to determine how the columns should be aligned. This example aligns int, float, double, and decimal data on the right and all other values on the left. You can customize this code if you like. You may also want to add default formatting (for example, format decimal values with 2 digits after the decimal point).
Next the program adds up the column widths to get a total width for the grid. Finally the program calls PrintGrid to draw the grid.
The rest of this post describes the methods called by this event handler to do all of the more complicated work. The following code shows the FindColumnSizes method and the CheckColWidths helper method that it uses.
// Calculate column sizes.
private int[] FindColumnSizes(Graphics gr, Font header_font,
Font body_font, string[] headers, object[][] values)
{
int num_rows = values.Length;
int num_cols = values[0].Length;
// Check the header widths.
int[] col_wid = new int[num_cols];
CheckColWidths(col_wid, gr, header_font, headers);
// Check the data widths.
foreach (object[] row in values)
{
CheckColWidths(col_wid, gr, body_font, row);
}
// Add a margin.
for (int i = 0; i < num_cols; i++)
{
col_wid[i] += 20;
}
// Return the result.
return col_wid;
}
// Update the column widths for the values in this array.
private void CheckColWidths(int[] col_wid, Graphics gr,
Font the_font, object[] values)
{
for (int i = 0; i < values.Length; i++)
{
SizeF txt_size =
gr.MeasureString(values[i].ToString(), the_font);
if (col_wid[i] < txt_size.Width)
col_wid[i] = (int)txt_size.Width;
}
}
The FindColumnSizes method calls CheckColWidths to calculate column widths for the grid's headers. It then loops through the data's rows, calling CheckColWidths to check the widths for each row of data. It finishes by adding a margin to each column.
The CheckColWidths method loops through the values in an array (either the header array or a row of data). For each value, it uses MeasureString to see how big the value will be when printed and updates the corresponding column width if necessary.
The FindColumnAlignments method shown in the following code builds an array of StringAlignment values to indicate how each column should be aligned. It assumes that each column contains only one kind of data: int, string, and so forth.
// Define the column alignments.
private StringAlignment[] FindColumnAlignments(object[][] values)
{
StringAlignment[] alignments =
new StringAlignment[values.Length];
for (int i = 0; i < values[0].Length; i++)
{
if (values[0][i].GetType() == typeof(int) ||
values[0][i].GetType() == typeof(float) ||
values[0][i].GetType() == typeof(double) ||
values[0][i].GetType() == typeof(decimal))
{
alignments[i] = StringAlignment.Far;
}
else
{
alignments[i] = StringAlignment.Near;
}
}
return alignments;
}
The FindColumnAlignments method loops through the values in the first row of data, uses GetType to see what data type each value has, and sets the alignment accordingly.
The following PringGrid method generates the print out.
// Print the grid.
private void PrintGrid(Rectangle grid_bounds, Graphics gr,
int[] col_wid, StringAlignment[] alignments,
Font header_font, Font body_font, string[] headers,
object[][] values)
{
// Print the headers.
int x = grid_bounds.Left;
int y = grid_bounds.Top;
// Fill the header's background.
Rectangle bg_rect = new Rectangle(
x, y, grid_bounds.Width, RowMargin + HeaderMargin);
gr.FillRectangle(Brushes.Blue, bg_rect);
// Draw the header text.
using (StringFormat string_format = new StringFormat())
{
for (int i = 0; i < headers.Length; i++)
{
RectangleF layout_rect = new
RectangleF(x + ColMargin, y,
col_wid[i], RowMargin);
string_format.Alignment = alignments[i];
string_format.LineAlignment = StringAlignment.Near;
gr.DrawString(headers[i],
header_font, Brushes.White,
layout_rect, string_format);
x += col_wid[i] + 2 * ColMargin;
}
}
bg_rect.Height -= HeaderMargin;
y += HeaderMargin;
// Print the values.
int max_x = x;
for (int r = 0; r < values.Length; r++)
{
x = grid_bounds.Left;
y += RowMargin;
// Fill the row's background.
bg_rect.Y = y;
if (r % 2 == 0)
{
gr.FillRectangle(Brushes.LightGreen, bg_rect);
}
else
{
gr.FillRectangle(Brushes.LightBlue, bg_rect);
}
using (StringFormat string_format = new StringFormat())
{
for (int i = 0; i < values[r].Length; i++)
{
// Draw the text.
RectangleF layout_rect = new
RectangleF(x + ColMargin, y,
col_wid[i], RowMargin);
string_format.Alignment = alignments[i];
string_format.LineAlignment = StringAlignment.Near;
gr.DrawString(values[r][i].ToString(),
body_font, Brushes.Black,
layout_rect, string_format);
x += col_wid[i] + 2 * ColMargin;
}
}
gr.DrawLine(Pens.Black, grid_bounds.X, y, max_x, y);
}
// Outline the grid.
grid_bounds = new Rectangle(
grid_bounds.X, grid_bounds.Y, grid_bounds.Width,
(values.Length + 1) * RowMargin + HeaderMargin);
gr.DrawRectangle(Pens.Black, grid_bounds);
}
The PrintGrid method starts by filling a rectangle behind the header area. It then loops through the header strings, drawing them. It uses the alignments array to align the headers appropriately. (You could change this to left-align all headers even if their columns contain numeric data if you like.)
Next the code loops through the data rows. For each row, the code fills the row's background with LightGreen or LightBlue. It then loops through the row's values, printing each aligned appropriately.
After it has printed all of the data, PrintGrid draws a box around the grid's area.
Download the example to experiment with it and to see additional details.
|