Delphi - 在新创建的表单上创建表单和对象

时间:2017-06-17 06:35:15

标签: forms delphi

动态表单创建主题已被多次覆盖,但我找不到解决问题的方法,所以我在......再次......: - )

我以前的问题让我认为,如果不是所有表单都是在启动时创建的,而是在需要时动态创建,我的应用程序会更快启动。 这大部分都是正确的,当我只创建主表单和数据模块时,启动速度会快得多。

点击按钮,这里是我用来创建和免费提供表格的代码(主要受到我在Jerry Dodge和Craig Young的答案中找到的启发,感谢他们的帮助):

procedure TfrmWelcome.BtKeywordsClick(Sender: TObject);
  var
    F_Keywords : TfrmKeywords;
  begin
    F_Keywords := Tfrmkeywords.Create(nil);
      try
        F_Keywords.ShowModal;
      finally
        F_Keywords.Free;
      end;
end;

同样,这样可以正常工作,但是在创建frmKeywords时,主表格网格应该由显示表单时触发的FDQuery填充。 当然(或者我不会在这里),添加

frmKeywords.FDQuery1.Open;
FormShow或FormCreate事件中的

最终会出现“访问冲突错误”。

所以我修改了我的创建代码,它现在看起来像:

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有效,所以我没有想法......

提前致谢

数学

2 个答案:

答案 0 :(得分:4)

问题是您在尝试访问它时正在访问预先声明的全局变量 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;

关于显式关闭数据集在发布之前,您不需要在发布之前显式Close数据集。这发生在内部。

最后一点,在更改参数值时,您不必重新打开数据集以刷新数据视图。它实际上不是想要的。您设置SQL查询,打开在DBMS上准备查询的数据集,然后您只是修改参数并调用Refresh来刷新视图,因此在您的情况下代码可以简化为(不要在此之前忘记{strong} {strong> FDQuery2 Open,但只有一次):

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;

或者查看Master-Detail Relationship (M/D)主题,了解如何在没有任何代码的情况下执行您想要的操作(由于您使用的是数据库感知控件,因此适用于您)。

答案 1 :(得分:1)

如果您在frmKeywords Ctrl +单击,您将被带到Delphi自动生成(imho unhelpfully)的标识符的默认全局定义你。

请注意,在运行时创建表单时,您将新创建的表单分配给完全不同的引用:F_Keywords := Tfrmkeywords.Create(nil);。请注意,这不会设置全局变量frmKeywords,这一点非常重要。如果您调试了以下任何访问冲突:

  • 在违规行上设一个断点。
  • 在调试模式下运行。
  • 当您点击断点时,请检查frmKeywords的值,您会发现它是 nil
  • 触发AV的原因是:您正试图访问“无”的成员。
  

提示:虽然表单和数据模块具有一些特殊功能,但它们仍然是“普通对象”。因此它们的行为与其他对象完全相同:

     
      
  • 可以创建多个实例。
  •   
  • 仍然必须明确指定参考文献。
  •   
  • 对象方法可以轻松引用自己的成员。
  •   
     

(你可能知道上面的内容,但正如你所看到的,有意识的提醒很重要。)

因此,您可能认为只需将运行时创建更改为frmKeywords := Tfrmkeywords.Create(nil);即可。是的,这可行,但是一个坏主意。你最好不要使用全局变量。所以你应该删除Delphi创建的全局变量。此时您将发现您的应用程序不再编译,因为您仍然有许多对全局变量的引用。 E.g。

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行会产生编译器错误。具有讽刺意味的是,这些引用完全没必要,因为FDQuery2TfrmKeywords的成员。如果您有多个表单实例,它们也会导致不正确的行为。除了可能的AV:即使frmKeywords确实引用了有效的实例,OnCellClick事件处理程序也可能会修改 错误格式的 查询!< / p>

维多利亚已经解释了上述解决这些问题的方法的简单改变。我想指出一些与使用全局引用相关的其他问题(除了你已经经历过的那些)。

  • 全局参考意味着可以从程序中的任何位置访问对象。
  • 这使得评估与全局变量交互的变更的影响变得更加困难。在一个看似完全不相关的应用领域中,一个单元的更改可能会产生意想不到的后果。
  • 全局变量使您的程序模块化变得更加困难,因为全局有效地将您的单独单元联系在一起。

与全局变量相关的问题已得到很好的研究,并且有大量有关该主题的信息。简而言之,它们严重妨碍了可维护性。你可以简单地删除Delphi为你生成的所有全局变量。 Imho,不幸的是,这些是创造的,有一些微不足道的替代品。而且我觉得所选择的方法会给初学程序员带来坏习惯。

相关问题