2009-08-11 179 views
8

我有一個WPF Grid與一些行和列,例如,在WPF Grid中,如何在鼠標位置找到Row&Column?

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

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

對於.cs文件中的MouseMove的處理程序,例如,

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

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

我希望能夠找到其中的行和列在網格中的鼠標已經結束了,這可能嗎?

[注:這是問題的一個簡化版本,所以它看起來有點奇怪目前它以這種方式 - 這是電網功能之間存在一些阻力&下降的部分]

+0

查看FluidKit中的拖放功能。你不必擔心像這樣的hacky shnit。 – Will 2009-08-11 11:12:19

+0

我已經在使用FluidKit,'MouseMove'實際上是'OnDropCompleted',鼠標位置實際上是'dropPoint' - 我希望能夠放入表格中的特定單元格,而我無法獲得工作尚未。 – Wilka 2009-08-11 12:18:14

回答

15

我希望你已找到一個辦法。我面臨同樣的問題,我發現這個未答覆的帖子。所以我覺得下一個會非常樂意看到這樣的解決方案:

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

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

這隻適用於每個單元中有UIElements的情況。所以這個問題沒有答案。 – GreenEyedAndy 2014-02-05 07:42:40

5

這下面可以做(但不應該在大電網中使用)。這只是行,但第二循環可應用於列...

尼古拉斯的答案只能如果網格包含了一些UI元素(其在網格中的位置確定 - 我想這將也會導致如果一個元素跨越幾行/列會出現問題)。

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

我有完全相同的問題,我也使用FluidKit。我正在嘗試構建一個表單設計器,您可以從Toolbox拖動控件並將其放入Grid單元格中。這是我如何解決它:

我創建了一個網格,第一行中的兩個虛擬矩形:

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

它看起來如下:我定義的

Grid

公告DropTargetAdvisor爲我的每個矩形,現在的邏輯如下:

  • 你拖/放從工具箱中的控件成小區
  • OnDropCompleted方法在DropTargetAdvisor然後將刪除你在哪裏掉落的控制,並從Grid.RowGrid.Column附加屬性得到其座標的矩形。
  • 獲得座標後,您可以將這些附加屬性設置爲拖動的控件並將其添加到網格中。

這裏是我的DefaultDropTargetAdvisor.OnDropCompleted方法:

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

用法:

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

希望能有所幫助。這對我的應用程序

 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

對於Telerik的RadGridView,最好的辦法運作良好,如果電網不包含UI元素與IsMouseOver LINQ表達式使用ChildrenOfType <>。

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

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 Property返回與設備無關的像素值,所以我只是彌補我的位置一次循環遍歷列之前。

請注意,如果CanContentScroll = True,則ScrollViewer.VerticalOffset Property將返回項目數。所以在我的例子中,在每個循環中,我用一個項目(DataGridRow)的高度來抵消計數器。如果CanContentScroll = False,那麼可以像列索引循環一樣處理它。

我無法找到.NET 4.0中DataGrid行定義在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 

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

代替參考Grid.ColumnDefinitions到網格部件

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; 
} 
相關問題