Note: In this post, I assume you have seen How to: Host Controls in Windows Forms DataGridView Cells which shows how you can create a datetime picker for your DataGridView. So this post is trying to show how to handle Up and Down arrow key while editing the cell.
--------------
Handle arrow keys while editing the cell - Add or Subtract day by UP and Down key
In the default implementation of DataGridViewTextBoxEditingControl, when it receives arrow keys, using the EditingControlWantsInputKey method, it decide whether it should let the key be handled by the editing control, or should it pass the arrow key to DataGridView; for example when the user press down arrow, if there's no new line in the edit control, then it pass the down arrow key to the DataGridView, otherwise, let the control handle it which goes to the next line of text. The default handling of Up and Down arrow keys in TextBox is moving the caret to next or previous line if available, or if there's no line available, move to left or right.
To change the behavior and run a custom logic when the use press Up or Down arrow keys, you need to do the following:
- You need to change the logic of editing control to not pass the Up and Down arrow keys keys to DataGridView
- You need to override the default handling of Up and Do
The first thing, creating a custom editing control, to disable passing Up and Down to DataGridView:
using System;
using System.Windows.Forms;
public class MyDataGridViewTextBoxColumn : DataGridViewTextBoxColumn
{
public MyDataGridViewTextBoxColumn()
{
this.CellTemplate = new MyDataGridViewTextBoxCell();
}
}
public class MyDataGridViewTextBoxCell : DataGridViewTextBoxCell
{
public override Type EditType => typeof(MyDataGridViewTextBoxEditingControl);
}
public class MyDataGridViewTextBoxEditingControl : DataGridViewTextBoxEditingControl
{
public override bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
{
if ((keyData == Keys.Up) || (keyData == Keys.Down))
return true;
return base.EditingControlWantsInputKey(keyData, dataGridViewWantsInputKey);
}
}
Then in the next step, make sure you add this column to your DataGridView in designer or in code:
dataGridView1.Columns.Add(new MyDataGridViewTextBoxColumn() { DataPropertyName = "ColumnName", HeaderText = "Header Text" });
Then what you need is handling the KeyDown event of the editing control, and run your custom logic. To to do, make sure you handle the EditingControlShowing event of the DataGridView in designer or in the code:
dataGridView1.EditingControlShowing += DataGridView1_EditingControlShowing;
And there, Handle KeyDown event of the EditingControl:
private void DataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
var cell = dataGridView1.CurrentCell;
var edit = e.Control as TextBox;
if (cell != null && cell.ColumnIndex == 0 && edit != null)
{
edit.KeyDown -= edit_KeyDown; //Make sure it handles the event once
edit.KeyDown += edit_KeyDown;
}
}
private void edit_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Up || e.KeyData == Keys.Down)
{
e.Handled = true;
e.SuppressKeyPress = true;
MessageBox.Show("You custom logic goes here!");
}
}
Now the control shows the expected behavior:
- In edit mode, for your specific column, Up and Down doesn't move to the top or bottom cell
- In normal mode, arrow keys work as expected and move between cells
- Up and Down arrow and have been handled and a custom logic runs.
Now your custom logic. First make sure you handle the CellFormatting and CellParsing, which are responsible to show the data in the desired format, and later after editing, convert it back to the desired value of the data source:
dataGridView1.CellFormatting += DataGridView1_CellFormatting;
dataGridView1.CellParsing += DataGridView1_CellParsing;
Then I use this logic:
private void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex >= 0)
{
e.CellStyle.Format = "dd/MM/yyyy";
}
}
private void DataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex >= 0)
{
if(DateTime.TryParseExact((string)e.Value,
"dd/MM/yyyy",
Thread.CurrentThread.CurrentUICulture,
System.Globalization.DateTimeStyles.None,
out DateTime d))
{
e.Value = d;
e.ParsingApplied = true;
}
}
}
Now it's time for modifying the Up and Down arrow logic in the edit_KeyDown event handler:
private void edit_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Up || e.KeyData == Keys.Down)
{
e.Handled = true;
var txt = (TextBox)sender;
if (DateTime.TryParseExact(txt.Text,
"dd/MM/yyyy",
Thread.CurrentThread.CurrentUICulture,
System.Globalization.DateTimeStyles.None,
out DateTime d))
{
var dx = e.KeyData == Keys.Up ? +1 : -1;
txt.Text = d.AddDays(dx).ToString("dd/MM/yyyy");
}
}
}
There you go!