Search code examples
delphidrag-and-dropolevirtualtreeviewtvirtualstringtree

Ending or Cancelling a Drag Drop Operation in a Delphi Application


I am dragging and dropping email attachments from outlook. The files are dropped into a Virtual Tree View.

My import function at the end of the drag over event takes a while to process the files and it freezes the outlook application until the function ends.

I want to be able to end the drag operation half way through the function.

procedure TForm.vstItemsDragDrop(Sender: TBaseVirtualTree;
  Source: TObject; DataObject: IDataObject; Formats: TFormatArray;
  Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode);
var
  fileList          : IStringList;
  drop              : IOleDrop;
begin
  fileList:= TIStringList.Create;

  drop := COleDrop.Create.def(DataObject);
  fileList := drop.GetDroppedFileList(fileWarnings);

  //I want to terminate the drag operator here because I already have what I need

  //This imports parts takes a while to run so I want to end the drag and drop operation
  //Outlook freezes still has cursor state on copy and doesn't respond to clicks or ESC
  ImportParts( fileList)    
end;

Solution

  • I usually just pick up the information in the Drop event (ie. do not process it) and then send a message back to my form that there is new information to be processed. The Drop event thus exits rather quickly, and the message handler then picks up and handles whatever was dropped.

    In your case, you should make something like this:

    CONST
      WM_PROCESS_DROPPED_FILES = WM_USER+42;
    
    TYPE
      TMainForm = CLASS(TForm)
                    .
                    .
                  PRIVATE
                    fileList : IStringList; // Ie. move the declaration here...
                    PROCEDURE ProcessFiles(VAR MSG : TMessage); MESSAGE WM_PROCESS_DROPPED_FILES;
                    .
                    .
                  END;
    

    and in your drop event, remove the declaration of fileList (you have made it a private member of the form instead of a local variable) and replace

    ImportParts(fileList)
    

    with

    PostMessage(Handle,WM_PROCESS_DROPPED_FILES)
    

    then implement

    PROCEDURE TMainForm.ProcessFiles(VAR MSG : TMessage);
      BEGIN
        ImportParts(fileList)
      END;
    

    It may be necessary to make the fileList variable a TStringList instead and copy the information there, in case the reference to the IStringList is ended when the Drop event exits, but the general principle is the same - don't process the data in the Drop event, defer it to after the Drop event exits.