2017-06-17 140 views
1

动态表单创建主题已经被多次讨论过了,但是我找不到解决我的问题的东西,所以在这里我再次...... :-)德尔福 - 在新创建的表单上创建表单并创建对象

我之前的问题让我认为,如果不是所有表单都是在启动时创建的,而是在需要时动态创建的,那么我的应用程序的启动速度会更快。 大多数情况都是如此,当我只创建了我的主Form和Datamodule时,启动速度要快得多。

在按一下按钮,下面是我用它来创建的代码,并免费点播形式(主要是由什么我发现这里从杰里·道奇和克雷格·杨的回答启发,感谢他们的帮助):

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填充。 当然,(否则我就不会在这里),在FormShow或FORMCREATE事件添加

frmKeywords.FDQuery1.Open; 

与“访问冲突错误”结束。

所以我修改了创建代码,它现在的样子:

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,该辅助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不存在也许?

所以我的问题是:当你动态地创建一个窗体,窗体的所有视觉和非可视化组件都会自动创建? Dbgrid出现在我的表单上,似乎工作,因为数据显示(至少在其中一个),但第二个FDQuery只是不想工作。 我明显错过了这里的东西。我排除了FDConnection是在数据模块,因为FDQuery1的作品,所以我的想法......提前

感谢

数学

回答

4

的问题是,你正在访问的预声明全局变量frm关键字这就是当时你正试图访问它。相反,你正在实例化你的表单并在一个局部变量中存储一个引用,这本身就很好,而不是访问那个未赋值的变量。所以,只要修改代码如下所示:

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来刷新视图,所以在你的情况下代码可以简化为(不要忘记OpenFDQuery2某处发生这种情况之前,但只有一次):由于您使用DB意识到控制

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)话题,看看如何做你想做的,没有任何代码(这是适用于你)。

+0

这么简单,但离我所能想到的还很远。感谢十亿次,作品,我明白为什么:-) – Mathmathou

+0

不客气! :)我已经添加了一些关于控制数据集的注释。 – Victoria

+1

删除所有这些全局变量,它们是撒旦的产物。他们只存在模仿VB编码风格。你想编程,就好像你在写VB一样。 –

1

如果按Ctrl +点击frmKeywords,你会被带到该标识符,德尔福自动生成(恕我直言帮倒忙)为您的默认全局定义。

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

  • 在违规行上设置断点。
  • 以调试模式运行。
  • 当你点击断点时,检查frmKeywords的值,你会注意到它是nil
  • 这就是触发AV的原因:你试图访问“nothing”的成员。

提示:虽然形式和数据模块有一些特殊的功能,他们仍然“正常对象”。因此它们的行为与其他对象完全相同:

  • 可以创建多个实例。
  • 引用仍然必须明确分配使用。
  • 对象方法可以轻松引用自己的成员。

(你可能知道上述情况,但正如你所看到的,有意识的提醒是非常重要的。)


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

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

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

与全局变量相关的问题已经得到很好的研究,并且有关这个主题的大量信息。总之,它们严重妨碍了可维护性。你最好简单地删除Delphi为你生成的所有全局变量。 Imho,不幸的是,这些都是创建的,有一些微不足道的选择。我觉得所选择的方法会导致开始程序员的坏习惯。

+0

感谢您花时间向我解释所有这一切。花了我一些时间来消化,但最后,我最终评论了Delphi生成的** Type **和** Implementation **之间的'var frmKeywords:TfrmKeywords;',并且也清理了所有“自我”引用,有用。 – Mathmathou

+0

现在我还不清楚的是:如何从F_Keywords上的一个按钮单击另一个动态创建的表单,如何访问Dbgrid或我想更改的标签? – Mathmathou

+0

也许我在这里回答不同的问题可能会有所帮助:https://stackoverflow.com/a/5777482/224704 –