Click here to Skip to main content
15,886,919 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello all,

Goal:
Make a custom property like Size property in many of windows desktop application controls, and has the same behavior in design time and runtime exactly. I named it, StartLength.

Note: I skipped any changed event handlers.

Error:
I change the value of StartLength property from PropertyGrid at design time and then click 'Save All' toolbar button in Visual Studio 2013 (installed on Windows 10 x64), and finally rebuild solution, everything is OK.
But, when repeat the same for second time, I take this error message:

Code generation for property 'StartLength' failed. Error was: ''StartLengthConverter' is unable to convert 'TestApp2.MyClasses.StartLength' to 'System.ComponentModel.Design.Serialization.InstanceDescriptor'.'

How:
I create a new C# Windows Form application (.Net framework 4) with VS2013 and named it TestApp2. Then I add two folders to my project. One is MyClasses and another is MyControls. Now, I add two classes (StartLength and StartLengthConverter) to MyClasses folder, and a Custom Control (MyTextBox) to MyControls folder. The source of these classes are below. Now I rebuild solution and appear MyTextBox custom control in Toolbox. Drag and drop it on Form1, save and rebuild project, then try to change the StartLength property in MyTextBox.

Note: This is the TestApp2.zip download link.

StartLength source:
C#
#region using
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
#endregion

namespace TestApp2.MyClasses
{
    [ComVisible(true)]
    [Serializable]
    [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")]
    [TypeConverter(typeof(StartLengthConverter))]
    public struct StartLength
    {

        public static readonly StartLength Empty = new StartLength();

        #region Properties

        #region Start
        public int Start
        {
            get
            {
                return _start;
            }
            set
            {
                _start = value;
            }
        }
        private int _start;
        #endregion

        #region Length
        public int Length
        {
            get
            {
                return _length;
            }
            set
            {
                _length = value;
            }
        }
        private int _length;
        #endregion

        #region IsEmpty
        [Browsable(false)]
        public bool IsEmpty
        {
            get
            {
                return (_start == 0) && (_length == 0);
            }
        }
        #endregion

        #endregion

        #region Methods

        #region StartLength
        public StartLength(int start, int length)
        {
            _start = start;
            _length = length;
        }
        #endregion

        #region Add
        public static StartLength Add(StartLength left, StartLength right)
        {
            return new StartLength(left.Start + right.Start, left.Length + right.Length);
        }
        #endregion

        #region Subtract
        public static StartLength Subtract(StartLength left, StartLength right)
        {
            return new StartLength(left.Start - right.Start, left.Length - right.Length);
        }
        #endregion

        #region operator +
        public static StartLength operator +(StartLength left, StartLength right)
        {
            return Add(left, right);
        }
        #endregion

        #region operator -
        public static StartLength operator -(StartLength left, StartLength right)
        {
            return Subtract(left, right);
        }
        #endregion

        #region operator ==
        public static bool operator ==(StartLength left, StartLength right)
        {
            return (left.Start == right.Start) && (left.Length == right.Length);
        }
        #endregion

        #region operator !=
        public static bool operator !=(StartLength left, StartLength right)
        {
            return !(left == right);
        }
        #endregion

        #region ToString
        public override string ToString()
        {
            return "{Start=" + _start.ToString(CultureInfo.CurrentCulture) + ", Length=" + _length.ToString(CultureInfo.CurrentCulture) + "}";
        }
        #endregion

        #region Equals
        public override bool Equals(object obj)
        {
            if (obj is StartLength)
            {
                StartLength startLength = (StartLength)obj;

                return (_start == startLength.Start) && (_length == startLength.Length);
            }

            return false;
        }
        #endregion

        #region GetHashCode
        public override int GetHashCode()
        {
            return _start ^ _length;
        }
        #endregion

        #endregion

    }
}


StartLengthConverter source:
C#
#region using
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
#endregion

namespace TestApp2.MyClasses
{
    public class StartLengthConverter : TypeConverter
    {

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

            return base.CanConvertFrom(context, sourceType);
        }
        #endregion
        
        #region CanConvertTo
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(InstanceDescriptor))
                return true;

            return base.CanConvertTo(context, destinationType);
        }
        #endregion

        #region ConvertFrom
        [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            string strValue = value as string;

            if (strValue != null)
            {
                string text = strValue.Trim();

                if (text.Length == 0)
                    return null;

                if (culture == null)
                    culture = CultureInfo.CurrentCulture;

                char sep = culture.TextInfo.ListSeparator[0];
                string[] tokens = text.Split(new char[] { sep });
                int[] values = new int[tokens.Length];
                TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int));

                for (int i = 0; i < values.Length; i++)
                    values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]);

                if (values.Length == 2)
                    return new StartLength(values[0], values[1]);

                throw new ArgumentException("The format error. It must be 'Start,Length'.");
            }

            return base.ConvertFrom(context, culture, value);
        }
        #endregion

        #region ConvertTo
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == null)
                throw new ArgumentNullException("destinationType");
            
            if (value is StartLength)
            {
                if (destinationType == typeof(string))
                {
                    if (culture == null)
                        culture = CultureInfo.CurrentCulture;

                    StartLength startLength = (StartLength)value;
                    string sep = culture.TextInfo.ListSeparator + " ";
                    TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int));
                    string[] args = new string[2];
                    int nArg = 0;

                    args[nArg++] = intConverter.ConvertToString(context, culture, startLength.Start);
                    args[nArg++] = intConverter.ConvertToString(context, culture, startLength.Length);

                    return string.Join(sep, args);
                }

                if (destinationType == typeof(InstanceDescriptor))
                {
                    StartLength startLength = (StartLength)value;
                    ConstructorInfo ctor = typeof(StartLength).GetConstructor(new Type[] { typeof(int), typeof(int) });

                    if (ctor != null)
                        return new InstanceDescriptor(ctor, new object[] { startLength.Start, startLength.Length });
                }
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
        #endregion

        #region CreateInstance
        [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
        [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
        public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
        {
            if (propertyValues == null)
                throw new ArgumentNullException("propertyValues");

            object start = propertyValues["Start"];
            object length = propertyValues["Length"];

            if ((start == null) || (length == null) || (!(start is int)) || (!(length is int)))
                throw new ArgumentException("Invalid propertyValues entry.");
            
            return new StartLength((int)start, (int)length);
        }
        #endregion

        #region GetCreateInstanceSupported
        public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        #endregion

        #region GetProperties
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(StartLength), attributes);
            
            return props.Sort(new string[] { "Start", "Length" });
        }
        #endregion

        #region GetPropertiesSupported
        public override bool GetPropertiesSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        #endregion

        #endregion

    }
}


MyTextBox source:
C#
#region using
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using TestApp2.MyClasses;
#endregion

namespace TestApp2.MyControls
{
    [DefaultEvent("TextChanged")]
    [DefaultProperty("Text")]
    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(TextBox))]
    public partial class MyTextBox : TextBox
    {

        #region Properties

        #region StartLength
        [Browsable(true)]
        [Category("My")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public virtual StartLength StartLength
        {
            get
            {
                return _startLength;
            }
            set
            {
                _startLength = value;
            }
        }
        private StartLength _startLength = new StartLength(0, 0);
        #region DefaultStartLength
        private StartLength DefaultStartLength
        {
            get
            {
                return new StartLength(0, 0);
            }
        }
        #endregion
        #region ResetStartLength
        private void ResetStartLength()
        {
            this.StartLength = DefaultStartLength;
        }
        #endregion
        #region ShouldSerializeStartLength
        private bool ShouldSerializeStartLength()
        {
            return !(this.StartLength.Equals(DefaultStartLength));
        }
        #endregion
        #endregion

        #endregion

        #region Methods

        #region MyTextBox
        public MyTextBox()
        {
            InitializeComponent();
        }
        #endregion

        #endregion

    }
}


What I have tried:

Make a custom property like the 'Size' property behaviors.
Posted
Updated 11-Sep-16 3:12am
v4

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900