I would need an event that fires when the current row of a System.Windows.Forms.DataGridView
is going to be changed and which allows me to cancel this change, eg by setting the Cancel-property of the EventArgs to true.
I know about the CurrentCellChanged
(the row has already changed when the event is called) and the RowLeave
(no possibility to cancel the leave-operation) events, but neither provide what I would need. I also tried to use the RowValidating
event, but this event is also called when the row is just going to be validated (without the intention to leave it), for example when I call <ParentForm>.Validate()
, which leads to many confusions.
Is there any other possibility or a clean(er) solution to get the desired behaviour?
Just Ran into a similar issue and after many attempts my only work around was to use "Enter and Leave" to know when the form was NotActive to avoid Validating - Luckily the firing order was before the row\\col level events
HTH - Mike
private bool IsActive = false;
private void dgbList_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
{
if (IsActive)
{
if (Do_I_NeedTo_Cancel)
e.Cancel = true;
}
}
private void dgList_Leave(object sender, EventArgs e)
{
IsActive = false;
}
private void dgList_Enter(object sender, EventArgs e)
{
IsActive = true;
}
I think your best option is to use RowValidating with a bool condition to check if you call .Validate().
EDIT
Per your last comment, why not add a check for dataGridView.IsCurrentRowDirty
? For example:
private void dataGridView1_RowValidating(object sender, DataGridViewCellCancelEventArgs e) {
if (dataGridView1.IsCurrentRowDirty) {
if (dataCheck())
if (MessageBox.Show("Ok?", "Save?", MessageBoxButtons.YesNoCancel) == DialogResult.Cancel) {
e.Cancel = true;
}
}
}
If there is no dirty data, no matter who calls the validation the dataCheck won't be made and the messageBox will not appear.
EDIT
You can replace the 'if' clauses with any check you want, including one for dataGridView2.
You can also extend the dataGridView control if you have very complicated requirements.
EDIT
I now understand your requirement. I don't think there is a quick and clean solution. I would use the SelectionChanged event and there set the logic to prevent the change. Something like:
//rember the selection of the index
private int _currentIndex;
private bool _rollingBackSelection;
private void SelectionChanged(...){
//when changing back to the selection in dgv1 prevent dgv2 check
if (_rollingBackSelection) {
_rollingBackSelection = false;
return;
}
if (dgv2IsDirty()) {
var result = MessageBox.Show("Ok?", "Save?", MessageBoxButtons.YesNoCancel);
if (result == DialogResult.Cancel) {
_rollingBackSelection = true;
//rollback to the previous index
dgv1.Rows[_currentIndex].Selected = true;
return;
}
if (result == DialogResult.Yes)
dgv2Save();
dgv2Load();
_currentIndex = dgv1.SelectedRows[0].Index;
}
}
I think something like above is your best shot.
After trying lots of different things I came to the solution that the easiest and (for me) best working solution would be to check which control is focused in the RowValidating
event of the DataGridView
. This solution addresses exactly the problem I had: the RowValidating
event was thrown by clicking other buttons for example. There are still some special cases which cause the RowValidating
event to arise even if the current row isn't changing (sorting the DataGridView
by clicking a column header, for example) but I think I can live with this minor issues. Maybe a future version of .NET will implement a DataGridView
with a RowLeaving
event which can be cancelled.
Changing the datagridview selected row will not clear any textbox or any other form controls automatically unless you assign an event handler to datagridview's ".SelectionChanged" event that clears data.
The trick is that you have to check the validity of the selected row index before clearing form data or taking any other action. If the data has been modified in a form control and you want to keep the modified data in form controls, you have to prevent calling the procedure that clears the form data.
I have the complete code below. This is reliable, stable, and I guess the easiest way to do it. Create a Form, and then create a datagridview object named "DGVobj" and a button named "Button1" to test the code. "Button1" toggles the boolean value to allow or not allow the selected row to be changed.
"TakeAction()" procedure is only and only executed if function "CheckIfDataHasChanged()" returns false. In other words, "TakeAction()" is executed only if the form data are not changed. If form data are changed, the procedure "SelectThePreviousRow()" is executed. This procedure clears the selection of the rows selected by the user, and selects the previous row again. The index of the valid row is stored in the variable "PrvRowIdx". "PrvRowIdx" is needed in case you do not want to allow the user to change row selection.
You need the procedure "DGV_CellBeginEdit()" to handle the datagridview's "CellBeginEdit" event. In case the data are changed, and the row index of the cells which the user will edit is different frm the index of the row whose data are already edited by the user, you don't want to allow the user to edit a new row.
You see that I have not used ".RowValidating" event and its event canceling approach. Because ".RowValidating" event is fired before ".SelectionChanged" event, and you will have no opportunity to check whether the user has selected a new row or not.
The boolean variables "bln_clearingSelection", "bln_CancelingEdit", and "bln_RowSelectionIsChanging" are used to prevent multiple calls to the procedures and prevent "StackOverFlow" exception. This prevents "StackOverFlow" exception from being fired if the user insists on changing the selected row by clicking the rows and cells nonstop.
"DataGridViewTestForm" is a form object.
Public Class DataGridViewTestForm
Private WithEvents DGVobj3 As New System.Windows.Forms.DataGridView
Private dgvSelectedRow As System.Windows.Forms.DataGridViewRow
Dim PrvRowIdx As Integer = -1
Private bln_AllowRowChange As Boolean = False
Private bln_clearingSelection As Boolean = False, bln_CancelingEdit As Boolean = False, bln_RowSelectionIsChanging As Boolean = False
Public Sub New()
' This call is required by the designer.
InitializeComponent()
DGVobj.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect
DGVobj.MultiSelect = False
Button1.Text = "Not Allowed"
CreateNewDataTable()
End Sub
Private Sub CreateNewDataTable()
Dim objTable As New System.Data.DataTable
Dim col1 As New System.Data.DataColumn("Column1")
Dim col2 As New System.Data.DataColumn("Column2")
objTable.Columns.Add(col1)
objTable.Columns.Add(col2)
Dim rw1 As System.Data.DataRow = objTable.NewRow
Dim rw2 As System.Data.DataRow = objTable.NewRow
Dim rw3 As System.Data.DataRow = objTable.NewRow
objTable.Rows.Add(rw1)
objTable.Rows.Add(rw2)
objTable.Rows.Add(rw3)
DGVobj.DataSource = objTable
End Sub
Private Sub DGV_SelectionChanged(sender As DataGridView, e As EventArgs) Handles DGVobj.SelectionChanged
If (bln_clearingSelection Or bln_CancelingEdit Or bln_RowSelectionIsChanging) Then
Exit Sub
End If
If CheckIfDataHasChanged() Then
SelectThePreviousRow()
Else
TakeAction()
End If
End Sub
Private Sub TakeAction()
bln_RowSelectionIsChanging = True
Dim dgvSRows As DataGridViewSelectedRowCollection = DGVobj.SelectedRows
If dgvSRows IsNot Nothing Then
If dgvSRows.Count = 1 Then
dgvSelectedRow = dgvSRows.Item(0)
PrvRowIdx = dgvSelectedRow.Index
End If
End If
ClearFormControls()
bln_RowSelectionIsChanging = False
End Sub
Private Sub ClearFormControls()
End Sub
Private Function SelectThePreviousRow() As Boolean
bln_clearingSelection = True
Dim bln_Reverted As Boolean = False
Dim dgvRowCollection As DataGridViewSelectedRowCollection = DGVobj.SelectedRows
If dgvRowCollection IsNot Nothing Then
DGVobj.ClearSelection()
bln_Reverted = True
End If
If PrvRowIdx >= 0 Then
If DGVobj.Rows IsNot Nothing Then
DGVobj.Rows.Item(PrvRowIdx).Selected = True
End If
End If
bln_clearingSelection = False
Return bln_Reverted
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If bln_AllowRowChange Then
bln_AllowRowChange = False
Button1.Text = "Not Allowed"
Else
bln_AllowRowChange = True
Button1.Text = "Allowed"
End If
End Sub
Private Sub DGV_CellBeginEdit(sender As DataGridView, e As DataGridViewCellCancelEventArgs) Handles DGVobj.CellBeginEdit
Dim bln_CancelingEdit = True
Dim bln_EditWasCanceled As Boolean = False
Dim RowIdx As Integer = e.RowIndex
Dim dgvRowCollection As DataGridViewSelectedRowCollection = DGVobj.SelectedRows
If dgvRowCollection IsNot Nothing Then
Dim rwCnt As Integer = dgvRowCollection.Count
If rwCnt = 1 Then
If PrvRowIdx <> RowIdx Then
e.Cancel = True
bln_EditWasCanceled = True
End If
Else
e.Cancel = True
bln_EditWasCanceled = True
End If
Else
e.Cancel = True
bln_EditWasCanceled = True
End If
bln_CancelingEdit = False
End Sub
Private Function CheckIfDataHasChanged() As Boolean
If bln_AllowRowChange Then
Return False
Else
Return True
End If
End Function
End Class
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.