C++빌더 개발자들이 드래그&드롭은 가끔씩 사용하고 자료를 구하기 어렵지도 않습니다.
가장 복사해서 사용하기 쉬운 형태는 아마도 폼 파일 헤더파일에 아래와 같이 처리하는 것이 아닌가 합니다.
//---------------------------------------------------------------------------
class TFormMain : public TForm
{
__published: // IDE-managed Components
TLabel *Label1;
TListView *ListView1;
TBitBtn *BitBtn1;
TBitBtn *BitBtn2;
TLabel *Label2;
TBitBtn *BitBtn3;
void __fastcall FormCreate(TObject *Sender);
void __fastcall BitBtn1Click(TObject *Sender);
private: // User declarations
protected:
protected:
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_DROPFILES, TWMDropFiles, WMDropFiles) // 드롭 메시지 받아 들이기.
END_MESSAGE_MAP(TForm)
// 드래그&드롭 메시지 핸들러
//
void __fastcall WMDropFiles(TWMDropFiles &msg)
{
int nFileCount = DragQueryFile((HDROP)msg.Drop, 0xFFFFFFFF, NULL, 0);
for(int c = 0; c < nFileCount; c++)
{
char filename[MAX_PATH];
int nLength = DragQueryFile((HDROP)msg.Drop, c, filename, MAX_PATH);
// Do something..
if (nLength)
AddFile(filename);
}
DragFinish((HDROP)msg.Drop);
}
// 드래그 받을 WinControl을 지정하는 부분. FormCreate 핸들러에서 호출하는게 좋음.
//
void InitDragAccept()
{
DragAcceptFiles(Handle, true);
}
public: // User declarations
__fastcall TFormMain(TComponent* Owner);
void AddFile(String filename);
};
//---------------------------------------------------------------------------
헤더는 이렇게 하면 되고, FormCreate 이벤트 핸들러에서 이렇게 호출해주면
드래그&드롭을 받아 들일 준비가 됩니다.
//---------------------------------------------------------------------------
void __fastcall TFormMain::FormCreate(TObject *Sender)
{
InitDragAccept();
}
//---------------------------------------------------------------------------
드래그&드롭이 일어나면 WM_DROPFILES 메시지가 발생하고,
이를 처리해주면 됩니다.
소스를 보면 쉽게 알수 있듯이 몇개의 파일 또는 폴더 명이 드롭되었나 구한 뒤,
for 루프를 돌며 개개별로 실제 이름을 구해서 그것을 처리해주면 됩니다.
위와 같이 구성한 이유는 쉽게 복사해서 사용하기 위해서 저렇게 한 것입니다.
이렇게 하면 폼 전체가 Drag&Drop을 받아 들이게 됩니다.
여기까지는 쉽게 알수 있는데, 문제는 폼 위의 특정한 컨트롤만 드래그&드롭을 받아 들이게 하고 싶을때는
어떻게 하는게 가장 쉬울까요?
프로그램 만들다 보면 이런 요구가 가끔씩 생기게 되는데, 이는 어떻게 처리하는게 가장 간편할까요?
이에 대한 코드로 컴포넌트 교체식으로 구현해 봤는데,
역시 마찬가지로 가장 쉽게 복사해서 쓸수 있는 형태로 아래처럼 꾸몄습니다.
예제는 폼위의 TListView 에 파일명이 열거되게 하고 싶고, 여기에 드래그 & 드롭을 받아 들이게 하고 싶을때
처리하는 방법입니다.
void AddFile(String filename);
//---------------------------------------------------------------------------
// TListView 에만 드래그&드롭을 받는 방법
class TListView : public Comctrls::TListView
{
typedef Comctrls::TListView inherited;
public:
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_DROPFILES, TWMDropFiles, WMDropFiles) // 드롭 메시지 받아 들이기.
END_MESSAGE_MAP(inherited)
// 드래그&드롭 메시지 핸들러
//
void __fastcall WMDropFiles(TWMDropFiles &msg)
{
int nFileCount = DragQueryFile((HDROP)msg.Drop, 0xFFFFFFFF, NULL, 0);
for(int c = 0; c < nFileCount; c++)
{
char filename[MAX_PATH];
int nLength = DragQueryFile((HDROP)msg.Drop, c, filename, MAX_PATH);
// Do something..
if (nLength)
AddFile(filename);
}
DragFinish((HDROP)msg.Drop);
}
// 드래그 받을 WinControl을 지정하는 부분. FormCreate 핸들러에서 호출하는게 좋음.
//
void InitDragAccept()
{
DragAcceptFiles(Handle, true);
}
};
#define TListView ::TListView
//---------------------------------------------------------------------------
이 부분을 헤더 파일
class TForm1 : public TForm
바로 위에 두면 됩니다.
그러면 TListView 컨트롤만 드래그&드롭을 받을 수 있습니다.
초기화는 FormCreate 핸들러에서 아래처럼 ListView1 인스턴스가 드래그&드롭을 받아 들이게 해주면 됩니다.
//---------------------------------------------------------------------------
void __fastcall TFormMain::FormCreate(TObject *Sender)
{
ListView1->InitDragAccept();
}
//---------------------------------------------------------------------------
이렇게 하면 TListView 의 인스턴스 중에서 ListView1 만 드래그&드롭을 받아 들이게 됩니다.
TListView 뿐만 아니라 TWinControl 후손이면
모든 컨트롤에 이와 같은 방법을 쓸수 있습니다.
들어온 파일명은 대략 아래처럼 처리할 수 있고요...
//---------------------------------------------------------------------------
//
void TFormMain::AddFile(String filename)
{
TListItems *items = ListView1->Items;
TListItem *item = items->Add();
item->Caption = filename;
}
void AddFile(String filename)
{
FormMain->AddFile(filename);
}
//---------------------------------------------------------------------------
이렇게 컴포넌트 교체식으로 해결하지 않으면
메시지 핸들러를 서브클래싱해서 해결하면 됩니다.
이 부분은 장성호님 강좌가 잘 되어 있으니 보시면 됩니다.
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tip&no=622
그럼.
다음과 같이 코딩하면 됩니다. 빌더와 마찬가지로 매우 간편하게 처리 가능합니다.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ShellApi;
type
TListView = class(ComCtrls.TListView)
protected
procedure WMDropFiles(var msg: TWMDropFiles); message WM_DROPFILES;
public
procedure InitDragAccept();
end;
TForm1 = class(TForm)
Button1: TButton;
ListView1: TListView;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
procedure AddFile(filename: string);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
ListView1.InitDragAccept;
end;
procedure TForm1.AddFile(filename: string);
var
item: TListItem;
begin
item := ListView1.Items.Add;
item.Caption := filename;
end;
procedure AddFile(filename: string);
begin
Form1.AddFile(filename);
end;
{ TListView }
procedure TListView.InitDragAccept;
begin
DragAcceptFiles(Handle, true);
end;
procedure TListView.WMDropFiles(var msg: TWMDropFiles);
var
c,
nFileCount: integer;
filename: array[0..MAX_PATH] of char;
begin
nFileCount := DragQueryFile(msg.Drop, $FFFFFFFF, nil, 0);
for c := 0 to nFileCount - 1 do begin
DragQueryFile(msg.Drop, c, filename, MAX_PATH);
AddFile(filename);
end;
DragFinish(msg.Drop);
end;
end.