2017-06-17 71 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 –