0

I have here a string to print:

string print;    

StringBuilder cust = new StringBuilder();
foreach (var cli in customer.Products)
{
    cus.AppendFormat("\n\n{0}.....................PRICE: ${1} - DATE: {2}", cli.ProdName, cli.ProdPrice, cli.ProdDate);
}

print = cus.ToString();

In printPage event:

e.Graphics.DrawString(print, Font, Brushes.Black, e.MarginBounds, align);

With this code my output in page is something like:

Bike.....................PRICE: $600 - DATE: 15/11/2014
Car.....................PRICE: $30000 - DATE: 15/11/2014

What I want to do is show this in columns, like:

PRODUCT           PRICE           DATE
Bike                     $600              15/11/2014
Car                      $30000          15/11/2014

I don't know how to print de string formatted by this way. And how about draw rectangles separating? I've searched but found nothing relevant in how to draw columns to print. Thanks all in advance!

DotNet
  • 59
  • 3
  • 9

4 Answers4

3

Try this:-

StringBuilder cust = new StringBuilder();
cust.AppendLine("PRODUCT\tPRICE\tDATE");
foreach (var pro in customer.Products)
{
    cust.AppendFormat("{0}\t${1}\t{2}", pro.Name, pro.Price, pro.Date).AppendLine();
}

Edit

Try using Padding:- You can try using String.Format method and specify padding. Something like this:-

cust.AppendLine(String.Format("{0}{1,10}{2,10}","PRODUCT","PRICE","DATE"));

The constant after comma here denotes padding.
A positive number means to right-align & negative number means to left-align.

Rahul Singh
  • 21,585
  • 6
  • 41
  • 56
  • The `\t` seems not taking any effect. How to get the space between words and get them aligned like my example? – DotNet Nov 15 '14 at 19:06
2

Since you are using the System.Drawing class you should draw each column separately, modifying drawing rectangle and the horizontal alignment as shown here.

If you want to draw rectangles then you can use e.Graphics.DrawRectangle.

Here is an example:

    Product[] data = new Product[] {
        new Product {Name="Bike",Price=600, Date=DateTime.Now },
        new Product {Name="Car", Price=30000, Date=DateTime.Now.AddDays(1) }
    };

    string[] colHeaders = { "Name", "Price", "Date" };
    float[] colWidths = { 150.0F, 100.0F, 200.0F };
    float colPadding = 25.0F;
    float rowHeight = 25.0F;

    float verticalOffset = 50F;
    float horizontalOffset = 50.0F;

    Font textFont = SystemFonts.MenuFont;
    Brush textBrush = SystemBrushes.MenuText;

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        RectangleF startRect = new RectangleF(horizontalOffset, verticalOffset, colWidths[0], rowHeight);  //This is the top left start position
        RectangleF curRect = startRect;
        StringFormat format = new StringFormat();
        format.Alignment = StringAlignment.Near;

        //Print Headers
        for (int col = 0; col < colHeaders.Length; col++)
        {
            e.Graphics.DrawString(colHeaders[col], textFont, textBrush, curRect, format);
            curRect.Offset(colPadding + colWidths[col], 0F);  //This doesn't change startRect, since it is a struct
        }

        //Print Data
        int row = -1;
        foreach (var cli in data)
        {
            row++;      //Rewritten to use foreach as requested by user

            //Print Name
            curRect = startRect;
            curRect.Offset(0F, (float)(row + 1) * rowHeight * 1.5F);
            e.Graphics.DrawString(cli.Name, textFont, textBrush, curRect, format);

            //Print Price (DrawRect around first one)
            curRect.Offset(colPadding + colWidths[0], 0F);
            curRect.Width = colWidths[1];
            e.Graphics.DrawString(string.Format("{0:C}",cli.Price), textFont, textBrush, curRect, format);

            Rectangle border = Rectangle.Round(curRect);
            border.Offset(-5, -5);  //Text was being drawn in the top right of the curRect, Move the drawing rect up and left a bit to give a margin.
            e.Graphics.DrawRectangle(new Pen(Color.Red, 4F), border);

            //Print Date
            curRect.Offset(colPadding + colWidths[1], 0F);
            curRect.Width = colWidths[2];
            e.Graphics.DrawString(string.Format("{0:MMMM dd, yyyy}",cli.Date), textFont, textBrush, curRect, format);

            border = Rectangle.Round(curRect);
            border.Offset(-5, -5);  //Text was being drawn in the top right of the curRect, Move the drawing rect up and left a bit to give a margin.
            e.Graphics.DrawRectangle(new Pen(Color.Blue, 4F), border);


        }

    }
}

internal class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public DateTime Date { get; set; }
}

Sample Output

bigtlb
  • 1,512
  • 10
  • 16
  • The problem I am facing is with the HasMorePages. I have searched and searched, tried to do something but without success. I do not know how to make the columns that do not fit on the page be written in another page together with the string that exists below them. – DotNet Nov 24 '14 at 23:53
  • I have provided a multiple column multiple page solution in a separate answer. You really should have asked it as a different question. – bigtlb Nov 25 '14 at 23:35
1

This is to answer the follow up questions about how to break for columns that span pages, and multiple pages.

The answer is to do the math yourself, oto figure out if you need another page, and whether the next page is to the right (has a left offset), or below (starts at the row you left off from.

internal class MyReport : PrintDocument
{

    internal class Product
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
        public DateTime Date { get; set; }

        public string Test { get; set; }
    }

    Product[] data = new Product[] {
        new Product {Name="Bike",   Price=600,      Date=DateTime.Now,              Test = "Some Test" },
        new Product {Name="Car",    Price=30000,    Date=DateTime.Now.AddDays(1),   Test = "Second Test" },
        new Product {Name="Train",  Price=1600,     Date=DateTime.Now.AddDays(2),   Test = "Some Test" },
        new Product {Name="Boat",   Price=13000,    Date=DateTime.Now.AddDays(3),   Test = "Second Test" },
        ... //Data goes here            
    };

    string[] colHeaders = { "Name", "Price", "Date", "Spacer1", "Spacer2", "Spacer3", "Spacer4", "Spacer5", "Spacer6", "Spacer7", "Test" };
    float[] colWidths = { 100.0F, 100.0F, 100.0F, 100.0F, 100.0F, 100.0F, 100.0F, 100.0F, 100.0F, 100.0F, 200.0F };
    //Column offset adjustments to account for orphaning (where a column straddles two pages)
    float[] colOffsets = { 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f };


    Font textFont = SystemFonts.MenuFont;
    Brush textBrush = SystemBrushes.MenuText;
    float colPadding = 25.0F;
    float rowHeight = 25.0F;

    float pageLeftOffset = 0.0f;

    int nextRow = 0;
    bool needMoreColumns = false;
    RectangleF bounds = RectangleF.Empty;

    StringFormat curFormat = new StringFormat();
    Graphics graphics = null;

    protected override void OnQueryPageSettings(QueryPageSettingsEventArgs e)
    {
        base.OnQueryPageSettings(e);
    }

    protected override void OnPrintPage(PrintPageEventArgs e)
    {
        bounds = e.MarginBounds;
        graphics = e.Graphics;
        rowHeight = textFont.GetHeight(graphics) * 1.5f;
        curFormat.Alignment = StringAlignment.Near;
        RectangleF curRect = RectangleF.Empty;
        needMoreColumns = false;

        //Print Headers
        var tempFont = textFont;
        var tempBrush = textBrush;
        textFont = new Font(tempFont.FontFamily, 14f, FontStyle.Bold | FontStyle.Underline);
        textBrush = new SolidBrush(Color.DarkBlue);
        for (int col = 0; col < colHeaders.Length; col++)
        {
            curRect = SetCell(col, nextRow-2);
            DrawCell(colHeaders[col], curRect, Color.Empty);
        }
        textFont.Dispose();
        textBrush.Dispose();
        textFont = tempFont;
        textBrush = tempBrush;


        int curRow = 0;

        //Print Data
        for (int row = nextRow; row< data.Length; row++) 
        {
            curRow = row;
            var cli = data[row];

            //Print Name
            curRect = SetCell(0, row);
            DrawCell(cli.Name, curRect, Color.Empty);

            //Print Price (DrawRect around first one)
            curRect = SetCell(1, row);
            DrawCell(string.Format("{0:C}", cli.Price), curRect, Color.Red);

            //Print Date
            curRect = SetCell(2, row);
            DrawCell(string.Format("{0:MMMM dd, yyyy}", cli.Date), curRect, Color.Blue);

            //Print Test
            curRect = SetCell(10, row);
            DrawCell(cli.Test, curRect, Color.Green);

            if (curRect.Bottom > bounds.Bottom) break;
        }


        if (curRect.Bottom > bounds.Bottom || needMoreColumns)
        {
            e.HasMorePages = true;
            if (needMoreColumns)
            {
                //Move over, don't move down
                pageLeftOffset = pageLeftOffset + bounds.Width;
            }
            else
            {
                //Move down one page, and back to the first columns
                pageLeftOffset = 0f;
                nextRow = curRow;
            }
            return;
        }

        e.HasMorePages = false;
    }

    private RectangleF SetCell(int col, int row)
    {
        float xoffset = 0;
        for (int x = 0; x < col; x++) xoffset += colWidths[x] + colOffsets[x] + colPadding;

        xoffset += colOffsets[col];

        //Columns stradling pages, move to the next page.
        if (xoffset - pageLeftOffset < 0 && Math.Abs(xoffset - pageLeftOffset) < colWidths[col])
        {
            colOffsets[col] = Math.Abs(xoffset - pageLeftOffset);
            xoffset += colOffsets[col];
        }

        RectangleF curRect = new RectangleF(
            (float)bounds.Left + xoffset - pageLeftOffset, 
            (float)bounds.Top + (float)(row-nextRow+2) * rowHeight, 
            colWidths[col], 
            textFont.GetHeight(graphics));  //This is the top left start position

        //Determine if there are columns off the right hand side of the page
        needMoreColumns |= curRect.Right > bounds.Right;
        return curRect;
    }


    private void DrawCell(string cellText, RectangleF curRect, Color color)
    {
        //Return if this cell isn't fully within this page
        if (!bounds.Contains(curRect)) return;

        graphics.DrawString(cellText, textFont, textBrush, curRect, curFormat);

        if (!color.IsEmpty)
        {
            curRect.Size = graphics.MeasureString(cellText, textFont).ToSize();
            curRect.Inflate(2f, 2f);  
            graphics.DrawRectangle(new Pen(color, 2F), curRect.X, curRect.Y, curRect.Width, curRect.Height);
        }
    }
}

enter image description here enter image description here enter image description here

bigtlb
  • 1,512
  • 10
  • 16
  • This new code looks completely different from the previous that I adapted and I'm using. Would you not have a solution with the `HasMorePages` to implement in the primary code? I also think it's better open a new question for this problem. – DotNet Nov 26 '14 at 12:48
  • This started as the original code (with some minor refactoring for clarity). This is everything that needs to be done for columns spanning more than one page, and rows spanning more than one page. This will be my last code post for this question. At this point this is feeling more like a tutoring session, and is not appropriate for this venue. – bigtlb Nov 27 '14 at 17:41
-1

You can draw a table in a console using the follwing solution:

How To: Best way to draw table in console app (C#)

I am sorry I don't have any idea about doing that by the way that you are asking for.

Community
  • 1
  • 1
hatem87
  • 157
  • 4