I have a piece of code that is giving me around my head. These are the facts:
I have User control that has a Struct property and I would like that this property will be visible in the Property Window of the Visual Studio Designer.
So far, I have achived that the Struct appears but it is not showing its propertoies. I mean, the plus sign (+) is not visible.
I'm using VS 2005. I tested in VS 2010. However the behaivor is the same.
[TypeConverter(typeof(ColumnDefinitionsConverter))]
public struct ColumnDefinitions
{
public ColumnDefinitions(string A, string B)
{
_ColName = A;
_ColType = B;
}
[Browsable(true)]
[NotifyParentProperty(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public string ColName
{
get { return _ColName; }
set { _ColName = value; }
}
[Browsable(true)]
[NotifyParentProperty(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public string ColType
{
get { return _ColType; }
set { _ColType = value; }
}
private string _ColName;
private string _ColType;
}
The code for the ConverterType
public class ColumnDefinitionsConverter : TypeConverter
{
/// <summary>
/// Can the framework call CreateInstance?
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
// Yes!
return true;
}
/// <summary>
/// Satisfy the CreateInstance call by reading data from the propertyValues dictionary
/// </summary>
/// <param name="context"></param>
/// <param name="propertyValues"></param>
/// <returns></returns>
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
{
// Get values of "Me" and "You" properties from the dictionary, and
// create a new instance which is returned to the caller
return new ColumnDefinitions(propertyValues["ColName"] as string, propertyValues["ColType"] as string);
}
/// <summary>
/// Does this struct expose properties?
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
// Yes!
return true;
}
/// <summary>
/// Return the properties of this struct
/// </summary>
/// <param name="context"></param>
/// <param name="value"></param>
/// <param name="attributes"></param>
/// <returns></returns>
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value, attributes);
// Putz around with the property collection if you like - here I'm changing the display order.
string[] sortOrder = new string[2];
sortOrder[0] = "ColName";
sortOrder[1] = "ColType";
// Return a sorted list of properties
return properties.Sort(sortOrder);
}
/// <summary>
/// Check what this type can be created from
/// </summary>
/// <param name="context"></param>
/// <param name="sourceType"></param>
/// <returns></returns>
public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
{
// Just strings for now
bool canConvert = (sourceType == typeof(string));
if (!canConvert)
canConvert = base.CanConvertFrom(context, sourceType);
return canConvert;
}
/// <summary>
/// Convert from a specified type to a Doofer, if possible
/// </summary>
/// <param name="context"></param>
/// <param name="culture"></param>
/// <param name="value"></param>
/// <returns></returns>
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
string sValue = value as string;
object retVal = null;
if (sValue != null)
{
// Check that the string actually has something in it...
sValue = sValue.Trim();
if (sValue.Length != 0)
{
// Parse the string
if (null == culture)
culture = CultureInfo.CurrentCulture;
// Split the string based on the cultures list separator
string[] parms = sValue.Split(new char[] { culture.TextInfo.ListSeparator[0] });
if (parms.Length == 2)
{
// Should have an integer and a string.
string colName = parms[0];
string colType = parms[1];
// And finally create the object
retVal = new ColumnDefinitions(colName, colType);
}
}
}
else
retVal = base.ConvertFrom(context, culture, value);
return retVal;
}
/// <summary>
/// Check what the type can be converted to
/// </summary>
/// <param name="context"></param>
/// <param name="destinationType"></param>
/// <returns></returns>
public override bool CanConvertTo(ITypeDescriptorContext context, System.Type destinationType)
{
// InstanceDescriptor is used in the code behind
bool canConvert = (destinationType == typeof(InstanceDescriptor));
if (!canConvert)
canConvert = base.CanConvertFrom(context, destinationType);
return canConvert;
}
/// <summary>
/// Convert to a specified type
/// </summary>
/// <param name="context"></param>
/// <param name="culture"></param>
/// <param name="value"></param>
/// <param name="destinationType"></param>
/// <returns></returns>
public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
{
object retVal = null;
ColumnDefinitions colDefi = (ColumnDefinitions)value;
// If this is an instance descriptor...
if (destinationType == typeof(InstanceDescriptor))
{
System.Type[] argTypes = new System.Type[2];
argTypes[0] = typeof(string);
argTypes[1] = typeof(string);
// Lookup the appropriate Doofer constructor
ConstructorInfo constructor = typeof(ColumnDefinitions).GetConstructor(argTypes);
object[] arguments = new object[2];
arguments[0] = colDefi.ColName;
arguments[1] = colDefi.ColType;
// And return an instance descriptor to the caller. Will fill in the CodeBehind stuff in VS.Net
retVal = new InstanceDescriptor(constructor, arguments);
}
else if (destinationType == typeof(string))
{
// If it's a string, return one to the caller
if (null == culture)
culture = CultureInfo.CurrentCulture;
string[] values = new string[2];
// I'm a bit of a culture vulture - do it properly!
TypeConverter numberConverter = TypeDescriptor.GetConverter(typeof(int));
//values[0] = numberConverter.ConvertToString(context, culture, doofer.Me);
values[0] = colDefi.ColName;
values[1] = colDefi.ColType;
// A useful method - join an array of strings using a separator, in this instance the culture specific one
retVal = String.Join(culture.TextInfo.ListSeparator + " ", values);
}
else
retVal = base.ConvertTo(context, culture, value, destinationType);
return retVal;
}
}
Now, the code inside the User control class
public partial class ucLoadFileInBatch : System.Web.UI.UserControl
{
ColumnDefinitions _objColDef = new ColumnDefinitions();
public ColumnDefinitions ColDef
{
get
{
return _objColDef;
}
set
{
_objColDef = value;
}
}
. . . . . .
As mentioned the ColDef method appears on the Property Window from VS, but + sign to expand the Struct and be able to see its ColName and ColType properties is not displayed.
Does anyone how to achieved this? or
What's wrong in the code? or
Does anyone has a step by step example of this?
I will really appreacite your help on this. As mentioned, it has giving me around my head for a few days :-S
Lesthad_mk
Member
96 Points
159 Posts
How to create an expandable property (struct) on a custom control
Mar 08, 2012 11:44 PM|LINK
Hi All
I have a piece of code that is giving me around my head. These are the facts:
The declaration of the Struct:
[TypeConverter(typeof(ColumnDefinitionsConverter))] public struct ColumnDefinitions { public ColumnDefinitions(string A, string B) { _ColName = A; _ColType = B; } [Browsable(true)] [NotifyParentProperty(true)] [EditorBrowsable(EditorBrowsableState.Always)] public string ColName { get { return _ColName; } set { _ColName = value; } } [Browsable(true)] [NotifyParentProperty(true)] [EditorBrowsable(EditorBrowsableState.Always)] public string ColType { get { return _ColType; } set { _ColType = value; } } private string _ColName; private string _ColType; }public class ColumnDefinitionsConverter : TypeConverter { /// <summary> /// Can the framework call CreateInstance? /// </summary> /// <param name="context"></param> /// <returns></returns> public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { // Yes! return true; } /// <summary> /// Satisfy the CreateInstance call by reading data from the propertyValues dictionary /// </summary> /// <param name="context"></param> /// <param name="propertyValues"></param> /// <returns></returns> public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { // Get values of "Me" and "You" properties from the dictionary, and // create a new instance which is returned to the caller return new ColumnDefinitions(propertyValues["ColName"] as string, propertyValues["ColType"] as string); } /// <summary> /// Does this struct expose properties? /// </summary> /// <param name="context"></param> /// <returns></returns> public override bool GetPropertiesSupported(ITypeDescriptorContext context) { // Yes! return true; } /// <summary> /// Return the properties of this struct /// </summary> /// <param name="context"></param> /// <param name="value"></param> /// <param name="attributes"></param> /// <returns></returns> public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value, attributes); // Putz around with the property collection if you like - here I'm changing the display order. string[] sortOrder = new string[2]; sortOrder[0] = "ColName"; sortOrder[1] = "ColType"; // Return a sorted list of properties return properties.Sort(sortOrder); } /// <summary> /// Check what this type can be created from /// </summary> /// <param name="context"></param> /// <param name="sourceType"></param> /// <returns></returns> public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType) { // Just strings for now bool canConvert = (sourceType == typeof(string)); if (!canConvert) canConvert = base.CanConvertFrom(context, sourceType); return canConvert; } /// <summary> /// Convert from a specified type to a Doofer, if possible /// </summary> /// <param name="context"></param> /// <param name="culture"></param> /// <param name="value"></param> /// <returns></returns> public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { string sValue = value as string; object retVal = null; if (sValue != null) { // Check that the string actually has something in it... sValue = sValue.Trim(); if (sValue.Length != 0) { // Parse the string if (null == culture) culture = CultureInfo.CurrentCulture; // Split the string based on the cultures list separator string[] parms = sValue.Split(new char[] { culture.TextInfo.ListSeparator[0] }); if (parms.Length == 2) { // Should have an integer and a string. string colName = parms[0]; string colType = parms[1]; // And finally create the object retVal = new ColumnDefinitions(colName, colType); } } } else retVal = base.ConvertFrom(context, culture, value); return retVal; } /// <summary> /// Check what the type can be converted to /// </summary> /// <param name="context"></param> /// <param name="destinationType"></param> /// <returns></returns> public override bool CanConvertTo(ITypeDescriptorContext context, System.Type destinationType) { // InstanceDescriptor is used in the code behind bool canConvert = (destinationType == typeof(InstanceDescriptor)); if (!canConvert) canConvert = base.CanConvertFrom(context, destinationType); return canConvert; } /// <summary> /// Convert to a specified type /// </summary> /// <param name="context"></param> /// <param name="culture"></param> /// <param name="value"></param> /// <param name="destinationType"></param> /// <returns></returns> public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { object retVal = null; ColumnDefinitions colDefi = (ColumnDefinitions)value; // If this is an instance descriptor... if (destinationType == typeof(InstanceDescriptor)) { System.Type[] argTypes = new System.Type[2]; argTypes[0] = typeof(string); argTypes[1] = typeof(string); // Lookup the appropriate Doofer constructor ConstructorInfo constructor = typeof(ColumnDefinitions).GetConstructor(argTypes); object[] arguments = new object[2]; arguments[0] = colDefi.ColName; arguments[1] = colDefi.ColType; // And return an instance descriptor to the caller. Will fill in the CodeBehind stuff in VS.Net retVal = new InstanceDescriptor(constructor, arguments); } else if (destinationType == typeof(string)) { // If it's a string, return one to the caller if (null == culture) culture = CultureInfo.CurrentCulture; string[] values = new string[2]; // I'm a bit of a culture vulture - do it properly! TypeConverter numberConverter = TypeDescriptor.GetConverter(typeof(int)); //values[0] = numberConverter.ConvertToString(context, culture, doofer.Me); values[0] = colDefi.ColName; values[1] = colDefi.ColType; // A useful method - join an array of strings using a separator, in this instance the culture specific one retVal = String.Join(culture.TextInfo.ListSeparator + " ", values); } else retVal = base.ConvertTo(context, culture, value, destinationType); return retVal; } }public partial class ucLoadFileInBatch : System.Web.UI.UserControl { ColumnDefinitions _objColDef = new ColumnDefinitions(); public ColumnDefinitions ColDef { get { return _objColDef; } set { _objColDef = value; } } . . . . . .As mentioned the ColDef method appears on the Property Window from VS, but + sign to expand the Struct and be able to see its ColName and ColType properties is not displayed.
Does anyone how to achieved this? or
What's wrong in the code? or
Does anyone has a step by step example of this?
I will really appreacite your help on this. As mentioned, it has giving me around my head for a few days :-S
Regards!!