delphi Drag and Drop sample 鼠标拖放操作实例

摘要:
拖放是使界面用户友好的常见操作:用户可以使用拖放信息来控制是否需要键入等。下面的示例说明了拖放的原因

Drag and Drop is a common operation that makes the interface user friendly: a user can drag/drop information to controls instead of having to type etc.

The following sample explains basics of drag and drop. For detailed information you should refer to other articles in the wiki and reference documentation.

Please note, since LCL is partially compatible with Delphi's VCL, some articles/examples about Delphi drag-and-drop may also apply to LCL.

Contents

Drag and Drop

Despite of the operation's simplicity from the user's point of view, it might give hard times to an inexperienced developer.

For the code, Drag and Drop operation always consists of at least these 3 steps:

  1. Some control starts the drag-and-drop operation. This is called the Source
  2. User drags the mouse cursor around, above other controls or the Source itself. Now a dragged over control needs to decide, if it is able to accept the dragged data.
  3. Drop happens if a control agrees to accept the dragged data. The accepting control is called the Sender.

To simplify drag-and-drop handling, the LCL provides "automatic" mode. It doesn't mean, that LCL does the whole drag-and-drop for you, but it will handle low-level drag object managing (which is not covered in this article).

Example

DnDTest.PNG

The example covers automatic drag-and-drop feature between 2 controls (Edit->Treeview) as well as inside a single control (Treeview->Treeview)

  • Start the new application.
  • Put a TreeView component and Edit on the form.
  • Enable Automatic drag-and-drop mode for TreeView and Edit in Object Inspector:

DragMode: dkAutomatic

You can launch the application now, and try to drag anything around. You should not get anything working for now. But, if you press the left mouse button on the Treeview, you'll probably see that the cursor icon changed, but still nothing happens when releasing the mouse .

Dragging between controls

Let's make a drag-and-drop operation between Edit and TreeView. There the content of Edit will be "dragged" to TreeView and a new tree node created.

To initiate the drag, controls have a special method: BeginDrag()


Create OnMouseDown event for the Edit:

procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton; 
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then   {check if left mouse button was pressed}
    Edit1.BeginDrag(true);  {starting the drag operation}
end;

If you launch the application right now and try drag and drop, you'll notice that Edit starts the operation, but still nothing happens then you're trying to drop to TreeView.

This is because TreeView doesn't accept the data. None of the controls accept data by default, so you'll always need to provide the proper event handler.

Assign TreeView.OnDragOver operation:

procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer; 
  State: TDragState; var Accept: Boolean);
begin
  Accept := true;
end;

Of course in some cases, TreeView might deny dropping (if Source or data cannot be handled), but for now, TreeView always accepts the dragging.

Run application and test. Everything should be better now, though still nothing happens on drop.

Assign TreeView.OnDragDrop operation:

procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  tv     : TTreeView; 
  iNode  : TTreeNode;  
begin
  tv := TTreeView(Sender);      { Sender is TreeView where the data is being dropped  }
  iNode := tv.GetNodeAt(x,y);   { x,y are drop coordinates (relative to the Sender)   }   
                                {   since Sender is TreeView we can evaluate          }
                                {   a tree at the X,Y coordinates                     } 
 
  { TreeView can also be a Source! So we must make sure }                                
  { that Source is TEdit, before getting its text }      
  if Source is TEdit then       
    tv.Items.AddChild(iNode, TEdit(Source).Text); {now, we can add a new node, with a text from Source }
end;

Run and test. It should be working now. Dragging a text from Edit to TreeView should create a new node.

Dragging within a control

Sender and Source can be the same control! It's not prohibited in any way. Let's add the ability to a TextView, to change its nodes location. Since TextView is in automatic DragMode, you don't need to start the drag by using DragBegin(). It's started automatically on mouse moved with left button hold.

Make sure you have "Accept:=true;" inside the DragOver operation for TreeView source.

Modify the DragDrop event handler to the following:

procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  tv     : TTreeView; 
  iNode  : TTreeNode;  
begin
  tv := TTreeView(Sender);      { Sender is TreeView where the data is being dropped  }
  iNode := tv.GetNodeAt(x,y);   { x,y are drop coordinates (relative to the Sender)   }   
                                {   since Sender is TreeView we can evaluate          }
                                {   a tree at the X,Y coordinates                     } 
 
  { TreeView can also be a Source! So we must make sure }                                
  { that Source is TEdit, before getting its text }      
  if Source is TEdit then       
    tv.Items.AddChild(iNode, TEdit(Source).Text) {now, we can add a new node, with a text from Source }
 
  else if Source = Sender then begin         { drop is happening within a TreeView   }
    if Assigned(tv.Selected) and             {  check if any node has been selected  }
      (iNode <> tv.Selected) then            {   and we're dropping to another node  }
    begin
      if iNode <> nil then 
        tv.Selected.MoveTo(iNode, naAddChild) { complete the drop operation, by moving the selectede node }
      else
        tv.Selected.MoveTo(iNode, naAdd); { complete the drop operation, by moving in root of a TreeView }
    end;
  end;
end;

That's it. If you run the application now, you should have both features working.

  • Adding a new node by dragging text from Edit to TreeView
  • Dragging nodes inside the treeview

Hints

  • You can?/cannot? use some global data to inspect what is being dragged now. Don't use global variables, but your form class's fields.
    • Put the data when you start the drag
    • Inspect the data, during control's drag over event, and modify Accept flag accordingly
    • Read and use the data on drop event

Dragging from other applications

You can drag/drop

Files

Files can be dropped and handled easily by implementing the FormDropFiles event, once AllowDropFiles on the form is set:

procedure TForm1.FormDropFiles(Sender: TObject; const FileNames: array of String);
var FileName : String;
begin
  for FileName in FileNames do
  begin
    ShowMessage(FileName);
  end;
end;

Text etc

You can drag and drop e.g. text from another application (e.g.t notepad) to a control on your application. The way this is implemented is platform-dependent.

Windows

This code lets you drag and drop selected text from other applications onto an edit control.

unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  Windows, ActiveX, ComObj;
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm, IDropTarget)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    // IDropTarget
    function DragEnter(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
    function DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
    function DragLeave: HResult;StdCall;
    function Drop(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD):HResult;StdCall;
    // IUnknown
    // Ignore referance counting
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    { public declarations }
  end;
 
 
var
  Form1: TForm1;
 
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  OleInitialize(nil);
  OleCheck(RegisterDragDrop(Handle, Self));
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
  RevokeDragDrop(Handle);
  OleUninitialize;
end;
 
function TForm1.DragEnter(const dataObj: IDataObject; grfKeyState: DWORD;
  pt: TPoint; var dwEffect: DWORD): HResult; StdCall;
begin
  dwEffect := DROPEFFECT_COPY;
  Result := S_OK;
end;
 
function TForm1.DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD
  ): HResult; StdCall;
begin
  dwEffect := DROPEFFECT_COPY;
  Result := S_OK;
end;
 
function TForm1.DragLeave: HResult; StdCall;
begin
  Result := S_OK;
end;
 
function TForm1._AddRef: Integer; stdcall;
begin
   Result := 1;
end;
 
function TForm1._Release: Integer; stdcall;
begin
   Result := 1;
end;
 
function TForm1.Drop(const dataObj: IDataObject; grfKeyState: DWORD;
  pt: TPoint; var dwEffect: DWORD): HResult; StdCall;
var
  aFmtEtc: TFORMATETC;
  aStgMed: TSTGMEDIUM;
  pData: PChar;
begin
  {Make certain the data rendering is available}
  if (dataObj = nil) then
    raise Exception.Create('IDataObject-Pointer is not valid!');
  with aFmtEtc do
  begin
    cfFormat := CF_TEXT;
    ptd := nil;
    dwAspect := DVASPECT_CONTENT;
    lindex := -1;
    tymed := TYMED_HGLOBAL;
  end;
  {Get the data}
  OleCheck(dataObj.GetData(aFmtEtc, aStgMed));
  try
    {Lock the global memory handle to get a pointer to the data}
    pData := GlobalLock(aStgMed.hGlobal);
    { Replace Text }
    Memo1.Text := pData;
  finally
    {Finished with the pointer}
    GlobalUnlock(aStgMed.hGlobal);
    {Free the memory}
    ReleaseStgMedium(aStgMed);
  end;
  Result := S_OK;
end;
 
 
end.

Source forum: http://forum.lazarus.freepascal.org/index.php/topic,25769.msg156933.html#msg156933

免责声明:文章转载自《delphi Drag and Drop sample 鼠标拖放操作实例》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Golang 编码规范c语言获取linux的CPU、内存、IO、磁盘、网速(本机编译通过)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MFC录制音频和播放音频

一、录制音频   在windows中提供了相应的API函数(waveIn这个族的函数)实现录音功能;在使用这些函数时,一定要引入相应的头文件 #include <windows.h> #include <Mmsystem.h> #pragram comment(lib, "Winmm.lib") 1、在开始录音之前,需要首先定义音频...

delphi函数调用约定

指令 参数存放位置 参数传递顺序 参数内存管理 使用地方 Register CPU寄存器 从左到右 被调用者 默认,published属性存取方法必须使用 Pascal 栈 从左到右 被调用者 向后兼容 Cdecl 栈 从右到左 调用者 调用c/c++共享库 Stdcall 栈 从右到左 被调用者 API调用 Safecall 栈...

Delphi中的ObjectList简单用法一则

最近项目中需要搞很多个同一类对象的管理和操作,我居然还想用数组array来实现。在当当的教育下,开始研究TObjectList。Delphi中将一系列对象进行数组形式的维护,TObjectList是一个不错的实现方法。他帮助我们添加、计数、删除、释放一个List列表中的内容。基本实现不难,自己做一个类,把对象数组像List一样封装到Items中去,然后根据...

Delphi中的Sender:TObject对象解析

procedure TForm1.Button1Click(Sender: TObject); begin end; 解析:Procedure是过程,TForm是窗体类,加上数字就是某个窗体,像TForm1就是Form1窗体。 Button1是你的按钮控件的名称,Button1Click就是按钮的单击事件,(Sender:Tobject)就是发送消息到对象...

【VC++积累】之五、进程注入技术

注入:就是把我的代码,添加到已经远行的远程进程的方法; 在WinNT以后的系列操作系统中,每个进程都有自己的4GB私有进程地址空间,彼此互不相关。 如 :   进程A中的一个地址,比如:0x12345678,到了进程B中的相同地方,存的东西完全不一样,或者说不可预料。            所以说如果进程A想要看看或者修改进程B地址空间中的内容,就必须深入...

WPF笔记(3)TreeView

TreeView表示的是层次化结构(hierarchical)数据。TreeView的每一项称为TreeViewItem。TreeViewItem既可以被定义成一个字符串,也可以是一个嵌套的Item对象集合。TreeView的类层次结构如下: ControlItemsControlHeaderedItemsControlMenuItemToolBarTre...