1

我正在实现一个样板功能——允许用户 TLabel在运行时更改某些组件的描述——比如 s。例如

TFooClass = Class ( TBaseClass)
 Label : Tlabel;
 ...
 End;

 Var FooClass : TFooClass;

...

在设计时,标签的标题属性值是“名字”,当应用程序运行时,有一个功能允许用户将标题值更改为“其他名称”。一旦更改,FooClass 类实例的标签标题将立即更新。

现在的问题是,如果用户出于某种原因想要恢复到say 'First Name' 的设计时间值,这似乎是不可能的。

我可以使用 RTTIContext 方法和所有其他方法,但归根结底,我似乎需要该类的实例来更改值,并且由于这已经被更改了 - 我似乎遇到了障碍绕过它。

我的问题是——有没有办法在不实例化类的情况下将旧的 RTTI 方法或新的 RTTIContext 东西用于类成员的属性——即从 ClassType 定义中获取属性。

这是我尝试这样做的代码片段:

  c : TRttiContext;
   z : TRttiInstanceType;
   w : TRttiProperty;
 Aform : Tform;
  ....
 Begin
 .....

   Aform := Tform(FooClass);

   for vCount := 0 to AForm.ComponentCount-1 do begin
    vDummyComponent := AForm.Components[vCount];
    if IsPublishedProp(vDummyComponent,'Caption') then begin
      c := TRttiContext.Create;
       try
         z := (c.GetType(vDummyComponent.ClassInfo) as TRttiInstanceType);
         w := z.GetProperty('Caption');
          if w <> nil  then
             Values[vOffset, 1] := w.GetValue(vDummyComponent.ClassType).AsString
        .....
        .....

....
....

我收到各种各样的错误,任何帮助将不胜感激。

4

4 回答 4

1

听起来您要做的是获取 DFM 中定义的某个属性的值。使用 RTTI 无法做到这一点,因为 RTTI 是基于检查其类定义指定的对象的结构。 DFM 不是类定义的一部分;它是从类定义创建对象后应用于对象的属性列表。

如果您想获取表单控件的属性值,您可能必须将它们缓存在某个地方。尝试在表单中放入一些OnCreate贯穿所有控件并使用 RTTI 填充TDictionary<string, TValue>所有属性值的内容。然后,您可以稍后在需要时查找它们。

于 2010-04-19T16:07:36.170 回答
1

RTTI 系统不提供您所追求的。类型信息目前仅在编译时确定。初始表单值在运行时使用 DFM 资源设置。您可以在已编译的应用程序中更改 DFM 资源中的值,因为它是在运行时评估的。

解析并使用存储它的 DFM 资源,或在运行时复制原始值。可能在初始更改点以减少内存占用。

梅森的使用建议TDictionary<string, TValue>是我会使用的。我会小心地将这些信息存储在数据库中,因为保持同步可能成为真正的维护噩梦。

于 2010-04-20T04:39:20.647 回答
0

好吧 - 我解决了这个问题。诀窍基本上是像这样实例化表单的另一个实例:

 procedure ShowBoilerPlate(AForm : TForm; ASaveAllowed : Boolean);
 var
    vCount           : Integer;
    vDesignTimeForm  : TForm;
    vDesignTimeComp  : TComponent;
    vDesignTimeValue : String;
    vCurrentValue    : String;
 begin
   ....
   ....
   vDesignTimeForm :=  TFormClass(FindClass(AForm.ClassName)).Create(AForm.Owner);

   try
     // Now I have two instances of the form - I also need to have at least one
     // overloaded constructor defined for the base class of the forms that will allow for 
     // boilerplating. If you call the default Constructor - no boilerplating
     // is done. If you call the overloaded constructor, then, boilerplating is done.
     // Bottom line, I can have two instances AForm - with boilerplated values and
     // vDesignForm without boilerplated values.
     for vCount := 0 to AForm.ComponentCount-1 do begin
       vDummyComponent := AForm.Components[vCount];
       if Supports (vDummyComponent,IdoGUIMetaData,iGetGUICaption)  then begin
          RecordCount := RecordCount + 1;
          Values[vOffset, 0] := vDummyComponent.Name;
          if IsPublishedProp(vDummyComponent,'Caption') then begin
           vDesignTimeComp := vDesignTimeForm.FindComponent(vDummyComponent.Name);
           if vDesignTimeComp <> nil then begin
             // get Design time values here
              vDesignTimeValue := GetPropValue(vDesignTimeComp,'Caption');
           end;
           // get current boilerplated value here
              vCurrentValue  := GetPropValue(vDummyComponent,'Caption');
         end;
        vOffset := RecordCount;;
       end;
     end;

  finally
    FreeAndNil(vDesignTimeForm);
  end;
end;

无论如何-谢谢大家的所有建议。

于 2010-04-23T15:42:38.127 回答
0

如果您试图实现它以恢复在设计时设置的值(即保存在 DFM 中的值),我会InitInheritedComponent用作起点。

可以在运行时获取 DFM 的内容。不过,解析起来可能会很痛苦。

检查也InternalReadComponentRes

这两个例程都可以在类单元中找到。

于 2010-04-19T17:32:15.743 回答