当前位置: 首页 > news >正文

一步一步学习使用LiveBindings() 使用TAdapterBindSource实现对象绑定

一步一步学习使用LiveBindings:使用TAdapterBindSource实现对象绑定

LiveBindings 是 RAD Studio(Delphi/C++Builder)中一套强大的数据绑定框架,它允许你在 UI 控件与数据源之间建立声明式连接,无需编写繁琐的事件处理代码。而TAdapterBindSource是其中极为关键的一环——它充当“适配器”,将任意对象(如类实例、记录、甚至嵌套属性)转换为 LiveBindings 可识别的数据流。本文将从原理出发,通过可运行的示例,带你逐步掌握TAdapterBindSource的使用。### 为什么需要 TAdapterBindSource?在 LiveBindings 生态中,TBindSourceDB用于绑定数据集(如TFDQuery),而TAdapterBindSource则用于绑定非数据集对象。它的核心价值在于:-统一接口:将普通对象的属性暴露为字段,供TBindingsListTBindScope使用。-双向绑定:支持从 UI 写回对象属性,自动处理类型转换。-动态字段:可在设计期或运行期定义字段映射。TAdapterBindSource内部使用TAdapter模式:它维护一个内部列表(FAdapter),每个元素对应一个对象实例,而字段则映射到对象的属性(通过 RTTI 或手动指定)。### 环境准备- Delphi 10.3+ 或 C++Builder(以下示例以 Delphi 为例)- 创建一个 VCL 或 FMX 应用,放置以下控件: -TAdapterBindSource(组件面板 → Data Binding) -TBindingsList(组件面板 → Data Binding) - 两个TEdit(输入姓名、年龄) - 一个TButton(触发写入)### 第一步:定义对象类型我们创建一个简单的TEmployee类,包含NameAge属性。注意属性必须声明为 published,以便 RTTI 访问。pascaltype TEmployee = class private FName: string; FAge: Integer; published property Name: string read FName write FName; property Age: Integer read FAge write FAge; end;### 第二步:设计期配置 TAdapterBindSource1. 双击TAdapterBindSource,打开“AdapterBindSource 编辑器”。2. 在“Fields”页签,点击“Add Field”两次,分别设置: - FieldName =Name,DataType =string- FieldName =Age,DataType =Integer3. 关闭编辑器。此时AdapterBindSource1已定义了两个逻辑字段。### 第三步:建立 UI 与字段的绑定在TBindingsList中创建两条绑定表达式:-Edit1.Text←→AdapterBindSource1.Name-Edit2.Text←→AdapterBindSource1.Age操作方式:在设计器中选择TBindingsList,右键 → “Binding 编辑器”,添加两条绑定。### 第四步:运行期绑定对象实例现在,我们需要将实际的TEmployee对象“挂载”到AdapterBindSource上。核心方法是AdapterBindSource1.AddObject或设置AdapterBindSource1.AutoEdit等属性。以下代码展示了完整流程:pascalprocedure TForm1.FormCreate(Sender: TObject);var Emp: TEmployee;begin // 创建对象实例 Emp := TEmployee.Create; Emp.Name := '张三'; Emp.Age := 30; // 将对象添加到 AdapterBindSource AdapterBindSource1.AddObject(Emp); // 激活绑定 AdapterBindSource1.Active := True; // 此时 Edit1.Text 自动变为 '张三',Edit2.Text 变为 '30'end;注意AddObject会将对象加入内部列表,但AdapterBindSource默认只显示当前记录。若需管理多个对象,可结合Navigate方法。### 第五步:从 UI 写回对象属性LiveBindings 支持双向同步。当用户修改Edit1.Text时,需要触发写入。最简单的方式是设置TBindingsListNotifyUpdatesTrue,或使用TEdit.OnChange事件。pascalprocedure TForm1.Edit1Change(Sender: TObject);begin // 强制将 UI 值同步到对象 BindingsList1.Notify(Sender, Edit1);end;或者,使用TAdapterBindSourceEditPost方法:pascalprocedure TForm1.Button1Click(Sender: TObject);begin // 开始编辑当前对象 AdapterBindSource1.Edit; // 更新字段值(通过绑定自动完成,但也可手动赋值) AdapterBindSource1.CurrentFields['Name'].Value := Edit1.Text; AdapterBindSource1.CurrentFields['Age'].Value := StrToInt(Edit2.Text); // 提交修改 AdapterBindSource1.Post;end;实际上,如果绑定表达式正确,Edit1.TextEdit2.Text的变化会自动反映到对象中,无需手动干预。但为了演示“写回”原理,上述代码展示了底层操作。### 进阶:动态绑定与嵌套属性TAdapterBindSource不仅支持简单属性,还能绑定嵌套对象。例如,假设TEmployee有一个Department属性,其类型为TDepartment,而TDepartmentDeptName属性。我们可以这样定义字段:pascaltype TDepartment = class private FDeptName: string; published property DeptName: string read FDeptName write FDeptName; end; TEmployee = class private FName: string; FAge: Integer; FDept: TDepartment; public constructor Create; destructor Destroy; override; published property Name: string read FName write FName; property Age: Integer read FAge write FAge; property Dept: TDepartment read FDept write FDept; end;AdapterBindSource的字段编辑器中,添加字段时路径可写为Dept.DeptName,LiveBindings 会自动解析嵌套属性。### 运行期添加字段(高级技巧)有时设计期无法预知所有字段,我们可以在运行期动态创建字段:pascalprocedure TForm1.DynamicAddField;var AField: TAdapterBindSourceField;begin AField := AdapterBindSource1.AddField('DeptName', TFieldType.ftString); // 将字段映射到对象的属性路径(通过 OnMapObjectToField 事件) AdapterBindSource1.OnMapObjectToField := DoMapObjectToField;end;procedure TForm1.DoMapObjectToField(ASender: TObject; AObject: TObject; AField: TAdapterBindSourceField; var AValue: TValue; var AHandled: Boolean);begin if AField.FieldName = 'DeptName' then begin AValue := (AObject as TEmployee).Dept.DeptName; AHandled := True; end;end;### 完整可运行示例下面是一个综合示例,演示了从创建对象、绑定、修改到写回的全过程。将以下代码放入一个 FMX 表单中,放置两个TEdit、一个TLabel和一个TButtonpascalunit Unit1;interfaceuses System.SysUtils, System.Types, System.UITypes, System.Rtti, FMX.Types, FMX.Controls, FMX.Forms, FMX.Edit, FMX.StdCtrls, Data.Bind.Components, Data.Bind.ObjectScope;type TEmployee = class private FName: string; FAge: Integer; published property Name: string read FName write FName; property Age: Integer read FAge write FAge; end; TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Button1: TButton; Label1: TLabel; AdapterBindSource1: TAdapterBindSource; BindingsList1: TBindingsList; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private Emp: TEmployee; public end;var Form1: TForm1;implementation{$R *.fmx}procedure TForm1.FormCreate(Sender: TObject);begin // 创建员工对象 Emp := TEmployee.Create; Emp.Name := '李四'; Emp.Age := 25; // 添加对象到 AdapterBindSource AdapterBindSource1.AddObject(Emp); AdapterBindSource1.Active := True; // 设计期已设置绑定:Edit1.Text <-> Name,Edit2.Text <-> Age // 此时界面自动显示李四和25end;procedure TForm1.Button1Click(Sender: TObject);begin // 修改对象属性 Emp.Name := '王五'; Emp.Age := 35; // 通知绑定系统刷新 UI BindingsList1.Notify(Sender, Emp); // 或者使用 AdapterBindSource1.Refresh 强制刷新 // AdapterBindSource1.Refresh;end;end.### 性能与注意事项-TAdapterBindSource基于 RTTI,因此对象必须编译为published属性,且开启{$RTTI EXPLICIT METHODS([]) PROPERTIES([vcPublished])}或默认设置。- 频繁调用NotifyRefresh可能影响性能,建议在批量更新时暂停绑定(BindingsList1.BeginUpdate/EndUpdate)。- 对于大型数据集,考虑使用TPrototypeBindSource或直接绑定TFDMemTable以获得更好性能。### 总结TAdapterBindSource是 LiveBindings 中连接对象世界与 UI 世界的桥梁。通过它,你可以:- 将任意对象属性暴露为可绑定的字段。- 实现 UI 与对象属性的自动双向同步。- 动态扩展字段,甚至绑定嵌套对象。掌握这一工具,意味着你可以告别传统的手动赋值和事件处理,以声明式编程的方式构建更清晰、更易维护的界面逻辑。从本文的示例出发,你可以进一步探索TAdapterBindSourceTListTObjectList的配合,实现列表导航,以及利用OnBeforeExecuteCommand等事件实现更复杂的业务逻辑。LiveBindings 的学习曲线略陡,但其带来的生产力提升是值得投入的。

http://www.jsqmd.com/news/1306566/

相关文章:

  • 半迭代探索:平衡确定性与灵活性的工程实践
  • LinkSwift:九大网盘直链解析工具,免客户端下载的终极解决方案
  • ncmdumpGUI完全指南:三步解锁网易云音乐加密文件,畅享个人音乐库
  • 如何深度优化Windows系统性能:专业级Windows Defender安全组件管理方案
  • 深入掌握pip配置:自定义路径、切换镜像源与离线安装whl文件
  • 西点培训哪家最靠谱,2026靠谱西点培训学校真实口碑榜,避坑不踩雷 - 工业推荐榜
  • C# Dictionary与ConcurrentDictionary深度解析:线程安全与高性能集合选型指南
  • ESP32-S3单路继电器模块:从硬件设计到智能控制节点的完整开发指南
  • 2026年8月盐城切条机哪家值得选 排行名单一览—陆涛机械 - 奔跑123
  • 雅思同义词替换技巧:characteristic、feature、property的精准使用
  • 树莓派舵机驱动板(HAT)原理与应用:从PCA9685到多路协同控制
  • TrollInstallerX终极指南:3分钟免费解锁iOS应用自由
  • Claude 新模型的上下文工程:从堆规则到设计上下文
  • Unity3D从入门到精通:结构化学习路径与核心模块实战指南
  • 多无人机协同作业算法在农业植保中的优化与应用
  • 在NVIDIA Jetson边缘设备部署Riva与Llama2:构建离线语音聊天机器人全流程
  • 微信小程序云开发数据库权限配置全解析:从核心模式到实战避坑
  • 《幻兽帕鲁》v1.0.1 Mod整合包:从安装到优化的完整指南
  • OptiMode:矢量有限元法-精度及优势
  • 公众号投票怎么做?海投票小程序2026免费搭建流程分享 - 微信投票小程序
  • 浪潮数据战略升级 + 自研 AI 数据操作系统
  • EMR Serverless Spark 基于 MinHash-LSH 实现 PB 级文本语义去重 4 倍加速
  • 九大网盘直链下载助手:彻底告别限速烦恼的终极解决方案
  • 如何用DevEco Profiler的录制功能对性能问题进行深度分析和定位
  • SpringBoot+Vue旅游数据分析系统架构与Hive优化实践
  • 程序员连续工作八年后,能不能 Gap 一年?
  • 终极免费解锁Wand专业版:5分钟移除2小时限制的完整指南
  • Python自动化神器pynput:从键盘鼠标监听控制到实战应用
  • Seraphine:基于LCU API的英雄联盟智能助手,实现游戏效率革命与数据驱动决策
  • C++异常处理:解决terminate called after throwing an instance of ‘char const*‘错误