Как в строке WPF Grid найти строку и столбец в месте расположения мыши? - PullRequest
8 голосов
/ 11 августа 2009

У меня есть WPF Grid с некоторыми строками и столбцами, например,

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

Я хочу найти строку и столбец в сетке, над которой находится мышь, возможно ли это?

[Примечание: это упрощенная версия проблемы, поэтому немного странно представить ее таким образом - это часть некоторой функции перетаскивания между сетками]

Ответы [ 8 ]

17 голосов
/ 14 января 2011

Надеюсь, вы уже нашли решение. Я столкнулся с той же проблемой, и я нашел этот пост без ответа. Поэтому я думаю, что следующий будет рад найти это решение:

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

    int c = Grid.GetColumn(element);
    int r = Grid.GetRow(element);
}
5 голосов
/ 16 августа 2012

Это может быть сделано следующим образом (но не должно использоваться на больших сетках). Это только для строк, но к столбцам можно применить второй цикл ...

Ответ Николаса работает, только если сетка содержит некоторые элементы UIE (чье положение в сетке определено - я думаю, это также вызовет проблемы, если элемент охватывает несколько строк / столбцов).

    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 голосов
/ 22 марта 2014
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 голос
/ 04 августа 2016

заменить Grid.ColumnDefinitions со ссылкой на компонент Grid

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;
}
1 голос
/ 21 мая 2015

Ответ Андре имеет небольшую ошибку, поскольку полученные координаты не учитывают заголовки строк и столбцов в 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.HorizontOffset возвращает значение в независимых от устройства пикселях, поэтому я просто смещаю свое местоположение один раз перед циклом прохождения по столбцам.

Обратите внимание, что свойство ScrollViewer.VerticalOffset возвращает количество элементов, если CanContentScroll = True. Поэтому в моем примере в каждом цикле я смещал счетчик на высоту одного элемента (DataGridRow). Если CanContentScroll = False, то это может быть обработано, как в случае цикла индекса столбца.

Не удалось найти определения строк в DataGrid для .Net 4.0 в 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

И функция FindVisualChild в 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 голос
/ 31 марта 2014

Надежда может помочь. это хорошо работает для моего приложения

        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 голосов
/ 29 января 2015

Для Telerik RadGridView лучшим подходом, если сетка не содержит элементов пользовательского интерфейса, является использование ChildrenOfType <> в выражении Linq с 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
}
0 голосов
/ 25 сентября 2012

У меня была точно такая же проблема, и я также использую 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.Row и Grid.Column.
  • Получив координаты, вы можете установить эти свойства Attached для перетаскиваемого элемента управления и добавить его в сетку.

Вот мой 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);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...