6

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

我知道CurrentCellChanged(调用事件时行已经改变)和RowLeave(不可能取消离开操作)事件,但都没有提供我需要的东西。我也尝试使用该RowValidating事件,但是当该行将被验证(无意离开它)时也会调用该事件,例如当我调用 时<ParentForm>.Validate(),这会导致很多混乱。

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

4

4 回答 4

2

刚刚遇到类似的问题,经过多次尝试,我唯一的解决方法是使用“进入和离开”来了解表单何时为 NotActive 以避免验证 - 幸运的是,触发顺序在 row\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;
    }
于 2014-03-26T19:40:17.390 回答
1

我认为你最好的选择是使用带有布尔条件的 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;
     }
}

我认为像上面这样的东西是你最好的选择。

于 2013-07-24T10:08:07.617 回答
1

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

于 2013-08-01T08:33:18.043 回答
0

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

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

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

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

您需要过程“DGV_CellBeginEdit()”来处理 datagridview 的“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
于 2020-12-29T09:20:37.720 回答