Delphi获得表单组件属性的值

时间:2010-04-19 15:04:48

标签: delphi reflection rtti

我正在实现Boilerplate功能 - 允许用户在运行时更改某些组件的描述 - 例如TLabel s。 e.g。

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

 Var FooClass : TFooClass;

...

在设计时,值Label的标题属性是 - 'First Name',当时 应用程序运行时,有一个功能允许用户更改标题 值得说'其他名字'。一旦更改,标签的标题为 FooClass的类实例会立即更新。

现在的问题是,如果用户出于某种原因想要恢复到设计 说“名字”的时间价值,似乎不可能。

我可以使用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 个答案:

答案 0 :(得分:1)

听起来你要做的就是获得DFM中定义的某个属性的值。这不能使用RTTI来完成,因为RTTI基于检查由其类定义指定的对象的结构。 DFM不是类定义的一部分;它是一个属性列表,在从类定义创建后应用于对象。

如果要获取表单控件属性的值,可能需要将它们缓存到某处。尝试在运行所有控件的表单OnCreate中放置一些内容,并使用RTTI使用所有属性的值填充TDictionary<string, TValue>。然后,您可以在以后需要时查看它们。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

如果您想要实现它以恢复在设计时设置的值(即在DFM中保存的值),我将使用InitInheritedComponent作为起点。

可以在运行时获取DFM的内容。虽然解析起来可能很痛苦。

同时检查InternalReadComponentRes

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

答案 3 :(得分: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;

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