C#.NET Winform keypress event can't be cancelled - c#

I have a (.NET 3.5) winform with a datagridview on which I added an event on checkboxes in the gridview like this. That post doesn't take into account that people can also use spacebar to toggle the checkbox, and because there is no CellKeyUp event like there is a CellMouseUp event, I enabled KeyPreview on the form and added this code to prevent toggling with the spacebar:
private void BulkOrderAddressDifferencesForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
e.Handled = true;
e.SuppressKeyPress = true;
}
}
That works mostly, but there is a scenario in which the event is still handled, even though the debugger shows e.Handled is set to true.
If I click on a checkbox, then 1, then 2, I can toggle the checkbox with the space bar again. I have no idea why this happens, nor do I know how to fix it.

You can override Form's ProcessCmdKey method:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Space && checkBox1.Focused)
{
//instead of checkBox1.Focused condition, you check if your DataGridView contains focus and active cell is of checkBox type
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}

If the goal is to always react immediately when the check is changed, rather than preventing the use of the spacebar (Unless I'm mistaken, the problem is that the cellmouseup approach doesn't include (un)checking with space, rather than the goal is that space shouldn't be used at all? ), you could use the celldirtychanged approach instead of cellmouseup to catch both
//grid.CurrentCellDirtyStateChanged += grid_CurrentCellDirtyStateChanged;
void grid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (grid.IsCurrentCellDirty)
{
var cell = grid.CurrentCell;
if (cell is DataGridViewCheckBoxCell)
{
grid.EndEdit();
//you could catch the cellvaluechanged event (or a bound listchanged event), or handle the change immediately here, e.g.:
//Console.WriteLine("{0} value changed to {1}", cell.OwningColumn.HeaderText, cell.Value);
}
}
}

How about the DataGridView.EditMode Property which
Gets or sets a value indicating how to begin editing a cell.
where
The default is EditOnKeystrokeOrF2.
and
All DataGridViewEditMode values except for EditProgrammatically allow a user to double-click a cell to begin editing it.
You have several options to choose from the DataGridViewEditMode Enumeration
EditOnEnter - Editing begins when the cell receives focus. This mode is useful when pressing the TAB key to enter values across a row, or when pressing the ENTER key to enter values down a column.
EditOnF2 - Editing begins when F2 is pressed while the cell has focus. This mode places the selection point at the end of the cell contents.
EditOnKeystroke - Editing begins when any alphanumeric key is pressed while the cell has focus.
EditOnKeystrokeOrF2 - Editing begins when any alphanumeric key or F2 is pressed while the cell has focus.
EditProgrammatically - Editing begins only when the BeginEdit method is called.
Update for DataGridViewCheckBoxCell:
It turns out that the DataGridViewEditMode does not work for the DataGridViewCheckBoxColumn.
In this case you can create your own DataGridViewCheckBoxColumn & DataGridViewCheckBoxCell. This allows you to override the cell's OnKeyUp event handler and reset the EditingCellFormattedValue if Space was pressed.
public class MyCheckBoxColumn : DataGridViewCheckBoxColumn
{
public MyCheckBoxColumn()
{
CellTemplate = new MyCheckBoxCell();
}
}
public class MyCheckBoxCell : DataGridViewCheckBoxCell
{
protected override void OnKeyUp(KeyEventArgs e, int rowIndex)
{
if (e.KeyCode == Keys.Space)
{
e.Handled = true;
if (EditingCellValueChanged)
{
// Reset the value.
EditingCellFormattedValue = !(bool)EditingCellFormattedValue;
}
}
else
{
base.OnKeyUp(e, rowIndex);
}
}
}
After you rebuild your project the new column should appear in the designer:

Related

Button Click not firing When focus is set to next cell in DataGridView on CellEndEdit

On Windows forms, I have a gridview with 3 columns and few buttons to handle data processing. The Grid is editable and I am using the below code to move focus to the next cell of the current row whenever a user presses the "Enter" key in editing mode.
private void dataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex != dataGridView.Columns.Count - 1)
{
this.BeginInvoke(new MethodInvoker(() =>
{
dataGridView.CurrentCell = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex + 1];
}));
}
}
The code works fine and is moving the focus to next cell as required. However, the problem arises when I click any of the button while grid's cell is in focus. Since the focus is on a certain cell, clicking the button fires the CellEndEdit event before the Click event of the button and as a result of my code, the focus moves to next cell and the button click is not fired at all. I want to ensure that the code written to move to next cell in CellEndEdit function is not fired when i click a button.
Edit a cell in GridView, press Enter, focus moves to next cell - Correct
Edit a call in GridView, click on any button, focus moves to next cell, button click event not fired - Problem
I have searched a lot on SO and Internet regarding this issue but couldn't find a permanent solution. Any help will be greatly appreciated.
You can use instead the KeyDown event.
It could be like this:
private void dataGridView_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Enter)
{
// the rest of your code
e.Handled = true;
}
}
Override ProcessCmdKey of the form, check if CurrentCell is in edit mode and Enter was clicked:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Enter && this.dataGridView.IsCurrentCellInEditMode)
{
if (this.dataGridView.CurrentCell.ColumnIndex != this.dataGridView.Columns.Count - 1)
{
//this.BeginInvoke(new MethodInvoker(() =>
//{
this.dataGridView.CurrentCell = this.dataGridView.CurrentRow.Cells[this.dataGridView.CurrentCell.ColumnIndex + 1];
//}));
}
return true;
}
else return base.ProcessCmdKey(ref msg, keyData);
}

DataGridView how to detect when user comesout from editing control by pressing escape?

In DataGridView I have CellValueChanged event, when user modify any cell value, this event is fired. When user modify one cell, value 1 is changed to 2, then user click the next cell and press Escape, value in first cell is changed from 2 to 1, CellValueChanged event isn't fired. I keep values in temporary lists of object, and I have update values in these lists too. Which event is fired when user press escape and comes out from editing control mode ?
Thanks
React to the CellEndEdit event.
There is also this place:
// Implements the IDataGridViewEditingControl.GetEditingControlFormattedValue method.
public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
{
if (context.ToString() == "Parsing, Commit")
{
// Do something here
}
return EditingControlFormattedValue;
}
If you set a break-point in the CellEndEdit event when the Escape key is pressed, one of the calls made is to the ProcessDataGridViewKey(...) method.
public class DataGridView2 : DataGridView {
private bool escapeKeyPressed = false;
protected override bool ProcessDataGridViewKey(KeyEventArgs e) {
escapeKeyPressed = (e.KeyData == Keys.Escape);
return base.ProcessDataGridViewKey(e);
}
protected override void OnCellEndEdit(DataGridViewCellEventArgs e) {
base.OnCellEndEdit(e);
if (!escapeKeyPressed) {
// process new value
}
escapeKeyPressed = false;
}
}
Note: Originally I tried using the the IsCurrentRowDirty property, but it's not consistent. Sometimes it says false, but actually the cell value was committed using the Enter key.
dgv.CellEndEdit += (o, e) => {
if (!dgv.IsCurrentRowDirty) { // not reliable
}
};

C# DataGridView : override keydown events

i am working with DataGridView trying to provide specific utility to my user...
what i want to do is when some key is presses instead of the normal function that the key was supposed to perform like updown arrows and page up down keys etc i want to stop the default action
like when on a selected row, datagrid in selectfullrow, when down arrow is press it shouldn't change the row selection or goto the next row
You should handle the KeyDown event and set the e.Handled to true to disable the default action:
private void dataGridView1_KeyDown(object sender, KeyEventArgs e) {
e.Handled = e.KeyCode == Keys.Down;
}

Preventing double events in DataGridView

I have a problem that I am not sure how to solve. I have a DataGridView (EditMode = EditProgrammatically). This grid has one checkbox column and one or more textbox columns. The functionality is as following (or should be at least):
When you click on a checkbox, the checkbox should be toggled
When a row (or many rows) are selected, and you press space, the checkboxes should be toggled.
I have these two event handlers:
private void grid_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && e.ColumnIndex == useColumn.Index)
{
if (ModifierKeys != Keys.Shift && ModifierKeys != Keys.Control)
{
ToggleRows(grid.SelectedRows);
}
}
}
private void RowSelectorForm_KeyDown(object sender, KeyEventArgs e)
{
if (grid.Focused && e.KeyCode == Keys.Space)
{
ToggleRows(grid.SelectedRows);
e.Handled = true; // Not sure if this is needed or even does anything
e.SuppressKeyPress = true; // Or this for that matter...
}
}
This almost works. The problem is when you press space and a checkbox cell is active. When a textbox cell is active, it works like it should. The problem is that when you press space and a checkbox cell is active, both events gets fired. Which means it first selects and then deselects (or the reverse). So the checkboxes end up being like they was. How can I prevent this?
I have thought about using a flag, but not sure where I can put it, since I can't really know if it was a double event or if it was just the user using space and then clicking with the mouse. So that can't really be used I think. Is there a different event I should use? Is there a way to see if the cell was clicked by mouse or by space? Is there a way to disable the automatic checkbox toggling when space is pressed? What can I do?
Note: Reason for RowSelectorForm_KeyDown and not just grid_KeyDown was that I was trying to use KeyPreview and then suppress the keypress if it was space and the grid was focused. But that SuppressKeyPress doesn't really seem to do anything at all =/ Maybe I've just misunderstood it...
Well, I didn't want to do it, but I have now fixed it with a timer... but if anyone knows how to do it properly, please let me know!!
Current solution:
private DateTime lastClick = DateTime.MinValue;
and in both events:
if (DateTime.Now - lastClick > TimeSpan.FromMilliseconds(400))
{
lastClick = DateTime.Now;
ToggleRows(grid.SelectedRows);
}
It has been a time since I worked with C#, but I assume that you can disconnect the grid_CellClick event handler before calling ToggleGrid in RowSelectorForm_KeyDown.
After the call, you can reconnect the event handler.
Also, there might be some way to supress the event from being fired in the first place. In some API's certain methods are specially provided that don't trigger any events.
The frustrating problems are:
.Handled doesn't prevent the check box from changing
.SuppressKeyPress doesn't prevent the checkbox from changing
If you DoubleClick on the checkbox, the first click fires the Click() event (toggling the checkbox) and the second click fires the DoubleClick() event (toggling the checkbox yet again).
However, the KeyDown event fires before any of the grid events. Perhaps setting a flag to indicate the spacebar was pressed or if the row was already selected, reset the value of the checkbox.
I used the following and it seemed to work well:
private Keys _ClickSource = 0;
private void dgv_CellClick(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
{
if (_ClickSource == 0 || _ClickSource != Keys.Space)
{
dgv.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = ! (System.Convert.ToBoolean(dgv.Rows[e.RowIndex].Cells[e.ColumnIndex].Value));
}
_ClickSource = null;
}
private void dgv_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
_ClickSource = e.KeyCode;
}

how to control datagridview cursor movement in C#

I'd like my datagridview cursor to move right to the next column instead of moving to the next row after entering data to a cell.
I've attempted to take control of the cursor by capturing the keys via dataGridView1_KeyDown event but this does not prevent the cursor from moving to the next row after entering data to a cell ...
Thanks in advance for your assistance.
Cheers,
Here are an answer from Mark Rideout (DatagridView Program Manager)
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=157055&SiteID=1
Scroll down 4 posts (and then more because they make better versions later on in the thread).
For future references if link will expire (All credits to Mark Rideout):
Create a new class named dvg that inherits from DataGridView. Compile project and then use this extended Datagridview-control instead of the normal and you'll have a datagridview that selects the next cell when pressing enter:
public class dgv : DataGridView
{
protected override bool ProcessDialogKey(Keys keyData)
{
Keys key = (keyData & Keys.KeyCode);
if (key == Keys.Enter)
{
return this.ProcessRightKey(keyData);
}
return base.ProcessDialogKey(keyData);
}
protected override bool ProcessDataGridViewKey(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
return this.ProcessRightKey(e.KeyData);
}
return base.ProcessDataGridViewKey(e);
}
}

Categories