delphimodalpopup

ShowModal on a created Form, no access to modal Form


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?


Solution

  • 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;