< Summary - PropertyGridHelpers Code Coverage

Information
Class: PropertyGridHelpers.UIEditors.ResourcePathEditor
Assembly: PropertyGridHelpers
File(s): c:\agent\_work\9\s\Code\PropertyGridHelpers\UIEditors\ResourcePathEditor.cs
Tag: PropertyGridHelpers Build_2025.7.15.1_#485
Line coverage
100%
Covered lines: 58
Uncovered lines: 0
Coverable lines: 58
Total lines: 196
Line coverage: 100%
Branch coverage
100%
Covered branches: 28
Total branches: 28
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

MethodBlocks covered Blocks not covered Branch coverage Crap Score Cyclomatic complexity Line coverage
ResourcePathEditor()30----
.ctor()--100%11100%
ResourcePathEditor(...)20----
.ctor(...)--100%11100%
EditValue(...)400----
EditValue(...)--100%1818100%
EditValue(...)340----
GetEditStyle(...)10100%11100%
GetPaintValueSupported(...)10100%11100%
get_IsDropDownResizable()--100%11100%
CreateListBox(...)420----
CreateListBox(...)--100%1010100%
CreateListBox(...)370----

File(s)

c:\agent\_work\9\s\Code\PropertyGridHelpers\UIEditors\ResourcePathEditor.cs

#LineLine coverage
 1using PropertyGridHelpers.Attributes;
 2using PropertyGridHelpers.Converters;
 3using PropertyGridHelpers.Support;
 4using System;
 5using System.Collections.Generic;
 6using System.ComponentModel;
 7using System.Drawing.Design;
 8using System.Windows.Forms;
 9using System.Windows.Forms.Design;
 10
 11namespace PropertyGridHelpers.UIEditors
 12{
 13    /// <summary>
 14    /// Provides a <see cref="UITypeEditor"/> that enables selection of embedded resource paths
 15    /// from a dropdown list in the PropertyGrid.
 16    /// </summary>
 17    /// <remarks>
 18    /// This editor dynamically retrieves available resource paths from the owning assembly and
 19    /// presents them as selectable values. It is commonly used in conjunction with the
 20    /// <see cref="OnlySelectableTypeConverter"/> to restrict editing to valid, predefined options
 21    /// and to prevent users from manually typing arbitrary values.
 22    /// </remarks>
 23    /// <example>
 24    /// <code language="csharp">
 25    /// [AllowBlank(includeItem: true, resourceItem: "Blank_ResourcePath")]
 26    /// [LocalizedCategory("Category_TestItems")]
 27    /// [LocalizedDescription("Description_ResourcePath")]
 28    /// [LocalizedDisplayName("DisplayName_ResourcePath")]
 29    /// [Editor(typeof(ResourcePathEditor), typeof(UITypeEditor))]
 30    /// [TypeConverter(typeof(OnlySelectableTypeConverter))]
 31    /// public string ResourcePath { get; set; }
 32    /// </code>
 33    /// </example>
 34    /// <seealso cref="UITypeEditor"/>
 35    /// <seealso cref="OnlySelectableTypeConverter"/>
 36    /// <seealso cref="AllowBlankAttribute"/>
 37    public class ResourcePathEditor : UITypeEditor
 38    {
 39        private readonly IResourceBaseNameExtractor _extractor;
 40
 41        /// <summary>
 42        /// Initializes a new instance of the <see cref="ResourcePathEditor"/> class,
 43        /// using the appropriate resource base name extractor for the current target framework.
 44        /// </summary>
 4845        public ResourcePathEditor() : this(
 4846#if NET8_0_OR_GREATER
 4847            new RangeBasedBaseNameExtractor()
 4848#else
 4849            new SubstringBasedBaseNameExtractor()
 4850#endif
 4851        )
 4052        {
 4853        }
 54
 55        /// <summary>
 56        /// Initializes a new instance of the <see cref="ResourcePathEditor"/> class with a custom
 57        /// resource base name extractor.
 58        /// </summary>
 59        /// <param name="extractor">
 60        /// The extractor used to identify resource base names in the assembly. Must not be <c>null</c>.
 61        /// </param>
 62        /// <exception cref="ArgumentNullException">
 63        /// Thrown if <paramref name="extractor"/> is <c>null</c>.
 64        /// </exception>
 4865        private ResourcePathEditor(IResourceBaseNameExtractor extractor) =>
 4866            _extractor = extractor;
 67
 68        /// <summary>
 69        /// Edits the specified value using a dropdown list of resource base names.
 70        /// </summary>
 71        /// <param name="context">
 72        /// The editing context, typically provided by the PropertyGrid. May be <c>null</c>.
 73        /// </param>
 74        /// <param name="provider">
 75        /// A service provider that can supply an <see cref="IWindowsFormsEditorService"/> for managing
 76        /// the dropdown UI. May be <c>null</c>.
 77        /// </param>
 78        /// <param name="value">
 79        /// The current property value. May be updated by the user’s selection.
 80        /// </param>
 81        /// <returns>
 82        /// The edited value, or the original value if editing was canceled or no selection was made.
 83        /// </returns>
 84        public override object EditValue(
 85            ITypeDescriptorContext context,
 86            IServiceProvider provider,
 87            object value)
 2888        {
 3689            var newValue = value;
 3690            if (context != null &&
 3691                context.Instance != null &&
 3692                provider != null)
 2493            {
 3294                if ((provider.GetService(typeof(IWindowsFormsEditorService)) is IWindowsFormsEditorService edSvc))
 2495                {
 3296                    var assembly = context.Instance.GetType().Assembly;
 97
 98                    // Get the embedded resource names
 3299                    var baseNames = _extractor.ExtractBaseNames(assembly.GetName().Name, assembly.GetManifestResourceNam
 100
 32101                    if (baseNames.Count > 0)
 24102                    {
 103                        // Build dropdown
 32104                        var allowBlank = AllowBlankAttribute.IsBlankAllowed(context);
 32105                        var blankLabel = allowBlank ? AllowBlankAttribute.GetBlankLabel(context) : String.Empty;
 32106                        var ResourceListBox = CreateListBox(baseNames, allowBlank, blankLabel, newValue);
 107
 32108                        ResourceListBox.SelectedIndexChanged += (s, e) => edSvc.CloseDropDown();
 109
 32110                        edSvc.DropDownControl(ResourceListBox);
 111
 32112                        if (ResourceListBox.SelectedItem is string selectedItem)
 32113                            newValue = allowBlank && string.Equals(selectedItem, blankLabel, StringComparison.Ordinal) ?
 24114                    }
 24115                }
 24116            }
 117
 36118            return newValue;
 28119        }
 120
 121        /// <summary>
 122        /// Returns the editing style of the editor, which is <see cref="UITypeEditorEditStyle.DropDown"/>.
 123        /// </summary>
 124        /// <param name="context">The editing context.</param>
 125        /// <returns>
 126        /// Always <see cref="UITypeEditorEditStyle.DropDown"/>.
 127        /// </returns>
 128        public override UITypeEditorEditStyle GetEditStyle(
 20129            ITypeDescriptorContext context) => UITypeEditorEditStyle.DropDown;
 130
 131        /// <summary>
 132        /// Indicates whether this editor supports painting a visual representation of a value.
 133        /// </summary>
 134        /// <param name="context">The editing context.</param>
 135        /// <returns>
 136        /// Always <c>false</c>, since this editor does not support paint previews.
 137        /// </returns>
 138        public override bool GetPaintValueSupported(
 20139            ITypeDescriptorContext context) => false;
 140
 141        /// <summary>
 142        /// Gets a value indicating whether the dropdown UI is resizable.
 143        /// </summary>
 144        /// <value>
 145        /// Always <c>false</c>.
 146        /// </value>
 20147        public override bool IsDropDownResizable => false;
 148
 149        /// <summary>
 150        /// Creates the list box control that displays resource base names for selection.
 151        /// </summary>
 152        /// <param name="baseNames">
 153        /// The list of resource base names to display.
 154        /// </param>
 155        /// <param name="allowBlank">
 156        /// If <c>true</c>, includes a blank option at the top of the list.
 157        /// </param>
 158        /// <param name="blankLabel">
 159        /// The label text to use for the blank selection.
 160        /// </param>
 161        /// <param name="value">
 162        /// The currently selected value, which will be preselected in the list if present.
 163        /// </param>
 164        /// <returns>
 165        /// A configured <see cref="ListBox"/> control containing the resource names.
 166        /// </returns>
 167        private static ListBox CreateListBox(
 168            IList<string> baseNames,
 169            bool allowBlank,
 170            string blankLabel,
 171            object value)
 24172        {
 32173            var listBox = new ListBox
 32174            {
 32175                BorderStyle = BorderStyle.None,
 32176                SelectionMode = SelectionMode.One,
 32177                IntegralHeight = false,
 32178                Height = Math.Min(200, baseNames.Count * 16)
 32179            };
 180
 32181            listBox.BeginUpdate();
 32182            if (allowBlank)
 28183                _ = listBox.Items.Add(blankLabel);
 160184            foreach (var name in baseNames)
 64185                _ = listBox.Items.Add(name);
 186
 32187            if (value is string selected && listBox.Items.Contains(selected))
 24188                listBox.SelectedItem = selected;
 24189            else if (listBox.Items.Count > 0)
 24190                listBox.SelectedIndex = 0;
 191
 32192            listBox.EndUpdate();
 32193            return listBox;
 24194        }
 195    }
 196}