RicH and FamouS

       Home         Glosar IT                                                                                                                                                                                                              SUBSCRIBE NOW!
        

11.05.2009

Drag and drop cu fisiere (Delphi)

Drag and drop cu fisiere din windows explorer intr-un form Delphi

Intro

Am cautat ceva vreme pana sa aflu ca pt a trage fisiere in fereastra aplicatiei mele scrise in Delphi trebuie sa folosesc functii din WinAPI.
Am gasit raspunsul, am mai citit si pe msdn, am mai incercat ceva si am ajuns la concluzia ca trebuie inmultite locurile in care se poate gasi informatii despre subiectul asta.

Pe scurt
Deci, pentru a trage fisiere intr-un control VCL, trebuie ca acel control sa fie marcat folosind functia DragAcceptFiles.
Apoi, fisierele care au fost tarate pe control se obtin cu DragQueryFile, care, pt ca e o scumpa, poate avea vreo 3 scopuri, in functie de parametrii pe care ii primeste.
In final, incheierea operatiei de drag and drop se marcheaza cu DragFinish.
Procesarea fisierelor tarate si apelarea lui DragFinish se fac intr-o functie care e handler pentru mesajul WM_DROPFILES (in exemplul dat, functia care face asta e procedure ProcessDroppedFiles(var msg: TMessage); message WM_DROPFILES;).

Exemple
Dau si 2 exemple de cod care se joaca cu functiile astea cu scopul de a pune intr-un listbox numele fisierelor pe care le taram oriunde pe form (primul exemplu), respectiv doar pe listbox (al doilea exemplu).

Primul exemplu
In primul exemplu e nevoie de o aplicatie noua VCL Forms care sa contina:
- un form
- un listbox pe form
- un panou (TPanel) pe form
- optional orice alte elemente (dar pe astea le puneti dupa ce dati copy-paste la cod; celelalte trebuiesc puse inainte de copy-paste)
Daca vreti sa nu scrieti cod, lasati numele implicite pentru obiectele de mai sus.
In fisierul corespunzator form-ului adaugat se pune codul urmator:


Cod:
unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, ShellAPI;

type
TForm1 = class(TForm)
ListBox1: TListBox;
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure ProcessDroppedFiles(var msg: TMessage); message WM_DROPFILES;
end;

var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
DragAcceptFiles(self.Handle, true); // permite sa se faca drag and drop cu fisiere din afara aplicatiei
end;

procedure TForm1.ProcessDroppedFiles(var msg: TMessage);
var
nFilesNumber: integer;
nFileLength: integer;
nCounter: integer;
pacFileName: PAnsiChar;
begin
GetMem(pacFileName, 0);
// ia numarul de fisiere
nFilesNumber := DragQueryFile(msg.WParam, $FFFFFFFF, pacFileName, 0);
for nCounter := 0 to nFilesNumber - 1 do
begin
// ia lungimea numelui fisierului
nFileLength := DragQueryFile(msg.WParam, nCounter, nil, 0);
GetMem(pacFileName, nFileLength + 1);
// ia numele fisierului
DragQueryFile(msg.WParam, nCounter, pacFileName, nFileLength + 1);
// il adauga in listbox
self.ListBox1.Items.Add(pacFileName);
end;

//anunta ca a terminat operatia
DragFinish(msg.WParam);
end;

end.

Intermezzo
Acum, daca se tarasc fisiere oriunde pe form, acestea vor fi adaugate in listbox. Se poate testa prin tragerea fisierelor pe panoul care a fost pus pe form, sau cu celelalte controale pe care eventual le-ati adaugat.

Toate bune si frumoase daca nu vrei musai sa poti trage fisierele doar pe listbox, pt ca poate iti trebuiesc celelalte controale la altceva (sau poate esti perfectionist si ti se pare ca metoda prezentata e "murdara").
In cazul in care vrei sa tragi doar pe listbox, ai 3 variante: una urata, dar simpla si repede scrisa (daca tot vbim aici despre cod scris intr-un mediu RAD, asta ar trebui sa conteze), alta draguta si chiar mai usor de scris si o alta frumoasa, dar mai complicata si mai greu scrisa.

Cea simpla si urata e sa se puna o conditie care sa testeze in ce control s-a dat drumul fisierelor tarate:

Cod:
if self.ControlAtPos(self.ScreenToClient(Mouse.CursorPos), false, true) = self.ListBox1
then self.ListBox1.Items.Add(pacFileName);

Avand in vedere ca drag and drop-ul se face cu mausul, e corect sa se verifice daca obiectul de sub maus e controlul care ne intereseaza.
In functie de controlul care trebuie sa primeasca fisierele tarate, functia ControlAtPos s-ar putea sa aibe nevoie de alti parametri.

Cea draguta si foarte simpla e sa se dea voie să se faca drag and drop doar în listbox, nu in tot form-ul.

Cod:
DragAcceptFiles(self.ListBox1.Handle, true);

Al doilea exemplu
Cealalta varianta consta in crearea unei clase proprii derivate din controlul pe care vrem sa taram fisierele (TListBox, in cazul de fata).
Evident asta prezupune ca respectivul control sa fie initializat la pornirea aplicatiei, si toate cele, lucru destul de neapetisant.
Codul pt o asemenea varianta este asta:

Cod:
unit Unit2;
interface
uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, ShellAPI;
type
TListBoxPersonalizat = class(TListBox)
public
constructor Create(AOwner: TComponent); override;
procedure ProcessDroppedFiles(var msg: TMessage); message WM_DROPFILES;
end;

TForm2 = class(TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
ListBoxDeTest: TListBoxPersonalizat;
end;

var
Form2: TForm2;
implementation
{$R *.dfm}
constructor TListBoxPersonalizat.Create(AOwner: TComponent);
begin
inherited;
self.Left := 0;
self.Top := 0;
self.Width := 400;
self.Height := 150;
self.Parent := TWinControl(AOwner); // seteaza parintele listbox-ului
// daca nu e setat, atunci functia DragAcceptFiles face urat
DragAcceptFiles(self.Handle, true);
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
self.ListBoxDeTest := TListBoxPersonalizat.Create(self); // creeaza listbox-ul de test
end;

procedure TListBoxPersonalizat.ProcessDroppedFiles(var msg: TMessage);
var
nFilesNumber: integer;
nFileLength: integer;
nCounter: integer;
pacFileName: PAnsiChar;
begin
GetMem(pacFileName, 0);
// ia numarul de fisiere
nFilesNumber := DragQueryFile(msg.WParam, $FFFFFFFF, pacFileName, 0);
for nCounter := 0 to nFilesNumber - 1 do
begin
// ia lungimea numelui fisierului
nFileLength := DragQueryFile(msg.WParam, nCounter, nil, 0);
GetMem(pacFileName, nFileLength + 1);
// ia numele fisierului
DragQueryFile(msg.WParam, nCounter, pacFileName, nFileLength + 1);
// il adauga in listbox
self.Items.Add(pacFileName);
end;

//anunta ca a terminat operatia
DragFinish(msg.WParam);
end;

end.

    Blog din Moldova    FastCounter 

 
Copyright © 2008-2010 Foster1. All rights reserved.