MVC开发框架

栏目:u9cloud知识作者:用友来源:用友发布:2024-08-20浏览:1

MVC开发框架

UI的基本概念

UI技术架构图

生成的代码与MVC关系

 

 

UIModel的构成

整个UIModel实际上由3个部份构成,如图:UIModel

 

 

UIModel:装载数据,并记录数据间的关系;

CommonAction:对UIModel的数据进入填充和读取;

CommonCRUD:对数据进行保存、删除、查询等数据库操作。

 

UIModel的生命周期

UIModel作为MVC框架的Model部份,其生命周期连续在整个MVC运行过程中,如图:

当用户发出Web请求时,创建对应的UIModel对象,直接用户离开页面,UIModel对象失去所有的引用,被回收为至。

WebPart事件顺序

 

如何在合适的WebPart事件扩展出的接口编写代码,可以参考下图:

 

UIModel编程基础

针对UIModel的编程目前都在Action的ActionExtend文件中进行;

可以在代码中,从以下方面操作UIModel:

  1. 访问UIModel、UIView、UIRecord、UIField、UIFilter;
  2. 拷贝克隆UIModel、UIView、UIRecord;
  3. 创建实例UIModel、UIView、UIRecord、UIFilter、
  4. 根据应用场景,使用上述对象API;

UIModel的数据格式

数据从数据库到界面或从界面到数据库分别要经历以下过程:

 

读取过程:数据库->持久层->UIModel->表示层控件

保存过程:表示层控件->数据收集->UIModel->持久层->数据库

 

其中,关键的三个数据存储点是表示层控件,UIModel和持久层,详细如下:

表示层控件:由于控件只负责数据显示和数据收集,所以,对于控件来说,可以简单的把所有数据都看成String类型,即数据类型不敏感。

UIModel:UIModel是一个数据容器,它以dot Net的数据类型对数据进行管理,数据类型敏感。

持久层:持久层直接和数据库交涉,所以,它更多的使用数据库类型,即DBType。

 

正因为各层次对数据类型的需求不同,导致的数据值的不同,特别的NULL值时。

 

为了适应表示层和持久层对数据要求的不同,在UIModel中,对数据值进行了转换。如下图:

 

NULL值的转换规则:

 

从UIModel到持久层时:

FieldType为EnumField或RefField时,转换为-1

数字型时,转换为

日期型、Byte、Char时,转换为对象的MinValue值

字符型时,转换为String.Empty

Bool型时,转换为False

 

从控件到UIModel时:

如果收集的数据可以转换为对应的类型时,则转换为对应的类型存放,如果不能转换时,

 

更多信息:

IUIField有一个属性,叫NullValue,提供当前IUIField的空值表示。

 

访问UIModel     

  1. 在Action基类中,有CurrentModel属性,可以在某个ActionExtend方法中中这样访问UIModel: this.CurrentModel;
  2. 在特殊情况下,在WebPart的CodeBehind中访问this.Model,得到强类型的UIModel;
  3. 通过UIModel,即可以以强类型或弱类型的方式访问得到UIView和Link集合;可以访问UIModel的属性、UIModel下UIView的属性、以及UIField属性;
  4. UIView是数据的具体对象与接口:OQlString、DefaultFilter、对应EntityFullName、ParentLink、Childlinks、ParentView、FocusedRecord、FocusedIndex、FocusRecord、Records等;
  5. UIView可以强名访问字段,也可以弱类型访问字段;

UIModel数据收集与界面绑定 

  1. 对于Action操作,可以在设计期设置该Action操作是否要收集数据和绑定数据;
  2. BaseWebForm实现IPart接口,有DataCollect()、DataBanding()方法和IsDataBanding属性,负责收集数据、界面绑定;
  3. 可以在WebPart的CodeBehindExtend代码的相应方法中编程,调用this.DataCollect()、this.DataBanding()等方法,完成数据收集与绑定;

操作UIModel     

  1. UIModel赋值、赋默认值和其他属性
    1. 在*ModelExtend.cs文件中AfterInitModel()方法中设置字段的defaultValue、加载UIView的记录数等
    2. 在AfterInitModel()中完成对UIModel的默认值的设置:

public override  void AfterInitModel()

{

     //记录数

     this.Organization.PageStrategy.PageSize=1;

     //默认值

     this.Organization.FieldEffective_EffectiveDate.DefaultValue = PlatformContext.Current.DateTime;

     this.Organization.FieldIsLegacyOrg.DefaultValue = true;

}

  1. 也可以在Action方法中直接对UIModel的字段赋值、赋默认值;方式同上;
  2. 在卡片界面做“新增”等操作时,需要增加一条空Record,这条空记录与界面完成绑定,达到清空界面的效果,例如,清空主Form的Card容器界面的处理:

int index = this.MainView.FocusedIndex;

IUIRecord newRecord = this.MainView.NewUIRecord();

if(this.MainView.RecordCount == 0 || index < 0)

     index = 0;

this.MainView.Records.Insert(index, newRecord);

this.MainView.FocusedRecord = newRecord;

  1. UIView与Record的处理:复制UIView、获取焦点记录、焦点记录索引、设置焦点记录、被选择记录的集合、设置Delete标志、移出Record、记录复制:

//UIView从DataTable、UIView中复制

this.MainView.FromDataTable(DataTable table);

this.MainView.FromUIView(UIView view);

//复制到DataTable

this.MainView.ToDataTable(DataTable table);

//UIRecord与DataRow复制

UIRecord.FromDataRow(DataRow dataRow);

UIRecord.ToDataRow(DataRow dataRow);

//获取焦点记录索引

int index = this.MainView.FocusedIndex;

//获取焦点记录

IUIRecord record = this.MainView.FocusedRecord;

//设置焦点记录

this.MainView.FocusedRecord = record;

//获取被选择的记录集合

IUIRecordCollection records = this.MainView.SelectRecords;

//获取/设置焦点字段

this.MainView.FocusedRecord;{支持getset}

//设置记录删除标志

UIRecord.Delete();

//移出记录

UIRecord.Remove();

//记录复制到UIRecord

UIRecord.CopyTo(IUIRecord);

//UIRecord与EntityData复制

UIRecord.FromEntityData(object entityData);

UIRecord.WriteToEntityData(object entityData);

//UIRecord与DataRow复制

UIRecord.FromDataRow(DataRow dataRow);

UIRecord.ToDataRow(DataRow dataRow);

//判断该记录是否为焦点、被选择的记录

UIRecord.IsFocused;

UIRecord.IsSelected;

  1. 推式拉式生单生成UIModel数据
  2. Form之间公用UIModel
  3. 参照多选生成UIModel数据
  4. BP调用生成UIModel数据

 

在通常情况下

Save”操作处理:

如在具体ActionExtend中操纵UIView的数据,

this.CurrentModel.Customer.FocusedRecord[this.CurrentModel.Customer.FieldName] = "UFIDA";

this.CurrentModel.Customer.Records[0][this.CurrentModel.Customer.FieldName] = "UFIDA"; 

  1. 知道UIView后,还可以UIView的默认Filter及UIParameter根据编程接口进行加工处理;

示例见后面

如果UIModel过滤条件参数没有赋值,则默认不会加载数据,直接返回空数据;

加载UIModel     

  1. 修改DefaultFilter参数值

示例:

1 取到UIView默认的Filter

         IUIFilter defaultFilter = this.CurrentModel.WorkCalendar.DefaultFilter;

2 给Filter的各个参数赋值

IUIParameter param;

param = view.DefaultFilter.GetParameter(“ID");

param.DataValue = “1”;

param = view.DefaultFilter.GetParameter(“Code");

param.DataValue = “1001”;

  1. 修改DefaultFilter条件

1 取到UIView默认的Filter

IUIFilter filter = this.CurrentModel.Customer.DefaultFilter;

2 修改Filter的OPath

filter.OPath = this.CurrentModel.Customer .FieldID.AttributeName+"=@ID";

3 增加参数

IUIParameter idParam = new UIParameter("@ID", typeof(Int64));

filter.Parameters.Add(idParam);

4 给参数赋值

idParam.DataValue = Convert.ToInt64("1024531");

UIModel中的UIView是根据Filter参数加载的,但是要求参数必须完备,即:每个参数必须赋值,如果有参数没有被赋值,会导致该UIView不会加载数据;

 

  1. UIModel的某个UIView增加Order by

做法:

直接在UIView的DefaultFilter的OPath属性上增加Order by子句

示例:

        在某个ActionExtend中写按照编码列进行排序:

IUIFilter filter = this.CurrentModel.Customer.DefaultFilter;

filter.OrderBy = this.CurrentModel.Customer.FieldCode.AttributeName;

  1. UIModel的某个UIView增加Group by

做法:

直接在UIView的DefaultFilter的OPath属性上增加Group by子句

示例:

        在某个ActionExtend中写按照编码列进行分组:

IUIFilter filter = this.CurrentModel.Customer.DefaultFilter;

filter.OPath += "  Group by "+

this.CurrentModel.Customer.FieldCode.AttributeName;

        Action

 

动态生成UIModel

UIModel的内容在Design Time不能确定下来,或者说不能完全确定下来,需要在RunTime时候根据上下文动态创建

UIModel关系使用

UIModel中视图(UIView)之中,有两套关系体系。一套是链接关系(UILink),一套是虚关系(UIVirtualLink)。

 

UILink描述了视图的链接关系,它遵循单父多子的约束,使视图形成树型结构,它在IDE设计确定;

UIVirtualLink描述了视图的引用关系,它没有约束条件,可以以图的形式存在,在运行期确定。

添加UIVirtualLink

添加IUIVirtualLink参照如下代码:

 

 

IUIVirtualLink vlink = UIModelRuntimeFactory.CreateVirtualLink(

model, ParentView.Name, ChildView.Name, ChildField.AttributeName);

model.UIVirtualLinkCollection.Add(vlink);

 

 

UIVirtualLink的应用

 

假设有一个UIModel,包括两个视图,数据如下:

UIViewA 
ID-2
NameA

 

UIViewB 
ID-4
NameXX
FieldXNull

 

调用CommonCRUD保存后,产生Business Entity如下:

 

BE_A 
ID1001
NameA

 

BE_B 
ID23001
NameXX
FieldXNull

 

这时,如果存在UIVirtualLink如下:

 

IUIVirtualLink  
ParentViewChildViewChildField
UIViewAUIViewBFieldX

 

则,CommonCRUD会根据这个Link关系,对UIVirtualLink进行赋值,赋值后的BE如下:

BE_A 
ID1001
NameA

 

BE_B 
ID23001
NameXX
FieldX1001

注意加黄部分。FieldX的值变为BE_A的ID值。

 

赋完值后,CommonCRUD再调用Session的Commit方法进行保存。

 

UIModel消息机制

异常基础

  1. 异常的分类

UIModel的异常主要分为两种,如下:

异常分类显示方式
业务异常在页面的左上角,以红叹号的方式报告异常信息
系统异常以黄页(错误页面)的方法报告异常信息

 

  1. 区别方式

如果异常类型是从ExceptionBase继承下来的,则认为是一个业务异常,否则为系统异常。

 

更简单的方法,使用方法

UBF.System.Exception.Excepti"margin-left:68.25pt;">检查异常是否系统异常。

 

绑定异常

为了实现在界面上显示异常信息的目的,系统在前台将对业务异常信息进行绑定。

 

  1. 自动绑定

自动绑定异常信息必须满足以下两个条件:

绑定的条件 
i.抛出异常必须在系统绑定之前。
ii.异常必须是一个业务异常

如果,抛出的异常不符合上述两个条件,将转为系统异常的显示方式

 

  1. 手动绑定

当自动绑定不能满足要求时,我们可以手工编程绑定异常信息。使用方法如下:

UFSoft.UBF.UI.MD.Runtime.UIErrorMessage的

public void SetErrorMessage(ref IUIModel model, Exception exception)

调用这个方法,可以将异常信息自动分解并绑定到界面上,当然,exception必须是一个业务异常。

 

  1. 手动绑定到属性栏

我们有时不仅仅需要将异常信息绑定到界面,而要绑定在某个属性字段上,如国家节点的编码不允许为空异常,就是绑定在编码属性上的。

 

绑定的方法和b手动绑定完全一样,唯一不同的是Exception对象必须为AttrsContainerException或它的继承Exception类。

 

在AttrsContainerException中有四个属性,如下:

AttrsContainerException的属性 
EntityName绑定实体的名称
AttrName绑定属性的名称
EntityID绑定记录的ID
Message绑定的错误消息
  

通过这个四个属性,我们就可以将异常绑定到属性上了,当然,它同时也会在页面的左上角绑定对应的错误信息。

 

UIModel数据状态的管理

状态基础

UIModel在运行过程中,为了标识记录的运行状态或处理逻辑,每一条记录都会有一个状态值。如下表:

状态值 
Unchanged没有发生变化,即UIModel的值与数据库保持一致。
Added新增的记录,数据库中没有的记录
Deleted删除的记录,数据库中有,尚未删除的记录
Modified修改的记录,数据库存在ID相同的记录,但数据不同
  

 

可以在运行态,通过IUIRecord的DataRecordState属性访问到该记录的状态值。参考代码如下:

 

//当记录状态为新增时,做某某操作

if (this.Records[0].DataRecordState == DataRowState.Added) {

     //Do something

}

 

状态的变化

在运行过程中,记录的状态是可能发生变化的。通常的变化分为两种情况,直接变化和间接变化。

 

  1. 直接变化

对本记录进行操作,而引发的变化。

UIModel记录状态的管理.webp

 

  1. 间接变化

间接变化是指由其它记录发生变化,而引起的自身状态变化的情况。通常有3种情况会引发,如下:

 

子记录状态发生变化而引发的变化。

间接变化主要来源于子记录的状态变化时,父记录状态发生同步的变化。假设,有A,B两个视图,A是B的父视图。所有变化规则如下:

 

间接状态变化的变化规则 
1.

B变为Modified、Added或Deleted时:

A如果是Unchanged,则变为Modified;

A如果为非Unchanged,则保持不变。

2.B变为Unchanged时,A保持不变。
3.B为Modified、Added、Deleted时,A必为非Unchanged状态。
  

 

也可参考以下的速查表,左边一栏是B变化后的值,顶部第一行是A变化前的值,其余是A变化后的值。

状态变化速查表    
 UnchangedAddedModifiedDeleted
UnchangedUnchangedAddedModifiedDeleted
AddedModifiedAddedModifiedDeleted
DeletedModifiedAddedModifiedDeleted
ModifiedModifiedAddedModifiedDeleted

如,A变化前是Unchanged状态,B变化为Added,则A变化为Modified。

 

添加子记录而引发的变化。

当A的子记录集合中,

  1. 添加了一条非Unchanged的子记录,如果A记录为Unchanged状态,则变为Modified,如果为非Unchanged,则保持不变。
  2. 添加了一条Unchanged的子记录,A保持不变。

 

子记录的关联字段值变化而引发的变化

假设A.ID与B.parentID为关联字段。A视图有两条记录a1和a2,B中的记录b原来是a1的子记录,由于修改了b的parentID字段,使得b成为a2的子记录。则a1的状态保持不变,a2的状态根据2.2.2 添加子记录而引发的变化的规则进行变化。

控制记录的状态

记录的状态通常不需要程序员参与控制。由平台的组件自动根据用户的操作变化记录的状态。状态的变化方式参考第二状态的变化

 

但,有的时候,程序员可能会需要参与状态的控制,让数据变化更合符实际情况,控制的方法有以下几种:

控制记录状态的方法 
IUIRecord.Delete()

删除当前记录。

  1. 当前数据状态为Added时,数据将被删除;
  2. 当前数据状态不为Added时,数据状态变化为Deleted。

注意:

  1. Delete方法只会影响UIModel中的数据,而不会影响数据库中的数据。要从数据库中删除,需要调用CommonAction中的Save方法
IUIRecord.this[string fieldname]

赋值操作,即对记录的某个字段进行赋值,当如下条件成立时,该记录的状态将变化Modified。

  1. 当前记录是Unchanged状态,其它状态则保持不变;
  2. 赋值的字段为直接字段;
  3. 记录中该字段的原始值与新值不同。
NavigationAction.Refresh()重新加载数据。加载成功时,将从任意状态改变为Unchanged状态;失败时,保持当前状态。
CommonAction.Save()保存数据。保存成功时,将从任意状态改变为Unchanged状态;失败时,保持当前状态。
IUIRecord.AddNewUIRecord()添加一条状态为Added的记录。

 

管理UIModel的数据变化

什么是连续新增

在卡片中,点新增按键,系统会判断,当前的数据是否发生的改变,如果没有改变,会直接进入新增状态,反之,则先保存当前数据,再进入新增状态。

 

系统是如何判断数据是否发生改变的?

在用户点下新增按键后,后台会进行数据收集,将界面上的数据收集到UIModel,并和UIModel中的现有数据进行判断,是否相同,如果收集的所有数据都在UIModel中存在并且相等,则认为数据没有发生改变。

理论上来说,界面的数据都来源于UIModel,所以,如果用户没有修改,界面上的数据应该是和UIModel保持一致的。

 

如何解决

解决方法很简单,主要是找出那一个或几个字段,在界面收集的数据会和UIModel不一致,再想办法解决。

 

以国家为例找出问题字段的方法是:

  1. 修改Portal/bin/UFIDA.UBF.Log.config文件,设置Log等级为DEBUG,如下:

 

    DEBUG" />

   

   

 

  1. 进入国家卡片的新增状态。
  2. 点[新增]键。
  3. 新增操作执行完成后,打开Portal/log/ UFSoft.UBF.log文件,如下:

3.4 常见错误

1. 在AfterUIModelBinding的方法中修改WebControl的值,这样会导致UIModel的值和UIModel的值不一致,导致连续新增失败。

 

2. 编程在子视图中,添加了一行,添加的这一行状态为ADD,导致连续新增失败。

 

3.5 高级内容

如果有兴趣的,可以继续阅读本节内容,以下操作通常由UIModel代码的开发人员进行,也是他们诊断这类错误的快捷方法。同样以国家为例进行说明。

1. 打开文件U9.VOB.UBF/ ADF/ UI/ NewMVCFramework/ UFSoft.UBF.UI.MD.Runtime/ Implement/ UIRecord.cs

2. 找到public object this[IUIField field]的set部份。

3. 在下面代码中加黄的行打上断点。

if (this.innerData[fieldIndex] == null) {

     if (value == null) {

         return;

     } else {

         if (logger.IsDebugEnabled) {

            string a = this.innerData[fieldIndex] == null ? " " : this.innerData[fieldIndex].ToString();

            string b = value == null ? " " : value.ToString();

            logger.Debug("[{0}]{1} ---TO--->> {2}", fieldName, a, b);

         }

         this.innerData[fieldIndex] = value;

         valueChanged = true;

     }

} else {

     if (!this.innerData[fieldIndex].Equals(value)) {

         if (logger.IsDebugEnabled) {

            string a = this.innerData[fieldIndex] == null ? " " : this.innerData[fieldIndex].ToString();

            string b = value == null ? " " : value.ToString();

            logger.Debug("[{0}]{1} ---TO--->> {2}", fieldName, a, b);

         }

         this.innerData[fieldIndex] = value;

         valueChanged = true;

     }

}

4. 找到private bool SetMulitLangValue(int fieldIndex, IUIField field, object value)方法。

5. 在下面代码中加黄的行打上断点。

if (value is MultiLangDataDict) {

     bool isChange = this.innerData[fieldIndex] == null || !(this.innerData[fieldIndex]  is MultiLangDataDict);

     MultiLangDataDict dict = (MultiLangDataDict)value;

     isChange = isChange || (!UIRuntimeHelper.Instance.CompareMultiLangDataAndResetState((MultiLangDataDict)innerData[fieldIndex],  ref dict));

     if (isChange) {

         if( logger.IsDebugEnabled && field!=null) {

            logger.Debug("Field[{0}] is changed.",field.Name);

         }

         this.innerData[fieldIndex] = dict;

         return true;

     }

}  else {

6. 启动应用,进入国家卡片页面,新增状态。

7. 调试程序,附加到前台,进程名称通常叫w3wp。

8. 在国家卡片中点[新增],断点中断。

9. 查看fieldName变量的值,或field.Name也可以。正常情况下,第一个被断下的FieldName应该是ID,这表示当前是系统是正确。如果不是ID,表示该字段在新增时,UIModel中的值与界面的值不一致,也就是影响连续新增的问题字段,我们寻找的目标。

这时,可以查看this.innerData[fieldIndex]和value的值。

this.innerData[fieldIndex]UIModel中当前记录的值
value界面传回来的值
field.defaultValueUIModel新建记录时的默认值

根据这几个值的信息,通常就可以诊断为什么不能连续新增了,想办法将this.innerData[fieldIndex]和value的值调为一致。

10. 按F5,继续寻找下一个问题字段。直到字段名变为ID。

 

UIModel的FocusedRecord使用方法

 

常见场景:

 cView.FocusedRecord = (IUIRecord)rec;

 

错误信息:所赋的焦点行不是可显示的记录行,可能当前设置的记录没有赋正确的父记录,不能赋焦点行。

 

错误产生原因:

  1. 在cView的Records中(不包含状态被设置为Delete的Record)找不到被赋值的Record对象,即rec对象不是cView.Records中未被设置为Delete的一个对象。

 

常见理解误区:

        认为可以随便为FocusedRecord赋值,以实现显示该记录的目的。这是不对的,FocusedRecord的约束条件是,FocusedRecord对应的Record必须是当前View的Records中存在的Record,并且不允许为删除状态。

 

正确的操作方法:

  1. 设置FocusedRecord为Records中包含的值,如下:
 

//设置cView.FocusedRecord为当前记录集中的第条记录

cView.FocusedRecord = cView.Records[2];

 

//更安全的代码

if (cView.Records[2].DataRecordState != System.Data.DataRowState.Deleted) {

     cView.FocusedRecord = cView.Records[2];

}

 

  1. 更高效的方法,通过设置FocusedIndex修改FocusedRecord的值。
 

if (cView.Records[2].DataRecordState != System.Data.DataRowState.Deleted) {

     cView.FocusedIndex = 2;

}

 

更多相关信息:

        1. FocusedRecord或FocusedIndex发生变化时,都会触发FocusedIndexChanged事件,你可以在FocusedIndexChanged事件中响应FocusedRecord变化。

基础控件

基本概念

对于UBF控件而言,可以使用UBF设计器直接设置好控件,也可以在UI编程时,动态生成控件。

Label控件

Label控件分为两类。

普通Label控件,表示字段、控件的显示名称。

带有事件的Label控件,可以实现Form引用功能。

对于事件Label会在Label的文字下多一条下划线,以表示可以弹出Form。下图中的两个控件都是事件Label,点击Label可以引发事件,弹出Form。

下图中显示三个Label控件,其中第一个为普通Label控件,第二个位可用的事件Label,点击Label可以相应事件,弹出Form。第三个则为不可用状态的事件Label控件。

弹出Form(非封装模式)

1     弹出的对话框页面和被弹出的页面共享UIModel

比如说Form1弹出Form2,Form1和Form2共享UIModel,只要在Form1中调用如下代码就能弹出Form2,并且把Form1当前的UIModel传过去,假如在ActionExtend中写:

this.CurrentPart.ShowModalDialog(PartID, "Form2", "400", "500", this.CurrentPart.TaskId.ToString());

2     弹出的对话框页面和被弹出的页面不共享UIModel

比如说Form1弹出Form2,Form1和Form2不共享UIModel,只要在Form1中调用如下代码就能弹出Form2,Form2自会加载自己的UIModel,假如在ActionExtend中写:

this.CurrentPart.ShowModalDialog(PartID, "Form2", "400", "500", null);

3     弹出的对话框页面和被弹出的页面进行参数传递

比如说Form1弹出Form2,Form1和Form2不共享UIModel;

Form2有参数ID,参数ID和这个Form2对应的UIModel的主UIView的过滤条件中的参数ID有映射关系,

只要在Form1中调用如下代码就能弹出Form2,并把参数ID传给Form2,Form2自会根据过滤条件加载自己的UIModel,假如在ActionExtend中写:

    NameValueCollection query = new NameValueCollection();

    query.Add("ID", "12345");

 this.CurrentPart.ShowModalDialog(PartID, "Form2", "400", "500", null, query);

 4   弹出的对话框页面和被弹出的页面进行数据交换

比如说Form1弹出Form2     

       Form1把数据传递给Form2

           string id = (string)this.CurrentPart.NameValues["ID"];

           string oldOpath=string.Empty;

           if (string.IsNullOrEmpty(id))

           {

                oldOpath = this.CurrentModel.ApprovalResult.DefaultFilter.OPath;

               this.CurrentModel.ApprovalResult.DefaultFilter.OPath = "";

           }

           this. oldOpath;

           }

           if (!string.IsNullOrEmpty(id))

           {

               this.CurrentModel.ApprovalResult_Process_Action.FocusedRecord =(ApprovalResult_Process_ActionRecord)this.CurrentModel.ApprovalResult_Process_Action.FindRecordByFieldValue(this.CurrentModel.ApprovalResult_Process_Action.FieldID.Name, id);

               if(this.CurrentModel.ApprovalResult_Process_Action.FocusedRecord!=null)

                {

                   this.CurrentModel.ApprovalResult_Process.FocusedRecord = (ApprovalResult_ProcessRecord)this.CurrentModel.ApprovalResult_Process.FindRecordByFieldValue(this.CurrentModel.ApprovalResult_Process.FieldID.Name, this.CurrentModel.ApprovalResult_Process_Action.FocusedRecord.Process);

                }

           }

Form2把数据传递给Form1

                通过共享UIModel,或者通过状态进行数据传递

Form引用

为了提高效率,原来Label上弹出对话框的做法修改为类似参照控件弹出参照页面的做法,我们叫Form引用,在设计期,Label的Form引用定义画面和参照控件的参照定义页面一样(可以定义Form引用的输入参数和输出参数),运行期的实现机制也一样;

通过Form引用弹出的Form和被弹出的Form共享UIModel

比如说Form1中的Label1控件定义了Form引用Form2,Form1和Form2要共享UIModel,只要在Form1中给Label1控件的CustomInParams赋个字符串就可以实现把Form1当前的UIModel传给Form2,假如在CodeBehindExtend中的AfterCreateChildControls写:

this.Label1.CustomInParams = Parent_TASKID + "=" + this.TaskId.ToString();

导航页面(非封装模式)

1     导航页面和被导航页面共享UIModel

       比如说Page1导航到Page2,Page1和Page2共享UIModel,只要在Page1中调用如下代码就能导航Page2,并且把Page1当前的UIModel传过去,假如在ActionExtend中写:

    NameValueCollection query = new NameValueCollection();

    query.Add("ParentTaskID", this.CurrentPart.TaskId.ToString());

this.CurrentPart.Navigate(PageID, query);

2     导航页面和被导航页面不共享UIModel

       比如说Page1导航到Page2,Page1和Page2不共享UIModel,只要在Page1中调用如下代码就能导航Page2,并且Page1自会加载自己的UIModel去,假如在ActionExtend中写:

    this.CurrentPart.Navigate(PageID, null);

3     导航页面和被导航页面进行参数传递

       比如说Page1导航到Page2,Page1和Page2不共享UIModel;

Page2有参数ID,参数ID和这个Page2对应的UIModel的主UIView的过滤条件中的参数ID有映射关系,

只要在Page1中调用如下代码就能导航到Page2,并把参数ID传给Page2,Page2自会根据过滤条件加载自己的UIModel,假如在ActionExtend中写:

    NameValueCollection query = new NameValueCollection();

    query.Add("ID", "12345");

    this.CurrentPart. Navigate(PageID, query);

 

4     导航页面和被导航页面进行数据交换

被导航页面把数据传递给导航页面

       导航页面把数据传递给被导航页面

 导航(封装模式)

除了上面的非封装弹出Form(WebPart)非封装页面导航模式外,我们还对其进行了封装,省去了许多不必要的参数设置;

封装导航完全支持上面所列的非封装弹出Form(WebPart)非封装页面导航的所有功能,使用了UFIDA.U9.UI.PDHelper.NavigateManager使用类完成Form(WebPart)弹出和页面切换导航;

  1. 页面切换导航:

① NavigateManager.NavigatePage(IPart part, string pageId),

完成无参数传递的页面导航;

② NavigateManager.NavigatePage(IPart part, string pageId , NaviteParamter param)

完成有参数的页面导航;

首先,实例化导航参数:

     NaviteParamter paramter = new NaviteParamter();

     paramter.SetPageNaivgate();//

     ……

 paramter.AddPDPageID(this.CurrentModel.Organization.FocusedRecord.ID.ToString());

     然后,进行导航

     NavigateManager.NavigatePage(this.CurrentPart, pageID, paramter);

  1. 弹出Form(WebPart):
    1. 弹出标准尺寸的无参数WebPart(Form);

     NavigateManager.ShowModelWebpart(IPart part, partId);

  1. 弹出标准尺寸的有参数WebPart(Form);

     NaviteParamter paramter = new NaviteParamter();

     paramter.SetModelPopup();

……

     NavigateManager.ShowModelWebpart(IPart part,, partId, paramter);

  1. 弹出自定义尺寸的无参数WebPart(Form);

NavigateManager.ShowModelWebpart(IPart part, string partId, int width, int height)

  1. 弹出自定义尺寸的有参数WebPart(Form);

     NaviteParamter paramter = new NaviteParamter();

     paramter.SetModelPopup();

……

NavigateManager.ShowModelWebpart(IPart part, string partId, int width, int height, NaviteParamter paramter)

  1. 导航参数设置:NaviteParamter
    1. 关键字名值对ShowType:

            PartShowType.ModalRef    :参照弹出

           PartShowType.NavigateForm :预留;

           PartShowType.NavigatePage :页面导航

            PartShowType.Normal      :预留;

            PartShowType.ShowModal   :Post模态弹出

            PartShowType.TitleLink   :TitleClick模态弹出;

是记录WebPart展现方式是“PopUp弹出”还是“Navigation页面切换”;

通过NavigateManager的导航与弹出缺省设置,也可以New导航参数实例进行设置,例如:

              NaviteParamter paramter = new NaviteParamter();

     paramter.SetPageNaivgate();设置为页面导航;

     或则

     paramter. SetModelPopup();设置为弹出窗口;

  1.  关键字名值对PDDataStatus:Edit、New、Browse

            PDDataStatus是记录数据状态是“Edit编辑”还是“New新增”、还是“Browse浏览”;

//根据NavigationStatus和PDDataStatus来决定按钮等状态;

 

通过New导航参数实例进行设置,例如:

              NaviteParamter paramter = new NaviteParamter();

     paramter.SetNewStatus();设置为新增状态;

     paramter.SetEditStatus();设置为编辑修改状态;

     paramter.SetBrowseStatus ();设置为浏览状态;

  1. 关键字名值对PDPageID

            PDPageID是数据记录的ID,根据它唯一查找定位数据;

通过New导航参数实例进行设置,例如:

              NaviteParamter paramter = new NaviteParamter();

     paramter.AddPDPageID(string id) ;传递一条记录ID;

     paramter.AddPDPageIDs(ArrayList ids); 传递多条记录ID;

 

         通过NavigateManager的IsMorePDDataID(IPart part)方法判断是否有多条记录

         通过NavigateManager的IsSinglePDDataID(IPart part)方法判断是否有一条记录

数字控件

在开发过程中,如果要表达一个数字类型时,例如数量、单价或金额(可能会带货币符号)等数字类型,可以使用数字控件。数字控件中的数字分为显示值和编辑值。当数字控件未获得焦点的时候,数字控件是显示值。当获得焦点后,进行编辑该值的时候,显示的是不带任何数字格式的真实的值。

下图是数字控件:

百分数的数字控件:

主要功能如下:

  • 带格式的数字

表达数字概念时,例如表达数量概念时,或者汇率等概念时,使用数字控件需要设置数字的取值范围(即最大值、最小值),数字格式。数字格式包含了该数是整数、小数还是百分数,另外还有位数,即当进行运算时,需要保留的位数,以及舍入规则。

支持自定义设置小数点分隔符号。

支持自定义设置千分位分隔符号。

当数字是负数的时候,支持自定义负号的表示符号,并且支持负数的表示格式。

例如下图就是同一个数在在不同的数字格式下的显示。

 

  • 货币数字

对于表达货币金额时,还可以设置该数字的货币符号。通过设置CurrencySymbol属性可以使该数字以带上货币的符号,例如人民币、美元、日元等。

下图中的控件就是带货币符号的数字控件

 

 

  • 舍入运算

对于数字的运算,原则上讲,应当在服务器端进行运算,主要考虑到客户端js运算会出现误差。对于一个有一定舍入规则的数字来说,可以调用RoundEx方法来进行摄入运算。

该方法定义为

public decimal RoundEx(decimal numberic,RoundType roundType, int roundValue, int scale)

有4个参数,参数列表如下:

第一个参数,要进行舍入的值

第二个参数,舍入类型

第三个参数,舍入值

第四个参数,要保留的精度

返回值为按照舍入类型、舍入值和精度进行舍入后的值。

 

  • 其他功能

数字控件中对0有特殊处理。在很多情况下,数字为0的时候,尤其是在也年初始化加载的时候,对于0,一般会表现为空白,数字控件中就提供了这样的功能。利用属性ZeroIsNull就会达到此效果。其值为true/false。

 

日期控件

在UI开发过程中,如果要表达一个日期的时候,应该使用日期控件。日期的输入有两种方法。

第一种,通过点击日期控件的日期按钮,弹出日历控件,进行选择日期。

第二种,手动在日期控件的输入框中输入日期。

日期控件如下图所示:

日期类型

通过设置日期控件的DateTimeType属性为日期型可以实现三种日期类型,分别是日期型、日期时间型和时间型。

 

多交期日期

使用IsCustomDateTimeFormat属性可以表达多交期。

CustomDateTimeFormat属性可以指定用户启用的自定义格式,应用于多交期场景。如果IsCustomDateTimeFormat属性为true,日期控件将根据CustomDateTimeFormat属性的值显示用户自定义的日期格式。默认值为"..."。

 

这里举一个使用日期控件多交期的例子。

//绑定之前调用

private

MVC开发框架

UI的基本概念UI技术架构图生成的代码与MVC关系  UIModel的构成整个UIModel实际上由3个部份构成,如图:  UIModel:装载数据,并记录数...
点击下载文档
标签: # U9C
分享:
上一篇:同步单个枚举脚本下一篇:开发表单
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息