阻止C#DataGridView更改当前行的事件

时间:2013-07-24 09:51:27

标签: c# winforms datagridview

我需要一个事件,当System.Windows.Forms.DataGridView的当前行将要更改时触发,并允许我取消此更改,例如通过将EventArgs的Cancel-property设置为true。

我知道CurrentCellChanged(调用事件时行已经更改)和RowLeave(不可能取消离开操作)事件,但都没有提供我需要的内容。我也尝试使用RowValidating事件,但是当该行要进行验证时(无意留下它)也会调用此事件,例如当我调用<ParentForm>.Validate()时,这会导致很多困惑。

是否有任何其他可能性或干净(呃)解决方案来获得所需的行为?

4 个答案:

答案 0 :(得分:1)

我认为你最好的选择是使用带有bool条件的RowValidating来检查你是否调用.Validate()。

修改

根据您的上次评论,为什么不添加dataGridView.IsCurrentRowDirty的支票? 例如:

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;
            }
    }
}

如果没有脏数据,无论谁调用验证,都不会生成dataCheck,也不会出现messageBox。

修改

您可以将“if”子句替换为您想要的任何检查,包括一个用于dataGridView2的子句。

如果您的要求非常复杂,也可以扩展dataGridView控件。

修改

我现在明白你的要求了。我不认为有一个快速和干净的解决方案。我会使用SelectionChanged事件并设置逻辑以防止更改。类似的东西:

//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;
     }
}

我认为上面的内容是你最好的镜头。

答案 1 :(得分:1)

在尝试了很多不同的事情之后,我找到了解决方案,最简单的(对我而言)最佳工作解决方案是检查哪个控件集中在RowValidating的{​​{1}}事件中。此解决方案完全解决了我遇到的问题:例如,通过单击其他按钮引发了DataGridView事件。仍有一些特殊情况会导致RowValidating事件出现,即使当前行没有改变(例如,通过单击列标题对RowValidating进行排序)但我认为我可以忍受这个小问题。也许.NET的未来版本将实现DataGridView,其DataGridView事件可以取消。

答案 2 :(得分:1)

刚刚遇到类似的问题,经过多次尝试,我唯一的工作就是使用&#34; Enter and Leave&#34;知道什么时候表单是NotActive以避免验证 - 幸运的是,点火顺序是在行\ col级别事件之前

HTH - 迈克

    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;
    }

答案 3 :(得分:0)

更改 datagridview 所选行不会自动清除任何文本框或任何其他表单控件,除非您将事件处理程序分配给 datagridview 的“.SelectionChanged”事件以清除数据。

诀窍是在清除表单数据或采取任何其他操作之前,您必须检查所选行索引的有效性。如果表单控件中的数据已被修改,并且您希望将修改后的数据保留在表单控件中,则必须防止调用清除表单数据的过程。

我有下面的完整代码。这是可靠的,稳定的,我想这是最简单的方法。 创建一个Form,然后创建一个名为“DGVobj”的datagridview 对象和一个名为“Button1”的按钮来测试代码。 “Button1”切换布尔值以允许或不允许更改所选行。

“TakeAction()”过程仅在函数“CheckIfDataHasChanged()”返回false时才执行。换言之,“TakeAction()”仅在表单数据未更改时执行。 如果表单数据发生更改,则执行过程“SelectThePreviousRow()”。此过程清除用户选择的行的选择,并再次选择前一行。有效行的索引存储在变量“PrvRowIdx”中。如果您不想允许用户更改行选择,则需要“PrvRowIdx”。

您需要过程“DGV_CellBeginEdit()”来处理数据网格视图的“CellBeginEdit”事件。如果数据发生更改,并且用户将编辑的单元格的行索引与用户已编辑数据的行的索引不同,您不希望用户编辑新行.

你看到我没有使用“.RowValidating”事件及其事件取消方法。因为“.RowValidating”事件在“.SelectionChanged”事件之前被触发,你将没有机会检查用户是否选择了新行。

布尔变量“bln_clearingSelection”、“bln_CancelingEdit”和“bln_RowSelectionIsChanging”用于防止对程序的多次调用和防止“StackOverFlow”异常。如果用户坚持通过不停地单击行和单元格来更改所选行,这可以防止触发“StackOverFlow”异常。

“DataGridViewTestForm”是一个表单对象。

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