Farseer

导航

Lookup窗体简介

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

-

posted on 2006-12-20 21:16  佛西亚  阅读(1222)  评论(4编辑  收藏  举报