Registering Custom Property Editors

   

Registering property editors is almost straightforward. I say almost because even though RegisterPropertyEditor() is all that is required, the parameters that need to be passed are not always so trivial. As with other registration functions, the RegisterPropertyEditor() function must be placed inside the package's Register() function. The declaration for the RegisterPropertyEditor() function is

 extern PACKAGE     void __fastcall RegisterPropertyEditor(Typinfo::PTypeInfo PropertyType,                                            TMetaClass* ComponentClass,                                            const AnsiString PropertyName,                                            TMetaClass* EditorClass); 

Each parameter's purpose and an example of its use are shown in Table 5.7. The PropertyType and PropertyName parameters are used to specify criteria that must be matched by a property for it to be considered for use with the property editor.

Table 5.7. RegisterPropertyEditor() Parameters

Parameter Name

Purpose

PropertyType

This parameter expects a pointer to a TTypeInfo structure that contains type information for the property for which the editor is to be used. This parameter must be specified. If the property type is a VCL-derived class, the pointer can be obtained using the __typeinfo macro:

__typeinfo(T VCLClass )

Otherwise, it must be obtained either by examining the typeinfo of a similar existing property or by manually creating it. Both techniques are discussed in this section.

ComponentClass

This parameter is used to specify whether the editor is to be used for all matching properties in all components or only matching properties in components of the type specified. To specify a particular component type, use the __classid operator (which returns TMetaClass* as required) with the component class name:

__classid(TComponentClassName)

Otherwise, specify all components by passing as the parameter.

PropertyName

This parameter is used to specify a property name, in the form of an AnsiString , that a property must have (in addition to having the same type information). It is used to restrict the property specification. If all properties of matching type information are required, an empty AnsiString is passed ( "" ). If ComponentClass is , this parameter is ignored.

EditorClass

This parameter must be specified. It tells the IDE which property editor you want to register. As in the ComponentClass parameter, a TMetaClass pointer is expected. The property editor class name is, therefore, passed wrapped in the __classid operator, such as

classid(TPropertyEditorClassName)

In Table 5.7 you can see that ComponentClass and PropertyName can both be given a value so that they do not restrict the property editor to a specific component class or property name, respectively. This is contrary to their normal use. The only parameter that requires any further comment is PropertyType . As was stated before, the __typeinfo macro can be used to retrieve this information if the property type is a VCL-based class (ultimately derived from TObject ). The __typeinfo macro is defined in $(BCB)\Include\Vcl\Sysmac.h as

 #define __typeinfo(type)  (PTypeInfo)TObject::ClassInfo(__classid(type)) 

If the property is not a VCL class, information must be obtained through other means. There are two approaches to this: Either the appropriate PTypeInfo can be obtained from the property's name and the PTypeInfo of the class it belongs to, or the PTypeInfo can be manually generated.

PTypeInfo is a pointer to a TTypeInfo structure:

 typedef TTypeInfo* PTypeInfo; 

TTypeInfo is declared in $(BCB)\Include\Vcl\Typinfo.hpp as

 struct TTypeInfo  {     TTypeKind Kind;     System::ShortString Name;  }; 

TTypeKind , declared in the same file, is an enumeration of type kinds. It is declared as

 enum TTypeKind { tkUnknown, tkInteger, tkChar,                   tkEnumeration, tkFloat, tkString,                   tkSet, tkClass, tkMethod,                   tkWChar, tkLString, tkWString,                   tkVariant, tkArray, tkRecord,                   tkInterface, tkInt64, tkDynArray }; 

The Name variable is a string version of the actual type. For example, int is "int" , and AnsiString is "AnsiString" . The following two sections discuss how a TTypeInfo* pointer can be obtained for non-VCL property types.

Obtaining a TTypeInfo* ( PTypeInfo ) from an Existing Property and Class for a Non-VCL Type

This approach requires that a VCL class containing the property already be defined and accessible. Then a PTypeInfo for that property type can be obtained using the GetPropInfo() function declared in $(BCB)\Include\Vcl\Typinfo.hpp . PPropInfo is a typedef for a TPropInfo pointer, as in the following:

 typedef TPropInfo* PPropInfo; 

The GetPropInfo() function returns a pointer to a TPropInfo structure ( PPropInfo ) for a property within a particular class with a given property name, and optionally of a specific TTypeKind . It is available in one of four overloaded versions:

 extern PACKAGE PPropInfo __fastcall GetPropInfo(PTypeInfo TypeInfo,                                                  const AnsiString PropName);  extern PACKAGE PPropInfo __fastcall GetPropInfo(PTypeInfo TypeInfo,                                                  const AnsiString PropName,                                                  TTypeKinds AKinds);  extern PACKAGE PPropInfo __fastcall GetPropInfo(TMetaClass* AClass,                                                  const AnsiString PropName,                                                  TTypeKinds AKinds);  extern PACKAGE PPropInfo __fastcall GetPropInfo(System::TObject* Instance,                                                  const AnsiString PropName,                                                  TTypeKinds AKinds); 

These overloaded versions all ultimately call the first overloaded version of the method listed, namely

 extern PACKAGE PPropInfo __fastcall GetPropInfo(PTypeInfo TypeInfo,                                                  const AnsiString PropName); 

This is the version we are most interested in. The other versions also allow a Set of type TTypeKinds to be specified. This is a Set of the TTypeKind enumeration and is used to specify a TypeKind or TypeKinds that the property must also match. From the PPropInfo returned, we can obtain a pointer to an appropriate PTypeInfo for the property, which is the PropType field of the TPropInfo structure. TPropInfo is declared in $(BCB)\Include\Vcl\Typinfo.hpp as

 struct TPropInfo  {     PTypeInfo* PropType;     void* GetProc;     void* SetProc;     void* StoredProc;     int Index;     int Default;     short NameIndex;     System::ShortString Name;  }; 

For example, the PTypeInfo for the Name property of TFont can be obtained by first obtaining a PPropInfo :

 PPropInfo FontNamePropInfo = Typinfo::GetPropInfo(__typeinfo(TFont),                                                    "Name"); 

Then, obtain the PTypeInfo for the required property:

 PTypeInfo FontNameTypeInfo = *FontNamePropInfo->PropType; 

This PTypeInfo value can now be passed to the RegisterPropertyEditor() function. What we have actually obtained from this is a pointer to the TTypeInfo for an AnsiString property. This PTypeInfo could, therefore, be obtained and used as the PTypeInfo parameter anytime the PTypeInfo for an AnsiString is required. Additionally, the PTypeInfo for a custom property for a custom component can be similarly obtained:

 PPropInfo CustomPropInfo = Typinfo::GetPropInfo(__typeinfo(TCustomComponent),                                                  "CustomPropertyName");  PTypeInfo CustomTypeInfo = *CustomPropInfo->PropType; 

Note that it is possibly more clear if TTypeInfo* and TPropInfo* are used instead of their respective typedef s ( PTypeInfo and PPropInfo ). The typedef s have been used here for easy comparison with the GetPropInfo() function declarations.

The intermediate steps shown to obtain the PTypeInfo can be ignored. For example, the following can be used as an argument to RegisterPropertyEditor() for the custom property of a custom component:

 *(Typinfo::GetPropInfo(__typeinfo(TCustomComponent),                         "CustomPropertyName"))->PropType 

This method of obtaining a TTypeInfo* relies on there being a published property of the desired type already in use by the VCL. This might not always be the case. Also, sometimes it might appear that a type already in use matches a type you want to use, but in fact it does not. An example of this is the Interval property of the TTimer component. The type of the Interval property is Cardinal , which is typedef ed to unsigned int in the file $(BCB)\Include\Vcl\Sysmac.h . It is reasonable, therefore, to believe that retrieving the TypeInfo* for this property would enable you to register property editors for unsigned int properties. This is not so. You must have a property whose type is unsigned int , and it must appear in a C++ “implemented class. There is an important lesson here: The TTypeInfo* for a non-VCL class type is not necessarily the same if the property belongs to an Object Pascal “implemented class and not a C++ “implemented class. There is a very simple and effective way around this problem, and that is to create a class containing published properties of the types we desire . We then use the techniques previously discussed to retrieve a suitable TTypeInfo* , which we then use to register our property editor. Listing 5.6 shows such a class.

Listing 5.6 Non-VCL Property Types in a Single Class
 class PACKAGE TNonVCLTypesClass : public TObject  {  public:  __published:     // Fundamental Integer Types     __property int IntProperty = {};     __property unsigned int UnsignedIntProperty = {};     __property short int ShortIntProperty = {};     __property unsigned short int UnsignedShortIntProperty = {};     __property long int LongIntProperty = {};     __property unsigned long int UnsignedLongIntProperty = {};     __property char CharProperty = {};     __property unsigned char UnsignedCharProperty = {};     __property signed char SignedCharProperty = {};     // Fundamental Floating Point Types     __property double DoubleProperty = {};     __property long double LongDoubleProperty = {};     __property float FloatProperty = {};     // Fundamental Boolean type     __property bool BoolProperty = {};     // The AnsiString class     __property AnsiString AnsiStringProperty = {};     private:     // Private Constructor, class cannot be instantiated from     inline __fastcall TNonVCLTypesClass() : TObject()     { }  }; 

If you created a component called TTestComponent with an unsigned int property called Size , the following code would allow you to register a custom property editor:

 RegisterPropertyEditor( *(Typinfo::GetPropInfo                                    (__typeinfo(TNonVCLTypesClass),                                     "UnsignedIntProperty")                           )->PropType,                          __classid(TTestComponent),                          "Size",                          __classid(TUnsignedProperty) ); 

The first parameter is a bit confusing. It is shown again for clarification :

 *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                         "UnsignedIntProperty"))->PropType 

This is the same as the code we saw earlier in this section. It's not very attractive to look at or easy to write. To help make it easier to use, you can create a class that contains static member functions that return the correct TTypeInfo* for each type. The definition for such a class is shown in Listing 5.7.

Listing 5.7 NonVCLTypeInfo.h
 //---------------------------------------------------------------------------//  #ifndef NonVCLTypeInfoH  #define NonVCLTypeInfoH  //---------------------------------------------------------------------------//  #ifndef TypInfoHPP  #include <TypInfo.hpp>  #endif  //---------------------------------------------------------------------------//  class PACKAGE TNonVCLTypeInfo : public TObject  {  public:     // Fundamental Integer Types     static PTypeInfo __fastcall Int();     static PTypeInfo __fastcall UnsignedInt();     static PTypeInfo __fastcall ShortInt();     static PTypeInfo __fastcall UnsignedShortInt();     static PTypeInfo __fastcall LongInt();     static PTypeInfo __fastcall UnsignedLongInt();     static PTypeInfo __fastcall Char();     static PTypeInfo __fastcall UnsignedChar();     static PTypeInfo __fastcall SignedChar();     // Fundamental Floating Point Types     static PTypeInfo __fastcall Double();     static PTypeInfo __fastcall LongDouble();     static PTypeInfo __fastcall Float();     // Fundamental Boolean type     static PTypeInfo __fastcall Bool();     // The AnsiString class     static PTypeInfo __fastcall AnsiString();  private:     // Private Constructor, class cannot be instantiated from     inline __fastcall TNonVCLTypeInfo() : TObject()     { }  };  // The definition for TNonVCLTypesClass goes here (Listing 5.6)  //---------------------------------------------------------------------------//  #endif 

The implementation is shown in Listing 5.8.

Listing 5.8 NonVCLTypeInfo.cpp
 #include <vcl.h>  #pragma hdrstop  #include "NonVCLTypeInfo.h"  //---------------------------------------------------------------------------//  #pragma package(smart_init)  //---------------------------------------------------------------------------//  PTypeInfo __fastcall TNonVCLTypeInfo::Int()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "IntProperty"))->PropType;  }  PTypeInfo __fastcall TNonVCLTypeInfo::UnsignedInt()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "UnsignedIntProperty"))->PropType;  }  //---------------------------------------------------------------------------//  PTypeInfo __fastcall TNonVCLTypeInfo::ShortInt()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "ShortIntProperty"))->PropType;  }  PTypeInfo __fastcall TNonVCLTypeInfo::UnsignedShortInt()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "UnsignedShortIntProperty"))->PropType;  }  //---------------------------------------------------------------------------//  PTypeInfo __fastcall TNonVCLTypeInfo::LongInt()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "LongIntProperty"))->PropType;  }  PTypeInfo __fastcall TNonVCLTypeInfo::UnsignedLongInt()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "UnsignedLongIntProperty"))->PropType;  }  //---------------------------------------------------------------------------//  PTypeInfo __fastcall TNonVCLTypeInfo::Char()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "CharProperty"))->PropType;  }  PTypeInfo __fastcall TNonVCLTypeInfo::UnsignedChar()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "UnsignedCharProperty"))->PropType;  }  PTypeInfo __fastcall TNonVCLTypeInfo::SignedChar()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "SignedCharProperty"))->PropType;  }  //---------------------------------------------------------------------------//  PTypeInfo __fastcall TNonVCLTypeInfo::Double()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "DoubleProperty"))->PropType;  }  PTypeInfo __fastcall TNonVCLTypeInfo::LongDouble()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "LongDoubleProperty"))->PropType;  }  PTypeInfo __fastcall TNonVCLTypeInfo::Float()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "FloatProperty"))->PropType;  }  //---------------------------------------------------------------------------//  PTypeInfo __fastcall TNonVCLTypeInfo::Bool()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "BoolProperty"))->PropType;  }  //---------------------------------------------------------------------------//  PTypeInfo __fastcall TNonVCLTypeInfo::AnsiString()  {     return *(Typinfo::GetPropInfo(__typeinfo(TNonVCLTypesClass),                                   "AnsiStringProperty"))->PropType;  }  //---------------------------------------------------------------------------// 

Using our previous example of registering a property editor for an unsigned int property called Size in a component called TTestComponent , the registration function is

 RegisterPropertyEditor(TNonVCLTypeInfo::UnsignedInt(),                         __classid(TTestComponent),                         "Size",                         __classid(TUnsignedProperty)); 

The previous code is simple, easy to understand, and easy to write. This should be your preferred method of registering property editors for non-VCL based properties.

It was mentioned earlier that determining a TTypeInfo* for a non-VCL property implemented in Object Pascal is not the same as one implemented in C++. An example of this is the PasswordChar property of TMaskEdit . To register a new property editor for all char types requires two registrations: one for Object Pascal “implemented properties and one for C++ implementations. The previous approach (a special class containing the appropriate non-VCL type properties) works fine for C++ implementations , but to get the correct TTypeInfo* for the Object Pascal implementations, the TTypeInfo* pointer must be determined directly from the VCL class, in this case from the PasswordChar property of TMaskEdit . This was the very first way we used to obtain a TTypeInfo* . If we want to register a new char property editor called TCharPropertyEditor for all components and all properties of type char , the registrations required are

 TPropInfo* VCLCharPropInfo = Typinfo::GetPropInfo(__typeinfo(TMaskEdit),                                                    "PasswordChar");  // Register the property editor for native VCL (Object Pascal) components  RegisterPropertyEditor(*VCLCharPropInfo->PropType,                         0,                         "",                         __classid(TCharPropertyEditor));  // Register the property editor for C++ implemented components  RegisterPropertyEditor(TNonVCLTypeInfo::Char(),                         0,                         "",                         __classid(TCharPropertyEditor)); 

Obtaining a TTypeInfo* ( PTypeInfo ) for a Non-VCL Type by Manual Creation

Creating a TTypeInfo* manually is an alternative approach to obtaining a TTypeInfo* from a VCL class for a non-VCL type. It is shown largely for comparison purposes and also because it is a commonly used technique. However, it should generally be avoided in preference to the first method. Manually creating the required PTypeInfo pointer can be done in place before the call to RegisterPropertyEditor() , or the code can be placed in a function that will return the pointer.

There are two ways to write the code to do this. One is to declare a static TTypeInfo structure locally, assign the appropriate values to it, and use a reference to it as the PTypeInfo argument. The other is to allocate a TTypeInfo structure dynamically, assign the appropriate values, and then use the pointer as the PTypeInfo argument. Both methods for generating a suitable PTypeInfo for an AnsiString property are shown in Listing 5.9. Note that this code and other similar functions are found in the GetTypeInfo unit on the CD-ROM.

Listing 5.9 Manually Creating a TTypeInfo*
 //---------------------------------------------------------------------------//  //                             As Functions                                  //  //---------------------------------------------------------------------------//  TTypeInfo* AnsiStringTypeInfo(void)  {     static TTypeInfo TypeInfo;     TypeInfo.Name = "AnsiString";     TypeInfo.Kind = tkLString;     return &TypeInfo;  }  // OR  TTypeInfo* AnsiStringTypeInfo(void)  {     TTypeInfo* TypeInfo = new TTypeInfo;     TypeInfo->Name = "AnsiString";     TypeInfo->Kind = tkLString;     return TypeInfo;  }  //---------------- In the Registration code simply write:--------------------//  RegisterPropertyEditor(AnsiStringTypeInfo(),                         0 ,                         "",                         __classid(TAnsiStringPropertyEditor));  //---------------------------------------------------------------------------//  //             In Place Before RegisterPropertyEditor()                      //  //---------------------------------------------------------------------------//  static TTypeInfo AnsiStringTypeInfo;  TypeInfo.Name = "AnsiString";  TypeInfo.Kind = tkLString;  RegisterPropertyEditor(&AnsiStringTypeInfo,                         0 ,                         "",                         __classid(TAnsiStringPropertyEditor));  // OR  TTypeInfo* AnsiStringTypeInfo = new TTypeInfo;  TypeInfo->Name = "AnsiString";  TypeInfo->Kind = tkLString;  RegisterPropertyEditor(AnsiStringTypeInfo,                         0 ,                         "",                         __classid(TAnsiStringPropertyEditor));  

Notice that when the TTypeInfo structure is dynamically allocated (with new ), it is not deleted after the call to RegisterPropertyEditor() . If this is done, the registration will fail. The reason for this is explained in the following section.

How to Obtain a TTypeInfo* for a Non-VCL Type

Which of the two approaches you use to obtain a TTypeInfo* for a non-VCL type ” determine it from a VCL class or manually create it ”is straightforward. Always use the first method when you can. In particular, you must use the first method if you are writing a property editor to override an existing property editor for which an editor has been specifically registered by the VCL (as opposed to being determined dynamically) or one that has been previously registered using the first approach. In general, the first approach is more robust because you are using the VCL's representation of the TTypeInfo* for the given property. The need to use the first method to override a property editor registered using the first method should be noted. Creating a class with static member functions to return a suitable TTypeInfo* makes the first method just as easy as the manual creation method and should be considered the superior technique.

An important point about using the two approaches is that writing a function to a specific PTypeInfo (the second method) is not the same as obtaining the PTypeInfo from the VCL (the first method). The reason for this is that the implementation of TPropertyClassRec , used internally by the RegisterPropertyEditor() function, maintains only a PTypeInfo variable, not the actual values that it points to, namely the Name and Kind of the TTypeInfo . This is why a reference to a locally declared non- static TTypeInfo structure cannot be used and a dynamically allocated TTypeInfo structure must not be deleted (it is simply abandoned on free store).

Registering property editors is then relatively easy. However, care must be taken to ensure that the parameters passed are exact. Often it is possible to compile and install property editors that do not appear to function, only to find later that the registration code is not quite right (such as when the PropertyName parameter has been spelled incorrectly) and that the property editor worked all along.

Rules for Overriding Property Editors

With the knowledge of how to register custom property editors and the realization that it is possible to override any previously installed property editor, the question is this: What are the rules for overriding property editors? The following highlights the two main considerations:

  • In general, property editors are used from newest to oldest. In other words, the most recently installed property editor for a given property will be used. The exception to this is noted in the next point.

  • A newly registered property editor will override an existing property editor only if the specification used to register it is at least as specific as that used to register the existing editor. For example, if a property editor is registered specifically for the Shape property (of type TShapeType ) in the TShape component, installing a new editor for properties of type TShapeType for all components ( ComponentClass == 0 ) will not override the property editor for the Shape property of TShape .

The only other consideration when overriding property editors is the method used to obtain the appropriate PTypeInfo , as previously discussed. Such property overriding can be seen in practice by examining the EnhancedEditors package on the accompanying CD-ROM.


   
Top


C++ Builder Developers Guide
C++Builder 5 Developers Guide
ISBN: 0672319721
EAN: 2147483647
Year: 2002
Pages: 253

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net