2

我正在尝试Caption从另一个表单访问 dbgrid.field 的。

我在这里使用 MDI,两种形式都是 MDIChildren。

我试图ShowMessage从另一个表单执行以下操作,但它导致了访问冲突:

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

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

使用已设置在 2 种形式之间。

错误信息是

模块中地址 000010363F9 的访问冲突。读取地址 000000006F0。

我在这里想念什么?


更新:这是这种情况的精确复制(RME)。

MDI 父级

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.

错误信息

模块“Project7.exe”中地址 0081B314 的访问冲突。读取地址 000003D0。

4

2 回答 2

2

可能,其中一个对象没有被分配,我怀疑它可能是Columns[1](注意Columns集合是从零开始的索引,所以第一列是Columns[0]

尝试这个:

if(not Assigned(Form1)) then 
  raise Exception.Create('Form1 not assigned');

if(not Assigned(Form1.DBGrid1)) then 
  raise Exception.Create('Form1.DBGrid1 not assigned');

if(Form1.DBGrid1.Columns.Count < 2) then 
  raise Exception.Create('Form1.DBGrid1 has not the Columns[1] item');
于 2020-02-25T07:36:50.667 回答
1

编写类似的代码Form1Unit.ChildForm1.dbgrd1.Columns[2].Title.Caption)正在制造等待发生的事故,因为它假定您要操作的 ChildForm1 的实例是自动创建的ChildForm1

使用自动创建的表单(可能是主表单除外)通常被认为是不好的做法,因为它会鼓励这样的事故,所以最好改掉使用它们的习惯。

从另一个表单(或数据模块)访问一个表单(或数据模块)的一种不太容易发生事故的技术是以需要您指定要操作的对象实例的方式在“其他”表单上编写代码。像这样的东西:

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

procedure TChildForm2.btn1Click(Sender: TObject);
begin
  DoSomethingWithForm1(Form1Unit.ChildForm1);
end;

这样做的意义在于,它迫使您考虑您指的是哪个 Form1 实例,因为当您拥有同一个表单的多个实例时(即使您没有,因为它可能提示您怀疑实例是否已创建)。

于 2020-02-25T12:27:18.713 回答