Niall
Niall

Reputation: 1621

Is it possible to have Multi-line DataGridView cells without wrapping text?

I know I can set WrapMode to true on the DefaultCellStyle of the RowTemplate, however this doesn't give me the behaviour I want. I am displaying a list of strings within each cell, and I therefore want the carriage returns to be recognised, but I don't want text from long items wrapping.

Does anyone know if it's possible to achieve this?

Upvotes: 12

Views: 16586

Answers (7)

       if ((!e.Value.Equals("OK")) && e.ColumnIndex == 6)
        {
            e.CellStyle.WrapMode = DataGridViewTriState.True;
            //dgvObjetivos.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
            dgvObjetivos.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
        }

http://kshitijsharma.net/2010/08/23/showing-multiline-string-in-a-datagridview-cell/

Upvotes: 0

Ofir
Ofir

Reputation: 2194

This class get the DataGridView instance and add the behavior for trimming multi line elipsis (...) for width and height as well.

Usage:

MultilineTriming.Init(ref dataGridView); // that's it!

Enjoy,

public static class MultilineTriming
{
    private static int _rowMargins;

    public static void Init(ref DataGridView dataGridView)
    {
        dataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;
        dataGridView.DefaultCellStyle.WrapMode = DataGridViewTriState.False;

        _rowMargins = dataGridView.RowTemplate.Height - dataGridView.Font.Height;

        Unregister(dataGridView);

        dataGridView.CellEndEdit += DataGridViewOnCellEndEdit;
        dataGridView.CellPainting += DataGridViewOnCellPainting;
        dataGridView.RowsAdded += DataGridViewOnRowsAdded;
        dataGridView.Disposed += DataGridViewOnDisposed;
    }

    private static void DataGridViewOnRowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
    {
        DataGridView view = sender as DataGridView;
        DataGridViewRow row = view.Rows[e.RowIndex];
        foreach (DataGridViewCell cell in row.Cells)
        {
            if (cell.FormattedValue == null)
            {
                continue;
            }
            Size size = TextRenderer.MeasureText((string)cell.FormattedValue, view.Font);
            row.Height = Math.Max(size.Height + _rowMargins, row.Height);
        }
    }

    private static void DataGridViewOnDisposed(object sender, EventArgs eventArgs)
    {
        DataGridView dataGridView = sender as DataGridView;
        Unregister(dataGridView);

    }

    public static void Unregister(DataGridView dataGridView)
    {
        dataGridView.RowsAdded -= DataGridViewOnRowsAdded;
        dataGridView.CellEndEdit -= DataGridViewOnCellEndEdit;
        dataGridView.CellPainting -= DataGridViewOnCellPainting;
    }

    private static void DataGridViewOnCellEndEdit(object sender, DataGridViewCellEventArgs e)
    {
        DataGridView view = sender as DataGridView;
        DataGridViewRow row = view.Rows[e.RowIndex];
        DataGridViewCell cell = row.Cells[e.ColumnIndex];

        string text = (string)cell.FormattedValue;

        if (string.IsNullOrEmpty(text)) return;

        Size size = TextRenderer.MeasureText(text, view.Font);
        row.Height = Math.Max(size.Height + _rowMargins, row.Height);
    }

    private static void DataGridViewOnCellPainting(object sender, DataGridViewCellPaintingEventArgs e)
    {
        if (e.ColumnIndex == -1 || e.RowIndex == -1 || e.FormattedValue == null)
        {
            return;
        }
        e.Paint(e.ClipBounds, DataGridViewPaintParts.All ^ DataGridViewPaintParts.ContentForeground);

        DataGridView view = sender as DataGridView;

        string textToDisplay = TrimTextToFit(string.Format("{0}", e.FormattedValue), (int)(e.CellBounds.Width * 0.96) - 3, e.CellBounds.Height - _rowMargins, view.Font);

        bool selected = view.Rows[e.RowIndex].Cells[e.ColumnIndex].Selected;
        SolidBrush brush = new SolidBrush(selected ? e.CellStyle.SelectionForeColor : e.CellStyle.ForeColor);

        e.Graphics.DrawString(textToDisplay, view.Font, brush, e.CellBounds.X + 1, e.CellBounds.Y + _rowMargins / 2);

        e.Handled = true;
    }

    private static string TrimTextToFit(string text, int contentWidth, int contentHeight, Font font)
    {
        Size size = TextRenderer.MeasureText(text, font);

        if (size.Width < contentWidth && size.Height < contentHeight)
        {
            return text;
        }

        int i = 0;
        StringBuilder sb = new StringBuilder();
        while (i < text.Length)
        {
            sb.Append(text[i++]);
            size = TextRenderer.MeasureText(sb.ToString(), font);

            if (size.Width < contentWidth) continue;

            sb.Append("...");

            while (sb.Length > 3 && size.Width >= contentWidth)
            {
                sb.Remove(sb.Length - 4, 1);
                size = TextRenderer.MeasureText(sb.ToString(), font);
            }

            while (i < text.Length && text[i] != Environment.NewLine[0])
            {
                i++;
            }
        }
        string res = sb.ToString();

        if (size.Height <= contentHeight)
        {
            return res;
        }

        string[] lines = res.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
        i = lines.Length;
        while (i > 1 && size.Height > contentHeight)
        {
            res = string.Join(Environment.NewLine, lines, 0, --i);
            size = TextRenderer.MeasureText(res, font);
        }

        return res;
    }
}

Upvotes: 0

Ofir
Ofir

Reputation: 2194

I hope this is what you are looking for: Screen shot

I used two events:

  1. I've measured the height after a cell edit.
  2. I've measured text when painting cell, and trimmed it if needed, and repeat till it fits.

Code:

public partial class Form1 : Form
{
    private readonly int _rowMargins;

    public Form1()
    {
        InitializeComponent();
        int rowHeight = dataGridView1.Rows[0].Height;
        _rowMargins = rowHeight - dataGridView1.Font.Height;
    }

    private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
    {
        DataGridView view = sender as DataGridView;
        DataGridViewCell cell = view.Rows[e.RowIndex].Cells[e.ColumnIndex];
        string text = string.Format("{0}", cell.FormattedValue);
        if (!string.IsNullOrEmpty(text))
        {
            Size size = TextRenderer.MeasureText(text, view.Font);
            view.Rows[e.RowIndex].Height = Math.Max(size.Height + _rowMargins, view.Rows[e.RowIndex].Height);
        }
    }

    private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
    {
        if (e.ColumnIndex == -1 || e.RowIndex == -1)
        {
            return;
        }
        e.Paint(e.ClipBounds, DataGridViewPaintParts.All ^ DataGridViewPaintParts.ContentForeground);

        DataGridView view = sender as DataGridView;

        string textToDisplay = TrimTextToFit(string.Format("{0}", e.FormattedValue), (int) (e.CellBounds.Width * 0.96), view.Font);

        bool selected = view.Rows[e.RowIndex].Cells[e.ColumnIndex].Selected;
        SolidBrush brush = new SolidBrush(selected ? e.CellStyle.SelectionForeColor : e.CellStyle.ForeColor);

        e.Graphics.DrawString(textToDisplay, view.Font, brush, e.CellBounds.X, e.CellBounds.Y + _rowMargins / 2);

        e.Handled = true;
    }

    private static string TrimTextToFit(string text, int contentWidth, Font font)
    {
        Size size = TextRenderer.MeasureText(text, font);

        if (size.Width < contentWidth)
        {
            return text;
        }

        int i = 0;
        StringBuilder sb = new StringBuilder();
        while (i < text.Length)
        {
            sb.Append(text[i++]);
            size = TextRenderer.MeasureText(sb.ToString(), font);

            if (size.Width <= contentWidth) continue;

            sb.Append("...");

            while (sb.Length > 3 && size.Width > contentWidth)
            {
                sb.Remove(sb.Length - 4, 1);
                size = TextRenderer.MeasureText(sb.ToString(), font);
            }

            while (i < text.Length && text[i] != Environment.NewLine[0])
            {
                i++;
            }
        }
        return sb.ToString();
    }

}

Enjoy,
Ofir

Designer code:

partial class Form1
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.dataGridView1 = new System.Windows.Forms.DataGridView();
        this.LineNumber = new System.Windows.Forms.DataGridViewTextBoxColumn();
        this.Content = new System.Windows.Forms.DataGridViewTextBoxColumn();
        ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
        this.SuspendLayout();
        // 
        // dataGridView1
        // 
        this.dataGridView1.AllowUserToDeleteRows = false;
        this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
        | System.Windows.Forms.AnchorStyles.Left) 
        | System.Windows.Forms.AnchorStyles.Right)));
        this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
        this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
        this.LineNumber,
        this.Content});
        this.dataGridView1.Location = new System.Drawing.Point(13, 13);
        this.dataGridView1.Name = "dataGridView1";
        this.dataGridView1.RowHeadersWidth = 55;
        this.dataGridView1.RowTemplate.DefaultCellStyle.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
        this.dataGridView1.Size = new System.Drawing.Size(493, 237);
        this.dataGridView1.TabIndex = 0;
        this.dataGridView1.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellEndEdit);
        this.dataGridView1.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.dataGridView1_CellPainting);
        // 
        // LineNumber
        // 
        this.LineNumber.FillWeight = 30F;
        this.LineNumber.Frozen = true;
        this.LineNumber.HeaderText = "#";
        this.LineNumber.MaxInputLength = 3;
        this.LineNumber.Name = "LineNumber";
        this.LineNumber.ReadOnly = true;
        this.LineNumber.Resizable = System.Windows.Forms.DataGridViewTriState.False;
        this.LineNumber.Width = 30;
        // 
        // Content
        // 
        this.Content.HeaderText = "Content";
        this.Content.Name = "Content";
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(518, 262);
        this.Controls.Add(this.dataGridView1);
        this.Name = "Form1";
        this.Text = "Is it possible to have Multi-line DataGridView cells without wrapping text?";
        ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.DataGridView dataGridView1;
    private System.Windows.Forms.DataGridViewTextBoxColumn LineNumber;
    private System.Windows.Forms.DataGridViewTextBoxColumn Content;
}

Upvotes: 4

mojtaba
mojtaba

Reputation: 339

i test this code and result is very good test this please :

Note : create a form and Datagrid on , set following datagrid Properties

1- AutoSizeRowsMode to AllCells.
2- WrapMode for Target Column to True

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace DGMultiLine
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int max = 12; //min Column Width in Char
            //do this for all rows of column for max Line Size in values

            string str = "Hello\r\nI am mojtaba\r\ni like programming very very \r\nrun this code and  pay attention to result\r\n Datagrid Must show this Line Good are you see whole of this Thats Finished!";
            string[] ss = str.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

            //find max Line Size To Now
            for (int i = 0; i < ss.Length; i++)
                if (ss[i] != null && ss[i] != "")
                    if (ss[i].Length > max)
                        max = ss[i].Length;
            //Set target Column Width
            dataGridView1.Columns[0].Width = max*5;//for adequate value you must refer to screen resolution
            //filling datagrigView for all values
            dataGridView1.Rows[0].Cells[0].Value = str;
        }

    }
}

you can add a hidden Column by real fullText and a visible Column that show string value by above code but in string lines size that exceed from your Max Column Size clip string and add ... to end . ( if it isn't clear ask to write full code )

Upvotes: 2

Omid
Omid

Reputation: 823

It has worked for me by doing \r\n together with the string.

For example "Hello" + \r\n. It then goes to the next row.

EDIT:

Just saw that this was WinForms. The trick above only works in WPF.

EDIT2:

You can use:

dataGridView1.DefaultCellStyle.WrapMode = DataGridViewTriState.True;

And if you don't want long items wrapping, you just do:

String stringTest = "1234567891";
if (stringTest.Length > 8)
{
   stringTest = stringTest.Replace(stringTest.Substring(8), "...");
}

This will add "..." if the String is longer then 8.

Upvotes: 1

Charles
Charles

Reputation: 132

I have not found a way to do this without setting WrapMode to true. However, you should be able to "trick" the DataGridView into doing this by setting the width of the cell to be wide enough to display all items on a single line.

Here's an example of this being done with a ComboBo.

Upvotes: 0

Sachin
Sachin

Reputation: 2148

One way is you can just put some of words visible and then the Full Text can be shown in Tooltip on mouse over that cell.

Upvotes: 0

Related Questions