2
votes

I am using C++ Builder 10.2.2 Tokyo with FireMonkey (FMX).

I want to add drag and drop functionality to a TTreeView, so a user can rearrange the order of the tree items. I have added a handler to the TTreeView.OnMouseDown event, based on this Drag and Drop sample project.

With this, the program can now drag and drop to rearrange items, but it seems that there is some default behavior to move a TTreeViewItem to be a child of the TTreeViewItem it is dropped onto, instead of inserting after that item.

How can I override this default behavior, so that a TTreeViewItem is inserted at the same level in the TTreeView, and at an index 1 greater than the TTreeViewItem it is dropped onto?

1
Please include the essential code in your question. Nobody should need to fetch details from another site. Your question becomes worthless if the linked resource is removed or moved.Tom Brunberg
@Tom, although I was using the code provided in the sample to start the drag and drop, as it provided for a custom bmp for the drag and drop operation, it is not required. Set the control's AllowDrag to true, and make sure the DragMode is set to dmManual. Same question - how does one insert the item at the same level as the item, instead of the default behavior of adding it as a child of the item it is dropped onAnthony Burg
You can create your own component extends TTreeViewItem then you can change, override or anything you want to do.Abdullah Ilgaz
@Abdullah, can you say more about this, including specifics?Anthony Burg
@AnthonyBurg, did you ever create a custom component? Make a package project and make TMyTreeView class from TTreeView and override all behaviors that you want to change. Everything that you needed to change can be changed from your custom class. Then you can change node index automatically after executing related event.Abdullah Ilgaz

1 Answers

1
votes

Following on Abdullah's suggestion, you can achieve this by creating a custom component. Directions to create a custome component in general are here. I recommend installing it in Standard on the Tool Palette, as that's where TTreeView is.

The custom component, here called TMyTreeView, has this in the header in particular:

class PACKAGE TMyTreeView : public TTreeView
{
private:
    bool IsAncestor (TTreeViewItem* oItem, TTreeViewItem* oTargetItem);
protected:
    int DragDelta;
    void StartDrag ();
    void __fastcall DragDrop (const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point);
    void __fastcall MouseDown (System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, float X, float Y);
    void __fastcall MouseMove (System::Classes::TShiftState Shift, float X, float Y);
public:
    __fastcall TMyTreeView(TComponent* Owner);
    __fastcall ~TMyTreeView ();
    TBitmap* DragBmp;
    TPointF MouseDownPoint;
    TTreeViewItem* DragStartItem;
__published:

};
//---------------------------------------------------------------------------

where the functions are as follows in the corresponding cpp file:

    __fastcall TMyTreeView::TMyTreeView(TComponent* Owner)
    : TTreeView(Owner)
{
    DragBmp = NULL;
    DragStartItem = NULL;
    DragDelta = 5;
}
//---------------------------------------------------------------------------

__fastcall TMyTreeView::~TMyTreeView ()
{
    if (DragBmp)
        delete DragBmp;
}
//---------------------------------------------------------------------------

void __fastcall TMyTreeView::MouseMove (System::Classes::TShiftState Shift, float X, float Y)
{
    TTreeView::MouseMove (Shift, X, Y);

    if ((abs (X-MouseDownPoint.X) > DragDelta) || (abs (Y-MouseDownPoint.Y) > DragDelta))
        StartDrag ();
}
//---------------------------------------------------------------------------

void TMyTreeView::StartDrag ()
{
    if (!AllowDrag)
        return;
    if (!DragStartItem)
        return;

    if (DragBmp)
        delete DragBmp;

    _di_IFMXDragDropService service;
    if ((TPlatformServices::Current->SupportsPlatformService (__uuidof(IFMXDragDropService)) &&
        (service = TPlatformServices::Current->GetPlatformService (__uuidof(IFMXDragDropService)))))
    {
        TDragObject dragData;
        if (!DragStartItem)
            return;
        dragData.Source = DragStartItem;
        DragBmp = DragStartItem->MakeScreenshot ();
        dragData.Data = DragBmp;
        service->BeginDragDrop  ((TForm*)this->Owner, dragData, DragBmp);
        DragStartItem = NULL;
    }

}
//---------------------------------------------------------------------------

void __fastcall TMyTreeView::MouseDown (System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, float X, float Y)
{
    TTreeView::MouseDown (Button, Shift, X, Y);

    if (AllowDrag)
    {
        DragStartItem = ItemByPoint (X, Y);
        MouseDownPoint.X = X;
        MouseDownPoint.Y = Y;
    }
    else
        DragStartItem = NULL;
}
//---------------------------------------------------------------------------

void __fastcall TMyTreeView::DragDrop (const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point)
{
    TTreeViewItem* item = ItemByPoint (Point.X, Point.Y);
    if (!item)
        return;

    TTreeViewItem* srcItem = (TTreeViewItem*)Data.Source;
    if (!srcItem)
        return;

    if (IsAncestor (srcItem, item))
        return;

    if (item->ParentItem ())
        item->ParentItem ()->InsertObject (item->Index, srcItem);
    else
        this->InsertObject (item->Index, srcItem);

    //TTreeView::DragDrop (Data, Point);
}
//---------------------------------------------------------------------------

bool TMyTreeView::IsAncestor (TTreeViewItem* oItem, TTreeViewItem* oTargetItem)
{
    for (int i=0; i<oItem->Count; i++)
    {
        TTreeViewItem* item = oItem->Items [i];
        if (item == oTargetItem)
            return true;
        if (IsAncestor (item, oTargetItem))
            return true;
    }
    return false;
}
//---------------------------------------------------------------------------

After installing the custom component to your Tool Palette, you can then add it to your form as you would any other component.

Special thanks to Mike Sutton, who had code to modify an earlier version of TTreeView here.

Once added to a form, set the TMyTreeView control's AllowDrag to true.