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

Posted

技术标签:

【中文标题】在 WPF 中的网格中的单元格之间拖放自定义控件【英文标题】:Drag and Drop custom controls between cells in a grid in WPF 【发布时间】:2014-10-06 14:14:27 【问题描述】:

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

有没有简单的方法来做到这一点?

老实说,如果我能得到它结束的当前行/列,那么我需要做的就是将它的列/行设置为它们,这可能会做到,然后只需担心将其保留在里面网格。

【问题讨论】:

可能是这样的:dotnetcurry.com/showarticle.aspx?ID=677? 不过,它们是任意控件。尽管它们都是同一事物的子类。我需要更多,所以我将项目拖到的行/列。 【参考方案1】:

我想出了一个很好玩的方法!

我在 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();
    

我希望这对其他人有所帮助,因为我很难找到答案,最后我设法获得了很多关于如何做事的小片段,并最终想出了我自己的解决方案。

【讨论】:

WPF ThumbControl 应该能够在没有 Margin 技巧的情况下处理这个问题,但事实并非如此。在与ThumbControls 相处了很多很多无济于事的小时后,我终于如你所愿,@Gentatsu。干得好,干得好。 parent 指的是什么?什么是DControl 你能发布整个代码吗?重现该问题时遇到问题。【参考方案2】:

有没有简单的方法来做到这一点?

我想说这个问题的答案很大程度上取决于您使用拖放功能的经验......对于初学者,我会说这个问题的答案是否定的,但对于有一定经验的人来说一些常识,可能还不错。

要确定用户的鼠标在哪个Grid 单元格上并不简单。您可以处理PreviewDragOver 事件并使用VisualTreeHelper.HitTest method 检查鼠标当前位于哪个控件上:

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,您仍然需要做一些相当大的工作,因为您要通过 UIElements 直到您到达 Grid。 ..您当然可以进一步使用GetParentOfType 方法来简化您的工作。

【讨论】:

我设法通过计算行高+列宽直到它击中鼠标点来获取鼠标在当前网格中的位置。然后,我计算出鼠标与控件左上角的相对位置,并将其从该位置移开,以便将其分配给该位置。它得到了正确的值,但是当我在该项目上执行Grid.SetRow(this, row) 并且同样使用Grid.SetColumn 时,它会将它们放在一个随机的位置。有什么理由吗?是因为它使用边距移动控件吗? Welp,是的。当我更新它们时,我重置了边距,效果很好!我不知道为什么我要评论这个答案,因为我没有使用它的任何东西,但还是感谢你帮助我! 我不知道我为什么要评论这个答案...我也不知道。将您的解决方案添加到答案中,并尽可能接受它。跨度> 对不起!我已经这样做了,但很遗憾,我有几天不能接受它。

以上是关于在 WPF 中的网格中的单元格之间拖放自定义控件的主要内容,如果未能解决你的问题,请参考以下文章

动画拖放自定义 Delphi 组件

在 QListWidget 之间拖放自定义小部件项目

如何拖放自定义小部件?

如何基于二维数组填充 WPF 网格

WPF 数据网格。无法突出显示自定义样式的单元格

WPF 获取DataGrid 控件选中的单元格信息