I want to create a table in C#. The table contains two column headers. One column header is the products and the other column header is the price of the products. I want to enter the data by clicking on a cell in the table. I want to enter only numbers in the cells of the products price column. Does anyone know how to allow only numbers in the cell below the product price?
To start, I made one global TextBox
variable called CurrentCellBeingEdited
. This variable will be set to the currently edited cell. This is used for convenience and is not really necessary, however for this example, it makes things a little clearer.
TextBox CurrentCellBeingEdited = null;
We will assign this variable to the cell the user typed into IF the cell is one of the numeric valued cells. Then we will subscribe (wire-up) the TextBox
’s KeyPress
event to the proper KeyPress
event. This is all done in the grids EditingControlShowing
event and may look something like…
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) {
if (CurrentCellBeingEdited != null) {
CurrentCellBeingEdited.KeyPress -= new KeyPressEventHandler(DecimalNumbersOnlyCell_KeyPress);
CurrentCellBeingEdited.KeyPress -= new KeyPressEventHandler(NumbersOnlyCell_KeyPress);
}
string targetCellColName = dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name;
if (targetCellColName == "Cost" || targetCellColName == "Qty") {
CurrentCellBeingEdited = (TextBox)e.Control;
if (targetCellColName == "Cost") {
CurrentCellBeingEdited.KeyPress = new KeyPressEventHandler(DecimalNumbersOnlyCell_KeyPress);
}
else {
// Qty cell
CurrentCellBeingEdited.KeyPress = new KeyPressEventHandler(NumbersOnlyCell_KeyPress);
}
}
}
This event simply wires up the TextBox
cell to the proper key press event if needed. The first if statement…
if (CurrentCellBeingEdited != null) {
CurrentCellBeingEdited.KeyPress -= new KeyPressEventHandler(DecimalNumbersOnlyCell_KeyPress);
CurrentCellBeingEdited.KeyPress -= new KeyPressEventHandler(NumbersOnlyCell_KeyPress);
}
is used to unsubscribe (un-wire) any previously subscribed to event. This prevents the event from firing in the wrong cells. Example; if the user selects the Description cell. And the same idea applies to the Quantity cell where we do not want the user to type a period for a decimal place. The main point is that if we do not “unsubscribe” the text box from the event, then it may get fired multiple times and possibly for the wrong cells.
After any previously enabled event is un-subscribed, the code simply checks which cell is being edited. If the edited cell is a Cost or Quantity column, then the code casts our global variable CurrentCellBeingEdited
to the edited cell, then subscribes to the appropriate event.
Next we will need the two KeyPress
events for the Cost and Quantity cells and they may look something like…
// Quantity cell
private void NumbersOnlyCell_KeyPress(object sender, KeyPressEventArgs e) {
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar)) {
e.Handled = true;
}
}
// Cost cell
private void DecimalNumbersOnlyCell_KeyPress(object sender, KeyPressEventArgs e) {
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && e.KeyChar != '.') {
e.Handled = true;
}
if (e.KeyChar == '.' && (sender as TextBox).Text.IndexOf('.') > -1) {
e.Handled = true;
}
}
And finally, the grids CellValidating
event. Note, the code allows cells to be “empty.” The code checks to see if the cell is a Cost or Quantity column, then applies a TryParse
to the appropriate cell to validate if the text in the cell is indeed a valid int
or decimal
value. If the TryParse
fails, then the cell is colored red and a message box is displayed indicating the invalid text. After the user clicks the OK button on the message box, the edit is canceled.
private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) {
if (CurrentCellBeingEdited != null) {
string targetCellColName = dataGridView1.Columns[e.ColumnIndex].Name;
if (targetCellColName == "Cost" || targetCellColName == "Qty") {
if (!string.IsNullOrEmpty(CurrentCellBeingEdited.Text)) { // <- Allow empty cells
bool valid = true;
if (targetCellColName == "Cost") {
if (!decimal.TryParse(CurrentCellBeingEdited.Text, out decimal value)) {
valid = false;
}
}
if (targetCellColName == "Qty") {
if (!int.TryParse(CurrentCellBeingEdited.Text, out int value2)) {
valid = false;
}
}
if (!valid) {
CurrentCellBeingEdited.BackColor = Color.LightCoral;
MessageBox.Show("Invalid input - value will revert to previous amount");
dataGridView1.CancelEdit();
CurrentCellBeingEdited.BackColor = Color.White;
}
}
}
}
}
Putting all this together and to complete the example, the code below uses the events above to demonstrate this functionality.
TextBox CurrentCellBeingEdited = null;
public Form1() {
InitializeComponent();
dataGridView1.CellValidating = new DataGridViewCellValidatingEventHandler(dataGridView1_CellValidating);
dataGridView1.EditingControlShowing = new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
}
private void Form1_Load(object sender, EventArgs e) {
DataTable GridTable = GetDT();
GetData(GridTable);
dataGridView1.DataSource = GridTable;
}
private DataTable GetDT() {
DataTable dt = new DataTable();
dt.Columns.Add("Description", typeof(string));
dt.Columns.Add("Cost", typeof(decimal));
dt.Columns.Add("Qty", typeof(int));
dt.Columns.Add("Total", typeof(decimal), "Cost * Qty");
return dt;
}
private void GetData(DataTable dt) {
dt.Rows.Add("Product1", 12.49, 2);
dt.Rows.Add("Product3", 2.33, 3);
dt.Rows.Add("Product16", 5.00, 12);
}
I hope this makes sense and helps.