2009-08-11 6 views
8

Tengo un WPF Grid con algunas filas y columnas, p. Ej.En una cuadrícula de WPF, ¿cómo puedo encontrar la fila y la columna en la ubicación del mouse?

<Grid Name="myGrid" MouseMove="OnMouseMove"> 
    <Grid.RowDefinitions> 
     <RowDefinition/> 
     <RowDefinition/> 
     <RowDefinition/> 
    </Grid.RowDefinitions> 

    <Grid.ColumnDefinitions> 
     <ColumnDefinition/> 
     <ColumnDefinition/> 
     <ColumnDefinition/> 
    </Grid.ColumnDefinitions> 
</Grid> 

Con un controlador para MouseMove en el archivo .cs, por ejemplo,

private void OnMouseMove(object sender, MouseEventArgs e) 
{ 
    var position = e.GetPosition(myGrid); 

    // What row & col is the mouse over? 
} 

Quiero ser capaz de encontrar qué fila y columna en la cuadrícula se ha terminado el mouse, ¿es esto posible?

[Nota: esta es una versión simplificada del problema, por lo que parece un poco extraño para presentarlo de esta manera - que es parte de un arrastre & gota entre la funcionalidad rejillas]

+0

Mire la funcionalidad de arrastrar/soltar en FluidKit. No tendrás que preocuparte por hacky shnit como este. – Will

+0

Ya estoy usando FluidKit, que 'MouseMove' es realmente 'OnDropCompleted' y la posición del mouse es realmente 'dropPoint' - Quiero poder colocarme en celdas específicas de la tabla, que no he podido obtener trabajando aún – Wilka

Respuesta

15

espero que ya tiene hallazgo una solución. Me enfrenté al mismo problema y encontré esta publicación sin respuesta. Así que creo que la próxima será feliz de encontrar esta solución:

private void OnMouseMove(object sender, MouseEventArgs e) 
{ 
    var element = (UIElement)e.Source; 

    int c = Grid.GetColumn(element); 
    int r = Grid.GetRow(element); 
} 
+3

Esto solo funciona, si tiene elementos UIE en cada celda. Por lo tanto, no responde a la pregunta formulada. – GreenEyedAndy

5

Esto lo siguiente se puede hacer (pero no debe utilizarse en mayor Grids). Es solo para Filas, pero se puede aplicar un segundo bucle a las columnas ...

La respuesta de Nicolas solo funciona si la grilla contiene algunos UIElements (cuya posición en la grilla está determinada, supongo que eso también causaría problema si un elemento abarca varias filas/columnas).

private void ItemsGrid_MouseMove(object sender, MouseEventArgs e) 
    { 
     double y = e.GetPosition(ItemsGrid).Y; 
     double start = 0.0; 
     int row = 0; 
     foreach(RowDefinition rd in ItemsGrid.RowDefinitions) 
     { 
      start += rd.ActualHeight; 
      if (y < start) 
      { 
       break; 
      } 
      row++; 
     } 
     System.Diagnostics.Debug.WriteLine("Row : " + row); 
    } 
0

Tuve exactamente el mismo problema y también estoy usando FluidKit. Intento crear un diseñador de formularios en el que puedas arrastrar un control desde una Caja de Herramientas y soltarlo en una celda de Grilla. Aquí es cómo lo resolví:

creé una cuadrícula con dos rectángulos ficticias en la primera fila:

 <Grid Name="myCanvas" ShowGridLines="True" > 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="Auto" /> 
       <ColumnDefinition /> 
      </Grid.ColumnDefinitions> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="Auto" /> 
       <RowDefinition Height="Auto" /> 
      </Grid.RowDefinitions> 
      <Rectangle DragDrop:DragDropManager.DropTargetAdvisor="{StaticResource targetAdvisor1}" 
         Grid.Row="0" Grid.Column="0" Width="200" Height="100" Fill="Blue" Stroke="Black" StrokeThickness="4" /> 
      <Rectangle DragDrop:DragDropManager.DropTargetAdvisor="{StaticResource targetAdvisor2}" 
       Grid.Row="0" Grid.Column="1" Width="200" Height="100" Fill="Red" Stroke="Black" StrokeThickness="4" /> 
     </Grid> 

se ve de la siguiente manera:

Grid

Tenga en cuenta que he definido una DropTargetAdvisor para cada uno de mis rectángulos, ahora la lógica es la siguiente:

  • Se arrastrar/soltar un control de la caja de herramientas en una celda
  • El método OnDropCompleted en el DropTargetAdvisor luego quitar el rectángulo donde usted está cayendo el control y obtendrá sus coordenadas a partir de los Grid.Row y Grid.Column propiedades adjuntas.
  • Una vez que tenga las coordenadas, puede establecer esas propiedades adjuntas en el control arrastrado y agregarlas a su cuadrícula.

Aquí es mi DefaultDropTargetAdvisor.OnDropCompleted método:

public void OnDropCompleted(IDataObject obj, Point dropPoint) 
    { 
     UIElement dragged_control = ExtractElement(obj); 
     //Get the current Rectangle 
     FrameworkElement fe = TargetUI as FrameworkElement; 
     //Get parent Grid from this Rectangle 
     Grid g = fe.Parent as Grid; 
     //Get row and columns of this Rectangle 
     int row = Grid.GetRow(TargetUI); 
     int col = Grid.GetColumn(TargetUI); 
     //Remove Rectangle 
     g.Children.Remove(TargetUI); 
     //Set row and column for the dragged control 
     Grid.SetRow(dragged_control, row); 
     Grid.SetColumn(dragged_control, col); 
     //Add dragged control to Grid in that row/col 
     g.Children.Add(dragged_control); 
    } 
2
public static class GridExtentions 
{ 
    public static T Parent<T>(this DependencyObject root) where T : class 
    { 
     if (root is T) { return root as T; } 

     DependencyObject parent = VisualTreeHelper.GetParent(root); 
     return parent != null ? parent.Parent<T>() : null; 
    } 

    public static Point GetColumnRow(this Grid obj, Point relativePoint) { return new Point(GetColumn(obj, relativePoint.X), GetRow(obj, relativePoint.Y)); } 
    private static int GetRow(Grid obj, double relativeY) { return GetData(obj.RowDefinitions, relativeY); } 
    private static int GetColumn(Grid obj, double relativeX) { return GetData(obj.ColumnDefinitions, relativeX); } 

    private static int GetData<T>(IEnumerable<T> list, double value) where T : DefinitionBase 
    { 
     var start = 0.0; 
     var result = 0; 

     var property = typeof(T).GetProperties().FirstOrDefault(p => p.Name.StartsWith("Actual")); 
     if (property == null) { return result; } 

     foreach (var definition in list) 
     { 
      start += (double)property.GetValue(definition); 
      if (value < start) { break; } 

      result++; 
     } 

     return result; 
    } 
} 

Uso:

protected override void OnMouseDown(MouseButtonEventArgs e) 
{ 
    base.OnMouseDown(e); 
    var hit = VisualTreeHelper.HitTest(this, e.GetPosition(this)); 
    if (hit == null) { return; } 

    var grid = hit.VisualHit.Parent<Grid>(); 
    if (grid == null) { return; } 

    var gridPosition = grid.GetColumnRow(e.GetPosition(grid)); 
    MessageBox.Show(string.Format("Grid location Row: {1} Column: {0}", gridPosition.X, gridPosition.Y)); 
} 
1

esperanza puede ayudar.Está funcionando bien para mi aplicación

 public class GridCell 
     { 
      public int GridRow { get; set; } 
      public int GridCol { get; set; } 
     } 

     private void Window_MouseMove(object sender, MouseEventArgs e) 
     { 
      Point point = e.GetPosition(this.myGrid); 

      Grid gridTarget = GetLastedGridContainPoint(point, this.myGrid); 

      Point pointTarget = this.myGrid.TranslatePoint(point, gridTarget); 

      GridCell cell = GetGridCellContainPoint(pointTarget, gridTarget); 
     } 

     private bool IsPointInGrid(Point relativePoint, Grid grid) 
     { 
      if (relativePoint.X < 0 || relativePoint.X > grid.ActualWidth || 
       relativePoint.Y < 0 || relativePoint.Y > grid.ActualHeight) 
      { 
       return false; 
      } 

      return true; 
     } 

     private Grid GetLastedGridContainPoint(Point relativePoint, Grid gridParent) 
     { 
      Grid gridReturn = null; 

      if (gridParent.Children.Count > 0) 
      { 
       Point relativeChildPoint; 
       foreach (UIElement e in gridParent.Children) 
       { 
        if (e is Grid) 
        { 
         relativeChildPoint = gridParent.TranslatePoint(relativePoint, (e as Grid)); 
         gridReturn = GetLastedGridContainPoint(relativeChildPoint, (e as Grid)); 

         if (gridReturn != null) 
         { 
          break; 
         } 
        } 
       } 
      } 

      if (gridReturn == null) 
      { 
       if (IsPointInGrid(relativePoint, gridParent)) 
       { 
        gridReturn = gridParent; 
       } 
      } 

      return gridReturn; 
     } 

     private GridCell GetGridCellContainPoint(Point relativePoint, Grid gridTarget) 
     { 
      if (!IsPointInGrid(relativePoint, gridTarget)) 
      { 
       return null; 
      } 

      GridCell cell = new GridCell(); 
      double dbStart = 0; 
      double dbEnd = 0; 

      if (gridTarget.ColumnDefinitions.Count > 0) 
      { 
       for (int i = 0; i < gridTarget.ColumnDefinitions.Count; i++) 
       { 
        dbStart = dbEnd; 
        dbEnd += gridTarget.ColumnDefinitions[i].ActualWidth; 

        if (relativePoint.X >= dbStart && relativePoint.X < dbEnd) 
        { 
         cell.GridCol = i; 
         break; 
        } 
       } 
      } 

      dbStart = 0; 
      dbEnd = 0; 

      if (gridTarget.RowDefinitions.Count > 0) 
      { 
       for (int i = 0; i < gridTarget.RowDefinitions.Count; i++) 
       { 
        dbStart = dbEnd; 
        dbEnd += gridTarget.RowDefinitions[i].ActualHeight; 

        if (relativePoint.Y >= dbStart && relativePoint.Y < dbEnd) 
        { 
         cell.GridRow = i; 
         break; 
        } 
       } 
      } 

      return cell; 
     } 
0

Para un Telerik RadGridView, el mejor enfoque si la red no contiene elementos de interfaz de usuario es utilizar ChildrenOfType <> en una expresión LINQ con IsMouseOver.

private void myGridView_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) 
      { 
       MyCustomClass myClass = null; 

       var rows = this.myGridView.ChildrenOfType<GridViewRow>().Where(r => r.IsMouseOver == true); 
       foreach (var row in rows) 
       { 
        if (row is GridViewNewRow) break; 
        GridViewRow gvr = (GridViewRow)row; 
        myClass = (MyCustomClass)gvr.Item; 
       } 
// do something with myClass here if we have found a row under mouse 
} 
1

La respuesta de Andre tiene un pequeño error, ya que las coordenadas obtenidas no tienen en cuenta los encabezados de fila y columna en el DataGrid. Al menos este fue el caso cuando implementé la solución en Visual Basic.

También puede modificar los ejemplos que se muestran para dar cuenta de un DataGrid grande. Me parece que la limitación no se basa en la idea de desplazado, por lo muestro dos implementaciones de esta corrección:

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 

Tenga en cuenta que la ScrollViewer.HorizontalOffset Property devuelve un valor en píxeles independientes del dispositivo, por lo que sólo compensa mi lugar de una vez antes de recorrer las columnas.

Tenga en cuenta que el ScrollViewer.VerticalOffset Property devuelve el número de elementos si CanContentScroll = True. Entonces, en mi ejemplo, en cada ciclo compenso el contador por la altura de un elemento (el DataGridRow). Si CanContentScroll = False, se puede manejar como en el caso del bucle de índice de columna.

no pude encontrar las definiciones de fila en la cuadrícula de datos para .NET 4.0 en Visual Basic, pero la siguiente función de apoyo para la obtención de ayuda a la 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 

Y la función FindVisualChild en 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 
1

reemplazar Grid.ColumnDefinitions con referencia al componente Cuadrícula

int GetColumn(double point) 
{ 
    int index = 0; 
    foreach(var column in Grid.ColumnDefinitions) 
    { 
     if(point > column.Offset && point < (column.Offset + column.ActualWidth)) 
      return index; 
     index++; 
    } 
    return 0; 
} 
Cuestiones relacionadas