I'm trying to set up a way to configure a database connection. I found a simple property grid on GitHub, added it to my project, and bound a DbConnectionStringBuilder
descendant to it, and it immediately broke. It found the names and types of all the properties, but it appears to not actually be linked to the object instance, so all the properties show null values and attempting to edit them causes various problems.
After filing a bug report with the developer got me nowhere, I tried about half a dozen other property grids, both free and (demo versions of) commercial offerings, and every last one of them had the same problem! Some connection string builders work just fine, others break, sometimes it's different for different grids, but none of them binds correctly to the entire set of 5 I'm using, for SQL Server, Postgres, Firebird, MySQL, and a very simple test case class I developed just to repro this. (In fact, my simple test case for connecting to a "CSV database" was the only one that broke all of them!)
Is there something particularly weird about DbConnectionStringBuilder
that WPF data binding is allergic to?
Repro case if anyone wants to try it. CSV database configuration class:
using System;
using System.ComponentModel;
using System.Data.Common;
namespace Repro
{
public class CsvConfigurator : DbConnectionStringBuilder
{
public CsvConfigurator() { }
public CsvConfigurator(string conf)
{
ConnectionString = conf;
}
public string Delimiter
{
get => GetString(nameof(Delimiter));
set => this[nameof(Delimiter)] = value;
}
public bool AutoDetectDelimiter
{
get => GetBool(nameof(AutoDetectDelimiter));
set => this[nameof(AutoDetectDelimiter)] = value;
}
public bool UsesHeader
{
get => GetBool(nameof(UsesHeader));
set => this[nameof(UsesHeader)] = value;
}
public bool UsesQuotes
{
get => GetBool(nameof(UsesQuotes));
set => this[nameof(UsesQuotes)] = value;
}
public char QuoteChar
{
get => GetChar(nameof(QuoteChar), '"');
set => this[nameof(QuoteChar)] = value;
}
public char EscapeChar
{
get => GetChar(nameof(EscapeChar), '\\');
set => this[nameof(EscapeChar)] = value;
}
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];
}
}
}
- Create a project with a property grid -- any property grid -- on it.
- Instantiate the above class.
- Bind it to the grid as the object to be inspected.
- Put breakpoints on the getters and setters so you can see when the data binding is actually happening.
- Watch everything not actually be bound. (Look at the checkbox controls and how they're in a null state despite the properties in question not being
bool?
type.) - Do some editing.
- Watch the breakpoints not get hit.