开发者

Delphi getting value of form components properties

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.


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.


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.


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜