TColumn.FieldName property editor - delphi

I'm analyzing the DBGrids.pas unit. There's a TColumn class which have published the FieldName property
property FieldName: String read FFieldName write SetFieldName;
It's declared as a string but in the object inspector it's appear as a editable combobox (TDataFieldProperty)
I analyzed almost all DBGrids unit and can't find place where that trick is done. Where should I look?

What you're looking for - dear past me - is the RegisterPropertyEditor method.
Call RegisterPropertyEditor to associate the property editor class
specified by the EditorClass parameter with the property type
specified by the PropertyType parameter.
In your case you need a TDataFieldProperty so it will be like:
RegisterPropertyEditor(TypeInfo(string), TColumn, 'FieldName', TDataFieldProperty);

Related

Delphi - can a property read from a property?

So I just joined this forum because I could not find an answer to my simple question.
I want to declare a read-only property, and it should read from a private members read-only property. It seems like that won't work. Can I work myself around that blockade?
Here is the code snippet:
property Mine: TMineType read mMine.MineType;
Edit: Maybe I should clarify. mMine is of class TMine, which has a property MineType. MineType is of type TMineType and is read-only.
A property getter can be one of two things:
A field, or
A function.
Your code attempts to implement the getter with a property, and that does not meet the requirement stated above.
The documentation contains all the details: http://docwiki.embarcadero.com/RADStudio/en/Properties
On a practical level, you could do something like this:
function GetMineType : TMineType;
begin
result := mMine.MineType;
end;
property Mine: TMineType read GetMineType;
If you declare the GetMineType function as inline it will not generate any code.
Based on your additional information that you provided in your comments (such information should be put into the original question itself right from the start) I believe I can provide an answer which would hopefully provide solution to the problem you are facing.
In the code bellow I'm showing of how you can make parent class property accessible through descendant class by using property forwarding or property publishing.
//Base class we use
TBaseClass = class(TObject)
//Private section. Methods, fields and properties declared here are only visible from within classes declared in same unit
//but not from classes or method declared in other units.
private
FPrivateField: Integer;
function GetPrivateField: Integer;
procedure SetPrivateField(AValue: Integer);
//This property is making use of getter and setter methods in order to access FPrivateField
property PrivateFieldProperty: Integer read GetPrivateField write SetPrivateField;
//Strict private section. Methods, fields and properties declared here are only visible from descendant classes and their
//methods but not from other classes and their methods even if they are declared in the same unit.
strict private
FStrictPrivateField: Integer;
function GetStrictPrivateField: Integer;
procedure SetStrictPrivateField(AValue: Integer);
//Public section. Methods, Field and properties declared here are visible from any other class or methods regardless of
//where they are declared
public
//This property is accessing its field directly
property DirectPrivateFieldProperty: Integer read FPrivateField write FPrivateField;
//This property is making use of getter and setter methods in order to access FStrictPrivateField
property StrictPrivateFieldProperty: Integer read GetStrictPrivateField write SetStrictPrivateField;
end;
//Sub class is actually a descendant class from our base class
TSubClass = class(TBaseClass)
public
//If your parent class already has declared certain property and you want to use that property in your descendant
//class you can use so called "property forwarding" approach where you only specify the parents property name that
//you want to make available to your descendant class. This approach only allows accessing fields that are declared
//in parents class.
//If you need to access field declared in descendant class then its property must directly access that field or use
//getter and/or setter methods declared in descendant class. Such approach is called property overloading.
property DirectPrivateFieldProperty;
property PrivateFieldProperty;
//If you want to limit the access level of a descendants class property (making it read only) you need to follow standard
//property declaration guideline where you declare property type and the field to which it is accessing either directly or
//by using getter or setter methods
property ReadOnlyPrivateFieldProperty1: Integer read FPrivateField;
property ReadOnlyPrivatefieldProperty2: Integer read GetPrivateField;
//You can also declare forwarded descendant class property with different visibility of the one declared in parent class.
//Such approach is usually referred as property publishing.
property StrictPrivateFieldProperty;
end;
Note the above approaches only work when accessing properties or fields of a parent class from a descendant class but not for accessing fields or properties of other classes.
The reason for this is that property may only directly access fields of its own class or any parent classes.
Also property can only use getter and setter methods that are declared either in its own class or in any of the parent classes.
I hope this answer of mine might give you better idea about working with properties.
Now at the moment I guess properties might look a bit confusing to you but believe me when you will learn to work with them you will realize of how powerful they can actually be. I must admit that I'm still finding out new ways to use them in my projects every now and then.

Date/Time Picker as Property Editor in a TCollectionItem descendant

I'm writing a component which requires properties of type Date, Time, and Date/Time. I would like these properties to be visible in the Object Inspector, with an option to use a popup property editor.
I have tried TDate as a published property, and this gives me the results I need for just Date alone. However I need the same thing for TTime and TDateTime but they don't come with a property editor, and in fact it won't even accept any value I type in there either.
I have found the TDateTimeProperty which can be used as a property editor, or so I understand anyway. I have done the necessary implementation when registering this component. This property I need to apply it to is actually a TCollectionItem descendant, not necessarily a part of the component but within it.
This is how I'm registering it...
RegisterComponents('My Page', [TMyComponent]);
RegisterPropertyEditor(TypeInfo(TDateTime), TMyCollectionItem, 'MyPropName', TDateTimeProperty);
Although this compiles, when I install it, there is no property editor on this property. I have tried using my component's class name in place of TMyCollectionItem but same issue.
What am I doing wrong here to show this property editor?
You don't need to register the built-in property editors for TDateTime, TDate and TTime. They are already registered. That's why your attempts to register them have no impact.
The built-in property editors for these types simply convert between the underlying floating point value and a string representation. They don't implement date time pickers or anything like that.
You say:
However I need the same thing for TTime and TDateTime but they don't come with a property editor, and in fact it won't even accept any value I type in there either.
That is in fact incorrect. They do come with property editors. They are the same built-in property editors that you named in your question. And they do accept values. They don't accept the values you provided because you provided invalid values.
If you want to register a property editor that does provide a visual date time picker, then you will have to write the property editor yourself.

Set Default Integer value of firemonkey component

We are working on creating custom component in firemonkey on Delphi Xe2 platform. I am creating published property with firemonkey custom component. I have set default value such as Boolean, custom type, but I am setting default integer value. I am using class TControl1 = class(TControl).
Published
Property Test : Integer read FTest write Set Test default 10;
On component viewer my custom component shows 0 default.Sorry for my bad english.
Please anyone help me
If I remember correctly, default directive does not set your private member, FTest. You have to initialize it in your component's constructor like this:
TControl1 = class(TControl)
private
FTest: Integer;
procedure SetTest(Value: Integer);
public
constructor Create(AOwner: TComponent); override;
published
property Test: Integer read FTest write SetTest default 10;
end;
.
.
.
constructor TControl1.Create;
begin
inherited;
FTest := 10;
end;
Specifying the default property value does not assign that value to the property at runtime. All it does is control how the property is stored. If the property's value is equal to the default when the property is stored, then the VCL streaming framework omits the property.
The documentation says it like this:
When you declare a property, you can specify a default value for it. The VCL uses the default value to determine whether to store the property in a form file. If you do not specify a default value for a property, the VCL always stores the property.
...
Declaring a default value does not set the property to that value. The component's constructor method should initialize property values when appropriate. However, since objects always initialize their fields to 0, it is not strictly necessary for the constructor to set integer properties to 0, string properties to null, or Boolean properties to False.
In other words, you must initialise the property in the component's constructor, in addition to setting the default value. And the onus is on you to ensure that you initialise it to the same value as you specified as the default.
I have personally always found the duplication inherent in the design to be somewhat frustrating. The designers have succeeded in building into the language a violation of the DRY principle. The very fact that you have asked this question illustrates the weakness of the design. Having specified the default value you are surprised that the compiler appears to ignore you and demands that you set it again.

What's the difference between public and published class members in Delphi?

Please could someone explain me what's the difference between public and published class members in Delphi?
I tried to look at Delphi help and I understand that these members have the same visibility, but I don't understand very well how they differ and when should I use published members instead of public ones.
Thanks a lot.
The compiler generates RTTI (Run-Time Type Information) metadata for published members, but not for public members (by default). The main effect of this is that the published properties of an object will appear in the Object Inspector at design time.
I do not know if you are writing components, but if you do, you probably know that properties and events are normally published, so that they can be set using the Object Inspector.
Public
public
property MyProperty: integer read FMyProperty write FMyProperty
MyProperty will not be visible in the Object Inspector.
Published
published
property MyProperty: integer read FMyProperty write FMyProperty
MyProperty will be visible in the Object Inspector.
Public properties and published properties have the same visibility, as you already stated. Published properties are included in RTTI, public properties aren't.
As a side note, there is another special thing with published:
The default visibility of class members is published, so check for unsafe code like:
TTopSecret = class(TObject)
Name: string;
Password: string;
function DecryptPassword(const AValue): string;
public
constructor Create(const AName, AEncryptedPassword: string);
end;
Name, Password and DecryptPassword() are visible 'world-wide'.
Published properties will export Runtime Type Information (RTTI).
Have a look here about RTTI in Delphi
It seems there are lots of good answers already, pointing out the Object INspector, RTTI,
etc. These are all pieces of the puzzle.
If you take away the published keyword, the entire Delphi RAD tool design would require some way to specify which properties are stored in a DFM, inspected in the component property inspector, and can be reloaded at runtime from a DFM when the form or data module is created.
This, in a word, is what Published is for. It is interesting to me that the designers of QT (originally TrollTech, later part of Nokia, later still spun off to Digia) had to emulate this level of RTTI for their C++ RAD library "QT", adding a "published" equivalent and a "property" equivalent, while pure C++ still lacks this fundamental facility.
Runtime Type Informations (RTTI) are only generated for published class members.
At run-time, entries in the published and public sections are equally accessible.
The principal difference between them is that published items of a component appear in the Object Inspector at design-time.
This happens because, for fields in published section RTTI is automatically generated.
The Object Inspector picks this up and uses it to identify what to add to its list of properties and events.
In addition to the other answers:
Published properties are automatically stored by the streaming system.
For instance if you have a TComponent's descendant instance and write it to a TStream with WriteComponent, all (well, not all, but that is another question) published properties are written to the stream without any further coding.
Of course, the streaming system only can do that because the RTTI is available for those published properties.

Delphi: How do i know what my property editor is editing?

i have a property editor (descendant of TPropertyEditor) that is used to edit a property.
When it comes time to edit my property, how do i know what property of what object i'm editing? If i'm going to edit a property, i have to know what property i'm editing.
i've been pulling my hair out, sifting through the Delphi help, the online help, and the TPropertyEditor and descendant source code, and i can't find the answer.
i expected something like:
TPropertyEditor = class(...)
public
procedure Initialize(TheObject: TObject; ThePropertyName: string);
end;
As far as i can tell, my property editor is created, and i will be told to "Edit", and i just have to divine what property they wanted me to edit.
From the help:
Editing the property as a whole
You can optionally provide a dialog
box in which the user can visually
edit a property. The most common use
of property editors is for properties
that are themselves classes. An
example is the Font property, for
which the user can open a font dialog
box to choose all the attributes of
the font at once.
To provide a
whole-property editor dialog box,
override the property-editor class’s
Edit method.
Edit methods use the same
Get and Set methods used in writing
GetValue and SetValue methods. In
fact, an Edit method calls both a Get
method and a Set method. Because the
editor is type-specific, there is
usually no need to convert the
property values to strings. The editor
generally deals with the value “as
retrieved.”
When the user clicks the ‘...’ button
next to the property or double-clicks
the value column, the Object Inspector
calls the property editor’s Edit
method.
Within your implementation of
the Edit method, follow these steps:
Construct the editor you are using
for the property.
Read the current
value and assign it to the property
using a Get method.
When the user
selects a new value, assign that value
to the property using a Set method.
Destroy the editor.
Answer
It's tucked away, and not documented, but i found out how. The property i'm editing that i edit:
TheCurrentValue := TMyPropertyThing(Pointer(GetOrdValue));
Now that i have the value, i can edit it. If i want to replace the property with some other object:
SetOrdValue(Longint(TheNewValue));
The full code:
Create a property editor that descends from TClassProperty:
TMyPropertyEditor = class(TClassProperty)
public
procedure Edit; override;
function GetAttributes: TPropertyAttributes; override;
end;
First is the housekeeping, telling Delphi's object inspector that my property editor will display a dialog box, this will make a "..." appear next to the property:
function TMyPropertyEditor.GetAttributes: TPropertyAttributes;
begin
//We show a dialog, make Object Inspector show "..."
Result := [paDialog];
end;
Next is the actual work. When the user clicks the "..." button, the object inspector calls my Edit method. The trick that i was missing is that i call my GetOrdValue method. Even though my property isn't an ordinal, you still use it, and just cast the resulting thing to an object:
procedure TMyPropertyEditor.Edit;
var
OldThing: TMyPersistentThing;
NewThing: TMyPersistentThing;
begin
//Call the property's getter, and return the "object" i'm editing:
OldThing:= TMyPersistentThing(Pointer(GetOrdValue));
//now that i have the thing i'm editing, do stuff to "edit" it
DoSomeEditing(OldThing);
//i don't have to, but if i want to replace the property with a new object
//i can call the setter:
NewThing := SomeVariant(OldThing);
SetOrdValue(Longint(NewThing));
end;
If I understand your question right, you're wondering how you're supposed to actually find the value you need to be editing, especially if the object in question contains more than one of them. The answer is that the IDE sets that up for you and the property editor comes "preloaded" by the time Edit is called. TPropertyEditor comes with a bunch of GetValue methods that your Edit function can use to retrieve the value. Or if it's not one of those types, (if it's an object descended from TPersistent, for example,) then you can call GetOrdValue and cast the result to a TPersistent.
Also, you might want to check out TJvPersistentPropertyEditor in the JvDsgnEditors unit of the JVCL to use as a base class. It provides some of the functionality for you.
BTW if you really need it, you can use the GetName method, which will give you the name of the property, but you usually shouldn't have to. And be careful if you're inheriting from something other than TPropertyEditor itself, as GetName is virtual and can be overridden.
A property editor keeps the information about which objects and properties it's editing in the private FPropList variable. The IDE fills that in by calling your editor's SetPropEntry method. You're then generally supposed to call the various methods of TPropertyEditor to find out the properties' values.
It's not really supposed to matter which property you were asked to edit. Your property editor edits properties of a particular type. For in-place editing, your editor provides an implementation of SetValue that translates the string from the Object Inspector into a value of the proper type for the property, and then you call the appropriate Set function, such as SetOrdValue or SetIntfValue. For whole-property editing, the IDE won't call SetValue. Instead, it will call Edit, and you're expected to call GetOrdValue or GetIntfValue (for example) yourself, since your editor already knows what type of property it's designed to edit.
Remember that property editors, in general, can edit multiple properties simultaneously. The name of the property will be the same for all of them, but the type of component they belong to may vary, and thus so may their getters and setters. Call your property editor's GetName method to find out the name. To get the objects, call GetComponent for each index from 0 to PropCount - 1. (Be careful; there's no range checking in those Get functions.) You can also check whether a specific object is in the list by calling HasInstance. The GetPropInfo method will tell you the PPropInfo pointer for the first property, but I don't think that will necessarily be equal to the pointers of all the other properties. Aside from that, you don't get direct access to the PPropInfo data, but again, it really shouldn't matter. If you think you need that for your editor, you're probably doing something wrong; post a new question with more specific information about your underlying task.
Property editors only care about (and are registered for) the type of the property, not the specific property itself.

Resources