Andre 的回答有一个小错误,因为获得的坐标没有考虑 DataGrid 中的行和列标题。至少当我在 Visual Basic 中实现该解决方案时就是这种情况。
您还可以修改显示的示例以考虑大型 DataGrid。在我看来,那里的限制是基于滚动视图,所以我展示了这个更正的两个实现:
Private Sub myGrid_MouseMove(sender As Object, e As MouseEventArgs) Handles myGrid.MouseMove
Dim total As Double
Dim myScrollViewer As ScrollViewer = FindVisualChild(Of ScrollViewer)(myGrid)
Dim cursorPositionX = e.GetPosition(myGrid).X
Dim columnIndex As Integer = -1
total = 0
'Horizontal offset'
Dim rowHeaders As DataGridRowHeader = FindVisualChild(Of DataGridRowHeader)(myGrid)
cursorPositionX -= (rowHeaders.ActualWidth - myScrollViewer.HorizontalOffset)
For Each column As DataGridColumn In myGrid.Columns
If cursorPositionX < total Then Exit For
columnIndex += 1
total += column.Width.DisplayValue
Next
Dim cursorPositionY = e.GetPosition(myGrid).Y
Dim rowIndex As Integer = -1
total = 0
'Vertical offset'
Dim originalOffset As Double = myScrollViewer.VerticalOffset
Dim colHeadersPresenter As DataGridColumnHeadersPresenter = FindVisualChild(Of DataGridColumnHeadersPresenter)(myGrid)
cursorPositionY -= colHeadersPresenter.ActualHeight
For Each row As System.Data.DataRowView In myGrid.Items
If cursorPositionY < total Then Exit For
rowIndex += 1
Dim dgRow As DataGridRow = GetRowByIndex(myGrid, rowIndex)
total += dgRow.ActualHeight
'GetRowByIndex will scroll the view to bring the DataGridRow of interest into view, which throws off the counter. This adjusts for that'
myGrid.UpdateLayout()
If Not myScrollViewer.VerticalOffset = originalOffset Then myGrid.ScrollIntoView(myGrid.Items(CInt(myScrollViewer.ViewportHeight + originalOffset - 1)))
myGrid.UpdateLayout()
If myScrollViewer.VerticalOffset > rowIndex Then cursorPositionY += dgRow.ActualHeight
Next
End Sub
请注意,ScrollViewer.HorizontalOffset 属性返回设备独立像素中的值,因此我只是在遍历列之前偏移我的位置一次。
请注意,如果 CanContentScroll = True,则ScrollViewer.VerticalOffset 属性返回项目数。因此,在我的示例中,在每个循环中,我将计数器偏移一个项目(DataGridRow)的高度。如果 CanContentScroll = False,则可以按照列索引循环的情况进行处理。
我在 Visual Basic 中的 .Net 4.0 的 DataGrid 中找不到行定义,但以下支持函数有助于获取 DataGridRow:
Function GetRowByIndex(ByVal p_dataGrid As DataGrid,
ByVal p_index As Integer) As DataGridRow
Dim row As DataGridRow
row = CType(p_dataGrid.ItemContainerGenerator.ContainerFromIndex(p_index), DataGridRow)
If IsNothing(row) Then
'May be virtualized, bring into view and try again.'
p_dataGrid.UpdateLayout()
p_dataGrid.ScrollIntoView(p_dataGrid.Items(p_index))
row = CType(p_dataGrid.ItemContainerGenerator.ContainerFromIndex(p_index), DataGridRow)
End If
Return row
End Function
Visual Basic 中的 FindVisualChild 函数:
Function FindVisualChild(Of childItem As DependencyObject)(ByVal p_obj As DependencyObject) As childItem
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(p_obj) - 1
Dim child As DependencyObject = VisualTreeHelper.GetChild(p_obj, i)
If child IsNot Nothing AndAlso TypeOf child Is childItem Then
Return CType(child, childItem)
Else
Dim childOfChild As childItem = FindVisualChild(Of childItem)(child)
If childOfChild IsNot Nothing Then
Return childOfChild
End If
End If
Next i
Return Nothing
End Function