One of the questions I frequently get is how to get the same behavior as Excel when entering numeric values. With the same behavior I mean that Excel detects the current users setting for decimal separator and makes the point on the numeric keyboard input the correct character. This is only done for the numeric keypad, the point and comma on regular part of the keyboard remain unchanged.
Doing so in a single TextBox isn't all that hard, see
this for an explanation, but in a
DataGridView things get a bit more complicated. The code below does the
trick though so you don't have to figure it out :-).
Imports
System.Windows.Forms
'''
<summary>
''' Standard textbox for
all numeric input.
'''
</summary>
'''
<remarks></remarks>
Class
NumericTextBox
Inherits
TextBox
'''
<summary>
''' Preprocesses
keyboard or input messages within the message loop before they are
dispatched.
'''
</summary>
'''
<param name="msg"></param>
'''
<returns></returns>
'''
<remarks></remarks>
Public
Overrides
Function PreProcessMessage(ByRef
msg As Message)
As Boolean
If msg.Msg
= &H102 Then
If
msg.WParam.Equals(New IntPtr(&H2E)) _
AndAlso
msg.LParam.Equals(New IntPtr(&H530001))
Then
' Point
in the numeric keypad pressed
If
System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator
= "," Then
'
And the decimal separator is a ',' for the current settings
'
Change the message to enter a comma instead of a poin
msg.WParam =
New IntPtr(&H2C)
msg.LParam =
New IntPtr(&H330001)
End
If
End
If
End
If
' Do the default
actions
Return
MyBase.PreProcessMessage(msg)
End
Function
End
Class
'''
<summary>
''' DataGridView to
support numeric input.
'''
</summary>
'''
<remarks></remarks>
Class
NumericDataGridView
Inherits
DataGridView
'''
<summary>
''' Preprocesses
keyboard or input messages within the message loop before they are
dispatched.
'''
</summary>
'''
<param name="msg"></param>
'''
<returns></returns>
'''
<remarks>
''' Required for the
first key stroke in a cell. The others are handled by the
NumericTextBox::PreProcessMessage().
'''
</remarks>
Public
Overrides
Function PreProcessMessage(ByRef
msg As Message)
As Boolean
If msg.Msg
= &H102 Then
If
msg.WParam.Equals(New IntPtr(&H2E)) _
AndAlso
msg.LParam.Equals(New IntPtr(&H530001))
Then
' Point
in the numeric keypad pressed
If
System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator
= "," Then
'
And the decimal separator is a ',' for the current settings
'
Change the message to enter a comma instead of a poin
msg.WParam =
New IntPtr(&H2C)
msg.LParam =
New IntPtr(&H330001)
Return False
End
If
End
If
End
If
' Do the default
actions
Return
MyBase.PreProcessMessage(msg)
End
Function
End
Class
'''
<summary>
''' DataGridView column
to support numeric input.
'''
</summary>
'''
<remarks></remarks>
Class
NumericDataGridViewTextBoxColumn
Inherits
DataGridViewColumn
Sub
New()
MyBase.New(New
NumericDataGridViewTextBoxCell)
End
Sub
End
Class
'''
<summary>
''' DataGridView cell to
support numeric input.
'''
</summary>
'''
<remarks></remarks>
Class
NumericDataGridViewTextBoxCell
Inherits
DataGridViewTextBoxCell
'''
<summary>
''' Gets the type of
the cell's hosted editing control.
'''
</summary>
'''
<value></value>
'''
<returns></returns>
'''
<remarks></remarks>
Public
Overrides
ReadOnly Property EditType()
As System.Type
Get
Return
GetType(NumericCellTextBox)
End
Get
End
Property
'''
<summary>
''' Gets or sets the
data type of the values in the cell.
'''
</summary>
'''
<value></value>
'''
<returns></returns>
'''
<remarks></remarks>
Public
Overrides
ReadOnly Property ValueType()
As System.Type
Get
Return
GetType(String)
End
Get
End
Property
End
Class
'''
<summary>
''' DataGridView TextBox
Cell to support numeric input.
'''
</summary>
'''
<remarks></remarks>
Class
NumericCellTextBox
Inherits
NumericTextBox
Implements
IDataGridViewEditingControl
' Private fields to
store state
Private
_dataGridView As DataGridView
Private
_rowIndex As
Integer
Private _valueChanged
As Boolean
'''
<summary>
''' Changes the
control's user interface (UI) to be consistent with the specified cell
style.
'''
</summary>
'''
<param name="dataGridViewCellStyle"></param>
'''
<remarks></remarks>
Public
Sub ApplyCellStyleToEditingControl(ByVal
dataGridViewCellStyle As
DataGridViewCellStyle) _
Implements
IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.BackColor
= dataGridViewCellStyle.BackColor
Me.Font =
dataGridViewCellStyle.Font
Me.ForeColor
= dataGridViewCellStyle.ForeColor
End
Sub
'''
<summary>
''' Gets or sets the
DataGridView that contains the combo box control.
'''
</summary>
'''
<value></value>
'''
<returns></returns>
'''
<remarks></remarks>
Public
Property EditingControlDataGridView()
As DataGridView _
Implements
IDataGridViewEditingControl.EditingControlDataGridView
Get
Return
_dataGridView
End
Get
Set(ByVal
value As DataGridView)
_dataGridView = value
End
Set
End
Property
'''
<summary>
''' Gets or sets the
formatted representation of the current value of the text box control.
'''
</summary>
'''
<value></value>
'''
<returns></returns>
'''
<remarks></remarks>
Public
Property EditingControlFormattedValue()
As Object
_
Implements
IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return
Text
End
Get
Set(ByVal
value As Object)
Text = value.ToString()
End
Set
End
Property
'''
<summary>
''' Gets or sets the
index of the owning cell's parent row.
'''
</summary>
'''
<value></value>
'''
<returns></returns>
'''
<remarks></remarks>
Public
Property EditingControlRowIndex()
As Integer
_
Implements
IDataGridViewEditingControl.EditingControlRowIndex
Get
Return
_rowIndex
End
Get
Set(ByVal
value As
Integer)
_rowIndex = value
End
Set
End
Property
'''
<summary>
''' Gets or sets a
value indicating whether the value of the editing control differs from the
value of the hosting cell.
'''
</summary>
'''
<value></value>
'''
<returns></returns>
'''
<remarks></remarks>
Public
Property EditingControlValueChanged()
As Boolean
_
Implements
IDataGridViewEditingControl.EditingControlValueChanged
Get
Return
_valueChanged
End
Get
Set(ByVal
value As
Boolean)
_valueChanged = value
End
Set
End
Property
'''
<summary>
''' Determines
whether the specified key is a regular input key that the editing control
should process or a special key that the DataGridView should process.
'''
</summary>
'''
<param name="keyData"></param>
'''
<param name="dataGridViewWantsInputKey"></param>
'''
<returns></returns>
'''
<remarks></remarks>
Public
Function EditingControlWantsInputKey(ByVal
keyData As Keys,
ByVal dataGridViewWantsInputKey
As Boolean)
As Boolean
_
Implements
IDataGridViewEditingControl.EditingControlWantsInputKey
Return
True
End
Function
'''
<summary>
''' Gets the cursor
used during editing.
'''
</summary>
'''
<value></value>
'''
<returns></returns>
'''
<remarks></remarks>
Public
ReadOnly
Property EditingPanelCursor() As
Cursor _
Implements
IDataGridViewEditingControl.EditingPanelCursor
Get
Return
MyBase.Cursor
End
Get
End
Property
'''
<summary>
''' Retrieves the
formatted value of the cell.
'''
</summary>
'''
<param name="context"></param>
'''
<returns></returns>
'''
<remarks></remarks>
Public
Function GetEditingControlFormattedValue(ByVal
context As DataGridViewDataErrorContexts)
As Object
_
Implements
IDataGridViewEditingControl.GetEditingControlFormattedValue
Return
Text
End
Function
'''
<summary>
''' Prepares the
currently selected cell for editing.
'''
</summary>
'''
<param name="selectAll"></param>
'''
<remarks></remarks>
Public
Sub PrepareEditingControlForEdit(ByVal
selectAll As
Boolean) _
Implements
IDataGridViewEditingControl.PrepareEditingControlForEdit
End
Sub
'''
<summary>
''' Gets a value
indicating whether the cell contents need to be repositioned whenever the
value changes.
'''
</summary>
'''
<value></value>
'''
<returns></returns>
'''
<remarks></remarks>
Public
ReadOnly
Property RepositionEditingControlOnValueChange()
As Boolean
_
Implements
IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return
False
End
Get
End
Property
'''
<summary>
''' Raises the
TextChanged event and notify that the cell value has changed.
'''
</summary>
'''
<param name="e"></param>
'''
<remarks></remarks>
Protected
Overrides Sub
OnTextChanged(ByVal e
As System.EventArgs)
MyBase.OnTextChanged(e)
_valueChanged =
True
_dataGridView.NotifyCurrentCellDirty(True)
End
Sub
End
Class