I am using a piece of software that David Heffernan has shared here:
How can I allow a form to accept file dropping without handling Windows messages?
I have changed the component from a Form to a Panel, and that works fine in the MainForm.
However, when using in a ShowModal()
environment, I do not have access to the values of components in the modal Form.
I made a small demo to show the problem.
Unit1:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Unit2;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
protected
Public
End;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
Var aForm: TForm2;
Begin
aForm := TForm2.Create(Nil);
Try
aForm.ShowModal;
Finally
aForm.Free;
End;
End;
End.
Unit2:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, DragAndDrop;
type
TmyPanel = class(TPanel, IDragDrop)
private
FDropTarget: TDropTarget;
// implement IDragDrop
function DropAllowed(const FileNames: array of string): Boolean;
procedure Drop(const FileNames: array of string);
protected
// procedure WMDropFiles(var Message: TWMDropFiles); message WM_DROPFILES;
procedure CreateWindowHandle(Const Params: TCreateParams); override;
procedure DestroyWindowHandle; override;
end;
TForm2 = class(TForm)
DropRefPanel: TPanel;
Label1: TLabel;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormActivate(Sender: TObject);
procedure Button1Click(Sender: TObject);
{ Private declarations }
public
{ Public declarations }
DropPanel: TmyPanel;
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TmyPanel.CreateWindowHandle(Const Params: TCreateParams);
begin
inherited;
FDropTarget := TDropTarget.Create(WindowHandle, Self);
end;
procedure TmyPanel.DestroyWindowHandle;
begin
FreeAndNil(FDropTarget);
inherited;
end;
function TmyPanel.DropAllowed(const FileNames: array of string): Boolean;
begin
Result := True;
end;
procedure TmyPanel.Drop(const FileNames: array of string);
Var i: Integer;
begin
// Form2.Label1.Caption := 'Drop';
begin
for i := 0 to Length(FileNames)-1 do
ShowMessage(Form2.Label1.Caption + ': ' + FileNames[i]);
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
showMessage(Label1.Caption);
Label1.Caption := 'Button';
end;
procedure TForm2.FormActivate(Sender: TObject);
begin
Label1.Caption := 'Activation';
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
Label1.Caption := 'FormCreate';
DropRefPanel.Color := $00BBF7B3 -50;
DropPanel := TmyPanel.Create(Form2);
with DropPanel do
begin
Parent := DropRefPanel;
Left := 3;
Top := 3;
Width := DropRefPanel.Width - 6;
Height := DropRefPanel.Height - 6;
Visible := True;
ParentBackground := False;
Color := $00BBF7B3;
BevelOuter := bvLowered;
Caption := 'DropBox';
end;
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FreeAndNil(DropPanel);
end;
end.
The DragAndDrop
unit has the unchanged code as made by David.
I have demonstrated the problem here with Label1
.
When I am in the Drop()
procedure and ask for the Form2.Label1.Caption
, it shows the Label1
value from design-time, or the value as assigned in FormCreate()
, but not the value from FormActivate()
or assigned later in the program.
It looks like there are 2 forms.
PS1: Works fine when in the MainForm.
PS2: Changing Form2
to nil
in TmyPanel.Create(Form2)
does not change the behaviour.
PS3: Moving the Create()
function to OnActivate
does not change the behaviour.
PS4. I'm using Delphi XE4.
Does anyone have an idea what is wrong in this ShowModal()
example?
When showing the modal Form, you are creating a new instance of TForm2
but not assigning it to the global Form2
variable that Drop()
is trying to access. By default, that global variable is used only for an auto-created (ie design-time) TForm2
object, not a dynamically created object.
You are trying to assign the created TForm2
object as the Owner
of the TmyPanel
object, but you are using the global Form2
variable (which doesn't point at the created TForm2
object!) when you should be using FormCreate()
's implicit Self
parameter instead, eg:
DropPanel := TmyPanel.Create({Form2}Self);
Now, Drop()
can access the Form via the panel's Owner
property, eg:
procedure TmyPanel.Drop(const FileNames: array of string);
var
i: Integer;
myFrm: TForm2;
begin
myFrm := Owner as TForm2;
myFrm.Label1.Caption := 'Drop';
begin
for i := 0 to Length(FileNames)-1 do
ShowMessage(myFrm.Label1.Caption + ': ' + FileNames[i]);
end;
end;