wpftoolkit icon indicating copy to clipboard operation
wpftoolkit copied to clipboard

PropertyGrid binding fails for many DbConnectionStringBuilder descendants

Open masonwheeler opened this issue 2 years ago • 1 comments

Consider the following test class, for configuring a hypothetical CSV-based database:

using System.ComponentModel;

namespace MyProject
{
    public class CsvConfigurator : DbConnectionStringBuilder, INotifyPropertyChanged
    {
        public CsvConfigurator() { }

        public CsvConfigurator(string conf) : this()
        {
            ConnectionString = conf;
        }

        [Category("CSV")]
		[Description("The character used to separate CSV fields")]
        [DefaultValue(',')]
		public char Delimiter
        {
            get => GetChar(nameof(Delimiter), ',');
            set => this[nameof(Delimiter)] = value;
        }

        [Category("CSV")]
		[DisplayName("Auto-detect delimiter")]
		[Description("If true, analyze the file to discover its delimiter rather than using the Delimiter property")]
        [DefaultValue(true)]
		public bool AutoDetectDelimiter
        {
            get => GetBool(nameof(AutoDetectDelimiter));
            set => this[nameof(AutoDetectDelimiter)] = value;
        }

        [Category("CSV")]
        [Description("If true, treat the first line of the file as a header")]
        [DefaultValue(false)]
        public bool UsesHeader
        {
            get => GetBool(nameof(UsesHeader));
            set => this[nameof(UsesHeader)] = value;
        }

        [Category("CSV")]
        [Description("If true, check columns for quoted data")]
        [DefaultValue(true)]
        public bool UsesQuotes
        {
            get => GetBool(nameof(UsesQuotes));
            set => this[nameof(UsesQuotes)] = value;
        }

        [Category("CSV")]
        [DisplayName("Quote character")]
        [Description("The character used to quote an entire field")]
        [DefaultValue('"')]
        public char QuoteChar
        {
            get => GetChar(nameof(QuoteChar), '"');
            set => this[nameof(QuoteChar)] = value;
        }

        [Category("CSV")]
        [DisplayName("Escape character")]
        [Description("The character used to escape quote marks inside a quote")]
        [DefaultValue('\\')]
        public char EscapeChar
        {
            get => GetChar(nameof(EscapeChar), '\\');
            set => this[nameof(EscapeChar)] = value;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public override object this[string key]
        {
            get => base[key];
            set {
                TryGetValue(key, out var existing);
                if (existing == null) {
                    if (value == null) {
                        return;
                    }
                }
                else if (existing.Equals(value)) {
                    return;
                }
                base[key] = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(key));
            }
        }

        protected string GetString(string key) => TryGetValue(key, out var value) ? (string)value : null;
        protected bool GetBool(string key) => TryGetValue(key, out var value) ? Convert.ToBoolean(value) : false;
        
        protected char GetChar(string key, char defaultValue)
        {
            var result = GetString(key);
            return string.IsNullOrEmpty(result) ? defaultValue : result[0];
        }
    }
}

When trying to edit an instance of this class in PropertyGrid, it fails to bind to the object's properties:

image

The same glitch can be seen for the connection string builders for Firebird and MySql:

image

image

For whatever reason, the ones for MS Sql Server and Postgres bind correctly:

image

image

Looking through the source, it appears that PropertyGrid has no knowledge of ICustomTypeDescriptor, which the DbConnectionStringBuilder class (as well as several other things in .NET) uses for the express purpose of providing an interface to its properties for GUI editors such as this one. That might be part of the problem.

masonwheeler avatar Sep 25 '21 19:09 masonwheeler

On a second look, it looks like there is some support for ICustomTypeDescriptor in other files that are used by PropertyGrid. It retrieves the PropertyDescriptors correctly from the custom type descriptor, but I can't see any place where it calls GetValue or SetValue to actually interact with the properties in the prescribed manner.

masonwheeler avatar Oct 02 '21 13:10 masonwheeler