Sami Jildeh
Sami Jildeh

Reputation: 55

How to calculate cell height [that has long text] when printing DataGridView in C#

I searched but didn't find how to set the cell height when printing DataGridView when the cell has long text.

I didn't find one result that concentrate how to calculate the height with long text. And I don't want to use 3rd party DLL file that print it with right cell height.

I use this code to calculate the height but text always cut and short text has lower cell height that on DGV.

var tallestHeight = 0;
foreach (DataGridViewCell cell in GridRow.Cells)
{
    if (!cell.Visible) { continue; }
    var s = e.Graphics.MeasureString(cell.FormattedValue.ToString(), dataGridView1.Font);
    var tempHeight = (int)(s.Height * Math.Ceiling(s.Width / dataGridView1.Columns[cell.ColumnIndex].Width));
    if (tempHeight > tallestHeight)
    {
        tallestHeight = tempHeight;
    }
    tallestHeight = (tallestHeight < 22) ? 22 : tallestHeight;
}
iCellHeight = tallestHeight;

I want when I print DataGridView to printer to show all the text in all cells without cutting. Long text increase the row height and if no long text row's height stays unchanged.

I have row height = 22

Text wrap for my DataGridView is enabled

Edit1:

Here is my DataGridView properties

Here is how i print my DataGridView: PrintDGV Class that i use

My DGV appears DGV

Print preview print

Yellow highlighted text isn't complete Full text is First Middle lastname- some text- 0130011511478- تجربةة 7427/01300/8346584584563846

The text below it complete.

How to show the first row at full?

Upvotes: 1

Views: 882

Answers (1)

dr.null
dr.null

Reputation: 4695

Apart from the grid settings, you need to use the output sizes to calculate the adequate height of each row to fit their contents. Also, calling a MeasureString method overload that takes StringFormat is necessary to get more accurate result.

I see in the printout image above you are dividing the MarginBounds.Width by the visible cells. Thus, the following:


Calculate


Create a method to calculate and return the proper height of each row.

// +
using System.Linq;

private int GetRowHeight(
    Graphics g, 
    DataGridViewRow row, 
    Rectangle bounds, 
    StringFormat sf, 
    int minHeight = 22)
{
    var cells = row.Cells.OfType<DataGridViewTextBoxCell>()
        .Where(c => c.Visible);

    if (cells == null) return minHeight;

    var cell = cells.Aggregate((DataGridViewTextBoxCell)null, (x, y) => x != null && 
    x.FormattedValue.ToString().Length > y.FormattedValue.ToString().Length ? x : y);

    if (cell == null) return minHeight;

    var h = g.MeasureString(cell.FormattedValue.ToString(),
        row.DataGridView.Font,
        new SizeF(bounds.Width / cells.Count(), bounds.Height),
        sf).ToSize().Height;

    return Math.Max(h + 6, minHeight); // 6 for top and bottom margins...
}

Call


Caller example and class variables to track the printing...

// +
using System.Drawing.Printing;

private int rowIndex;
private int cellCount;
private int pageNumber;
private readonly PrintDocument printDoc;
// ...

// ctor
public YourForm()
{
    InitializeComponent();

    printDoc = new PrintDocument();
    printDoc.PrintPage += OnPrintPage;
}

// Cleanup
private void YourForm_FormClosed(object sender, FormClosedEventArgs e)
{
    printDoc.Dispose();
}

// Preview
private void btnPrintPreview(object sender, EventArgs e) => Print(true);

// Print
private void btnPrint(object sender, EventArgs e) => Print();

// Print Routine
private void Print(bool preview = false)
{
    rowIndex = 0;
    cellCount = 0;
    pageNumber = 0;

    var rows = dataGridView1.Rows
        .Cast<DataGridViewRow>()
        .FirstOrDefault(r => !r.IsNewRow);

    if (rows != null)
        cellCount = rows.Cells
            .OfType<DataGridViewTextBoxCell>()
            .Where(c => c.Visible)
            .Count();

    if (cellCount == 0)
    {
        MessageBox.Show("Nothing to print...");
        return;
    }

    printDoc.DefaultPageSettings.Landscape = true;

    if (preview)
    {
        using (var pd = new PrintPreviewDialog())
        {
                    
            pd.Document = printDoc;
            pd.ShowDialog();
        }
    }
    else
    {
        using (var pd = new PrintDialog())
        {
            pd.Document = printDoc;
            if (pd.ShowDialog() == DialogResult.OK)
                pd.Document.Print();
        }
    }
}

Print


PrintDocument.PrintPage event example.

// +
using System.Drawing.Text;

private void OnPrintPage(object sender, PrintPageEventArgs e)
{
    var w = e.MarginBounds.Width / cellCount;
    var x = e.MarginBounds.X;
    var y = e.MarginBounds.Y;
    int h;
    Rectangle rec;

    using (var sf = new StringFormat())
    {
        sf.Alignment = StringAlignment.Center;
        sf.LineAlignment = StringAlignment.Center;

        // Maybe you need to set this? I see Arabic text in the images.
        // sf.FormatFlags = StringFormatFlags.DirectionRightToLeft;

        e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;

        // Uncomment to print the headers in the first page only.
        //if (pageNumber == 0)
        //{
        h = dataGridView1.RowTemplate.Height;

        foreach (var col in dataGridView1.Columns
        .OfType<DataGridViewTextBoxColumn>()
        .Where(c => c.Visible))
        {
            rec = new Rectangle(x, y, w, h);

            e.Graphics.FillRectangle(Brushes.Gainsboro, rec);
            e.Graphics.DrawString(
                col.HeaderText, 
                col.DataGridView.Font, 
                Brushes.Black, 
                rec, 
                sf);
            e.Graphics.DrawRectangle(Pens.Black, rec);

            x += w;
        }
                    
        x = e.MarginBounds.X;
        y += h;                    
        //}

        for (var i = rowIndex; i < dataGridView1.RowCount; i++)
        {
            var row = dataGridView1.Rows[i];

            if (row.IsNewRow) break;

            h = GetRowHeight(e.Graphics, row, e.MarginBounds, sf);

            if (h > e.MarginBounds.Height)
            {
                MessageBox.Show("Insufficient height.");
                e.Cancel = true;
                return;
            }

            foreach (var cell in row.Cells
                .OfType<DataGridViewTextBoxCell>()
                .Where(c => c.Visible))
            {
                rec = new Rectangle(x, y, w, h);

                if (rec.Bottom > e.MarginBounds.Bottom)
                {
                    pageNumber++;
                    rowIndex = i;
                    e.HasMorePages = true;
                    return;
                }

                e.Graphics.DrawString(
                    cell.FormattedValue.ToString(), 
                    dataGridView1.Font, 
                    Brushes.Black, 
                    rec, 
                    sf);
                e.Graphics.DrawRectangle(Pens.Black, rec);

                x += rec.Width;
            }

            x = e.MarginBounds.X;
            y += h;                    
        }
    }
}

Output


SOQ67132271

Upvotes: 0

Related Questions