Está en la página 1de 12

Cmo detectar la tecla pulsada en una celda del control DataGridView

Por Enrique Martnez Montejo


[Microsoft Most Valuable Professional - Visual Basic]
ltima revisin: 05/09/2007

Este es uno de los temas estrella del grupo de noticias en


espaol de Visual Basic .net, y su razn de ser tiene, porque en
principio, todos creemos que mediante los clsicos
eventos KeyDown, KeyPress y KeyUp del control DataGridView,
podemos tener un cierto control sobre lo que se escribe en sus
celdas. Pero cuando observamos que los
eventos KeyDown y KeyUp slo responden a un cierto tipo de
teclas que son obvias (modificadoras, de desplazamiento, de
funcin, escape, intro, etc.), y que ninguno de los tres eventos
se desencadena al pulsar las teclas correspondientes a letras,
nmeros o smbolos, nos entra la desesperacin y un tanto de
desilusin.
Es cierto que existen otras formas de controlar de una manera
general, la pulsacin de las teclas que se efectan en el
control DataGridView. Por ejemplo, podemos utilizar los tres
eventos de teclado del formulario que contiene al
control DataGridView, o podemos tambin reeemplazar la
funcin ProcessCmdKey. Pero mientras los primeros requieren
que la propiedad KeyPreview del formulario tenga el
valor True (para que se desencadenen los eventos del formulario
antes que los mismos eventos correspondientes a los controles
incluidos en aquel), el reemplazar la
funcin ProcessCmdKey requiere trabajar con mensajes de
Windows e identificadores de ventanas (los famosos
valores hWnd o Handled de la API de Windows).
Aparte, tanto los eventos del formulario, como la
funcin ProcessCmdKey, se desencadenaran, no slo para los
eventos de teclado del controlDataGridView, sino para todos los
eventos de teclado de los restantes controles existentes en el
formulario, con lo cual tendramos que estar comprobando qu
control tiene el foco, o el identificador de ventana (hWnd) del
control que ha provocado el evento. En mi opinin personal, hay

otra solucin ms eficaz, que es tratar por todos los medios que
los propios eventos de teclado del control DataGridView, sobre
todo el eventoKeyPress, respondan a las pulsaciones de las
teclas presionadas. Pero antes de entrar en detalle sobre los
eventos de teclado, vamos a conocer un poco ms sobre lo que
se esconde por dentro del control DataGridView, que no es, ni
ms ni menos, que una serie de controles que los
programadores de Visual Basic suelen conocer bastante bien.

La clase DataGridViewCell
Esta clase representa una celda individual existente en un
control DataGridView, que podemos referenciar fcilmente
efectuando una simple consulta a la propiedad CurrentCell del
control DataGridView:
' Referenciamos la celda actual
'
Dim dgvCell As DataGridViewCell = Me.DataGridView1.CurrentCell

De esta manera ya estamos en condiciones de obtener el valor


de las restantes propiedades de la celda, como el ndice de la
columna (ColumnIndex), el ndice de la fila (RowIndex), o el valor
de la celda (Value). Pero tambin podemos conocer igualmente,
el tipo de control de edicin que se aloja en la celda mediante la
propiedad EditType del control DataGridView, que dependiendo
del tipo asignado a la columna, en principio puede
corresponderse con alguna de las siguientes clases:
Tipos de columnas
DataGridViewButtonColumn
DataGridViewCheckBoxColumn
DataGridViewComboBoxColumn
DataGridViewImageColumn
DataGridViewLinkColumn
DataGridViewTextBoxColumn

Tipos de objetos existentes en las


celdas
DataGridViewButtonCell
DataGridViewCheckBoxCell
DataGridViewComboBoxCell
DataGridViewImageCell
DataGridViewLinkCell
DataGridViewTextBoxCell

Parece ser que el nombre de cada clase denota bien el tipo de


control que representan las columnas y celdas del
control DataGridView.

Cuando referenciamos una celda cualquiera del


control DataGridView, automticamente obtendremos el tipo de
control alojado en ella, que se corresponder con el tipo de
control asignado a la columna a la que pertenece la celda.
Continuando con el ejemplo anterior, si la celda actual se
encuentra en una columna tipo DataGridViewCheckBoxColumn,
la variable objeto dgvCell referenciar a un
objeto DataGridViewCheckBoxCell, y si la columna es del
tipo DataGridViewTextBoxColumn, el tipo de objeto de la celda
ser DataGridViewTextBoxCell.

La clase DataGridViewTextBoxCell
Esta clase es la encargada de mostrar texto modificable en una
celda del control DataGridView, y es la clase que por defecto
tendrn todas las celdas existententes en dicho control, salvo
que expresamente le hayamos asignado a la columna otro tipo
de clase DataGridViewXXXColumn de las mencionadas en el
apartado anterior, en cuyo caso, las celdas de esa columna
sern del tipo de objeto especializado.
La clase DataGridViewTextBoxCell hereda directamente de la
clase base DataGridViewCell, de la que toma la inmensa mayora
de sus propiedades y mtodos.
Si nuestra intencin es referenciar el
objeto DataGridViewTextBoxCell alojado en la celda actual del
control DataGridView, ejecutaramos algo parecido a lo
siguiente:
' Referenciamos el control DataGridViewTextBoxCell actual
'
Dim textBoxCell As DataGridViewTextBoxCell = _
TryCast(Me.DataGridView1.CurrentCell, DataGridViewTextBoxCell)

La importancia de esta clase radica en que la celda actualmente


seleccionada, aloja a su vez un control de la
claseDataGridViewTextBoxEditingControl, que es el control que
nos permite editar el valor de la celda, siempre y cuando el valor
de la propiedadReadOnly del
control DataGridViewTextBoxCell sea False.

El control DataGridViewTextBoxEditingControl
Por fin hemos llegado al objeto ms recndito de la celda; al
objeto DataGridViewTextBoxEditingControl alojado en el
objetoDataGridViewTextBoxCell existente a su vez en el
objeto DataGridViewCell, representado ste por la celda actual
del control DataGridView.
Desde luego, no me extraa que con tantos nombres parecidos,
y de gran longitud, ms de uno se haga un pequeo lo. Pero
cuando se hayanpeleado un par de veces con las celdas del
control DataGridView, les aseguro que vern las cosas de otra
manera.
Como bien nos d a entender el nombre del
control DataGridViewTextBoxEditingControl, ste hereda
directamente del clsico control de edicin de Visual Basic, el
control TextBox, por tanto, implementa todas las propiedades,
mtodos y eventos de ste ltimo control, permitindonos editar
el contenido de la celda.
Cuando una celda cualquiera del control DataGridView pasa a
modo de edicin, se desencadena el
evento EditingControlShowing, donde el segundo parmetro de
su firma es un objeto de la
clase DataGridViewEditingControlShowingEventArgs (otro
nombre ms largo an para aadir a la lista). Esta clase dispone
nicamente de dos propiedades pblicas: CellStyle (un
objeto DataGridViewCellStyle que referencia a la celda editada),
y Control(que es el control proporcionado para editar el valor de
la celda). Ser de la propiedad Control donde obtendremos el
escndido controlDataGridViewTextBoxEditingControl, tal y como
se muestra en el siguiente ejemplo:
Private Sub DataGridView1_EditingControlShowing( _
ByVal sender As Object, _
ByVal e As DataGridViewEditingControlShowingEventArgs) _
Handles DataGridView1.EditingControlShowing
' Referenciamos el control TextBox subyacente en la celda actual.
'

Dim cellTextBox As DataGridViewTextBoxEditingControl = _


TryCast(e.Control, DataGridViewTextBoxEditingControl)
' Obtenemos el valor actual de la celda.
'
MessageBox.Show(cellTextBox.Text)
' Obtenemos el estilo de la celda actual
'
Dim style As DataGridViewCellStyle = e.CellStyle
' Mientras se edita la celda, aumentaremos la fuente
' y rellenaremos el color de fondo de la celda actual.
'
With style
.Font = New Font(style.Font.FontFamily, 10, FontStyle.Bold)
.BackColor = Color.Beige
End With
End Sub

El resultado ser el que se muestra en la imagen, donde se


observa que la celda cambia a otro color de fondo y se aumenta
el tamao de la fuente, mientras se est editando.

Una vez que hemos sido capaces de referenciar el objeto


derivado de la clase TextBox existente en una celda, por qu no

vamos a ser capaces de controlar las pulsaciones de teclas que


se efectan en el mismo, si en definitiva se trata de un simple
control TextBox? La solucin se encuentra en instalar los
correspondientes controladores para los tres eventos del teclado
del control TextBox: KeyDown, KeyPress y KeyUp.
En el ejemplo anterior, la variable cellTextBox est declarada a
nivel del propio procedimiento que la contiene, por lo su mbito
es local. Es cierto que en el mismo procedimiento podemos
instalar los correspondientes controladores de evento mediante
la instruccin AddHandler, pero cada vez que se edite una celda,
se estar instalado un controlador, y si se editan 20.000 celdas,
se instalarn 20.000 controladores, que desencadenarn 20.000
eventos, que en el caso de estar instalados para los tres eventos
de teclado, producirn 60.000 eventos de teclado. Para evitar
que se instale un controlador cada vez que se edita una celda,
habra que eliminar el controlador con la
instruccin RemoveHandler, por ejemplo, en el
eventoCellEndEdit del control DataGridView, que se produce
cuando finalice la edicin de la celda.
Pero, claro! Esto ltimo tiene un gran inconveniente, porque si
eliminamos el controlador de evento en otro procedimiento
distinto (CellEndEdit) a aquel donde se ha instalado
(EditingControlShowing), necesitaremos una variable objeto con
un mbito superior al local para poder eliminar el controlador de
evento. La solucin pasa por declarar a nivel del propio
formulario que contiene el control DataGridView, una variable
objeto que ser la que sucesivamente estar referenciando al
control DataGridViewTextBoxEditingControl de la celda que
actualmente se encuentre en modo de edicin, y declararemos
la variable objeto con la palabra clave WithEvents, para no tener
que aadir y eliminar manualmente los controladores para los
tres eventos de teclado, tal y como muestro a continuacin:
Private WithEvents cellTextBox As DataGridViewTextBoxEditingControl
Private Sub cellTextBox_KeyDown( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.KeyEventArgs) Handles cellTextBox.KeyDo
wn

End Sub
Private Sub cellTextBox_KeyPress( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles cellTextBox.K
eyPress
End Sub
Private Sub cellTextBox_KeyUp( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.KeyEventArgs) Handles cellTextBox.KeyUp
End Sub
Private Sub DataGridView1_EditingControlShowing( _
ByVal sender As Object, _
ByVal e As DataGridViewEditingControlShowingEventArgs) _
Handles DataGridView1.EditingControlShowing
' Este evento se producir cuando la celda pasa a modo de edicin.
' Referenciamos el control DataGridViewTextBoxEditingControl actual.
'
cellTextBox = TryCast(e.Control, DataGridViewTextBoxEditingControl)
' Obtenemos el estilo de la celda actual
'
Dim style As DataGridViewCellStyle = e.CellStyle
' Mientras se edita la celda, aumentaremos la fuente
' y rellenaremos el color de fondo de la celda actual.
'
With style
.Font = New Font(style.Font.FontFamily, 10, FontStyle.Bold)
.BackColor = Color.Beige
End With

End Sub

Cmo declarar un objeto DataGridViewTextBoxEditingControl


para ser utilizado por los eventos del teclado
Bueno. Ya est en condiciones de detectar la tecla pulsada en
cualquier celda del control DataGridView que se encuentre
actualmente en modo de edicin.
Los eventos correspondientes al control de edicin de la celda,
unidos a los tres eventos del teclado del propio
control DataGridView, en teora nos debe de dar toda la
flexibilidad posible para detectar las teclas que el usuario ha
presionado.
A continuacin paso a exponer algunos ejemplos para trabajar
con los eventos del teclado descritos, dependiendo de ciertas
acciones en concreto. Con total seguridad, en la Red de redes
existirn miles de ejemplos mejores que los que encontrar
aqu, pero lo que slo pretendo es mostrarle simplemente una
manera ms de realizar una operacin concreta, que no quiere
decir que sea la mejor.

Permitir slo nmeros, el carcter separador decimal, y


el carcter negativo, en las celdas de un control
DataGridView.
Si en nuestro control DataGridView tenemos una columna donde
nos interesara que slo se introdujeran datos numricos
(enteros o decimales), controlaramos la pulsacin de teclas en
el evento KeyPress del
objeto DataGridViewTextBoxEditingControl declarado a nivel del
formulario.
Por supuesto que el ejemplo tambin servira para permitir los
citados caracteres en un simple control TextBox, que como se ha
explicado en el artculo, es el control que existe en las celdas de
un control DataGridView.

Private Sub cellTextBox_KeyPress( _


ByVal sender As Object, _
ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles cellTextBox.K
eyPress
' Referenciamos el control TextBox subyacente.
'
Dim tb As TextBox = TryCast(sender, TextBox)
' Si la conversin ha fallado, abandonamos el procedimiento.
'
If (tb Is Nothing) Then
e.Handled = True
Return
End If
Dim isDecimal, isSign, isValidChar As Boolean
Dim decimalSeparator As String = Nothing
Select Case e.KeyChar
Case "."c, ","c
' Obtenemos el carcter separador decimal existente
' actualmente en la configuracin regional de Windows.
'
decimalSeparator = _
Threading.Thread.CurrentThread. _
CurrentCulture.NumberFormat.NumberDecimalSeparator
' Hacemos que el carcter tecleado coincida con el
' carcter separador existentente en la configuracin
' regional.
'
e.KeyChar = decimalSeparator.Chars(0)
' Si el primer carcter que se teclea es el separador decimal,
' o si bien, existe un signo en el primer carcter, envo la
' combinacin '0,'.
'

If (((tb.TextLength = 0) OrElse (tb.SelectionLength =


tb.TextLength)) OrElse _
((tb.TextLength = 1) AndAlso ((tb.Text.Contains("-")) OrElse _
(Text.Contains("+"))))) Then
' NOTA: Envo la combinacin "0," mediante el mtodo Send,
' para que en el cdigo cliente se desencadenen los
' eventos de teclado.
'
SendKeys.Send("{0}")
SendKeys.Send("{" & decimalSeparator & "}")
e.Handled = True
Return
End If
' Es un carcter vlido.
'
isDecimal = True
isValidChar = True
Case "-"c, "+"c ' Signos negativo y positivo
' Es un carcter vlido.
'
isMinus = True
isValidChar = True
Case Else
' Slo se admitirn nmeros y la tecla de retroceso.
'
Dim isDigit As Boolean = Char.IsDigit(e.KeyChar)
Dim isControl As Boolean = Char.IsControl(e.KeyChar)
If ((isDigit) OrElse (isControl)) Then
isValidChar = True
Else
e.Handled = True
Return

End If
End Select
' Si es un carcter vlido, y el texto del control
' se encuentra totalmente seleccionado, elimino
' el valor actual del control.
'
If ((isValidChar) And (tb.SelectionLength = tb.TextLength)) Then
tb.Text = String.Empty
End If
If (isSign) Then
' Admitimos los caracteres positivo y negativo, siempre y cuando
' sea el primer carcter del texto, y no exista ya ningn otro
' signo escrito en el control.
'
If ((tb.SelectionStart <> 0) OrElse _
(tb.Text.IndexOf("-") >= 0) OrElse _
(tb.Text.IndexOf("+") >= 0)) Then
e.Handled = True
Return
End If
End If
If (isDecimal) Then
' Si en el control hay ya escrito un separador decimal,
' deshechamos insertar otro separador ms.
'
If (tb.Text.IndexOf(decimalSeparator) >= 0) Then
e.Handled = True
Return
End If
End If
End Sub

Como bien podr comprobar, el ejemplo tiene en cuenta el


carcter separador decimal existente en la configuracin

regional del usuario, que en algunos casos ser el punto (.), y en


otros la coma (,).
Asimismo, tambin tiene en cuenta si se desea introducir un
numro negativo, que slo se permitir si el signo menos (-) es
el primer carcter existente en la celda o en el control TextBox.

También podría gustarte