views:

304

answers:

0

Hi,

I am a bit new at this .NET stuff so my aplogies in advance. I have written a class that I wish to behave as an expandable property in the property grid. I have written a Type Converter....etc. The type converter stuff is from morganskinner.com and I adapted it to C++/CLI. Everything compiles correctly except (i) the class does not show in the correct category, (ii) the class is not expandable, and (iii) the subproperties of the class do not show in the value field of the class; i.e. the type converter does not seem to be working. What I get is the cell in the value field corresponding to the expandable property colored black. Clearly I have done something stupid but I have no idea what and even how to go about debugging this. Can anyone suggest how I might be able to debug/solve this problem? Any help would be gretaly appreciated. Thank you in advance.

Paul

My code:

ref class CustomPersonConverter; [TypeConverter(CustomPersonConverter::typeid)] ref class CustomPerson { private:

String^ _FirstName; String^ _LastName; int _ID;

public:

[DisplayName("Person's first name")] [Description("First Name"), Category("rcGroupBox Custom Parameters")] [System::ComponentModel::NotifyParentProperty(true)] property String^ FirstName { String^ get() {return _FirstName;} void set(String^ new_FirstName) {_FirstName=new_FirstName;} }

[Description("Last Name"), Category("rcGroupBox Custom Parameters")] [System::ComponentModel::NotifyParentProperty(true)] property String^ LastName { String^ get() {return _LastName;} void set(String^ new_LastName) {_LastName=new_LastName;} }

[Description("ID"), Category("rcGroupBox Custom Parameters")] [System::ComponentModel::NotifyParentProperty(true)] property int ID { int get() {return _ID;} void set(int new_ID) {_ID=new_ID;} }

CustomPerson (String^ FName, String^ LName, int IDN) { _FirstName = FName; _LastName = LName; _ID = IDN; }

// Class used as a converter for the CustomPerson class[System::Security::Permissions::PermissionSet(System::Security:: Permissions::SecurityAction::Demand, Name = "FullTrust")] ref class CustomPersonConverter : TypeConverter {

public:

virtual bool GetCreateInstanceSupported (ITypeDescriptorContext^ context ) override { return true ; }

virtual Object^ CreateInstance (ITypeDescriptorContext^ context , System::Collections::IDictionary^ propertyValues ) override {

    return gcnew CustomPerson ( (String^) propertyValues["FirstName"] , (String^) 
             propertyValues["LastName"] , (int) propertyValues["ID"] ) ;

}

virtual bool GetPropertiesSupported(ITypeDescriptorContext^ context) override { return true; }

virtual PropertyDescriptorCollection^ GetProperties(ITypeDescriptorContext^ context, Object^ value, array^ attributes) override { PropertyDescriptorCollection^ properties_CustomPerson = TypeDescriptor::GetProperties(value, attributes); // change the display order of the subproperties. array^ sortOrder = gcnew array (3); // there are three subproperties in the CustomPerson expandable property sortOrder[0] = "ID"; sortOrder[1] = "LastName"; sortOrder[2] = "FirstName"; //return a sorted list of properties return properties_CustomPerson->Sort (sortOrder); }

virtual bool IsValid(ITypeDescriptorContext^ context, Object^ value) override { return true; }

virtual bool CanConvertFrom(ITypeDescriptorContext^ context, Type^ sourceType) override { // strings for now if ( sourceType == String::typeid ) { return true; }

        // Always call the base to see if it can perform the conversion.
        return TypeConverter::CanConvertFrom( context, sourceType );

}

virtual Object^ ConvertFrom(ITypeDescriptorContext^ context, CultureInfo^ culture, Object^ value) override { // strings for now if ( value == String::typeid ) { Object^ returnValue = nullptr; String^ sValue = (String^) value; if ( sValue != nullptr ) { // Check that the string actually has something in it... sValue = sValue->Trim ( );

            if ( sValue->Length != 0 )
            {
                // Parse the string                             if ( nullptr == culture ) culture = CultureInfo::CurrentCulture;

                // Split the string based on the cultures list separator
    array<String^>^ parms = sValue->Split ( gcnew array<Char> { culture
                    ->TextInfo->ListSeparator[0] } ) ;

    //Format is string FirstName, string Last name, int ID
    if (parms->Length < 1) {
       throw gcnew ArgumentException("Failed to parse CustomPerson format");
                                        }

                                        if (parms->Length != 3) {
    throw gcnew ArgumentException("CustomPerson format parsed incorrectly");
                                        }

if ( parms->Length == 3 ) // three subproperties
     {
         // Should have a string, string and an integer subproperties.
                                            String^ FName = parms[0];                               String^ LName = parms[1];                                    int IDN = Convert::ToInt32 ( parms[2] );

         // And finally create the object
         returnValue = gcnew CustomPerson ( FName , LName, IDN ) ;
     }
 }

} return returnValue; }

// Always call the base to see if it can perform the conversion. return TypeConverter::ConvertFrom( context, culture, value ); }

virtual bool CanConvertTo( ITypeDescriptorContext^ context, Type^ destinationType ) override {

if ( destinationType == InstanceDescriptor::typeid ) { return true; }

// Always call the base to see if it can perform the conversion. return TypeConverter::CanConvertTo( context, destinationType ); }

virtual Object^ ConvertTo( ITypeDescriptorContext^ context, CultureInfo^ culture, Object^ value, Type^ destinationType ) override { Object^ returnValue = nullptr; CustomPerson^ customPerson = safe_cast (value);

//If this is an InstanceDescriptor ...
if ( destinationType == InstanceDescriptor::typeid )
{
        array<Type^>^ argTypes = gcnew array<Type^> (3); // three subproperties
   argTypes[0] = String::typeid;
   argTypes[1] = String::typeid;
   argTypes[2] = int::typeid;

// Lookup the appropriate CustomPerson constructor
                                ConstructorInfo^ constructor = CustomPerson::typeid->GetConstructor( argTypes );
                                array<Object^>^ arguments = {customPerson->FirstName,customPerson
                 ->LastName,customPerson->ID};

// And return an instance descriptor to the caller.
returnValue = gcnew InstanceDescriptor ( constructor, 
              safe_cast<System::Collections::ICollection^> (arguments));

     }
else if ( destinationType == String::typeid )
     {
// If it's a string, return one to the caller
if ( nullptr == culture ) culture = CultureInfo::CurrentCulture;

     array<String^>^ values = gcnew array<String^> (3) ; // three subproperties

     // culture
TypeConverter^ numberConverter = TypeDescriptor::GetConverter( int::typeid ) ;


values[0] = customPerson->FirstName ; // FirstName is a string already
values[1] = customPerson->LastName ;  // LastName is a sring already
values[2] = numberConverter->ConvertToString ( context , culture , customPerson
                 ->ID ) ;

    // join an array of strings using a separator, in this instance the culture
       specific one
                                returnValue = String::Join ( culture->TextInfo->ListSeparator + " " , values ) ;
     }
else
        // Always call base, even if you can't convert.
            returnValue = TypeConverter::ConvertTo( context, culture, value, 
                            destinationType );

return returnValue;

} }; };

In the main code I have:

[Description("Expandable property Person"), Category("rcGroupBox Custom Parameters")] [System::ComponentModel::DesignerSerializationVisibility (DesignerSerializationVisibility::Content)] [System::ComponentModel::NotifyParentProperty(true)] [Browsable(true)] property CustomPerson^ Person { CustomPerson^ get() {return _person;} void set(CustomPerson^ new_person) {_person=new_person; this->Invalidate();} }