我正在实现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
.....
.....
....
....
我遇到各种各样的错误,我们将非常感谢您的帮助。
答案 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;
无论如何 - 谢谢大家的所有建议。