August 1999

Registering AnsiString property editors

by

One of the most powerful features of C++Builder is the ability to create your own components. Certainly not all of you are writing your own components. Even fewer of you have written property editors for your components. On the other hand, if you have written a property editor for a property based on AnsiString, you almost certainly ran into serious problems trying to register the property editor. 

Simply put, the VCL will not let you register a property editor for a property based on AnsiString. This article will explain how to register a property editor for an AnsiString property. 

As part of this article I’ll also show you how to register property editors for the C++ integral data types such as int, char, long, and so on.

Identifying the problem

You’ve just written a property editor and, of course, you’re proud of your creation. All you have to do is register the property editor and try it out for the first time. Naturally, you try to register the property editor with the following code.

RegisterPropertyEditor(__typeinfo(String),
__classid(TMyComponent), "FileName",
__classid(TMyFileNameEditor));

You compile the package and are immediately greeted with an error from the compiler:

__classid requires VCL style class type.

At this point most of you would try to work out the problem and, no doubt, throw your hands up in frustration a few miserable hours later. This is the kind of error that is not easily understood, nor remedied.

The problem is that __typinfo used in RegisterPropertyEditor() requires a VCL-derived class or a Pascal data type.  AnsiString, being a pure C++ helper class, is not derived from any VCL class and, as such, does not qualify for use with __typeinfo. This problem not only affects AnsiString property editors, but also property editors for properties based on C++ types such as int or char.

Now that you see what the problem is, I’ll explain the solution.

The solution(s)

There are at least three solutions for the AnsiString property editor dilemma. One solution to the AnsiString property editor problem involved deriving a class from TPersistent and declaring an AnsiString as a class member. The class itself was registered as the type for the property and the AnsiString data member held the string data. This solution, while the only one available for a long time, is messy. I won’t go into the details, but you can take my word for it.

The remaining two solutions are easier as you’ll see in the following sections.

The Van Ditta solution 

Several months ago I read a post from Mark Van Ditta in the Borland newsgroups. Mark described how he had solved the AnsiString property editor problem. The solution was both simple and ingenious. Mark was determined to solve the AnsiString property editor problem. He discovered that the __typeinfo keyword was emulating what the Object Pascal TypeInfo() function does—return a pointer to a TTypeInfo structure. (Later I realized that the VCL help for RegisterPropertyEditor() has a hint that pointed in this direction.) With that information Mark created a function that can be used with RegisterPropertyEditor(). The function looked something like this:

TTypeInfo* AnsiStringTypeInfo()
{
  TTypeInfo* typeInfo = new TTypeInfo;
  typeInfo->Name = "AnsiString";
  typeInfo->Kind = tkLString;
  return typeInfo;
}

Like I said, simple yet ingenious. This function simply creates a dynamic instance of the TTypeInfo structure, sets the Name and Kind members of the structure, and then returns a pointer to the structure. (You don’t need to delete the memory associated with the structure because the VCL will take care of it for you.) The correct call to RegisterPropertyEditor() is shown below.

RegisterPropertyEditor(
  AnsiStringTypeInfo(),
  __classid(TMyComponent), “FileName”,
  __classid(TMyFileNameEditor));

So long as RegisterPropertyEditor() gets a pointer to a TTypeInfo structure, and as long as the Name and Kind members of the structure contain information that the VCL understands, everything works.

Once you understand the basic idea of the AnsiStringTypeInfo() function, it’s easy to create additional functions to register property editors of the integral data types, such as int. The key is in understanding TTypeInfo::Kind. The Kind member of TTypeInfo is an enumeration of type TTypeKind. TTypeKind is defined as follows:

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

Obviously, you can create functions similar to AnsiStringTypeInfo() by using one of the values of TTypeKind along with a corresponding value for the Name member of TTypeInfo. Here’s a type info function for a property of type int:

TTypeInfo* IntTypeInfo(void)
{
  TTypeInfo* typeInfo = new TTypeInfo;
  typeInfo->Name = "int";
  typeInfo->Kind = tkInteger;
  return typeInfo;
}

Note that I set the Name member to “int” and the Kind member to tkInteger. Some trial and error is required in order to get the Name and Kind members synchronized, but for the most part it’s pretty obvious what to pass for the Name member, given a known type. If your property editors don’t work then you may have to try different values for the Name member until the property editor works. 

The Mitov solution

Another solution was recently presented to me by Boian Mitov. Boian’s solution looks like this:

TPropInfo* PropInfo = ::GetPropInfo
  (__typeinfo(TMyComponent), "FileName" );
RegisterPropertyEditor(*PropInfo->PropType,
  __classid(TMyComponent), "FileName",
  __classid(TMyFileNameEditor));


This solution is actually a bit cleaner than the first solution presented. In this case a pointer to a TPropInfo structure is used. The property information for a particular property is first obtained by a call to 
GetPropInfo(). The result is a pointer to a TPropInfo structure. TPropInfo is defined in TYPEINFO.HPP:

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

Notice that the PropType parameter is a pointer to a PTypeInfo type. Since PTypeInfo is itself a pointer, the PropType member is a pointer to a pointer. Since PropType is a pointer to a pointer it must be dereferenced before passing it to the RegisterPropertyEditor() function. 

Here’s how the call to RegisterPropertyEditor() looks:

RegisterPropertyEditor(*PropInfo->PropType,
  __classid(TMyComponent), "FileName",
  __classid(TMyFileNameEditor));

As I have said, this solution is a bit cleaner than the first solution presented, although each works equally well. In the end, choose the method that makes the most sense to you.

Conclusion

Registering AnsiString property editors is as vexing as any aspect of writing components in C++Builder. Since the first version of the compiler, component developers have looked to Borland to provide a reasonable solution to this problem. Unfortunately, each new version of C++Builder has failed to address the issue. 
Thanks to the efforts of Mark Van Ditta and Boian Mitov, C++Builder programmers have not one, but two solutions to the problem.