将Excel剪贴板文本粘贴到数据绑定DataGridView和实体框架验证



在Visual Studio 2010中开发的Windows窗体项目中,我有一个绑定到BindingSource的DataGridView,该BindingSource的DataSource是BindingList(Of T)。T是来自实体框架5模型的实体。

我的实体实现INotifyPropertyChanged和IDataErrorInfo。

我的用户是Excel主管,并坚持要求我提供将Excel数据粘贴到应用程序中使用的网格中的功能。

因此,我提出了几个简单的规则。

  1. 尽可能地模仿复制&Excel中的粘贴行为
  2. 将数据粘贴到DataGridView中的新行中应创建并验证网格中表示的类型的新实体

我在这方面已经走了很长的路,但现在遇到了一些我无法理解的事情。

根据我能找到的信息判断,很明显,当粘贴到绑定网格中时,底层数据源应该是编辑和创建的目标。

还是应该这样?

两种方法我都试过了。

在针对单元格本身时,我希望我可以编写例程,以便在需要时触发DataGridView中内置的验证事件,无论我是编辑现有行还是创建新行。

我很快发现它没有像预期的那样工作,因为CellValidating直到细胞失去焦点才启动。

粘贴时,我希望在将值粘贴到单元格中时对其进行验证——如果粘贴操作失败,则取消其余的粘贴操作。

当以基础数据源为目标(行的DataBoundItem转换为适当的实体类型)时,我可以从剪贴板数据创建新的实体,并在将更改提交到DbContext之前对其进行验证。

在任何一种情况下,当验证失败时,DataGridView似乎已经丢失了该单元格的前一个值。

如果验证失败,将提示用户并退出例程。我希望用户能够按Esc键返回单元格的上一个值,但单元格仍然为空。

有人知道为什么用程序编辑单元格的值时,以前的值不再可用吗?

以下是我目前正在做的事情。我正在通过调用窗体的.Valide方法来强制激发验证事件。我不知道我是否应该那样做。这是不完整的,因为我还没有处理新行:

    Private Sub PasteFromClipboard(ByVal sender As Object, ByVal e As KeyEventArgs)
    Dim dgv = TryCast(sender, DataGridView)
    If Not IsNothing(dgv) Then
        If dgv.SelectedCells.Count > 0 Then
            Dim rowSplitter = {ControlChars.Cr, ControlChars.Lf}
            Dim columnSplitter = {ControlChars.Tab}
            Dim topLeftCell = CopyPasteFunctions.GetTopLeftSelectedCell(dgv.SelectedCells)
            If Not IsNothing(topLeftCell) Then
                Dim data = Clipboard.GetData(DataFormats.Text)
                If Not IsNothing(data) Then
                    Dim columnIndex = topLeftCell.ColumnIndex
                    Dim rowIndex = topLeftCell.RowIndex
                    Dim columnCount = dgv.Columns.Count
                    Dim rowCount = dgv.Rows.Count
                    'Split clipboard data into rows
                    Dim rows = data.ToString.Split(rowSplitter, StringSplitOptions.RemoveEmptyEntries)
                    For i = 0 To rows.Length - 1
                        'Split row into cell values
                        Dim values = rows(i).Split(columnSplitter)
                        For j = 0 To values.Length - 1
                            If (j <= (columnCount - 1)) AndAlso (i <= (rowCount - 1)) Then
                                Dim cell = dgv.Rows(rowIndex + i).Cells(columnIndex + j)
                                dgv.CurrentCell = cell
                                dgv.BeginEdit(False)
                                cell.Value = values(j)
                                If Not Me.Validate() Then
                                    dgv.CancelEdit()
                                    Exit Sub
                                Else
                                    dgv.EndEdit()
                                End If
                            Else
                                Debug.Print(String.Format("RowIndex: {0}, ColumnIndex: {1}", i, j))
                            End If
                        Next
                    Next
                End If
            End If
        End If
    End If
End Sub
Public Module CopyPasteFunctions
Public Function GetTopLeftSelectedCell(ByVal cells As DataGridViewSelectedCellCollection) As DataGridViewCell
    If Not IsNothing(cells) AndAlso cells.Count > 0 Then
        Dim cellList = (From c In cells.Cast(Of DataGridViewCell)()
                        Order By c.RowIndex, c.ColumnIndex
                        Select c).ToList
        Return cellList(0)
    End If
    Return Nothing
End Function
End Module

谢谢你在这方面的帮助。希望其他人正在与EF5和Winforms合作。如果没有,我只能靠自己了!

当网格包含一列时,这将完成任务。

假设开发人员正确处理了CellValidating事件,这意味着如果验证失败,该事件将被取消。

此例程与Excel中观察到的复制粘贴行为非常相似。

    Private Sub PasteFromClipboard(ByVal sender As Object, ByVal e As KeyEventArgs)
    Dim dgv = TryCast(sender, DataGridView)
    If Not IsNothing(dgv) AndAlso Clipboard.ContainsText Then
        If dgv.SelectedCells.Count > 0 Then
            Dim rowSplitter = {ControlChars.NewLine}
            Dim columnSplitter = {ControlChars.Tab}
            Dim topLeftCell = CopyPasteFunctions.GetTopLeftSelectedCell(dgv.SelectedCells)
            If Not IsNothing(topLeftCell) Then
                Dim clipBoardText = Clipboard.GetText(TextDataFormat.Text)
                Dim columnIndex = topLeftCell.ColumnIndex
                Dim rowIndex = topLeftCell.RowIndex
                Dim columnCount = dgv.Columns.Count
                Dim rows = clipBoardText.Split(rowSplitter, StringSplitOptions.None)
                For i = 0 To rows.Length - 2
                    'Split row into cell values
                    Dim values = rows(i).Split(columnSplitter)
                    Dim rowCount = dgv.Rows.Count
                    For j = 0 To values.Length - 1
                        If (i <= (rowCount - 1)) AndAlso ((j + 1) <= columnCount) Then
                            Dim cell = dgv.Rows(rowIndex + i).Cells(columnIndex + j)
                            dgv.CurrentCell = cell
                            dgv.BeginEdit(False)
                            dgv.EditingControl.Text = values(j)
                            If Not Me.Validate() Then
                                Exit Sub
                            Else
                                dgv.EndEdit()
                            End If
                        End If
                    Next
                Next
            End If
        End If
    End If
End Sub
Public Module CopyPasteFunctions
Public Function GetTopLeftSelectedCell(ByVal cells As DataGridViewSelectedCellCollection) As DataGridViewCell
    If Not IsNothing(cells) AndAlso cells.Count > 0 Then
        Dim cellList = (From c In cells.Cast(Of DataGridViewCell)()
                        Order By c.RowIndex, c.ColumnIndex
                        Select c).ToList
        Return cellList(0)
    End If
    Return Nothing
End Function
End Module

我会解析数据,用适合实体模型的类型的新对象更新实体框架对象。然后只需保存Entity对象并重新绑定DGV。

最新更新