使用SetPropValue()和RTTI与Delphi Tokyo更改组件属性

时间:2017-08-31 17:44:34

标签: delphi firemonkey rtti

我使用下面的代码在运行时创建的组件中使用RTTI和Delphi 10.2 Tokyo设置属性,一切正常,因为示例的属性是TypeLine,因为我可以访问直接。

  

Componente_cc是一个可以用任何类实例化的变量,无论是TLabelTButtonTEdit ......还是其他任何类。在下面的例子中,我将其实例化为TLine.

Var 
    Componente_cc: TControl;

    procedure TfrmPrincipal.AlteraPropriedades;
    begin
        if IsPublishedProp(Componente_cc, 'LineType') then
          SetPropValue(Componente_cc, 'LineType', 'Diagonal');
    end; 

但是,当有一个子属性时,我不明白该怎么办,例如 Stroke ,它有 Kind Color Cap Dash 等。如何使用SetPropValue()函数更改这些属性的值。我已经简化了示例代码以便更好地理解,但是在我的系统的一般上下文中,我将需要使用RTTI,当然直接通过代码更改属性会很简单,但我确实需要{{1 }}

1 个答案:

答案 0 :(得分:5)

这类似于您的other RTTI issue,您正在通过RTTI访问控件的TextSettings.Font属性。同样的事情适用于任何嵌套属性,如Stroke.Color等。

对于每个嵌套的子属性,您必须获取包含对象,根据需要重复,直到到达所需的子对象,然后您可以获取/设置它的属性值根据需要。

因此,在这种情况下,您必须使用GetObjectProp()来获取Stroke属性对象,然后您可以使用SetPropValue()来设置该对象的属性。例如:

uses
  ..., TypInfo;

var 
  Componente_cc: TControl;

procedure TfrmPrincipal.AlteraPropriedades;
var
  Stroke: TObject;
begin
  if IsPublishedProp(Componente_cc, 'Stroke') then
  begin
    Stroke := GetObjectProp(Componente_cc, 'Stroke');
    if Stroke <> nil then
      SetPropValue(Stroke, 'Color', ...);
  end;
end; 

或者,为了避免对命名属性进行双重RTTI查找:

uses
  ..., TypInfo;

var 
  Componente_cc: TControl;

procedure TfrmPrincipal.AlteraPropriedades;
var
  PropInfo: PPropInfo;
  Stroke: TObject;
begin
  PropInfo := GetPropInfo(Componente_cc, 'Stroke', [tkClass]);
  if PropInfo <> nil then
  begin
    Stroke := GetObjectProp(Componente_cc, PropInfo);
    if Stroke <> nil then
      SetPropValue(Stroke, 'Color', ...);
  end;
end; 

请注意,Delphi 2010中引入了更强大的Enhanced RTTI(此RTTI不仅限于已发布的属性,例如旧式RTTI),例如:

uses
  ..., System.Rtti;

var 
  Componente_cc: TControl;

procedure TfrmPrincipal.AlteraPropriedades;
var
  Ctx: TRttiContext;
  Prop: TRttiProperty;
  Stroke: TObject;
begin
  Ctx := TRttiContext.Create;

  Prop := Ctx.GetType(Componente_cc.ClassType).GetProperty('Stroke');
  if (Prop <> nil) and (Prop.PropertyType.TypeKind = tkClass) {and (Prop.Visibility = mvPublished)} then
  begin
    Stroke := Prop.GetValue(Componente_cc).AsObject;
    if Stroke <> nil then
    begin
      Prop := Ctx.GetType(Stroke.ClassType).GetProperty('Color');
      if (Prop <> nil) {and (Prop.Visibility = mvPublished)} then
        Prop.SetValue(Stroke, ...);
    end;
  end;
end; 

但是,一旦访问更高级别的对象,最好直接访问子属性,例如:

uses
  ..., TypInfo;

var 
  Componente_cc: TControl;

procedure TfrmPrincipal.AlteraPropriedades;
var
  PropInfo: PPropInfo;
  Stroke: TStrokeBrush;
begin
  PropInfo := GetPropInfo(Componente_cc, 'Stroke', [tkClass]);
  if PropInfo <> nil then
  begin
    Stroke := GetObjectProp(Componente_cc, PropInfo, TStrokeBrush) as TStrokeBrush;
    if Stroke <> nil then
      Stroke.Color := ...; // <-- no RTTI needed!
  end;
end; 

或者:

uses
  ..., System.Rtti;

var 
  Componente_cc: TControl;

procedure TfrmPrincipal.AlteraPropriedades;
var
  Ctx: TRttiContext;
  Prop: TRttiProperty;
  Stroke: TStrokeBrush;
begin
  Ctx := TRttiContext.Create;

  Prop := Ctx.GetType(Componente_cc.ClassType).GetProperty('Stroke');
  if (Prop <> nil) and (Prop.PropertyType.TypeKind = tkClass) {and (Prop.Visibility = mvPublished)} then
  begin
    Stroke := Prop.GetValue(Componente_cc).AsObject as TStrokeBrush;
    if Stroke <> nil then
      Stroke.Color := ...; // <-- no RTTI needed!
  end;
end;