Forms Delphi-表单创建和新创建表单上的对象
动态表单创建主题已经被涵盖了很多次,但我找不到解决我的问题的方法,所以我在这里。。。再次…:-) 我之前的问题让我认为,如果不是所有表单都是在启动时创建的,而是在需要时动态创建的,那么我的应用程序将启动得更快。 这基本上是正确的,当我只创建了主窗体和数据模块时,启动速度要快得多 点击按钮,下面是我用来创建和按需释放表单的代码(主要受Jerry Dodge和Craig Young的答案启发,感谢他们的帮助): 同样,这很好,但是在创建frmKeywords时,一个主表格网格应该由一个FDQuery填充,该FDQuery在显示表单时触发。 当然(或者我不会在这里),他补充道 在FormShow或FormCreate中,事件以“访问冲突错误”结束 所以我修改了我的创建代码,现在看起来像:Forms Delphi-表单创建和新创建表单上的对象,forms,delphi,Forms,Delphi,动态表单创建主题已经被涵盖了很多次,但我找不到解决我的问题的方法,所以我在这里。。。再次…:-) 我之前的问题让我认为,如果不是所有表单都是在启动时创建的,而是在需要时动态创建的,那么我的应用程序将启动得更快。 这基本上是正确的,当我只创建了主窗体和数据模块时,启动速度要快得多 点击按钮,下面是我用来创建和按需释放表单的代码(主要受Jerry Dodge和Craig Young的答案启发,感谢他们的帮助): 同样,这很好,但是在创建frmKeywords时,一个主表格网格应该由一个FDQuery
procedure TfrmWelcome.BtKeywordsClick(Sender: TObject);
var
F_Keywords : TfrmKeywords;
begin
F_Keywords := Tfrmkeywords.Create(nil);
try
F_Keywords.FDQuery1.Open;
F_Keywords.ShowModal;
finally
F_Keywords.FDQuery1.Close;
F_Keywords.Free;
end;
end;
(我甚至不确定FDQUery1.Close
在finally块中是否有用)
太好了,现在我的表单出现了,主数据网格中充满了数据
问题是,当用户在DBGrid1中单击时,所选记录的数据库id作为参数传递给辅助FDQuery,后者反过来用数据填充辅助DBGrid(DBGrid1中的主数据,DBGrid2中的子数据)
这是这样做的:
procedure TfrmKeywords.DBGridEh1CellClick(Column: TColumnEh);
var
kwid : Integer;
begin
frmKeywords.FDQuery2.Close; //Closing secondary query
kwid := FDQuery1.FieldByName('id').AsInteger; //Assigning kw_id according to selected row
frmKeywords.FDQuery2.ParamByName('kw_id').AsInteger := kwid; //Linking query2 parameter to kwid
frmKeywords.FDQuery2.Open; //Reopening query2 to display assets
end;
和之前一样,这里也有“访问冲突错误”。像FDQuery不存在可能吗
所以我的问题是:当动态创建表单时,该表单的所有可视和非可视组件是否都会自动创建?Dbgrids出现在我的表单上,似乎可以工作,因为数据已经显示(至少在其中一个中),但第二个FDQuery不想工作。
很明显,我遗漏了一些东西。我排除了数据模块上的FDConnection,因为FDQuery1可以工作,所以我没有想法
提前谢谢
数学问题在于,您正在访问预先声明的全局变量frmKeywords,该变量在您尝试访问它时为nil。相反,您正在实例化表单并将引用存储在局部变量中,这本身就很好,但不需要访问未分配的变量。因此,只需修改代码,如下所示:
procedure TfrmKeywords.DBGridEh1CellClick(Column: TColumnEh);
var
kwid: Integer;
begin
{ do not access the frmKeywords variable here; if you want to explicitly
hint yourself about accessing the current form instance, you can write
Self.FDQuery2.Close; but that Self is not necessary, e.g. the following
lines refer to the current form instance as well }
FDQuery2.Close;
kwid := FDQuery1.FieldByName('id').AsInteger;
FDQuery2.ParamByName('kw_id').AsInteger := kwid;
FDQuery2.Open;
end;
关于在数据集发布之前显式关闭数据集,您不需要在数据集发布之前显式关闭数据集。这在内部发生
最后一点注意,在更改参数值时,不必重新打开数据集来刷新数据视图。事实上,这不是我们想要的。您可以设置一个SQL查询,打开在DBMS上准备查询的数据集,然后您只需修改参数并调用以刷新视图,因此在您的情况下,代码可以简化为(不要忘记在发生这种情况之前的某个地方,但只需一次)FDQuery2:
或者查看主题,了解如何在不使用任何代码的情况下执行所需操作(它适用于您,因为您使用的是DB-aware控件)。问题在于,您正在访问预先声明的全局变量frmKeywords,在尝试访问它时,该变量为nil。相反,您正在实例化表单并将引用存储在局部变量中,这本身就很好,但不需要访问未分配的变量。因此,只需修改代码,如下所示:
procedure TfrmKeywords.DBGridEh1CellClick(Column: TColumnEh);
var
kwid: Integer;
begin
{ do not access the frmKeywords variable here; if you want to explicitly
hint yourself about accessing the current form instance, you can write
Self.FDQuery2.Close; but that Self is not necessary, e.g. the following
lines refer to the current form instance as well }
FDQuery2.Close;
kwid := FDQuery1.FieldByName('id').AsInteger;
FDQuery2.ParamByName('kw_id').AsInteger := kwid;
FDQuery2.Open;
end;
关于在数据集发布之前显式关闭数据集,您不需要在数据集发布之前显式关闭数据集。这在内部发生
最后一点注意,在更改参数值时,不必重新打开数据集来刷新数据视图。事实上,这不是我们想要的。您可以设置一个SQL查询,打开在DBMS上准备查询的数据集,然后您只需修改参数并调用以刷新视图,因此在您的情况下,代码可以简化为(不要忘记在发生这种情况之前的某个地方,但只需一次)FDQuery2:
或者看一下主题,看看如何在没有任何代码的情况下做你想做的事情(它适用于你,因为你使用的是DB-aware控件)。如果你按住Ctrl键并点击frmKeywords
,你将看到Delphi自动为你生成的标识符的默认全局定义(imho毫无帮助)
请注意,当您在运行时创建表单时,您正在将新创建的表单分配给一个完全不同的引用:F_关键字:=Tfrmkeywords.create(nil)代码>。请务必注意,这不会设置全局变量frmKeywords
。如果您调试了以下任何访问冲突:
- 在有问题的行上放置断点
- 在调试模式下运行
- 当你点击断点时,检查
frmKeywords
的值,你会发现它是nil
- 这就是AV触发的原因:你试图访问“无”的成员
提示:尽管表单和数据模块有一些特殊功能,但它们仍然是“普通对象”。因此,它们的行为与其他对象完全相同:
- 可以创建多个实例李>
- 引用仍然必须显式分配才能使用
- 对象方法可以很容易地引用它们自己的成员
(您可能知道上述内容,但正如您将看到的,有意识的提醒很重要。)
因此,您可能认为只需将运行时创建更改为frmKeywords:=Tfrmkeywords.Create(nil)代码>。是的,那会管用,但不是个好主意。不使用全局变量做得很好。因此,您应该删除Delphi创建的全局变量。此时,您将发现您的应用程序不再编译,因为您仍然有许多对全局变量的引用。例如
procedure TfrmKeywords.DBGridEh1CellClick(Column: TColumnEh);
var
kwid : Integer;
begin
frmKeywords.FDQuery2.Close;
kwid := FDQuery1.FieldByName('id').AsInteger;
frmKeywords.FDQuery2.ParamByName('kw_id').AsInteger := kwid;
frmKeywords.FDQuery2.Open;
end;
上面的3行将产生
procedure TfrmKeywords.DBGridEh1CellClick(Column: TColumnEh);
begin
{ dataset must be opened here, which means that FDQuery2.Open method has
been called before (but only once for the query) }
FDQuery2.ParamByName('kw_id').AsInteger := FDQuery1.FieldByName('id').AsInteger;
FDQuery2.Refresh;
end;
procedure TfrmKeywords.DBGridEh1CellClick(Column: TColumnEh);
var
kwid : Integer;
begin
frmKeywords.FDQuery2.Close;
kwid := FDQuery1.FieldByName('id').AsInteger;
frmKeywords.FDQuery2.ParamByName('kw_id').AsInteger := kwid;
frmKeywords.FDQuery2.Open;
end;