1

I have a control that has a minimum/maximum size contraint on it. This constraint is represented by a struct with two nullable properties (null means no limit). For the life of me I cannot get the WinForms designer to accept it.

I've tried a TypeConvertor (from the net and copied from the .NET 4 source) and I get

"Code generation for type 'BoxRange' failed. Error was 'specified cast is not valid.'"

or

"Type 'BoxRange' cannot be cast to type 'BoxRange.'"

and then it forgets the values that were set. What am I doing wrong here?

Although this example was from a Size3DConverter example, it's very little different from .NET4's SizeConverter. I have no idea why it can't split "#,#" the way it does with a Size or point structure.

[System.SerializableAttribute()]
[System.ComponentModel.TypeConverter(typeof(RKSHARP2.Designer.SizeRangeConvertor))]
public struct SizeRange
{
  private System.Byte? Byte_Minimum;
  private System.Byte? Byte_Maximum;

  [System.ComponentModel.DefaultValue(typeof(System.Nullable<System.Byte>), null)]
  public System.Byte? Minimum
  {
    get
    {
      return this.Byte_Minimum;
    }
    set
    {
      this.Byte_Minimum = value;
    }
  }

  [System.ComponentModel.DefaultValue(typeof(System.Nullable<System.Byte>),null)]
  public System.Byte? Maximum
  {
    get
    {
      return this.Byte_Maximum;
    }
    set
    {
      this.Byte_Maximum = value;
    }
  }

  public SizeRange (System.Byte? Byte_Minimum, System.Byte? Byte_Maximum)
  {
    this.Byte_Minimum = Byte_Minimum;
    this.Byte_Maximum = Byte_Maximum;
  }

  public static System.Boolean operator == (RKSHARP2.Controls.SizeRange Struct_Compare1, RKSHARP2.Controls.SizeRange Struct_Compare2)
  {
    return (Struct_Compare1.Minimum == Struct_Compare2.Minimum && Struct_Compare1.Maximum == Struct_Compare2.Maximum);
  }

  public static System.Boolean operator != (RKSHARP2.Controls.SizeRange Struct_Compare1, RKSHARP2.Controls.SizeRange Struct_Compare2)
  {
    return (Struct_Compare1.Minimum != Struct_Compare2.Minimum || Struct_Compare1.Maximum != Struct_Compare2.Maximum);
  }

  public override System.Boolean Equals (System.Object Object_Compare)
  {
    if ((Object_Compare is RKSHARP2.Controls.SizeRange) == false)
    {
      return false;
    }
    else
    {
      return ((((RKSHARP2.Controls.SizeRange)(Object_Compare)).Minimum == this.Minimum) && (((RKSHARP2.Controls.SizeRange)(Object_Compare)).Maximum == this.Maximum));
    }
  }

  public override System.Int32 GetHashCode ()
  {
    return this.Byte_Minimum.GetValueOrDefault() ^ this.Byte_Maximum.GetValueOrDefault();
  }

  public override System.String ToString ()
  {
    return RKSHARP2.Convertor.ToString(this.Minimum, "?") + "," + RKSHARP2.Convertor.ToString(this.Maximum, "?");
  }
}

public SizeRangeConvertor ()
{
  public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
  {
    if (sourceType == typeof(string))
      return true;
    return base.CanConvertFrom(context, sourceType);
  }

  public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType)
  {
    if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor))
      return true;
    return base.CanConvertTo(context, destinationType);
  }

  public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
  {
    if (value is string)
    {
      if (culture == null)
        culture = System.Globalization.CultureInfo.InvariantCulture;
      string sValue = (string)value;

      if (sValue == null || sValue == string.Empty)
        sValue = "?,?";
      string[] numbers = sValue.Split(new char[] { ',' });
      object[] values = null;
      System.Type[] types = null;

      if (numbers.Length == 2)
      {
        values = new object[numbers.Length];
        types = new System.Type[numbers.Length];
        for (int i = 0; i < numbers.Length; i++)
        {
          values[1] = RKSHARP2.Convertor.ToByte(numbers[i]);
          types[i] = typeof(byte?);
        }
      }

      if (values != null)
      {
        System.Type type = GetSize3DType(context);
        System.Reflection.ConstructorInfo constructorInfo = type.GetConstructor(types);
        return constructorInfo.Invoke(values);
      }
    }
    return base.ConvertFrom(context, culture, value);
  }

  public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
  {
    System.Type type = value.GetType();
    byte? width = RKSHARP2.Convertor.ToByte(type.GetProperty("Minimum").GetValue(value, null));
    byte? height = RKSHARP2.Convertor.ToByte(type.GetProperty("Maximum").GetValue(value, null));
    if (destinationType == typeof(string))
    {
      if (culture == null)
        culture = System.Globalization.CultureInfo.InvariantCulture;
      return string.Format("{0},{1}", width, height);
    }
    else if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor))
    {
      System.Reflection.ConstructorInfo constructorInfo = type.GetConstructor(new System.Type[] { typeof(byte?), typeof(byte?) });
      if (constructorInfo != null)
        return new System.ComponentModel.Design.Serialization.InstanceDescriptor(constructorInfo, new object[] { width, height });
    }
    return base.ConvertTo(context, culture, value, destinationType);
  }

  public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
  {
    System.Type type = GetSize3DType(context);
    System.Reflection.ConstructorInfo constructorInfo = type.GetConstructor(new System.Type[] { typeof(byte?), typeof(byte?) });
    return constructorInfo.Invoke(new object[] {
      propertyValues["Minimum"],
      propertyValues["Maximum"]});
  }

  public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context)
  {
    return true;
  }

  public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes)
  {
    System.Type type = GetSize3DType(context);
    return System.ComponentModel.TypeDescriptor.GetProperties(type, attributes).Sort(new string[] { "Minimum", "Maximum" });
  }

  public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context)
  {
    return true;
  }

  private System.Type GetSize3DType(System.ComponentModel.ITypeDescriptorContext context)
  {
    if (context == null)
      return typeof(RKSHARP2.Controls.SizeRange);
    return context.PropertyDescriptor.PropertyType;
  }
}
LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • The designer can only show text, so your TypeConverter has to be able to convert a null value to an empty string. Your error hints that it isn't doing that. Try editing your question to include the TypeConverter code. – LarsTech Jan 10 '12 at 13:42

1 Answers1

0

I re-worked your SizeRange struct below.

[Serializable()]
[TypeConverter(typeof(SizeRangeConverter))]
public struct SizeRange {
  private Byte? _min;
  private Byte? _max;

  public SizeRange(Byte? min, Byte? max) {
    _min = min;
    _max = max;
  }

  public Byte? Minimum {
    get { return _min; }
    set { _min = value; }
  }

  public Byte? Maximum {
    get { return _max; }
    set { _max = value; }
  }

  public override bool Equals(object obj) {
    if (obj is SizeRange)
      return ((SizeRange)obj).Minimum == this.Minimum && ((SizeRange)obj).Maximum == this.Maximum;
    else
      return false;
  }

  public override int GetHashCode() {
    return this.Minimum.GetValueOrDefault() ^ this.Maximum.GetValueOrDefault();
  }

  public override string ToString() {
    string minValue = this.Minimum == null ? "?" : this.Minimum.ToString();
    string maxValue = this.Maximum == null ? "?" : this.Maximum.ToString();
    return minValue + "," + maxValue;
  }

  public static Boolean operator ==(SizeRange sr1, SizeRange sr2) {
    return (sr1.Minimum == sr2.Minimum && sr1.Maximum == sr2.Maximum);
  }

  public static Boolean operator !=(SizeRange sr1, SizeRange sr2) {
    return !(sr1 == sr2);
  }
}

And here is a simple TypeConverter class that worked. It needs more validation work, but it worked in the designer for me:

public class SizeRangeConverter : TypeConverter {

  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
    if (sourceType == typeof(string))
      return true;

    return base.CanConvertFrom(context, sourceType);
  }

  public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
    if (value is string) {
      string[] v = ((string)value).Split(',');
      Byte? minValue = null;
      Byte? maxValue = null;
      Byte minTest;
      Byte maxTest;
      if (byte.TryParse(v[0], out minTest))
        minValue = minTest;
      if (byte.TryParse(v[1], out maxTest))
        maxValue = maxTest;

      return new SizeRange(minValue, maxValue);
    }
    return base.ConvertFrom(context, culture, value);
  }

  public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
    if (destinationType == typeof(string))
      return ((SizeRange)value).ToString();

    return base.ConvertTo(context, culture, value, destinationType);
  }
}
LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • Nope, I still get "code generation failed, specified cast is not valid" and then it forgets the value. – Ryan Killian Jan 11 '12 at 17:55
  • I use Visual Studio 2008 FWIW. I don't get a line, I just get a Visual Studio message box with "Code generation for property BoxRange failed. Error was (specified cast not valid)." Then compilation continues and it forgets everything. It's crazy because my TypeConvertor worked fine as a function call but the Designer is choking on it. – Ryan Killian Jan 11 '12 at 18:06