< 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: 47
Uncovered lines: 0
Coverable lines: 47
Total lines: 162
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
CanConvertTo(...)100%44100%
ConvertTo(...)100%44100%
CanConvertFrom(...)100%22100%
ConvertFrom(...)100%88100%

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) =>
 844            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)
 1269        {
 1270            if (sourceType == typeof(string))
 871            {
 872                var tType = typeof(T);
 73
 74#if NET7_0_OR_GREATER
 75                try
 876                {
 877                    var iParsableType = typeof(System.IParsable<>).MakeGenericType(tType);
 478                    return iParsableType.IsAssignableFrom(tType);
 79                }
 480                catch (ArgumentException)
 481                {
 82                    // If the type does not implement IParsable<T>, we will catch the exception
 83                    // and fall back to the custom IParsable<T> interface.
 484                }
 85#endif
 486                return typeof(IParsable<T>).IsAssignableFrom(tType);
 87            }
 88
 489            return base.CanConvertFrom(context, sourceType);
 1290        }
 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)
 32113        {
 32114            if (value is string s)
 24115            {
 24116                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>.
 24120                MethodInfo parseMethod = null;
 121
 122                try
 24123                {
 24124                    var iParsableType = typeof(System.IParsable<>).MakeGenericType(tType);
 8125                    if (iParsableType.IsAssignableFrom(tType))
 8126                    {
 8127                        parseMethod = tType.GetMethod("Parse", [typeof(string), typeof(IFormatProvider)]);
 8128                    }
 8129                }
 16130                catch (ArgumentException)
 16131                {
 132                    // If the type does not implement IParsable<T>, we will catch the exception
 133                    // and fall back to the custom IParsable<T> interface.
 16134                }
 135
 24136                if (parseMethod != null)
 8137                {
 138                    try
 8139                    {
 140                        // let this throw if parsing fails
 8141                        return parseMethod.Invoke(null, [s, culture]);
 142                    }
 4143                    catch (TargetInvocationException tie) when (tie.InnerException != null)
 4144                    {
 145                        // Rethrow the real exception from inside Parse
 4146                        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))
 12153                {
 12154                    var instance = Activator.CreateInstance<T>();
 8155                    return ((IParsable<T>)instance).Parse(s, culture);
 156                }
 4157            }
 158
 12159            return base.ConvertFrom(context, culture, value);
 8160        }
 161    }
 162}