在Visual Studio 2010中开发的Windows窗体项目中,我有一个绑定到BindingSource的DataGridView,该BindingSource的DataSource是BindingList(Of T)。T是来自实体框架5模型的实体。
我的实体实现INotifyPropertyChanged和IDataErrorInfo。
我的用户是Excel主管,并坚持要求我提供将Excel数据粘贴到应用程序中使用的网格中的功能。
因此,我提出了几个简单的规则。
- 尽可能地模仿复制&Excel中的粘贴行为
- 将数据粘贴到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。