Problem to track Node move action due inconsistent data provided by TreeView

2 Answers 61 Views
Treeview
Pascal
Top achievements
Rank 1
Iron
Pascal asked on 16 Oct 2023, 08:54 AM | edited on 16 Oct 2023, 08:56 AM

Hello!

I'm struggeling tracking move actions via the DragStarting, DragEnding & DragEnded event of the RadTreeView control.

The informatin provided by the event args are nearly impossible to use, as they are inconsitent as it looks like. Probably I just miss something, but that's why I'm writing this here. :)

What I need:

I need to check if the previous Node in the previous parent can be dropped to the new parent at the new position. This is a more complex check I can't explain in detail, but to clarify: I need ...

  • the old parent
  • the old index of the node within the old parent
  • the new parent
  • the new index of the node in the new parent

Current limits

I can't get all those infos at the DragEnding nor in the DragEnded event. At the moment:

  • Only the DragEnded event have 100% accurate infos about the new parent and new new index of the node in the new parent.
  • Only the DragEnding event has 100% accurate infos about the old parent and the old index of the node.

What I do now is not well done:

  • On DragEnding don't use the "Controller.Allow..." methods to see if the actions "Controller.Move..." and "Controller.SetNewEntilityParent" will succes. Instead I just catch the exception.
  • The exact destination I can't really track. So, I do some hacky checks to suppose the new parent and the new index of the node in the new parent. This is still not accurate and still lead to IndexOutOfBoundExceptions that the try-catch also catches.

My question

Is there any way to either track down the accurate new parent and the new index of the node in the new parent on the DragEnding event? I need to know them to cancel the drag event if the action is not allow and very specific cases.

Thank you in advance for an answer! We hope to get a solution soon. I already spend a lot of hours (several days) to this issue without a good solution.

This might also be a support ticket, but maybe someone of the community have a similar issue or already have a workaround/solution for it.

Code sample of a current implementation:

private void RadTreeView_BgrTree_DragStarting(object sender, RadTreeViewDragCancelEventArgs e)
{
    if (e.Node is not null)
    {
        oldNodeIndex = e.Node.Index;
        oldNodeParent = CheckForRootNode(e.Node.Parent);
    }
}

private void RadTreeView_BgrTree_DragEnding(object sender, RadTreeViewDragCancelEventArgs e)
{
    IMatrixTarget draggingTarget = e.Node?.Tag as IMatrixTarget;
    Entility newParentEntility = null;
    int indexToInsert = 0;

    void insertInSameParent(int offset)
    {
        if (e.TargetNode.Parent is not null)
            newParentEntility = CheckForRootNode(e.TargetNode.Parent)?.Tag as Entility;
        indexToInsert = e.TargetNode.Index + offset;
    };

    void insertAsChild(int offset)
    {
        if (e.TargetNode is not null)
            newParentEntility = CheckForRootNode(e.TargetNode)?.Tag as Entility;
        indexToInsert = newParentEntility.Childs.Count;
    };

    switch (e.DropPosition)
    {
        case DropPosition.BeforeNode:
            insertInSameParent(0);
            break;
        case DropPosition.AfterNode:
            {
                if (ReferenceEquals(e.TargetNode.Parent, e.Node.Parent))
                {
                    if (draggingTarget is Entility draggingEntility && draggingEntility.Index > e.TargetNode.Index)
                        insertInSameParent(0);
                    else
                        insertInSameParent(-1);
                }
                else if (draggingTarget is Entility && ReferenceEquals(((Entility)draggingTarget).Parent, e.TargetNode.Tag))
                    insertAsChild(-1);
                else
                    insertAsChild(0);
                break;
            }
        case DropPosition.AsChildNode:
            insertAsChild(0);
            break;
    }

    if (newParentEntility is not null)
    {
        try
        {
            if (draggingTarget is Einfluss)
                Controller.MoveIMatrixTarget(draggingTarget, indexToInsert);
            else if (draggingTarget is Entility entility)
                Controller.SetNewEntilityParent(newParentEntility, new[] { entility }, indexToInsert, false, false);
        }
        catch (Exception)
        {
            e.Cancel = true;
        }
    }
}

2 Answers, 1 is accepted

Sort by
0
Nadya | Tech Support Engineer
Telerik team
answered on 16 Oct 2023, 12:46 PM

Hello, Pascal,

In both DragEnd and DragEnding events you have access to the node that is currently dropped as well as the target node you are about to drop the new node:

private void RadTreeView2_DragEnded(object sender, RadTreeViewDragEventArgs e)
{
    RadTreeNode droppedNode = e.Node;
    RadTreeNode targetNode = e.TargetNode;
}
 
private void RadTreeView2_DragEnding(object sender, RadTreeViewDragCancelEventArgs e)
{
    RadTreeNode droppedNode = e.Node;
    RadTreeNode targetNode = e.TargetNode;
}

Through e.TargetNode.Parent you can get the parent node of the target node in DragEnding event. I am not familiar with the exact requirements that you have but I can suggest storing some values in separate variables and use them later when needed. 

In addition, I would like to note that RadTreeView handles the whole drag and drop operation by its TreeViewDragDropService. Additional information about the service can be found in the following help articles: 

TreeViewDragDropService - WinForms TreeView Control - Telerik UI for WinForms 

RadDragDropService - Telerik Presentation Framework - Telerik UI for WinForms

Note, PreviewDragOver event allows you to control on what targets the item being dragged can be dropped on by the RadDragOverEventArgs.CanDrop. The RadDragOverEventArgs gives you access to the dragged and the target item.

In case you are having further difficulties do not hesitate to contact me.

Regards,
Nadya
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Pascal
Top achievements
Rank 1
Iron
commented on 16 Oct 2023, 01:25 PM | edited

Thank you very much for your answer! I wasn't awere anymore about the PreviewDragOver event. I'll give it a try. :)

Through e.TargetNode.Parent you can get the parent node of the target node in DragEnding event.

This I thought too and then I started... And I regonized that e.TargetNode.Parent is not always the parent, somtimes e.TargetNode is already the parent.

This is the point where I checked e.DropPosition. It looks to be easy, "InsertAsChild", "BeforeNode" and "AfterNode". But more then others, "AfterNode" is very inconsistent. Sometimes it's true. But sometimes it's the same as "InsertAsChild" and sometimes the same as "BeforeNode".

How can I track that? What is the best way to identify the exact node what will be the new parent of my dragging node at the DragEnding event and how to get the exact new index?

Pascal
Top achievements
Rank 1
commented on 16 Oct 2023, 01:59 PM | edited

Alright, this is my new implementation now. It looks like to work fine, but I need to do more tests tomorrow as I still don't trust DropPosition and TargetNode.

private void RadTreeView_BgrTree_DragEnding(object sender, RadTreeViewDragCancelEventArgs e)
{
    IMatrixTarget draggingTarget = e.Node?.Tag as IMatrixTarget;
    IMatrixTarget newParentTarget = null;

    switch (e.DropPosition)
    {
        case DropPosition.BeforeNode:
        case DropPosition.AfterNode:
            newParentTarget = CheckForRootNode(e.TargetNode.Parent)?.Tag as Entility;
            break;
        case DropPosition.AsChildNode:
            newParentTarget = CheckForRootNode(e.TargetNode)?.Tag as Entility;
            break;
    }

    if (newParentTarget is null)
        e.Cancel = true;
    else if (draggingTarget is Einfluss)
    {
        if (!Controller.CanMoveIMatrixTarget(draggingTarget))
            e.Cancel = true;
    }
    else if (draggingTarget is Entility entility && newParentTarget is Entility newParentEntility)
    {
        if (!Controller.CanSetNewEntilityParent(newParentEntility, new[] { entility }))
            e.Cancel = true;
    }
    else
        e.Cancel = true;
}

private void RadTreeView_BgrTree_DragEnded(object sender, RadTreeViewDragEventArgs e)
{
    int newIndex = e.Node.Index;
    IMatrixTarget draggingTarget = e.Node.Tag as IMatrixTarget;
    IMatrixTarget newParentTarget = CheckForRootNode(e.Node.Parent).Tag as IMatrixTarget;

    if (draggingTarget is Einfluss)
        Controller.MoveIMatrixTarget(draggingTarget, newIndex);
    else if (draggingTarget is Entility entility && newParentTarget is Entility newParentEntility)
        Controller.SetNewEntilityParent(newParentEntility, new[] { entility }, newIndex, false, false);
    else
        throw new NotImplementedException();
}
I don't really need the new index on DragEnding, but it would be good to know for a case where it's needed. ;)
0
Nadya | Tech Support Engineer
Telerik team
answered on 17 Oct 2023, 09:59 AM

Hello, Pascal,

Thank you for sharing your code snippet here.

I am glad that you managed to find a working solution for your scenario. I would like to provide more information about the DropPositions in DragEnding event. It is important where exactly you are trying to drop into the dragged node. For example, if the cursor appears above a parent node and you drop the node right below the node the DropPosition would be AsChildNode. If you are trying to drop the node upon a child node the DropPositin would be BeforeNode or AfterNode. In Addition, in the Editing event, you can check which is the level of the target node via e.TargetNode.Level. However, the actual movement of the node is happening when the drop-down operation is completed. Then, you would be able to get the target index.  

In case you are having further difficulties, I would encourage you to check the TreeViewDragDropService. The following article contains an example of the whole drag-drop mechanism that could be quite useful in your case. The OnPreviewDragDrop method initiates the actual physical move of the nodes from one position to another: Drag and Drop in bound mode - WinForms TreeView Control - Telerik UI for WinForms

Please let me know if I can assist you further.

Regards,
Nadya
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Tags
Treeview
Asked by
Pascal
Top achievements
Rank 1
Iron
Answers by
Nadya | Tech Support Engineer
Telerik team
Share this question
or