使用变量/动态名称更改记录属性

时间:2014-06-06 17:13:19

标签: delphi field record

我有一个简单的Delphi记录:

type
   TCustomer = record
     name : string[30];
     age  : byte;
end;

我知道我可以通过在代码中对字段名称进行硬编码来设置此记录的字段:

 var
   customer : TCustomer;

 begin
   // Set up our customer record
   customer.name := 'Fred Bloggs';
   customer.age  := 23;
 end;

但我有一个TEdit,旁边有一个TComboBox,还有一个TButton。组合框是固定的,有两个项目,"名称"和"年龄"。它将首先设置为" Name"。用户在编辑框中键入其名称值。这是一个Save类型的按钮,它有一个OnClick事件,如:

procedure TMainForm.SaveButtonClick(Sender: TObject);
begin
  if(MyComboBox.Text = 'Name') then
  begin
    customer.name := MyEditBox.Text;
  end
  else
  begin
    customer.age := MyEditBox.Text;
  end;

end;

该记录已在其他地方初始化。我在这里得到的是在我的情况下有101种可能的组合框项目。我应该制作一个大规模的case语句来处理这个问题,还是可以通过将重新排序的字段名称与另一个控件(在这种情况下是组合框)设置的动态信息相匹配来合并代码?

2 个答案:

答案 0 :(得分:2)

如果您的Delphi版本具有增强的RTTI(Delphi 2010及更高版本),您可以这样做。

然而,有一些陷阱:

1.Short字符串必须定义为编译器的类型,以便为这些字段创建typeinfo(就像我对String30所做的那样)

2. TValue这是在增强型RTTI中携带事物的类型,不会自动进行类型转换(就像编辑到Age字段的Integer中的字符串一样)。这就是为什么我走进Variant并将其转换为字段的正确类型(仅适用于ShortStringInteger,其余部分留给读者练习)

3. TValue不喜欢来自不同ShortString类型的转化(String30ShortString不同),这就是我使用TValue.Make的原因。缺少的是检查提供的值是否与类型匹配(就像它超过30个字符)。它当然也不兼容unicode。

uses
  Rtti,
  TypInfo;

type
  String30 = string[30];

  TCustomer = record
    name: String30;
    age: byte;
  end;

var
  c: TCustomer;

function CastFromVariant(ATypeInfo: PTypeInfo; const AValue: Variant): TValue;
var
  asShort: ShortString;
begin
  case ATypeInfo.Kind of
    tkInteger: Result := TValue.From<Integer>(AValue);
    tkString:
    begin
      asShort := AValue;
      TValue.Make(@asShort, ATypeInfo, Result);
    end;
  end;
end;

procedure TForm5.Button1Click(Sender: TObject);
var
  ctx: TRttiContext;
  t: TRttiType;
  f: TRttiField;
  v: TValue;
begin
  t := ctx.GetType(TypeInfo(TCustomer));
  f := t.GetField(ComboBox1.Text);
  v := CastFromVariant(f.FieldType.Handle, Edit1.Text);
  f.SetValue(@c, v);
end;

procedure TForm5.ComboBox1Change(Sender: TObject);
var
  ctx: TRttiContext;
  t: TRttiType;
  f: TRttiField;
  v: TValue;
begin
  t := ctx.GetType(TypeInfo(TCustomer));
  f := t.GetField(ComboBox1.Text);
  v := f.GetValue(@c);
  Edit1.Text := v.ToString;
end;

答案 1 :(得分:0)

屏幕上的组件是它们自己的变量。它们的存在与您的记录无关。

您需要根据需要从一个副本复制到另一个。

(使用我的代码作为指导 - 在语法上可能不完全正确。)

// UI component declarations within the form
TForm1 = class(TForm)
  . . .
  cbo : TComboBox;
  edt : TEdit;
  . . .
end;
. . .
var
  Form1 : TForm1;
. . .
// to copy values from customer to UI components:
cbo.ItemIndex := customer.age; // assuming this is what the combobox is used for, 
                               // and it starts at zero
edt := customer.name;

// to copy from UI components into customer, you'll need to do it inside of one 
// of the event handlers. 
TForm1.cboCloseUp( sender : TObject ); // the onCloseUp handler, when the combo drop-down closes
begin
  customer.age := cbo.ItemIndex; // or StrToInt(cbo.Text)
end;

TForm1.edtChange( sender : TObject );  // the onChange handler whenever the edit changes
begin
  customer.name := edt.Text;
end;

这是它的要点。如果组合框中的年龄从零开始,那么当您复制客户记录和组合框时,您将要相应地调整偏移量。如果值是线性的,您可以设置ItemIndex + offset,或者只是将值写入cbo.Text。