< Summary - PropertyGridHelpers Code Coverage

Information
Class: PropertyGridHelpers.Converters.TypeConverter<T>
Assembly: PropertyGridHelpers
File(s): c:\agent\_work\9\s\Code\PropertyGridHelpers\Converters\TypeConverter.cs
Tag: PropertyGridHelpers Build_2025.7.15.1_#485
Line coverage
100%
Covered lines: 23
Uncovered lines: 0
Coverable lines: 23
Total lines: 162
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

File(s)

c:\agent\_work\9\s\Code\PropertyGridHelpers\Converters\TypeConverter.cs

#LineLine coverage
 1using System;
 2using System.ComponentModel;
 3using System.Globalization;
 4using System.Reflection;
 5using System.Windows.Forms;
 6
 7namespace PropertyGridHelpers.Converters
 8{
 9    /// <summary>
 10    /// A generic <see cref="ExpandableObjectConverter"/> that enables editing of
 11    /// complex types with nested properties directly in a <see cref="PropertyGrid"/>.
 12    /// </summary>
 13    /// <typeparam name="T">
 14    /// The type to expand in the property grid. This type should expose public
 15    /// properties to be displayed as editable sub-properties.
 16    /// </typeparam>
 17    /// <remarks>
 18    /// This converter supports:
 19    /// <list type="bullet">
 20    /// <item>Expanding nested objects in the property grid for inline editing.</item>
 21    /// <item>Conversion of <typeparamref name="T"/> to and from string via <c>ToString()</c>
 22    /// and optional parsing if <typeparamref name="T"/> implements <c>IParsable&lt;T&gt;</c>.</item>
 23    /// <item>Compatibility with .NET 7+ <c>IParsable&lt;T&gt;</c> interface when present.</item>
 24    /// </list>
 25    /// </remarks>
 26    /// <example>
 27    /// <code>
 28    /// public class Size
 29    /// {
 30    ///     public int Width { get; set; }
 31    ///     public int Height { get; set; }
 32    /// }
 33    ///
 34    /// [TypeConverter(typeof(TypeConverter&lt;Size&gt;))]
 35    /// public Size MySize { get; set; }
 36    /// </code>
 37    /// This will allow <c>MySize</c> to expand in a property grid with separate
 38    /// editors for <c>Width</c> and <c>Height</c>.
 39    /// </example>
 40    public partial class TypeConverter<T> : ExpandableObjectConverter
 41    {
 42        /// <inheritdoc/>
 43        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) =>
 1644            destinationType == typeof(T) || destinationType == typeof(string) || base.CanConvertTo(context, destinationT
 45
 46        /// <summary>
 47        /// Converts an object of type <typeparamref name="T"/> to the requested destination type.
 48        /// </summary>
 49        /// <param name="context">Context information from the property grid.</param>
 50        /// <param name="culture">The culture to use during conversion.</param>
 51        /// <param name="value">The current value to convert.</param>
 52        /// <param name="destinationType">The type to convert to (typically string).</param>
 53        /// <returns>
 54        /// A string representation of the value if <paramref name="destinationType"/> is <c>string</c>;
 55        /// otherwise defers to the base implementation.
 56        /// </returns>
 57        public override object ConvertTo(
 58            ITypeDescriptorContext context,
 59            CultureInfo culture,
 60            object value,
 61            Type destinationType) =>
 1662                destinationType == typeof(string) &&
 1663                value is T t
 1664                ? t.ToString()
 1665                : base.ConvertTo(context, culture, value, destinationType);
 66
 67        /// <inheritdoc/>
 68        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
 869        {
 1670            if (sourceType == typeof(string))
 871            {
 1672                var tType = typeof(T);
 73
 74#if NET7_0_OR_GREATER
 75                try
 76                {
 77                    var iParsableType = typeof(System.IParsable<>).MakeGenericType(tType);
 78                    return iParsableType.IsAssignableFrom(tType);
 79                }
 80                catch (ArgumentException)
 81                {
 82                    // If the type does not implement IParsable<T>, we will catch the exception
 83                    // and fall back to the custom IParsable<T> interface.
 84                }
 85#endif
 1686                return typeof(IParsable<T>).IsAssignableFrom(tType);
 87            }
 88
 1689            return base.CanConvertFrom(context, sourceType);
 890        }
 91
 92        /// <summary>
 93        /// Converts from a string to an instance of <typeparamref name="T" />, if supported.
 94        /// </summary>
 95        /// <param name="context">Context information from the property grid.</param>
 96        /// <param name="culture">Culture info to use for parsing.</param>
 97        /// <param name="value">The string to convert from.</param>
 98        /// <returns>
 99        /// An instance of <typeparamref name="T" /> if conversion is successful;
 100        /// otherwise defers to the base implementation.
 101        /// </returns>
 102        /// <remarks>
 103        /// Supports parsing if:
 104        /// <list type="bullet">
 105        /// <item><typeparamref name="T" /> implements .NET 7+ <c>IParsable&lt;T&gt;</c></item>
 106        /// <item>or the PropertyGridHelpers' custom <see cref="IParsable{T}" /> interface</item>
 107        /// </list>
 108        /// </remarks>
 109        public override object ConvertFrom(
 110            ITypeDescriptorContext context,
 111            CultureInfo culture,
 112            object value)
 8113        {
 16114            if (value is string s)
 8115            {
 16116                var tType = typeof(T);
 117
 118#if NET7_0_OR_GREATER
 119                // Attempts to locate a static Parse method if the type supports System.IParsable<T>.
 120                MethodInfo parseMethod = null;
 121
 122                try
 123                {
 124                    var iParsableType = typeof(System.IParsable<>).MakeGenericType(tType);
 125                    if (iParsableType.IsAssignableFrom(tType))
 126                    {
 127                        parseMethod = tType.GetMethod("Parse", [typeof(string), typeof(IFormatProvider)]);
 128                    }
 129                }
 130                catch (ArgumentException)
 131                {
 132                    // If the type does not implement IParsable<T>, we will catch the exception
 133                    // and fall back to the custom IParsable<T> interface.
 134                }
 135
 136                if (parseMethod != null)
 137                {
 138                    try
 139                    {
 140                        // let this throw if parsing fails
 141                        return parseMethod.Invoke(null, [s, culture]);
 142                    }
 143                    catch (TargetInvocationException tie) when (tie.InnerException != null)
 144                    {
 145                        // Rethrow the real exception from inside Parse
 146                        throw tie.InnerException;
 147                    }
 148                }
 149#endif
 150
 151                // Attempts to locate a static Parse method if the type supports IParsable<T>.
 16152                if (typeof(IParsable<T>).IsAssignableFrom(tType))
 8153                {
 16154                    var instance = Activator.CreateInstance<T>();
 16155                    return ((IParsable<T>)instance).Parse(s, culture);
 156                }
 8157            }
 158
 16159            return base.ConvertFrom(context, culture, value);
 8160        }
 161    }
 162}