Delphi获取表单组件属性的值

发布于 2024-08-29 09:38:20 字数 1221 浏览 6 评论 0原文

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

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
        .....
        .....

....
....

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

I am implementing a Boilerplate feature - allow users to Change descriptions of some components - like TLabels - at run time.
e.g.

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

 Var FooClass : TFooClass;

...

At design time, the value Label's caption property is say - 'First Name', when the
application is run, there is a feature that allows the user to change the caption
value to say 'Other Name'. Once this is changed, the caption for the label for
the class instance of FooClass is updated immediately.

The problem now is if the user for whatever reason wants to revert back to the design
time value of say 'First Name' , it seems impossible.

I can use the RTTIContext methods and all that but I at the end of the day, it seems
to require the instance of the class for me to change the value and since this
has already being changed - I seem to to have hit a brick wall getting around it.

My question is this - is there a way using the old RTTI methods or the new RTTIContext
stuff to the property of a class' member without instantiating the class - i.e. getting
the property from the ClassType definition.

This is code snippet of my attempt at doing that :

  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
        .....
        .....

....
....

I am getting all sorts of errors and any help will be greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

赴月观长安 2024-09-05 09:38:20

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

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

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

The RTTI System does not provide what you are after. Type information is currently only determined at compile time. Initial form values are set at Runtime using the DFM resource. You can change values in a DFM Resource in a compiled application because it's evaluated at runtime.

Parse and use the DFM Resource where it is stored, or make a copy of the original value at runtime. Possibly at the point of initial change to reduce your memory footprint.

Masons Suggestion of using TDictionary<string, TValue> is what I would use. I would be careful of storing this information in a database as keeping it in sync could become a real maintenance nightmare.

迎风吟唱 2024-09-05 09:38:20

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

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

Sounds like what you're trying to do is get the value of a certain property as defined in the DFM. This can't be done using RTTI, since RTTI is based on inspecting the structure of an object as specified by its class definition. The DFM isn't part of the class definition; it's a property list that gets applied to the objects after they've been created from the class definitions.

If you want to get the values of the properties of a form's controls, you'll probably have to cache them somewhere. Try putting something in the form's OnCreate that runs through all the controls and uses RTTI to populate a TDictionary<string, TValue> with the values of all the properties. Then you can look them up later on when you need them.

黑色毁心梦 2024-09-05 09:38:20

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

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

另请检查InternalReadComponentRes

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

If what you are trying to achieve it to restore the value that was set at design-time (i.e. that one that is saved in the DFM), I'd use InitInheritedComponent as a starting point.

It's possible to get the content of the DFM at runtime. Could be a pain to parse though.

Check also InternalReadComponentRes.

Both routine can be found in the classes unit.

若沐 2024-09-05 09:38:20

好吧 - 我解决了这个问题。诀窍基本上是实例化表单的另一个实例,如下所示:

 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;

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

Well - I solved the problem. The trick is basically instantiating another instance of the form like so :

 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;

Anyway - thank you all for all your advice.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文