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

これは次のように実行できます(ただし、より大きなグリッドでは使用しないでください)。これは行専用ですが、2番目のループを列に適用できます...

Nicolasの答えは、グリッドにいくつかのUIElementが含まれている場合にのみ機能します(グリッド内の位置が決定されます。要素が複数の行/列にまたがる場合にも問題が発生すると思います)。

    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 に答える
0

まったく同じ問題があり、FluidKit も使用しています。ツールボックスからコントロールをドラッグしてグリッド セルにドロップできるフォーム デザイナーを構築しようとしています。これが私がそれを解決した方法です:

最初の行に 2 つのダミーの Rectangle を含むグリッドを作成しました。

        <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メソッドは、コントロールをドロップしている四角形を削除し、およびAttached プロパティDropTargetAdvisorからその座標を取得します。Grid.RowGrid.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);
    }
于 2012-09-24T20:14:45.633 に答える
0

Andre の回答には、取得した座標が DataGrid の行ヘッダーと列ヘッダーを考慮していないため、小さな間違いがあります。少なくとも、Visual Basic でソリューションを実装したときはそうでした。

大規模な DataGrid を考慮して、示されている例を変更することもできます。そこにある制限はスクロール ビューに基づいているように思われるので、この修正の 2 つの実装を示します。

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 プロパティは項目数を返すことに注意してください。したがって、私の例では、各ループで、1 つの項目 (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 に答える