Drag and Drop custom controls between cells in a g

2020-02-11 07:36发布

I've got some custom controls which are dynamically added to a custom grid. These controls can span over several columns and rows(which are all the same size). I'd like to drag and drop between the rows and columns. I can drag the individual controls, but they can move anywhere without limit. Even off the grid. I'd like to do it so it can only be dragged inside the grid AND snaps to the column/row it's dragged to.

Is there any easy-ish way to do this?

Honestly, if I could get the current row/column that it's over, then all I'd need to do is set the column/row of it to them and that would probably do it and then just worry about keeping it inside the grid.

2条回答
Evening l夕情丶
2楼-- · 2020-02-11 08:04

Is there any easy-ish way to do this?

I'd say that the answer to this question very much depends on your experience using Drag and Drop functionality... for a beginner, I'd say that the answer to this was no, but for someone with some experience and some common sense, it might not be too bad.

To determine which Grid cell the user's mouse is over will not be straight forward. You can handle the PreviewDragOver event and use the VisualTreeHelper.HitTest method to check which control the mouse is currently over:

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

The GetParentOfType method is a useful extension method that I created, but you can convert it to a normal method easily enough:

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

Of course, once you have a Control in your controlUnderMouse variable, you'll still have some considerable work to do as you work your way through the UIElements until you get to the Grid... you can of course make further use of the GetParentOfType method to make your job easier.

查看更多
【Aperson】
3楼-- · 2020-02-11 08:09

I figured out a nice and fun way!

I worked out the position on the grid that the the mouse is on on the MouseUp event and then the relative position of the mouse on the control since it spans several rows/columns.

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

I then take away the relative positions from the normal positions so that when you drop it, it always drops on the top left of the screen. When I move my controls, I use margins to move it, which screws up the position on the grid at the time, as shown below:

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

To tackle this, I simply reset the margin.

public void updatePosition()
    {
        Grid.SetRow(this, (int)position.Y);
        Grid.SetColumn(this, (int)position.X);
        Margin = new Thickness();
    }

I hope this helps someone else since it was rather frustrating for me to find the answer and in the end I managed to get lots of little fragments of how to do things and eventually came up with my own solution.

查看更多
登录 后发表回答