MVC开发框架
UI的基本概念
UI技术架构图
生成的代码与MVC关系
UIModel的构成
整个UIModel实际上由3个部份构成,如图:
UIModel:装载数据,并记录数据间的关系;
CommonAction:对UIModel的数据进入填充和读取;
CommonCRUD:对数据进行保存、删除、查询等数据库操作。
UIModel的生命周期
UIModel作为MVC框架的Model部份,其生命周期连续在整个MVC运行过程中,如图:
当用户发出Web请求时,创建对应的UIModel对象,直接用户离开页面,UIModel对象失去所有的引用,被回收为至。
WebPart事件顺序
如何在合适的WebPart事件扩展出的接口编写代码,可以参考下图:
UIModel编程基础
针对UIModel的编程目前都在Action的ActionExtend文件中进行;
可以在代码中,从以下方面操作UIModel:
- 访问UIModel、UIView、UIRecord、UIField、UIFilter;
- 拷贝克隆UIModel、UIView、UIRecord;
- 创建实例UIModel、UIView、UIRecord、UIFilter、
- 根据应用场景,使用上述对象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
- 在Action基类中,有CurrentModel属性,可以在某个ActionExtend方法中中这样访问UIModel: this.CurrentModel;
- 在特殊情况下,在WebPart的CodeBehind中访问this.Model,得到强类型的UIModel;
- 通过UIModel,即可以以强类型或弱类型的方式访问得到UIView和Link集合;可以访问UIModel的属性、UIModel下UIView的属性、以及UIField属性;
- UIView是数据的具体对象与接口:OQlString、DefaultFilter、对应EntityFullName、ParentLink、Childlinks、ParentView、FocusedRecord、FocusedIndex、FocusRecord、Records等;
- UIView可以强名访问字段,也可以弱类型访问字段;
UIModel数据收集与界面绑定
- 对于Action操作,可以在设计期设置该Action操作是否要收集数据和绑定数据;
- BaseWebForm实现IPart接口,有DataCollect()、DataBanding()方法和IsDataBanding属性,负责收集数据、界面绑定;
- 可以在WebPart的CodeBehindExtend代码的相应方法中编程,调用this.DataCollect()、this.DataBanding()等方法,完成数据收集与绑定;
操作UIModel
- UIModel赋值、赋默认值和其他属性:
- 在*ModelExtend.cs文件中AfterInitModel()方法中设置字段的defaultValue、加载UIView的记录数等
- 在AfterInitModel()中完成对UIModel的默认值的设置:
public override void AfterInitModel()
{
//记录数
this.Organization.PageStrategy.PageSize=1;
//默认值
this.Organization.FieldEffective_EffectiveDate.DefaultValue = PlatformContext.Current.DateTime;
this.Organization.FieldIsLegacyOrg.DefaultValue = true;
}
- 也可以在Action方法中直接对UIModel的字段赋值、赋默认值;方式同上;
- 在卡片界面做“新增”等操作时,需要增加一条空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;
- 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;{支持get、set}
//设置记录删除标志
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;
- 推式拉式生单生成UIModel数据
- Form之间公用UIModel
- 参照多选生成UIModel数据
- BP调用生成UIModel数据
在通常情况下
“Save”操作处理:
如在具体ActionExtend中操纵UIView的数据,
this.CurrentModel.Customer.FocusedRecord[this.CurrentModel.Customer.FieldName] = "UFIDA";
this.CurrentModel.Customer.Records[0][this.CurrentModel.Customer.FieldName] = "UFIDA";
- 知道UIView后,还可以UIView的默认Filter及UIParameter根据编程接口进行加工处理;
示例见后面
如果UIModel过滤条件参数没有赋值,则默认不会加载数据,直接返回空数据;
加载UIModel
- 修改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”;
- 修改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不会加载数据;
- UIModel的某个UIView增加Order by
做法:
直接在UIView的DefaultFilter的OPath属性上增加Order by子句
示例:
在某个ActionExtend中写按照编码列进行排序:
IUIFilter filter = this.CurrentModel.Customer.DefaultFilter;
filter.OrderBy = this.CurrentModel.Customer.FieldCode.AttributeName;
- 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 |
Name | A |
UIViewB | |
ID | -4 |
Name | XX |
FieldX | Null |
调用CommonCRUD保存后,产生Business Entity如下:
BE_A | |
ID | 1001 |
Name | A |
BE_B | |
ID | 23001 |
Name | XX |
FieldX | Null |
这时,如果存在UIVirtualLink如下:
IUIVirtualLink | ||
ParentView | ChildView | ChildField |
UIViewA | UIViewB | FieldX |
则,CommonCRUD会根据这个Link关系,对UIVirtualLink进行赋值,赋值后的BE如下:
BE_A | |
ID | 1001 |
Name | A |
BE_B | |
ID | 23001 |
Name | XX |
FieldX | 1001 |
注意加黄部分。FieldX的值变为BE_A的ID值。
赋完值后,CommonCRUD再调用Session的Commit方法进行保存。
UIModel消息机制
异常基础
- 异常的分类
UIModel的异常主要分为两种,如下:
异常分类 | 显示方式 |
业务异常 | 在页面的左上角,以红叹号的方式报告异常信息 |
系统异常 | 以黄页(错误页面)的方法报告异常信息 |
- 区别方式
如果异常类型是从ExceptionBase继承下来的,则认为是一个业务异常,否则为系统异常。
更简单的方法,使用方法
UBF.System.Exception.Excepti"margin-left:68.25pt;">检查异常是否系统异常。
绑定异常
为了实现在界面上显示异常信息的目的,系统在前台将对业务异常信息进行绑定。
- 自动绑定
自动绑定异常信息必须满足以下两个条件:
绑定的条件 | |
i. | 抛出异常必须在系统绑定之前。 |
ii. | 异常必须是一个业务异常 |
如果,抛出的异常不符合上述两个条件,将转为系统异常的显示方式
- 手动绑定
当自动绑定不能满足要求时,我们可以手工编程绑定异常信息。使用方法如下:
UFSoft.UBF.UI.MD.Runtime.UIErrorMessage的 public void SetErrorMessage(ref IUIModel model, Exception exception) |
调用这个方法,可以将异常信息自动分解并绑定到界面上,当然,exception必须是一个业务异常。
- 手动绑定到属性栏
我们有时不仅仅需要将异常信息绑定到界面,而要绑定在某个属性字段上,如国家节点的编码不允许为空异常,就是绑定在编码属性上的。
绑定的方法和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 } |
状态的变化
在运行过程中,记录的状态是可能发生变化的。通常的变化分为两种情况,直接变化和间接变化。
- 直接变化
对本记录进行操作,而引发的变化。
- 间接变化
间接变化是指由其它记录发生变化,而引起的自身状态变化的情况。通常有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变化后的值。
状态变化速查表 | ||||
Unchanged | Added | Modified | Deleted | |
Unchanged | Unchanged | Added | Modified | Deleted |
Added | Modified | Added | Modified | Deleted |
Deleted | Modified | Added | Modified | Deleted |
Modified | Modified | Added | Modified | Deleted |
如,A变化前是Unchanged状态,B变化为Added,则A变化为Modified。
添加子记录而引发的变化。
当A的子记录集合中,
- 添加了一条非Unchanged的子记录,如果A记录为Unchanged状态,则变为Modified,如果为非Unchanged,则保持不变。
- 添加了一条Unchanged的子记录,A保持不变。
子记录的关联字段值变化而引发的变化
假设A.ID与B.parentID为关联字段。A视图有两条记录a1和a2,B中的记录b原来是a1的子记录,由于修改了b的parentID字段,使得b成为a2的子记录。则a1的状态保持不变,a2的状态根据2.2.2 添加子记录而引发的变化的规则进行变化。
控制记录的状态
记录的状态通常不需要程序员参与控制。由平台的组件自动根据用户的操作变化记录的状态。状态的变化方式参考第二状态的变化。
但,有的时候,程序员可能会需要参与状态的控制,让数据变化更合符实际情况,控制的方法有以下几种:
控制记录状态的方法 | |
IUIRecord.Delete() | 删除当前记录。
注意:
|
IUIRecord.this[string fieldname] | 赋值操作,即对记录的某个字段进行赋值,当如下条件成立时,该记录的状态将变化Modified。
|
NavigationAction.Refresh() | 重新加载数据。加载成功时,将从任意状态改变为Unchanged状态;失败时,保持当前状态。 |
CommonAction.Save() | 保存数据。保存成功时,将从任意状态改变为Unchanged状态;失败时,保持当前状态。 |
IUIRecord.AddNewUIRecord() | 添加一条状态为Added的记录。 |
管理UIModel的数据变化
什么是连续新增
在卡片中,点新增按键,系统会判断,当前的数据是否发生的改变,如果没有改变,会直接进入新增状态,反之,则先保存当前数据,再进入新增状态。
系统是如何判断数据是否发生改变的?
在用户点下新增按键后,后台会进行数据收集,将界面上的数据收集到UIModel,并和UIModel中的现有数据进行判断,是否相同,如果收集的所有数据都在UIModel中存在并且相等,则认为数据没有发生改变。
理论上来说,界面的数据都来源于UIModel,所以,如果用户没有修改,界面上的数据应该是和UIModel保持一致的。
如何解决
解决方法很简单,主要是找出那一个或几个字段,在界面收集的数据会和UIModel不一致,再想办法解决。
以国家为例找出问题字段的方法是:
- 修改Portal/bin/UFIDA.UBF.Log.config文件,设置Log等级为DEBUG,如下:
DEBUG" />
|
- 进入国家卡片的新增状态。
- 点[新增]键。
- 新增操作执行完成后,打开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.defaultValue | UIModel新建记录时的默认值 |
根据这几个值的信息,通常就可以诊断为什么不能连续新增了,想办法将this.innerData[fieldIndex]和value的值调为一致。
10. 按F5,继续寻找下一个问题字段。直到字段名变为ID。
UIModel的FocusedRecord使用方法
常见场景:
cView.FocusedRecord = (IUIRecord)rec; |
错误信息:所赋的焦点行不是可显示的记录行,可能当前设置的记录没有赋正确的父记录,不能赋焦点行。
错误产生原因:
- 在cView的Records中(不包含状态被设置为Delete的Record)找不到被赋值的Record对象,即rec对象不是cView.Records中未被设置为Delete的一个对象。
常见理解误区:
认为可以随便为FocusedRecord赋值,以实现显示该记录的目的。这是不对的,FocusedRecord的约束条件是,FocusedRecord对应的Record必须是当前View的Records中存在的Record,并且不允许为删除状态。
正确的操作方法:
- 设置FocusedRecord为Records中包含的值,如下:
//设置cView.FocusedRecord为当前记录集中的第条记录 cView.FocusedRecord = cView.Records[2];
//更安全的代码 if (cView.Records[2].DataRecordState != System.Data.DataRowState.Deleted) { cView.FocusedRecord = cView.Records[2]; } |
- 更高效的方法,通过设置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)弹出和页面切换导航;
- 页面切换导航:
① 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);
- 弹出Form(WebPart):
- 弹出标准尺寸的无参数WebPart(Form);
NavigateManager.ShowModelWebpart(IPart part, partId);
- 弹出标准尺寸的有参数WebPart(Form);
NaviteParamter paramter = new NaviteParamter();
paramter.SetModelPopup();
……
NavigateManager.ShowModelWebpart(IPart part,, partId, paramter);
- 弹出自定义尺寸的无参数WebPart(Form);
NavigateManager.ShowModelWebpart(IPart part, string partId, int width, int height)
- 弹出自定义尺寸的有参数WebPart(Form);
NaviteParamter paramter = new NaviteParamter();
paramter.SetModelPopup();
……
NavigateManager.ShowModelWebpart(IPart part, string partId, int width, int height, NaviteParamter paramter)
- 导航参数设置:NaviteParamter
- 关键字名值对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();设置为弹出窗口;
- 关键字名值对PDDataStatus:Edit、New、Browse
PDDataStatus是记录数据状态是“Edit编辑”还是“New新增”、还是“Browse浏览”;
//根据NavigationStatus和PDDataStatus来决定按钮等状态;
通过New导航参数实例进行设置,例如:
NaviteParamter paramter = new NaviteParamter();
paramter.SetNewStatus();设置为新增状态;
paramter.SetEditStatus();设置为编辑修改状态;
paramter.SetBrowseStatus ();设置为浏览状态;
- 关键字名值对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开发框架
本文2024-08-20 18:14:51发表“u9cloud知识”栏目。
本文链接:https://wenku.my7c.com/article/yonyou-u9cloud-1184.html