delphimdichilddbgridmdiparenttform

Access DBGrid1.Columns[1].Title.Caption from another Form


I am trying to access the Caption of the dbgrid.field from another form.

I am using MDI here and both forms are MDIChildren.

I tried to execute the following ShowMessage from another form but it caused an access violation:

ShowMessage(Form1.DBGrid1.Columns[1].Title.Caption); // 1st try

ShowMessage(Unit1.Form1.DBGrid1.Columns[1].Title.Caption); // 2nd try

Uses set already between 2 forms.

The error message is

Access Violation at address 000010363F9 in module. Read of address 000000006F0.

What am I missing here?


UPDATE: Here's the exact replicate (RME) of this case.

MDI Parent

unit MainUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus;

type
  TParentForm = class(TForm)
    mmMenu: TMainMenu;
    miForm1: TMenuItem;
    miForm2: TMenuItem;
    procedure miForm1Click(Sender: TObject);
    procedure miForm2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ParentForm: TParentForm;

implementation

uses
  Form1Unit, Form2Unit;

{$R *.dfm}

procedure TParentForm.miForm1Click(Sender: TObject);
begin
  TChildForm1.Create(self).Show;
end;

procedure TParentForm.miForm2Click(Sender: TObject);
begin
  TChildForm2.Create(self).Show;
end;

end.

MDI ChildForm1

unit Form1Unit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Datasnap.DBClient,
  Datasnap.Provider, MemDS, DBAccess, Uni, UniProvider, MySQLUniProvider,
  Vcl.Grids, Vcl.DBGrids;

type
  TChildForm1 = class(TForm)
    dbgrd1: TDBGrid;
    ucn1: TUniConnection;
    mup1: TMySQLUniProvider;
    uq1: TUniQuery;
    dsp1: TDataSetProvider;
    cds1: TClientDataSet;
    ds1: TDataSource;
    smlntfldcds1actor_id: TSmallintField;
    strngfldcds1first_name: TStringField;
    strngfldcds1last_name: TStringField;
    dtmfldcds1last_update: TDateTimeField;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ChildForm1: TChildForm1;

implementation

uses
  MainUnit, Form2Unit;

{$R *.dfm}

end.

MDI ChildForm2

unit Form2Unit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TChildForm2 = class(TForm)
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ChildForm2: TChildForm2;

implementation

uses
  MainUnit, Form1Unit;

{$R *.dfm}

procedure TChildForm2.btn1Click(Sender: TObject);
begin
  ShowMessage(Form1Unit.ChildForm1.dbgrd1.Columns[2].Title.Caption);
end;

end.

Error Message

Access Violation at address 0081B314 in module 'Project7.exe'. Read of address 000003D0.


Solution

  • Writing code like Form1Unit.ChildForm1.dbgrd1.Columns[2].Title.Caption) is creating an accident waiting to happen, because it assumes that the instance of ChildForm1 you want to operate on is the auto-created ChildForm1.

    Using auto-created forms, except perhaps the main form, is generally considered to be bad practice because it encourages accidents like this, so it is probably best to get out of the habit of using them.

    A less accident prone technique for accessing one form (or datamodule) from another is to write the code on the "other" form in a way which requires you to specify the object instance to operate on. Something like this:

    procedure TChildForm2.DoSomethingWithForm1(Form1Instance : TForm1);
    begin
      ShowMessage(Form1Instance.dbgrd1.Columns[2].Title.Caption);
    end;
    
    procedure TChildForm2.btn1Click(Sender: TObject);
    begin
      DoSomethingWithForm1(Form1Unit.ChildForm1);
    end;
    

    The point of doing it that way is that it forces you to think about which Form1 instance you mean, because getting that right can be very important when you have multiple instances of the same form (and even when you don't, because it might prompt you to wonder whether the instance has been created).