在WPF中网格中的单元格之间拖放自定义控件

本文关键字:单元格 之间 拖放 自定义控件 WPF 中网 网格 | 更新日期: 2023-09-27 18:17:58

我有一些自定义控件,它们被动态地添加到自定义网格中。这些控件可以跨越多个列和行(它们的大小都相同)。我想在行和列之间拖放。我可以拖动单个控件,但它们可以无限制地移动到任何地方。即使是在电网之外。我想这样做,所以它只能被拖到网格内,并捕捉到它被拖到的列/行。

有什么简单的方法可以做到这一点吗?

老实说,如果我能得到当前的行/列,那么我所需要做的就是将它的列/行设置为它们,这可能会做到这一点,然后只需要担心将它保持在网格内。

在WPF中网格中的单元格之间拖放自定义控件

我想出了一个又好又有趣的方法!

我在MouseUp事件中计算出鼠标在网格上的位置,然后计算出鼠标在控件上的相对位置,因为它跨越了几行/列。

public void getPosition(UIElement element, out int col, out int row)
    {
        DControl control = parent as DControl;
        var point = Mouse.GetPosition(element);
        row = 0;
        col = 0;
        double accumulatedHeight = 0.0;
        double accumulatedWidth = 0.0;
        // calc row mouse was over
        foreach (var rowDefinition in control.RowDefinitions)
        {
            accumulatedHeight += rowDefinition.ActualHeight;
            if (accumulatedHeight >= point.Y)
                break;
            row++;
        }
        // calc col mouse was over
        foreach (var columnDefinition in control.ColumnDefinitions)
        {
            accumulatedWidth += columnDefinition.ActualWidth;
            if (accumulatedWidth >= point.X)
                break;
            col++;
        }
    }
然后我从正常位置中去掉相对位置,这样当你放下它时,它总是落在屏幕的左上角。当我移动控件时,我使用边距来移动它,这会改变网格上的位置,如下所示:
void Chart_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        if (IsMouseCaptured)
        {
            Point mouseDelta = Mouse.GetPosition(this);
            mouseDelta.Offset(-mouseOffset.X, -mouseOffset.Y);
            Margin = new Thickness(
                Margin.Left + mouseDelta.X,
                Margin.Top + mouseDelta.Y,
                Margin.Right - mouseDelta.X,
                Margin.Bottom - mouseDelta.Y);
        }
    }
    void Chart_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        mouseOffset = Mouse.GetPosition(this);
        CaptureMouse();
        parent.currentObject = this;
    }
为了解决这个问题,我简单地重置了边距。
public void updatePosition()
    {
        Grid.SetRow(this, (int)position.Y);
        Grid.SetColumn(this, (int)position.X);
        Margin = new Thickness();
    }

我希望这能帮助别人,因为它是相当令人沮丧的我找到答案,最后我设法得到了很多如何做事情的小片段,并最终提出了我自己的解决方案。

有什么简单的方法可以做到这一点吗?

我想说这个问题的答案在很大程度上取决于你使用拖放功能的经验……对于初学者来说,我的答案是否定的,但对于有经验和常识的人来说,这可能并不太糟糕。

要确定用户的鼠标在哪个Grid单元格上将不是直截了当的。您可以处理PreviewDragOver事件,并使用VisualTreeHelper.HitTest方法来检查鼠标当前在哪个控件上:

private void PreviewDragOver(object sender, DragEventArgs e)
{
    HitTestResult hitTestResult = VisualTreeHelper.HitTest(adornedUIElement, 
        e.GetPosition(adornedUIElement));
    Control controlUnderMouse = hitTestResult.VisualHit.GetParentOfType<Control>();
}

GetParentOfType方法是我创建的一个有用的扩展方法,但是您可以很容易地将其转换为普通方法:

public static T GetParentOfType<T>(this DependencyObject element) where T : DependencyObject
{
    Type type = typeof(T);
    if (element == null) return null;
    DependencyObject parent = VisualTreeHelper.GetParent(element);
    if (parent == null && ((FrameworkElement)element).Parent is DependencyObject) parent = ((FrameworkElement)element).Parent;
    if (parent == null) return null;
    else if (parent.GetType() == type || parent.GetType().IsSubclassOf(type)) return parent as T;
    return GetParentOfType<T>(parent);
}

当然,一旦你在controlUnderMouse变量中有了Control,你仍然有一些相当大的工作要做,因为你通过UIElement的方式工作,直到你得到Grid…当然,您可以进一步使用GetParentOfType方法使您的工作更容易。