跳至主要內容

控件赋值问题

Entity大约 5 分钟

控件赋值问题

为什么金蝶中给控件设置值是使用this.View.Model.SetValue(),而不是this.View.GetControl("").SetValue()呢?为什么this.view中有model、context,但是this中也有model、context呢?分别有什么区别?

这是金蝶云·星空(BOS)插件开发中最容易混淆但又最核心的概念之一,搞清楚下面这几个对象的关系,你以后写代码就不会再迷糊了。

1. 为什么给控件赋值用 this.View.Model.SetValue() 而不是 GetControl("").SetValue()

操作方式实际修改的是什么适用场景是否推荐
this.View.Model.SetValue("FNumber", "1001", 0)修改业务数据对象(DataObject)所有需要保存到数据库的字段赋值强烈推荐
this.View.GetControl("FNumber").SetValue("1001")只改界面显示(前端控件的值),不改后台数据极少数只影响界面、不需要保存的临时显示基本不用

核心原因: 金蝶是典型的“数据驱动界面”架构:

  • 所有单据真正保存的是 Model.DataObject(一个 DynamicObject)
  • 界面上所有控件的值默认都是从 Model.DataObject 读取并绑定的
  • 当你调用 Model.SetValue() 时:
    • 后台数据变了
    • 框架会自动把新值推送到界面控件(双向绑定)
    • 保存时直接保存 DataObject
  • 当你用 GetControl().SetValue() 时:
    • 只改了界面控件的值
    • DataObject 没变 → 保存时还是旧值
    • 下次刷新界面又会把旧值刷回来(你改的东西就没了)

官方结论(金蝶开发文档原话):

“在插件中,建议所有涉及业务数据的赋值操作,统一使用 Model.SetValue / Model.GetValue 接口,不要直接操作控件值。”

只有极少数场景(比如一个只用于查询条件的文本框,不参与保存)才允许直接操作控件。

2. this.View、this.Model、this.Context 到底是什么?谁是谁?

金蝶插件的继承链(最常见的情况):

public class MyBillPlugIn : AbstractBillPlugIn    // ← 你的插件类
{
    // this          → 指的是你的插件实例本身(MyBillPlugIn)
    // this.View     → 当前单据界面视图对象(DynamicFormView)
    // this.Model    → this.View.Model 的快捷方式!!!
    // this.Context  → this.View.Context 的快捷方式!!!
}
属性实际对象类型属于谁?说明
this你的插件类实例(继承自 AbstractBillPlugIn 等)插件本身生命周期与当前单据窗口一致
this.ViewDynamicFormView界面层代表整个窗体,提供了 UpdateView、ShowMessage、GetControl 等界面操作
this.ModelBillDataModel(实际是 this.View.Model)数据模型层快捷属性!等于 this.View.Model,操作单据业务数据
this.ContextBOSContext(实际是 this.View.Context)上下文环境快捷属性!等于 this.View.Context,包含用户、组织、数据库连接等信息
this.View.ModelBillDataModel数据模型层原始写法,和 this.Model 完全一样
this.View.ContextBOSContext上下文环境原始写法,和 this.Context 完全一样

金蝶在基类 AbstractBillPlugIn 中写了下面两个属性(源码):

protected IDynamicFormView View { get; set; }
protected new FormDataModel Model => this.View.Model;      // 快捷
protected new Context Context => this.View.Context;        // 快捷

所以:

  • this.Modelthis.View.Model
  • this.Contextthis.View.Context

this.Model.SetValue() 和写 this.View.Model.SetValue() 是 100% 等价的,只是前者更简洁。

3. 总结一览表(背下来就无敌了)

写法意思推荐程度
this.Model.SetValue(...)推荐!最常用,改业务数据★★★★★
this.View.Model.SetValue(...)和上面完全一样,只是更长★★★★
this.View.GetControl("F").SetValue(...)只改界面,不改数据,几乎别用
this.Context.UserId推荐!获取当前用户等上下文信息★★★★★
this.View.Context.UserId完全一样,只是更长★★★★

记住金蝶开发铁律

  • 所有业务字段的取值赋值 → 用 this.Model.GetValue / SetValue
  • 所有界面外观、控件显示隐藏 → 用 this.View.GetControl(...)
  • 所有用户、组织、数据库连接 → 用 this.Context

我使用this.View.Model.SetValue(...)给某一个修改业务字段时,SetValue的参数是控件Id还是控件的绑定源属性呢?

答案:必须传的是「绑定源属性(Field Key)」,而不是控件ID(Control Key)!

这也是金蝶开发者最容易掉坑的地方之一。

正确示范与错误示范对比

写法传的是什么?是否正确说明
this.Model.SetValue("FMaterialId", 1001)绑定源属性(Field Key)正确推荐写法
this.Model.SetValue("F_MaterialId", 1001)绑定源属性(Field Key)正确推荐写法(带下划线前缀的也是Field Key)
this.Model.SetValue("FEntity_FMaterialId", 1001, 0)单据体绑定源属性正确单据体字段必须这样写
this.Model.SetValue("FTextBox1", "测试")控件ID(Control Key)错误完全无效!保存后还是空
this.Model.SetValue("FNumber", "1001")控件ID碰巧和Field Key一样侥幸成功极度不推荐,换个单据就坏了

怎么快速知道「绑定源属性(Field Key)」是什么?

有 5 种万能方法(任选其一,推荐前两种):

方法操作步骤得到的结果
方法1(最快)在设计器里点开字段 → 看「键名」就是你要传的字符串
方法2(代码里)this.View.BusinessInfo.GetField("物料").Key直接在代码里输出就是Field Key
方法3打开单据 → F12浏览器调试 → 找 <input data-fieldkey="????">属性值就是Field Key
方法4代码里遍历所有字段:
foreach (var f in this.View.BusinessInfo.GetAllFields()) { Log(f.Key); }
打印出来一堆,就是全部Field Key
方法5单据体字段一般是:实体Key + "_" + 字段Key(如 FEntity_FMaterialId规则固定

官方铁律(金蝶文档原话)

Model.SetValue(string fieldKey, object value, int rowIndex = -1)

  • fieldKey:字段的键名(即设计器中字段属性的“键名”),不是控件键名!
  • 单据头字段:直接写字段键名
  • 单据体字段:写“实体键名_字段键名” 或 “实体键名.字段键名”

经典错误案例(血的教训)

// 错误!FText_Material 是控件ID,不是字段键名
this.Model.SetValue("FText_Material", materialObj);

// 正确!字段键名一般是 FMaterialId 或 F_MaterialId
this.Model.SetValue("FMaterialId", materialObj);           // 单据头
this.Model.SetValue("FEntity_FMaterialId", materialObj, 0); // 单据体第1行

终极记忆口诀(背下来一劳永逸)

“Model 改数据,传 FieldKey;
控件改显示,才用 ControlKey。
99% 的业务字段赋值,都只用 Model.SetValue(FieldKey, ...)”

记住了这点,你在金蝶里给字段赋值就永远不会再出错了。