9

How can I make the Enter key behave in a Winforms DataGridViewTextBoxCell like it does in a normal Winforms TextBox (add a new line to the text, instead of changing the current cell)?

Zach Johnson
  • 23,678
  • 6
  • 69
  • 86

3 Answers3

10

Well, I found out how to solve the problem. First, create a class called CustomDataGridViewTextBoxEditingControl which derives from DataGridViewTextBoxEditingControl, and override EditingControlWantsInputKey like this:

public class CustomDataGridViewTextBoxEditingControl : DataGridViewTextBoxEditingControl
    {
        public override bool EditingControlWantsInputKey(
        Keys keyData, 
        bool dataGridViewWantsInputKey)
        {
            switch (keyData & Keys.KeyCode)
            {
                case Keys.Enter:
                    // Don't let the DataGridView handle the Enter key.
                    return true;
                default:
                    break;
            }

            return base.EditingControlWantsInputKey(keyData, dataGridViewWantsInputKey);
    }
}

This stops the DataGridView from handing the Enter key and changing the current cell. It does not, however, make the Enter key add a new line. (It appears that the DataGridViewTextBoxEditingControl has removed the functionality of the Enter key). Therefore, we need to override OnKeyDown and implement the functionality ourselves, like this:

protected override void OnKeyDown(KeyEventArgs e)
{
    switch (e.KeyCode & Keys.KeyCode)
    {
        case Keys.Enter:
            int oldSelectionStart = this.SelectionStart;
            string currentText = this.Text;

            this.Text = String.Format("{0}{1}{2}",
                currentText.Substring(0, this.SelectionStart),
                Environment.NewLine,
                currentText.Substring(this.SelectionStart + this.SelectionLength));

            this.SelectionStart = oldSelectionStart + Environment.NewLine.Length;
            break;
        default:
            break;
    }

    base.OnKeyDown(e);
}

Then, create a class called CustomDataGridViewTextBoxCell which derives from DataGridViewTextBoxCell, and override the EditType property to return the type of CustomDataGridViewTextBoxEditingControl.

public class CustomDataGridViewTextBoxCell : DataGridViewTextBoxCell
{
    public override Type EditType
    {
        get
        {
            return typeof(CustomDataGridViewTextBoxEditingControl);
        }
    }
}

After you do this, you can set the CellTemplate property on an existing column to a CustomDataGridViewTextBoxCell, or you can create a class derived from DataGridViewColumn with CellTemplate preset to a CustomDataGridViewTextBoxCell, and you will be all set!

jmoreno
  • 12,752
  • 4
  • 60
  • 91
Zach Johnson
  • 23,678
  • 6
  • 69
  • 86
  • Hay, I like this solution. It has worked for me but there is one thing missing. How can i increase the row height of all of the cells which are on the same row as this datagridview textbox. Is this possible?. Obviously this would also need to happen on enter key press. – Joe Bell Mar 24 '14 at 16:56
  • Apologies, the problem i was having was being caused by my autosize rows mode property being set to all cells. Me.EditingControlDataGridView.Rows(EditingControlRowIndex).Height += 10 worked a charm – Joe Bell Mar 24 '14 at 17:11
6

You can do this by setting the DataGridViewCellStyle.WrapMode property to true. From MSDN:

If WrapMode is False for a cell that contains text, the cell displays the text on a single line, and displays any embedded newline characters as box characters. If WrapMode is True for a cell that contains text, the cell displays newline characters as line breaks, but also wraps any lines that exceed the width of the cell.

You can set this for specific cells by accessing the Style property on a cell, or for all cells in a column by using the DefaultCellStyle for the column.

[Update] To disable the Enter key selectively in your DataGridView, add a Message Filter to the Form containing the DataGridView as shown below:

private KeyMessageFilter m_filter = null;

private void Form1_Load(object sender, EventArgs e)
{
    m_filter = new KeyMessageFilter(this); 
    Application.AddMessageFilter(m_filter);

}

Here is the message filter class:

public class KeyMessageFilter : IMessageFilter
{
    private Form m_target = null;

    public KeyMessageFilter(Form targetForm)
    {
        m_target = targetForm;
    }

    private const int WM_KEYDOWN = 0x0100;

    private const int WM_KEYUP = 0x0101;


    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == WM_KEYDOWN)
        {
            //Note this ensures Enter is only filtered if in the 
            // DataGridViewTextBoxEditingControl and Shift is not also  pressed.
            if (m_target.ActiveControl != null && 
                m_target.ActiveControl is DataGridViewTextBoxEditingControl && 
                (Keys)m.WParam == Keys.Enter && 
                (Control.ModifierKeys & Keys.Shift) != Keys.Shift)
            {
                return true;
            }

        }

        return false;
    }
}

Now, the Enter key is disabled when editing text and you must press the tab key to move to the next cell. Shift + Enter still adds a newline to the text you are editing.

Hope this helps.

Community
  • 1
  • 1
Ash
  • 60,973
  • 31
  • 151
  • 169
  • Thanks! Your answer fixed the issue of allowing multiple lines in the cell, but unfortunately the Enter key still changes the selected cell. – Zach Johnson Jan 09 '10 at 21:18
  • Just to make it clearer, if WrapMode is set to True the enter key will still move to the next row but you can now use Shift+Enter to embed a newline in the text. If you are happy with that behaviour the message filter is not required. – JonP Aug 14 '17 at 08:11
  • @ZachJohnson, it's working for me, but you have to be editing the text in order for the filter to prevent moving to the next cell. If you just highlight a cell and hit enter, it still moves to the next line. – Nielsvh Jan 10 '20 at 15:57
0

Although @Zach Johnson has answered the main part of this question, his code was not working for me. After investing long time and reading different threads I realized that you have also to set some properties to get that work. So here is the full code, so you can run an example: Define CustomDataGridViewTextBoxCell:

class CustomDataGridViewTextBoxCell: DataGridViewTextBoxCell
{
    public override Type EditType => typeof(CustomDataGridViewTextBoxEditingControl);
}

Then define the class CustomDataGridViewTextBoxEditingControl

class CustomDataGridViewTextBoxEditingControl : DataGridViewTextBoxEditingControl
{
    public override bool EditingControlWantsInputKey(
        Keys keyData,
        bool dataGridViewWantsInputKey)
    {
        switch (keyData & Keys.KeyCode)
        {
            case Keys.Enter:
                // Don't let the DataGridView handle the Enter key.
                return true;
            default:
                break;
        }

        return base.EditingControlWantsInputKey(keyData, dataGridViewWantsInputKey);
    }
    protected override void OnKeyDown(KeyEventArgs e)
    {
        switch (e.KeyCode & Keys.KeyCode)
        {
            case Keys.Enter:
                int oldSelectionStart = this.SelectionStart;
                string currentText = this.Text;

                this.Text = String.Format("{0}{1}{2}",
                    currentText.Substring(0, this.SelectionStart),
                    Environment.NewLine,
                    currentText.Substring(this.SelectionStart + this.SelectionLength));

                this.SelectionStart = oldSelectionStart + Environment.NewLine.Length;
                break;
            default:
                break;
        }

        base.OnKeyDown(e);
    }

}

Then define DataGridViewRolloverCell:

public class DataGridViewRolloverCell : DataGridViewTextBoxCell
{
    public override Type EditType => typeof(CustomDataGridViewTextBoxEditingControl);
}

After that define the class DataGridViewCustomColumn:

public class DataGridViewCustomColumn : DataGridViewColumn
{
    public DataGridViewCustomColumn()
    {
        this.CellTemplate = new CustomDataGridViewTextBoxCell();
    }
}

Now if you have a DatagridViewControl called dgv, your code would look like this:

DataGridViewCustomColumn col =  new DataGridViewCustomColumn();
dgv.Columns.Add(col);

Now important: You still have to set in the DefaultCellStyle the property WrapText to true. Do it in the designer or with this code:

DataGridViewCellStyle dataGridViewCellStyle1 = DataGridViewCellStyle();
dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
this.dgv.DefaultCellStyle = dataGridViewCellStyle1;

And do not forget to set the property Datagridview.AutoSizeRowsMode to AllCells. Then it will work.

Code Pope
  • 5,075
  • 8
  • 26
  • 68