2013年2月10日星期日

How to using Macro

宏带给人的印象坏的远多于好的,即使没有写过程序的人也知道宏病毒,每次打开带有宏的Excel的时候都要提醒你是否要打开,打开可能会染毒的警告.
从程序实现的角度讲,Axapta中的宏跟Excel中的宏没什么区别,都是一段可执行代码,或者一个变量的定义,当然打开Axapta的时候不会因为有宏的存在就提醒你要不要打开Axapta,呵呵.
另外对于X++的程序员来说,宏带来的也是负面多余正面,因为宏不能精确定义出错的行,写起代码来确实很不方便.
但是一个事物存在总有它的道理,所谓存在即合理.
宏有什么好处那?我的理解是宏提供了一个集中管理代码的方式,这不正是所谓的OO要达到的封装变化的目的吗?不能用类和方法代替吗?大多数情况下是可以的,但是也有例外.
我接触最多的关于宏的使用是pack,unPack这两个方法的使用,还有就是库存模块中的几个宏.
随便打开一个继承自Runbase的类,都会在classdeclaration发现如下宏的定义:
    #define.CurrentVersion(1)
    
#define.version1(1)
    #localmacro.CurrentList
         localVar1,
         localVar2
    #endmacro
其中localvar1,localVar2等是类中的变量,CurrentVersion代表存放在数据库中的变量的版本.
在pack和unpack中使用的情况如下(以pack为例)
public container pack()
{
    
return [#CurrentVersion,#CurrentList];
}
当然这个可以用get方法或者直接把变量放到[]里来实现,但我这里用宏更简单,至少可以在类定义的地方修改,不是吗?
如果说上述需求还可以变通实现的话,库存模块中的几个宏,我还真想不出变通的方法.以#InventDimJoin为例,该宏的定义如下:
/* %1 InventDimId           */
/* %2 InventDim             */
/* %3 InventDimCriteria     */
/* %4 InventDimParm         */
/* %5 Index hint            */

join tableId from 
%2
    #ifnot.empty(
%5)
        index hint 
%5
    
#endif
    where (
%2.InventDimId       == %1&&
          (
%2.ConfigId          == %3.ConfigId              || ! %4.ConfigIdFlag)           &&
          (
%2.InventSizeId      == %3.InventSizeId          || ! %4.InventSizeIdFlag)       &&
          (
%2.InventColorId     == %3.InventColorId         || ! %4.InventColorIdFlag)      &&
          (
%2.InventLocationId  == %3.InventLocationId      || ! %4.InventLocationIdFlag)   &&
          (
%2.InventBatchId     == %3.InventBatchId         || ! %4.InventBatchIdFlag)      &&
          (
%2.WMSLocationId     == %3.WMSLocationId         || ! %4.WMSLocationIdFlag)      &&
          (
%2.WMSPalletId       == %3.WMSPalletId           || ! %4.WMSPalletIdFlag)        &&
          (
%2.InventSerialId    == %3.InventSerialId        || ! %4.InventSerialIdFlag)

#InventDimDevelop
调用代码,以InventSumPhysical类中的setValueQty为例:
select sum(physicalValue),sum(received),sum(deducted),sum(registered),sum(picked) from inventSum
            where inventSum.itemId      
== itemId       &&
                  inventSum.closed      
== NoYes::No
        #inventDimJoin(inventSum.InventDimId,inventDim,inventDimCriteria,inventDimParm);
类方法中的代码跟宏#inventDimJoin拼凑成了一段代码,还真想不出除了宏还有什么其他办法可以这样玩,当然可以把这些宏直接硬编码到各个类方法中,但是这样的结果是灾难性的,正是这里采用了宏,才使得增加物料维组有了可能,要不然每增加一个物料维组,修改这些代码就要累死人.
引自Farseer的博客

How to using x++ code create report

  QueryRun                qr;
  ProdTable               _pt;
  ReportDesign            reportDesign;
  ReportAutoDesignSpecs   reportAutoDesignSpecs;
  ReportSection           RS1,RS2,RS3;
  ReportStringControl     RSC,RSC2;
  ReportTextControl       RTC;
  FormDataSource          ProdTable_ds;
  ProdTable               PD;
  ;
  reportDesign                = element.design();
  reportAutoDesignSpecs       = reportDesign.autoDesignSpecs();
  reportDesign.orientation(2);//Landscape    //info( int2str(reportDesign.orientation()));
  qr                          = new QueryRun(this);
  ProdTable_ds                = element.args().record().dataSource();
  RS1                         = reportAutoDesignSpecs.addProgrammableSection(1);
  RSC                         = RS1.addStringDisplayControl("method2");
  RSC.left(40,Units::Char);
  RTC =   RS1.addTextControl("HELO");
  RS2                         = reportAutoDesignSpecs.addProgrammableSection(2);
  RSC2                         = RS2.addStringDisplayControl("method2");
  RSC2.left(40,Units::Char);
  RTC = RS2.addTextControl("HELO2");
  this.execute(1);
  this.newPage();
  this.execute(2);
  RTC.text("HELO3");

How to achieve allow user selections multiple value on Dialog Field


用户要求在执行某个动作之前弹出一个对话框,让他选择一些供应商,只针对这些供应商去做动作,一下就会想到用Dialog这个类去做,于是写出如下代码:

static void dialogDemo(Args _args)
{
    Dialog              dialog 
= new Dialog();
    DialogField         dialogField;
    VendAccount         vendAccount;
    boolean             retValue;
    ;
    
    dialogField 
= dialog.addField(typeId(VendAccount));
    retValue 
= dialog.run();
    
    
if(retValue)
    {
        vendAccount 
= dialogField.value();
        
//do something else
        
    }

}
一切都看似完美,但是有个问题,只能选择一个供应商,用户要求的是选择多个供应商,控件的replaceOnLookup属性是用来控制这个的,但是dialogField没有这个方法,咋办?添上。
在类DialogField上添加方法replaceOnLookup,如下所示:

void replaceOnLookup(boolean r)
{
    str name;

    
// If properties exists then we are on server
    if (properties)
    {
        name 
= #PropertyReplaceonlookup;
        
if (! properties.exists(name))
            properties.add(name,
true);
        properties.value(name,r);
    }
    
else
        
this.fieldControl().replaceOnLookup(r);
}
修改一下unpack方法,加上我们新增的属性
case #PropertyReplaceonlookup:
     
this.replaceOnLookup(unpackedProperties.valueIndex(i));
     
break;
这样就可以调用这个方法来改变控件的属性了。

static void dialogDemo(Args _args)
{
    Dialog              dialog 
= new Dialog();
    DialogField         dialogField;
    VendAccount         vendAccount;
    boolean             retValue;
    ;
    
    dialogField 
= dialog.addField(typeId(VendAccount));
    dialogField.replaceOnLookup(
false);
    retValue 
= dialog.run();
    
    
if(retValue)
    {
        vendAccount 
= dialogField.value();
        
//do something else
        
    }

}
效果如下图所示: