Is there any way to get underlying control for a DataGridView cell?
I would like to attach normal texbox events to capture keystrokes and capture value changed.
So i have 4 columns, each of them contains number of cells and all cells in one row should be handled different way based on its type.
Basically i need my events to be fired only when a cell is being edited.
Subscribe the DataGridView.EditingControlShowing
event, then subscribe the TextBox
event you need.
Example with TextBox.KeyDown
:
void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
var txtBox = e.Control as TextBox;
if (txtBox != null)
{
// Remove an existing event-handler, if present, to avoid
// adding multiple handlers when the editing control is reused.
txtBox.KeyDown -= new KeyEventHandler(underlyingTextBox_KeyDown);
// Add the event handler.
txtBox.KeyDown += new KeyEventHandler(underlyingTextBox_KeyDown);
}
}
void underlyingTextBox_KeyDown(object sender, KeyEventArgs e)
{
// ...
}
EDIT:
Now the code is more correct, because it follows the suggestion given on MSDN:
The DataGridView control hosts one
editing control at a time, and reuses
the editing control whenever the cell
type does not change between edits.
When attaching event-handlers to the
editing control, you must therefore
take precautions to avoid attaching
the same handler multiple times. To
avoid this problem, remove the handler
from the event before you attach the
handler to the event. This will
prevent duplication if the handler is
already attached to the event, but
will have no effect otherwise. For
more information, see the example code
in the
DataGridViewComboBoxEditingControl
class overview.
EDIT 2:
As per comment:
TextChanged
event is called before EditingControlShowing
, and then again after it.
You can distinguish between the two calls using this trick:
void txtBox_TextChanged(object sender, EventArgs e)
{
var txtBox = (TextBox)sender;
if (txtBox.Focused)
{
// second call (after EditingControlShowing) the TextBox is focused
}
else
{
// first call (before EditingControlShowing) the TextBox is not focused
}
}
You can do it with the EditingControlShowing event
void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (e.Control is TextBox)
{
(e.Control as TextBox).KeyDown += new KeyEventHandler(Form1_KeyDown);
//add as you require
}
}
void Form1_KeyDown(object sender, KeyEventArgs e)
{
// your code here
}
Thanks to @digEmAll I was able to register events to an underlying TextBox however there is very strange events handling for that approach and some precautions must be taken.
I've noticed that TextChanged event for new cell text box was fired before EditingControlShowing. It results with firing handler registered originally for the cell just being leave with field that will be focused as the sender. That's why all events should be deregistered on leave to avoid that behavior.
Final working solution for my problem:
void dgv_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (e.Control is TextBox)
{
DataGridView dgv = sender as DataGridView;
DataGridViewColumn dgvCol= dgv.CurrentCell.OwningColumn;
TextBox tb = (TextBox)e.Control;
foreach (cFieldLayoutType fieldLayout in FieldLayouts)
{
string context = dgvCol.Name.Substring(dgvCol.Name.LastIndexOf(".") + 1);
if (context == fieldLayout.columnName)
{
//See URL to check why it is done this way: http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.editingcontrolshowing.aspx
KeyPressEventHandler kpehAmount = new KeyPressEventHandler(oTextBoxAmount_KeyPress);
KeyPressEventHandler kpehDecimal = new KeyPressEventHandler(oTextBoxDecimal_KeyPress);
KeyPressEventHandler kpehDate = new KeyPressEventHandler(oTextBoxDate_KeyPress);
EventHandler textChangedHandlerAmount = new EventHandler(oTextBoxAmount_TextChanged);
tb.Leave += new EventHandler(textBox_DeregisterCellEventsOnLeave);
switch (fieldLayout.Type)
{
case cFieldType.amount:
{
tb.KeyPress += kpehAmount;
tb.TextChanged += textChangedHandlerAmount;
break;
}
case cFieldType.numeric:
{
tb.KeyPress += kpehDecimal;
break;
}
case cFieldType.date:
{
tb.KeyPress += kpehDate;
break;
}
case cFieldType.text:
{
break;
}
}
}
}
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void textBox_DeregisterCellEventsOnLeave(object sender, EventArgs e)
{
TextBox tb = (TextBox)sender;
KeyPressEventHandler kpehAmount = new KeyPressEventHandler(oTextBoxAmount_KeyPress);
KeyPressEventHandler kpehDecimal = new KeyPressEventHandler(oTextBoxDecimal_KeyPress);
KeyPressEventHandler kpehDate = new KeyPressEventHandler(oTextBoxDate_KeyPress);
EventHandler textChangedHandlerAmount = new EventHandler(oTextBoxAmount_TextChanged);
EventHandler textBoxDeregisterOnLeave = new EventHandler(textBox_DeregisterCellEventsOnLeave);
tb.KeyPress -= kpehAmount;
tb.KeyPress -= kpehDate;
tb.KeyPress -= kpehDecimal;
tb.TextChanged -= textChangedHandlerAmount;
tb.Leave -= textBoxDeregisterOnLeave;
}