DataGridView: Change Edit Control size while editing

Timo F picture Timo F · Mar 26, 2009 · Viewed 14k times · Source

in the DataGridView I want the cell size to expand according to the string length when I edit the cell. Excel does the same.

In the DataGridView, when entering edit mode, a DataGridViewTextBoxEditingControl is placed at the cell position. I tried to change the bounds/size of this control, but result is just a short flicker of my desired size. It gets directly overpainted the original, truncated way.

Any ideas on how to get this working?

Thanks,

Timo

Answer

VineAndBranches picture VineAndBranches · May 16, 2014

This question is quite old but hopefully my answer helps somebody down the road. I ran across the same problem and was able to use a process similar to the following to make the column width update dynamically as the user typed, in order to ensure the text fit in the column.

Events used:

  • CellBeginEdit
  • CellEndEdit
  • EditingControlShowing
  • TextBoxKeyPressEvent (i.e. KeyPress)

NOTE: The following code assumes that AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells

// ---------------------------------------------------------------------------

private void dataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
    // Copies the original column width because switching to DataGridViewAutoSizeColumnMode.None
    // will automatically make the column a default width.
    int origColumnWidth = dataGridView1.Columns[e.ColumnIndex].Width;

    dataGridView1.Columns[e.ColumnIndex].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;

    // Reverts back to the original width.
    dataGridView1.Columns[e.ColumnIndex].Width = origColumnWidth;
}

// ---------------------------------------------------------------------------

private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    dataGridView1.Columns[e.ColumnIndex].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
}

// ---------------------------------------------------------------------------

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
    if (e.Control is TextBox)
    {
        var tbox = (e.Control as TextBox);

        // De-register the event FIRST so as to avoid multiple assignments (necessary to do this or the event
        // will be called +1 more time each time it's called).
        tbox.KeyPress -= TextBoxKeyPressEvent;
        tbox.KeyPress += TextBoxKeyPressEvent;
    }
}

// ---------------------------------------------------------------------------

private void TextBoxKeyPressEvent(object sender, KeyPressEventArgs e)
{
    // Gets the text prior to the new character being added.  Appending an arbitrary "0" to the value
    // to account for the missing character when determining appropriate measurements.
    string prevText = dataGridView1.CurrentCell.EditedFormattedValue.ToString() + "0";

    Graphics editControlGraphics = dataGridView1.EditingControl.CreateGraphics();

    // Gets the length of the current text value.
    SizeF stringSize = editControlGraphics.MeasureString(prevText, dataGridView1.EditingControl.Font);

    int widthForString = (int)Math.Round(stringSize.Width, 0);

    // Makes the column width big enough if it's not already.
    if (dataGridView1.CurrentCell.OwningColumn.Width < widthForString)
    {
        dataGridView1.CurrentCell.OwningColumn.Width = widthForString;
    }
}

EDIT: Update to the TextBoxKeyPressEvent logic to account for Backspace:

private void TextBoxKeyPressEvent(object sender, KeyPressEventArgs e)
        {
            string prevText;
            bool wasBackspaced = false;

            // The following logic will either add or remove a character to/from the text string depending if the user typed
            // an additional character or pressed the Backspace key.  At the end of the day, the cell will (at least) be
            // sized to the configured minimum column width or the largest row width in the column because we're using 
            // AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells.
            if (e.KeyChar == Convert.ToChar(Keys.Back))
            {
                prevText = dataGridView1.CurrentCell.EditedFormattedValue.ToString();

                if (prevText.Length == 0)
                {
                    // Don't try to make it any smaller...
                    return;
                }

                // Remove an arbitrary character for determining appropriate measurements.
                prevText = prevText.Remove(prevText.Length - 1);
                wasBackspaced = true;
            }
            else
            {
                // Gets the text prior to the new character being added.  Appending an arbitrary "0" to the value
                // to account for the missing character when determining appropriate measurements.
                prevText = dataGridView1.CurrentCell.EditedFormattedValue.ToString() + "0";
            }

            Graphics editControlGraphics = dataGridView1.EditingControl.CreateGraphics();

            // Gets the length of the current text value.
            SizeF stringSize = editControlGraphics.MeasureString(prevText, dataGridView1.EditingControl.Font);

            int widthForString = (int)Math.Round(stringSize.Width, 0);

            // Makes the column width big, or small, enough if it's not already.
            if (dataGridView1.CurrentCell.OwningColumn.Width < widthForString ||  // 1. Applies when adding text
                (dataGridView1.CurrentCell.OwningColumn.Width > widthForString &&          // ---
                 dataGridView1.CurrentCell.OwningColumn.MinimumWidth < widthForString &&   // 2. Applies when backspacing
                 wasBackspaced))                                                           // ---
            {
                dataGridView1.CurrentCell.OwningColumn.Width = widthForString;
            }
        }