如何通过Delphi LiveBindings将枚举自动转为字符串显示在网格中
解决RAD Studio LiveBindings枚举转字符串显示问题
问题背景
在使用RAD Studio的LiveBindings实现数据绑定时,为TPerson类定义了TGender枚举类型,通过TListBindSourceAdapter将TObjectList<TPerson>数据绑定到网格控件。已注册TConverterDescription实现枚举到字符串的转换,但网格中仍显示枚举的整数值,需要在不添加额外类字段的前提下实现枚举自动转为字符串显示。
解决方案
问题核心在于自动生成的网格绑定未启用自定义转换器,需通过以下步骤配置:
1. 优化转换器注册(可选)
可以保留原硬编码转换逻辑,也可以改用RTTI实现通用枚举名称获取,避免硬编码枚举值:
procedure RegisterConverters; var RttiContext: TRttiContext; RttiType: TRttiEnumerationType; begin TValueRefConverterFactory.RegisterConversion(TypeInfo(TGender), TypeInfo(string), TConverterDescription.Create( procedure(const InValue: TValue; var OutValue: TValue) var Gender: TGender; begin Gender := InValue.AsType<TGender>; // 特殊值自定义显示,其余用RTTI获取枚举名称 if Gender = NonBinary then OutValue := TValue.From<string>('Non Binary') else begin RttiContext := TRttiContext.Create; try RttiType := RttiContext.GetType(TypeInfo(TGender)) as TRttiEnumerationType; OutValue := TValue.From<string>(RttiType.GetName(Gender)); finally RttiContext.Free; end; end; end, 'TGenderToString', 'TGenderToString', EmptyStr, True, EmptyStr, nil) ); end; initialization RegisterConverters;
2. 为网格绑定的Gender列指定转换器
自动生成的TLinkGridToDataSource不会自动应用转换器,需手动指定:
方法一:设计时配置
- 打开窗体设计器,选中
BindingsList1中的LinkGridToDataSourceAdapterBindSource12 - 在Object Inspector中找到
Columns属性,展开后选中Gender项 - 设置
OutputConverterName为TGenderToString(即注册转换器时指定的名称)
方法二:代码动态配置
在窗体的FormCreate事件中添加代码:
procedure TMainForm.FormCreate(Sender: TObject); var Col: TLinkGridColumn; begin Col := LinkGridToDataSourceAdapterBindSource12.Columns.FindMember('Gender'); if Assigned(Col) then Col.OutputConverterName := 'TGenderToString'; end;
3. 替代方案:使用网格OnGetValue事件直接转换
如果不想依赖LiveBindings转换器,也可以通过网格的OnGetValue事件直接处理显示逻辑:
procedure TMainForm.Grid1GetValue(Sender: TObject; const ACol, ARow: Integer; var Value: TValue); var Person: TPerson; begin // 假设Gender是第4列(索引从0开始为3) if ACol = 3 then begin Person := fMyPeople[ARow]; case Person.Gender of Female: Value := 'Female'; NonBinary: Value := 'Non Binary'; else Value := 'Male'; end; end; end;
注意:这种方法需要确保列索引对应正确,且fMyPeople是窗体类可访问的字段。
修改后的完整uMain.pas代码
unit uMain; interface uses Spring.Data.ObjectDataSet, System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, Data.Bind.EngExt, Fmx.Bind.DBEngExt, Data.Bind.Components, Data.Bind.ObjectScope, CollectionObjects, System.Rtti, Fmx.Bind.Grid, System.Bindings.Outputs, Fmx.Bind.Editors, Data.Bind.Grid, FMX.Layouts, FMX.Grid, FMX.Controls.Presentation, FMX.StdCtrls, System.Generics.Collections, REST.JSON, Data.Bind.GenData, FMX.ScrollBox, FMX.Memo, FMX.TabControl, Data.Bind.Controls, Fmx.Bind.Navigator, FMX.Grid.Style, System.JSON, FMX.Memo.Types; type TGender = (Male, Female, NonBinary); TPerson = class private FAge: Integer; FLastName: string; FFirstName: string; FGender: TGender; public constructor Create(FirstName, LastName: String; Age: Integer; Gender: TGender); property FirstName: string read FFirstName write FFirstName; property LastName: string read FLastName write FLastName; property Age: Integer read FAge write FAge; property Gender: TGender read FGender write FGender; end; TMainForm = class(TForm) AdapterBindSource1: TAdapterBindSource; BindingsList1: TBindingsList; TabControl1: TTabControl; GridTab: TTabItem; Grid1: TGrid; LinkGridToDataSourceAdapterBindSource12: TLinkGridToDataSource; DataGeneratorAdapter1: TDataGeneratorAdapter; NavigatorAdapterBindSource1: TBindNavigator; procedure AdapterBindSource1CreateAdapter(Sender: TObject; var ABindSourceAdapter: TBindSourceAdapter); procedure FormCreate(Sender: TObject); // 可选:添加OnGetValue事件 // procedure Grid1GetValue(Sender: TObject; const ACol, ARow: Integer; var Value: TValue); private fMyPeople: TObjectList<TPerson>; fMyPeopleDB: TObjectDataSet; public end; var MainForm: TMainForm; implementation {$R *.fmx} {$R *.LgXhdpiPh.fmx ANDROID} {$R *.iPhone4in.fmx IOS} constructor TPerson.Create(FirstName, LastName: String; Age: Integer; Gender: TGender); begin FFirstName := FirstName; FLastName := LastName; FAge := Age; FGender := Gender; end; procedure TMainForm.AdapterBindSource1CreateAdapter(Sender: TObject; var ABindSourceAdapter: TBindSourceAdapter); begin fMyPeople := TObjectList<TPerson>.Create; fMyPeople.Add(TPerson.Create('Gomez', 'Addams', 40, Male)); fMyPeople.Add(TPerson.Create('Morticia', 'Addams', 38, Female)); fMyPeople.Add(TPerson.Create('Pugsley', 'Addams', 8, NonBinary)); fMyPeople.Add(TPerson.Create('Wednesday', 'Addams', 12, NonBinary)); fMyPeople.Add(TPerson.Create('Uncle', 'Fester', 55, Male)); fMyPeople.Add(TPerson.Create('Grandmama', 'Frump', 72, Female)); fMyPeople.Add(TPerson.Create('', 'Lurch', 50, Male)); fMyPeople.Add(TPerson.Create('Thing T.', 'Thing', 99, NonBinary)); fMyPeople.Add(TPerson.Create('Cousin', 'Itt', 21, NonBinary)); ABindSourceAdapter := TListBindSourceAdapter<TPerson>.Create(Self, fMyPeople, True); end; procedure TMainForm.FormCreate(Sender: TObject); var Col: TLinkGridColumn; begin Col := LinkGridToDataSourceAdapterBindSource12.Columns.FindMember('Gender'); if Assigned(Col) then Col.OutputConverterName := 'TGenderToString'; end; // 可选:OnGetValue事件实现 // procedure TMainForm.Grid1GetValue(Sender: TObject; const ACol, ARow: Integer; var Value: TValue); // var // Person: TPerson; // begin // if ACol = 3 then // begin // Person := fMyPeople[ARow]; // case Person.Gender of // Female: Value := 'Female'; // NonBinary: Value := 'Non Binary'; // else // Value := 'Male'; // end; // end; // end; procedure RegisterConverters; var RttiContext: TRttiContext; RttiType: TRttiEnumerationType; begin TValueRefConverterFactory.RegisterConversion(TypeInfo(TGender), TypeInfo(string), TConverterDescription.Create( procedure(const InValue: TValue; var OutValue: TValue) var Gender: TGender; begin Gender := InValue.AsType<TGender>; if Gender = NonBinary then OutValue := TValue.From<string>('Non Binary') else begin RttiContext := TRttiContext.Create; try RttiType := RttiContext.GetType(TypeInfo(TGender)) as TRttiEnumerationType; OutValue := TValue.From<string>(RttiType.GetName(Gender)); finally RttiContext.Free; end; end; end, 'TGenderToString', 'TGenderToString', EmptyStr, True, EmptyStr, nil) ); end; initialization RegisterConverters; end.
内容的提问来源于stack exchange,提问作者Rick Wheeler
相关产品推荐
相关产品推荐

