Axapta的Lookup表單提供了一種通用的供使用者選擇資料的機制,使用者可以很方便地實現資料的選擇.比如在建立訂單的時候在訂單主檔可很方便地選擇客戶,訂單明細可以方便地選擇物料等.
本文試圖解釋Lookup表單的實現方式和原理,大多數內容是Axapta的線上說明的重新組織,部分內容沒有參考資料,屬於猜測.
準備知識
1.控制項類型
Axapta中的表單控制項根據與資料來源的綁定情況可以分為三種,繫結控制項,非繫結控制項和計算控制項.
所謂繫結控制項是指該控制項的DataSource屬性指定了某個具體的DataSource,DataField指定了DataSource中的某個欄位.
非綁定則沒有指定具體的資料來源,而ExtendedDataType指定了某個類型.
計算控制項是指通過Display或者Edit方法擷取的傳回值.
2.幾個需要關注的控制項方法
FormStringControl,FormDateControl等控制項類有幾個方法用於實現Lookup機制.
Lookup方法,這個方法是在使用者點擊下拉框時首先觸發的方法,該方法的Super()方法會根據該控制項與資料來源是否綁定分別調用不同的方法:
如果是繫結控制項,則會調用performDBLookup方法,performDBLookup繼續調用DataSource中欄位的Lookup方法.
如果是非繫結控制項則會調用performTypeLookup方法,performTypeLookup繼續調用PerformFormLookup方法.
從上述描述可以看出根據控制項類型,LookUp會走兩條不同的路線:
繫結控制項: 控制項LookUp方法->控制項performDBLookup方法->FormDataObject的Lookup方法.
非繫結控制項: 控制項LookUp方法->控制項performTypeLookup方法->控制項PerformFormLookup方法.
上述方法的原型如下:
public final void performDBLookup( [fieldId _fieldId, tableId _tableId, selectableDataArea _company] )
public final void performTypeLookup( [int _userType, int _arrayIndex, selectableDataArea _company] )
public void performFormLookup(FormRun _form)
前兩個方法是final類型的不允許覆蓋.
需要解決的問題
要搞清楚Lookup的原理,需要整明白幾件事情:
1.點擊下拉框時展示資料的到底是什麼?
這個毋庸置疑是一個表單,也就是一個FormRun的執行個體對象,由於下拉框一般用Grid展示,所以大部分Form只有一個Grid控制項.
2.Grid中展示的資料是如何添加進去的?比如為什麼CustTable這個表單中的客戶組欄位的Lookup表單顯示了客戶組和和說明兩個欄位而不是其他的欄位?
對於繫結控制項,在文檔中沒有找到具體的說明是如何?的,不過對於Grid中所包含的欄位有個說明,是通過表之間的關係來實現的.比如CustTable中的客戶組這個欄位是CustGroup這個表的外鍵,於是Grid的欄位由表CustGroup的TitleField1和TitleField2這兩個屬性(分別為CustGroup 和Name),另外就是這兩個表之間的關聯欄位CustGroup,還有表CustGroup的第一個索引.如果這些選項有重複的話會去掉重複的.如果不想用預設的這些屬性,那麼可以在Field Groups中的AutoLookUp中添加項,這樣出來的就是AutoLookUp中的項了.
對於非繫結控制項,預設情況下跟繫結控制項是一樣的,根據控制項EDT屬性中指定的EDT類型找到對應的表,Grid中包含的欄位跟繫結控制項類似.
在上述兩種情況下,通過設定EDT類型中的FormHelp都可以改變Lookup表單,比如可以將CustGroupId改成CustGroup等,不過由於Lookup表單需要特殊定製,一般的表單是不能滿足條件的.
對於非繫結控制項,可以重載performFormLookup或者控制項的lookup方法,使其調用其他的表單,如線上說明中的代碼所示:void Lookup()
{
FormRun FR = New FormRun(New Args("ColorLookup"));
FR.Init();
this.PerformFormLookup(FR);
}
3.Lookup表單是怎麼建立出來的?
表單的建立可以用兩種方式:
a.在AOT中建立
b.用代碼建立
這兩種情形的結果是一樣的,最終在記憶體中都是一個FormRun對象.由於看不到performDBLookup的原始碼,我們只能根據performFormLookup這個方法還推測其原理.
用代碼建立表單AOT中有一個挺好的例子 類SysTableLookup,這個類用於動態建立一個FormRun對象,然後調用表單控制項的performFormLookup方法.下面的代碼是使用該類的用戶端代碼:
void lookup()
{
Query query = new Query();
QueryBuildDataSource queryBuildDataSource;
QueryBuildRange queryBuildRange;
// Create an instance of SysTableLookup where 'this' the current Form control.
SysTableLookup sysTableLookup = SysTableLookup::newParameters(tableNum(custTable), this);
;
// The field to be shown in the lookup form.
sysTableLookup.addLookupField(fieldNum(custTable, accountNum));
sysTableLookup.addLookupField(fieldNum(custTable, name));
// Limit and arrange data selection.
queryBuildDataSource = query.addDataSource(tableNum(custTable));
queryBuildRange = queryBuildDataSource.addRange(fieldNum(custTable, accountNum));
queryBuildRange.value('A..B');
sysTableLookup.parmQuery(query);
// Perform lookup
sysTableLookup.performFormLookup();
// do not call super().
// super()
}
上述代碼就可以建立一個Lookup表單,需要注意的是,如果newParameters的入參是暫存資料表的話,需要用parmTmpBuffer這個函數將當前的暫存資料表傳入進去,否則查不出任何資料.原因很簡單,看一下SysTableLookup這個類的FormRun方法就知道了,它會把傳入的表作為資料來源,如果是實際的物理表這沒任何問題,因為每次都是從資料庫中查詢,但是由於每一個暫存資料表都對應物理磁碟的一個檔案,這樣如果只傳入一個暫存資料表的表名,根據表名它沒有辦法知道去尋找應該對應哪個物理磁碟檔案.該類的FormRun方法用如下語句建立關聯:
if (tmpBuffer)
{
formDataSource.init();
formDataSource.cursor().setTmp(); // if using non-temp table in tmp mode
formDataSource.cursor().setTmpData(tmpBuffer);
}
OK,到這裡,下拉框裡的表單真相大白了,那麼這個表單怎麼會在使用者選擇完之後就自動關掉了,並且會將Grid中的某個特定的值賦值到對應的控制項上那?
這裡用到了FormRun裡的兩個方法,selectMode和selectTarget.
其中selectMode指定取值欄位,selectTarget則指定把值賦值到哪個控制項上.
其函數原型如下:
public final void selectMode( [FormControl _control] )
public final FormControl selectTarget( [FormControl _target] )
selectMode的調用在SysTableLookup中可以找到
formGridControl = formRun.control(idx);
formGridControl.setFocus();
formRun.selectMode(formRun.control(controlIdx))
selectTarget這個方法只是在FormRun這個類的線上說明中找到了它的用法說明,沒找到在Lookup表單怎麼使用的,不過可以肯定的是在performFormLookup這個方法中調用了formRun的selectTarget將值賦值到了控制項上.
最佳實務:
沒有規矩不成方圓,以後俺也關注一下BP,免得被說老土,呵呵.
1.只有系統不能自動產生Lookup表單時才考慮在AOT中建立自己的Lookup表單,這些表單必須以Lookup作為尾碼.通常情況下通過定製AutoLookup組就可以滿足要求了.
2.如果需要指定Query,顯示欄位或者兩者都要顯示,則應考慮用SysTableLookup類的功能.
3.手動建立的表單必須與系統自動產生的表單具有相同的功能,支援查詢運算式,根據輸入的查詢條件自動對焦到相應行.
4.為了避免Lookup表單閃爍晃動,需要在run方法中禁用自動查詢功能.範例程式碼如下:void run()
{
FormStringControl callerControl =
SysTableLookup::getCallerStringControl(element.args());
boolean filterLookup = false;
;
if (callerControl.text() && callerControl.hasChanged())
{
filterLookup = true;
Common_ds.autoSearch(false);
}
super();
if (filterLookup)
{
Common_ds.research();
Common_LookupField.filter(callerControl.text());
}
}
5.相關屬性的設定
AllowCheck
|
Data source |
No |
安全檢查需要關掉 |
AllowEdit
|
Data source |
No |
不允許編輯 |
AllowCreate
|
Data source |
No |
不允許建立 |
AllowDelete
|
Data source |
No |
不允許刪除 |
OnlyFetchActive
|
Data source |
Yes |
只取在Grid中展示的欄位 |
Frame
|
Design |
Border |
將不會在表單上顯示內容 |
WindowType
|
Design |
Popup |
- |
ShowRowLabel
|
Grid |
No |
- |