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. |