Ответ Андре имеет небольшую ошибку, поскольку полученные координаты не учитывают заголовки строк и столбцов в 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.HorizontOffset возвращает значение в независимых от устройства пикселях, поэтому я просто смещаю свое местоположение один раз перед циклом прохождения по столбцам.
Обратите внимание, что свойство ScrollViewer.VerticalOffset возвращает количество элементов, если CanContentScroll = True. Поэтому в моем примере в каждом цикле я смещал счетчик на высоту одного элемента (DataGridRow). Если CanContentScroll = False, то это может быть обработано, как в случае цикла индекса столбца.
Не удалось найти определения строк в DataGrid для .Net 4.0 в Visual Basic, но следующая вспомогательная функция помогает получить 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
И функция FindVisualChild в Visual Basic:
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