2009-08-11 37 views
8

Tôi có một WPF Grid với một số hàng và cột, ví dụ:Trong một Lưới WPF, làm thế nào tôi có thể tìm thấy Hàng & Cột tại vị trí chuột?

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

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

Với một handler cho MouseMove trong file .cs, ví dụ

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

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

Tôi muốn có thể tìm thấy hàng và cột nào trong lưới mà con chuột kết thúc, điều này có khả thi không?

[Lưu ý: đây là một phiên bản đơn giản của vấn đề, vì vậy nó trông hơi kỳ quặc để trình bày nó theo cách này - đó là một phần của một số kéo & thả giữa chức năng lưới]

+0

Nhìn vào chức năng kéo/thả trong FluidKit. Bạn sẽ không phải lo lắng về hacky shnit như thế này. – Will

+0

Tôi đã sử dụng FluidKit, rằng 'MouseMove' thực sự là 'OnDropCompleted' và vị trí chuột thực sự là 'dropPoint' - Tôi muốn có thể thả vào các ô cụ thể trên bảng mà tôi chưa thể nhận được đang làm việc. – Wilka

Trả lời

15

Tôi hy vọng bạn đã tìm một giải pháp. Tôi phải đối mặt với cùng một vấn đề và tôi đã tìm thấy bài đăng chưa được trả lời này. Vì vậy, tôi nghĩ người tiếp theo sẽ vui lòng tìm giải pháp này:

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

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

Điều này chỉ hoạt động, nếu bạn có UIElements trong mỗi ô. Vì vậy, nó không có câu trả lời cho câu hỏi được hỏi. – GreenEyedAndy

5

Có thể thực hiện những điều sau (nhưng không nên sử dụng trên các ô lớn hơn). Nó chỉ dành cho Hàng, nhưng một vòng lặp thứ hai có thể được áp dụng cho các cột ...

Câu trả lời của Nicolas chỉ hoạt động nếu lưới có chứa một số UIElements (vị trí trong lưới được xác định - tôi đoán điều đó cũng sẽ gây ra rắc rối nếu một yếu tố kéo dài nhiều hàng/cột).

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

Tôi đã có cùng một vấn đề chính xác và tôi cũng đang sử dụng FluidKit. Tôi đang cố gắng để xây dựng một nhà thiết kế mẫu, nơi bạn có thể kéo một điều khiển từ một hộp công cụ và thả nó vào một ô lưới. Dưới đây là cách tôi giải quyết nó:

Tôi tạo ra một mạng lưới với hai hình chữ nhật giả trong hàng đầu tiên:

 <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> 

Nó trông giống như sau:

Grid

Chú ý rằng tôi đã xác định một DropTargetAdvisor cho mỗi hình chữ nhật của tôi, bây giờ logic là như sau:

  • Bạn kéo/thả một điều khiển từ hộp công cụ vào một tế bào
  • Phương pháp OnDropCompleted trong DropTargetAdvisor sau đó sẽ loại bỏ các hình chữ nhật nơi bạn đang bỏ sự kiểm soát và sẽ nhận được tọa độ của nó từ Grid.RowGrid.Column tính đính kèm.
  • Khi bạn có tọa độ, bạn có thể đặt các thuộc tính được đính kèm đó vào điều khiển kéo của bạn và thêm nó vào lưới của bạn.

Đây là phương pháp DefaultDropTargetAdvisor.OnDropCompleted tôi:

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; 
    } 
} 

Cách sử dụng:

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

Hy vọng có thể giúp đỡ.nó làm việc tốt cho ứng dụng của tôi

 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

Đối với một Telerik RadGridView, phương pháp tốt nhất nếu lưới không chứa các thành phần giao diện người dùng là sử dụng ChildrenOfType <> trong một biểu thức với IsMouseOver LINQ.

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

Câu trả lời của Andre có một sai lầm nhỏ do tọa độ thu được không tính đến tiêu đề hàng và cột trong DataGrid. Ít nhất đây là trường hợp khi tôi thực hiện các giải pháp trong Visual Basic.

Bạn cũng có thể sửa đổi các ví dụ được hiển thị để giải thích cho một DataGrid lớn. Dường như với tôi rằng giới hạn có được dựa trên quan điểm cuộn, vì vậy tôi thấy hai triển khai điều chỉnh này:

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 

Lưu ý rằng ScrollViewer.HorizontalOffset Property trả về một giá trị trong Pixels độc lập thiết bị, vì vậy tôi chỉ bù đắp vị trí của tôi một lần trước khi lặp qua các cột.

Lưu ý rằng số ScrollViewer.VerticalOffset Property trả về số mục nếu CanContentScroll = True. Vì vậy, trong ví dụ của tôi, trên mỗi vòng lặp tôi bù đắp bộ đếm theo chiều cao của một mục (DataGridRow). Nếu CanContentScroll = False, thì nó có thể được xử lý như trong trường hợp vòng lặp chỉ mục cột.

tôi không thể tìm thấy định nghĩa dòng trong DataGrid cho Net 4.0 trong Visual Basic, nhưng chức năng hỗ trợ sau đây sẽ giúp cho việc thu thập 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 

Và chức năng FindVisualChild trong 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

thay Grid.ColumnDefinitions với tham chiếu đến thành phần Lưới

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; 
} 
Các vấn đề liên quan