10

我有一个Grid带有一些行和列的 WPF,例如

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

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

MouseMove使用.cs 文件中的处理程序,例如

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

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

我希望能够找到鼠标在网格中的哪一行和哪一列,这可能吗?

[注意:这是问题的简化版本,因此以这种方式呈现它看起来有点奇怪 - 它是网格之间一些拖放功能的一部分]

4

8 回答 8

19

我希望你已经找到了解决方案。我遇到了同样的问题,我发现了这个未答复的帖子。所以我认为下一个会很高兴找到这个解决方案:

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

    int c = Grid.GetColumn(element);
    int r = Grid.GetRow(element);
}
于 2011-01-14T14:46:37.927 回答
5

可以执行以下操作(但不应在较大的网格上使用)。它仅适用于行,但可以将第二个循环应用于列...

Nicolas 的答案只有在网格包含一些 UIElements 时才有效(它们在网格中的位置是确定的——我想如果一个元素跨越多行/多列也会造成麻烦)。

    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);
    }
于 2012-08-16T17:10:06.143 回答
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));
}
于 2014-03-21T20:52:13.560 回答
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;
        }
于 2014-03-31T13:37:53.533 回答
1

参考 Grid 组件替换 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;
}
于 2016-08-03T22:24:55.277 回答
0

我遇到了完全相同的问题,我也在使用 FluidKit。我正在尝试构建一个表单设计器,您可以在其中将控件从工具箱中拖放到网格单元格中。这是我解决它的方法:

我在第一行创建了一个带有两个虚拟矩形的网格:

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

它看起来如下:

网格

请注意,我为每个矩形定义了一个 DropTargetAdvisor,现在逻辑如下:

  • 您将控件从工具箱拖放到单元格中
  • 然后OnDropCompleted, 中的方法DropTargetAdvisor将删除您要放置控件的矩形,并从Grid.RowGrid.ColumnAttached 属性中获取其坐标。
  • 获得坐标后,您可以将这些附加属性设置到拖动的控件并将其添加到网格中。

这是我的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);
    }
于 2012-09-24T20:14:45.633 回答
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
}
于 2015-01-29T16:24:13.050 回答
0

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.Horizo​​ntalOffset 属性返回设备独立像素中的值,因此我只是在遍历列之前偏移我的位置一次。

请注意,如果 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
于 2015-05-21T01:34:08.617 回答