在WPF网格中,我如何才能找到Row&鼠标位置的列?

时间:2009-08-11 10:45:08

标签: wpf grid mouseevent

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

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

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

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

8 个答案:

答案 0 :(得分:16)

我希望你已经找到了解决方案。我遇到了同样的问题,我找到了这个无人接听的帖子。所以我认为下一个会很乐意找到这个解决方案:

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

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

答案 1 :(得分:5)

以下可以完成(但不应在较大的网格上使用)。它仅适用于Rows,但第二个循环可以应用于列...

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

答案 2 :(得分: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));
}

答案 3 :(得分: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;
        }

答案 4 :(得分: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,则可以像列索引循环一样处理它。

我无法在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

答案 5 :(得分: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;
}

答案 6 :(得分: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>

看起来如下:

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

答案 7 :(得分:0)

对于Telerik RadGridView,如果网格不包含UI元素,最好的方法是使用ChildrenOfType&lt;&gt;在带有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
}